สตริง "จริง" และ "เท็จ" เป็นบูลีน


86

ฉันมีแอปพลิเคชัน Rails และฉันใช้ jQuery เพื่อค้นหามุมมองการค้นหาของฉันในพื้นหลัง มีสาขาอยู่q(คำค้นหา) start_date, และend_date ฟิลด์เป็นช่องทำเครื่องหมายและฉันใช้วิธีการในการสร้าง URL ที่ถูกสอบถาม:internalinternalis(:checked)

$.getScript(document.URL + "?q=" + $("#search_q").val() + "&start_date=" + $("#search_start_date").val() + "&end_date=" + $("#search_end_date").val() + "&internal=" + $("#search_internal").is(':checked'));

ตอนนี้ปัญหาของฉันเกิดขึ้นparams[:internal]เนื่องจากมีสตริงที่ประกอบด้วย "จริง" หรือ "เท็จ" และฉันจำเป็นต้องแคสต์เป็นบูลีน แน่นอนว่าฉันทำได้ดังนี้:

def to_boolean(str)
     return true if str=="true"
     return false if str=="false"
     return nil
end

แต่ฉันคิดว่าต้องมีวิธีที่ Ruby'ish มากกว่านี้ในการจัดการกับปัญหานี้! ไม่มี ... ?

คำตอบ:


134

เท่าที่ฉันรู้ไม่มีวิธีการสร้างสตริงให้เป็นบูลีน แต่ถ้าสตริงของคุณประกอบด้วยเพียงอย่างเดียว'true'และ'false'คุณสามารถย่อวิธีการให้เป็นดังต่อไปนี้:

def to_boolean(str)
  str == 'true'
end

8
การปรับเปลี่ยนเพียงเล็กน้อย str == 'true' || str = '1'
AMTourky

30
อาจจะ str.downcase == 'true' เพื่อความสมบูรณ์
JEMaddux

@AMTourky ไม่ควรเป็นstr == 'true' || str == '1'กับสอง "=="?
Pascal

@Lowryder ใช่! หากใช้ช่องทำเครื่องหมายเริ่มต้น
7urkm3 น.

49

ActiveRecord มอบวิธีการที่สะอาดในการดำเนินการนี้

def is_true?(string)
  ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(string)
end

ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES มีการแสดงค่า True ที่ชัดเจนทั้งหมดเป็นสตริง


16
ง่ายกว่าเพียงใช้ActiveRecord::ConnectionAdapters::Column.value_to_boolean(string)(ที่มา) apidock.com/rails/v3.0.9/ActiveRecord/ConnectionAdapters/Column/…
Mike Atlas

ใช่ในเวอร์ชันล่าสุด!
Satya Kalluri

6
ActiveRecord::Type::Boolean.new.type_cast_from_user("true")=> true ActiveRecord::Type::Boolean.new.type_cast_from_user("T")=> true
AlexChaffee

รายการค่าเท็จถูกย้ายไปที่ ActiveModel :: Type :: Boolean ใน Rails 5
DivideByZero

ActiveModel::Type::Booleanดูเหมือนจะเป็นเส้นทางที่เหมาะสมกว่ามาก - ในขณะที่ActiveRecord::ConnectionAdapters::Column::TRUE_VALUESมีค่า "จริง" แต่ก็สามารถโต้แย้งได้ว่ามันเป็นเพียงกรณีบังเอิญเท่านั้นและค่าที่ควรพิจารณาว่าเป็นจริงสำหรับกรณีการใช้งานเฉพาะนั้น แต่ไม่สามารถรวมค่าอื่น ๆ ได้ ในทางกลับกันActiveModel::Type::Booleanเห็นได้ชัดว่าได้รับการออกแบบมาเพื่อใช้ในลักษณะทั่วไป
Lyndsy Simon

24

ประกาศด้านความปลอดภัย

โปรดทราบว่าคำตอบนี้ในรูปแบบเปล่าเหมาะสำหรับกรณีการใช้งานอื่นที่ระบุไว้ด้านล่างเท่านั้นแทนที่จะเป็นคำตอบในคำถาม ในขณะที่ได้รับการแก้ไขส่วนใหญ่มีช่องโหว่ด้านความปลอดภัยที่เกี่ยวข้องกับ YAMLจำนวนมากซึ่งเกิดจากการโหลดอินพุตของผู้ใช้เป็น YAML


เคล็ดลับที่ฉันใช้ในการแปลงสตริงเป็นบูลคือYAML.load:

YAML.load(var) # -> true/false if it's one of the below

YAML boolยอมรับสตริงจริง / เท็จค่อนข้างมาก:

y|Y|yes|Yes|YES|n|N|no|No|NO
|true|True|TRUE|false|False|FALSE
|on|On|ON|off|Off|OFF

กรณีการใช้งานอื่น

สมมติว่าคุณมีโค้ด config ดังนี้:

config.etc.something = ENV['ETC_SOMETHING']

และในบรรทัดคำสั่ง:

$ export ETC_SOMETHING=false

ตั้งแต่ตอนนี้ENVvars สตริงเมื่อโค้ดภายในconfig.etc.something's ค่าจะเป็นสตริงและมันจะไม่ถูกต้องประเมิน"false" trueแต่ถ้าคุณทำเช่นนี้:

config.etc.something = YAML.load(ENV['ETC_SOMETHING'])

มันจะโอเคทั้งหมด เข้ากันได้กับการโหลดคอนฟิกจากไฟล์. yml เช่นกัน


1
เป็นการดีถ้าสตริงที่ส่งผ่านอยู่ภายใต้การควบคุมของคุณ ในกรณีของคำถามนี้ค่าที่ระบุมาจากเบราว์เซอร์ของผู้ใช้ดังนั้นจึงควรถือว่าไม่ปลอดภัย YAML ช่วยให้คุณสามารถซีเรียลไลซ์ / deserialize อ็อบเจ็กต์ Ruby ใด ๆ และอาจเป็นอันตรายได้ มีเหตุการณ์มากมาย: google.com/webhp?q=rails+yaml+vulnerability
Teoulas

1
@Teoulas ฉันเห็นด้วยกับคุณ อันที่จริงฉันกำลังเพิ่มประกาศเพื่อไม่ให้คนอื่นใช้สิ่งนี้ในทางที่ไม่ปลอดภัย
Halil Özgür

16

ไม่มีวิธีจัดการสิ่งนี้ในตัว (แม้ว่า actionpack อาจมีผู้ช่วยก็ตาม) ฉันจะแนะนำอะไรเช่นนี้

def to_boolean(s)
  s and !!s.match(/^(true|t|yes|y|1)$/i)
end

# or (as Pavling pointed out)

def to_boolean(s)
  !!(s =~ /^(true|t|yes|y|1)$/i)
end

สิ่งที่ใช้ได้ดีคือการใช้ 0 และไม่ใช่ 0 แทนตัวอักษรเท็จ / จริง:

def to_boolean(s)
  !s.to_i.zero?
end

3
คุณไม่จำเป็นต้องมียาม "s และ ... " ถ้าคุณใช้ "!! (s = ~ / regex_here /)" เพราะ "nil = ~ / anything /" ส่งกลับค่าศูนย์
Pavling

Ah แน่นอน ฉันเพิ่มเข้าไป แต่ยังคงความเก่าไว้เช่นกันเพราะฉันคิดว่า.matchอ่านง่ายกว่าเล็กน้อย
Marcel Jackwerth

7

ActiveRecord::Type::Boolean.new.type_cast_from_userทำสิ่งนี้ตามการแมปภายในของ Rails ConnectionAdapters::Column::TRUE_VALUESและConnectionAdapters::Column::FALSE_VALUES:

[3] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("true")
=> true
[4] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("false")
=> false
[5] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("T")
=> true
[6] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("F")
=> false
[7] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("yes")
DEPRECATION WARNING: You attempted to assign a value which is not explicitly `true` or `false` ("yes") to a boolean column. Currently this value casts to `false`. This will change to match Ruby's semantics, and will cast to `true` in Rails 5. If you would like to maintain the current behavior, you should explicitly handle the values you would like cast to `false`. (called from <main> at (pry):7)
=> false
[8] pry(main)> ActiveRecord::Type::Boolean.new.type_cast_from_user("no")
DEPRECATION WARNING: You attempted to assign a value which is not explicitly `true` or `false` ("no") to a boolean column. Currently this value casts to `false`. This will change to match Ruby's semantics, and will cast to `true` in Rails 5. If you would like to maintain the current behavior, you should explicitly handle the values you would like cast to `false`. (called from <main> at (pry):8)
=> false

ดังนั้นคุณสามารถสร้างเมธอดของคุณเองto_b(หรือto_boolหรือto_boolean) ในตัวเริ่มต้นเช่นนี้:

class String
  def to_b
    ActiveRecord::Type::Boolean.new.type_cast_from_user(self)
  end
end

2
เป็น ActiveRecord :: Type :: Boolean.new.cast (value) ใน Rails 5 (ดู CWitty ด้านล่าง)
Dave Burt


6

ใน Rails 5 คุณสามารถใช้ActiveRecord::Type::Boolean.new.cast(value)เพื่อส่งไปยังบูลีนได้


1
โปรดทราบว่า ActiveRecord :: Type :: Boolean.new.cast ("42") คืนค่า true
DivideByZero

3

ฉันไม่คิดอะไรแบบนั้นในตัว Ruby คุณสามารถเปิดคลาส String ขึ้นมาใหม่และเพิ่ม to_bool method ได้ที่นั่น:

class String
    def to_bool
        return true if self=="true"
        return false if self=="false"
        return nil
    end
end

จากนั้นคุณสามารถใช้งานได้ทุกที่ในโครงการของคุณดังนี้: params[:internal].to_bool


2
แน่นอนฉันไม่ต้องการให้to_boolฟังก์ชันกลับมาnil; ดูเหมือนจะผิด ฟังก์ชันการแปลงอื่น ๆ ไม่ทำสิ่งนี้: "a".to_iส่งคืน0ไม่ใช่nil
Krease

3

บางทีstr.to_s.downcase == 'true'เพื่อความสมบูรณ์ จากนั้นจะไม่มีสิ่งใดผิดพลาดแม้ว่าstrจะเป็นศูนย์หรือ 0 ก็ตาม


2

ดูซอร์สโค้ดของVirtusฉันอาจจะทำสิ่งนี้:

def to_boolean(s)
  map = Hash[%w[true yes 1].product([true]) + %w[false no 0].product([false])]
  map[s.to_s.downcase]
end

1

คุณอาจจะพิจารณาเฉพาะท้ายinternalURL ของคุณถ้ามันเป็นความจริงแล้วถ้าช่องไม่ได้ตรวจสอบและคุณไม่ผนวกparams[:internal]จะเป็นnilซึ่งประเมินเป็นเท็จในรูบี

ฉันไม่ค่อยคุ้นเคยกับ jQuery ที่คุณใช้ แต่มีวิธีที่สะอาดกว่าในการเรียกสิ่งที่คุณต้องการมากกว่าการสร้างสตริง URL ด้วยตนเองหรือไม่? คุณได้ดู$getและ$ajax?


1

คุณสามารถเพิ่มในคลาส String เพื่อให้มีเมธอด to_boolean จากนั้นคุณสามารถทำ 'true'.to_boolean หรือ' 1'.to_boolean

class String
  def to_boolean
    self == 'true' || self == '1'
  end
end

-5

ฉันแปลกใจที่ไม่มีใครโพสต์วิธีง่ายๆนี้ นั่นคือถ้าสตริงของคุณจะเป็น "จริง" หรือ "เท็จ"

def to_boolean(str)
    eval(str)
end

4
นั่นเป็นเพราะโซลูชันนี้เป็น Desaster ด้านความปลอดภัย : D
davidb

1
ปัญหาในการแก้ปัญหานี้คือการป้อนข้อมูลของผู้ใช้ - หากมีคนพิมพ์to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')")ก็จะทำลายฐานข้อมูลของคุณ (และส่งคืนจริง!) ขอให้สนุก: D
Ben Aubin

จุดดี ฉันไม่ได้คิดถึงบริบท ฉันกำลังคิดว่าจะใช้อักขระจำนวนน้อยที่สุด :)
povess

การแก้ไขที่ง่ายสำหรับช่องโหว่ดังกล่าว: bool = nil; bool = eval(str) if ["true", "false"].include?(str)เพียงแค่คิดว่าฉันควรเพิ่มเพื่อประโยชน์ในการชี้แจง
Fernando Cordeiro
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.