Chuỗi “true” và “false” thành boolean


85

Tôi có một ứng dụng Rails và tôi đang sử dụng jQuery để truy vấn chế độ xem tìm kiếm của tôi trong nền. Có các trường q(cụm từ tìm kiếm) start_date, end_dateinternal. Trường internalnày là một hộp kiểm và tôi đang sử dụng is(:checked)phương pháp để tạo url được truy vấn:

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

Bây giờ vấn đề của tôi là params[:internal]do có một chuỗi chứa "true" hoặc "false" và tôi cần truyền nó thành boolean. Tất nhiên tôi có thể làm như thế này:

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

Nhưng tôi nghĩ rằng phải có một cách tốt hơn Ruby để giải quyết vấn đề này! Không có ...?

Câu trả lời:


133

Theo như tôi biết thì không có cách nào được xây dựng để truyền chuỗi sang boolean, nhưng nếu chuỗi của bạn chỉ bao gồm 'true''false'bạn có thể rút ngắn phương thức của mình thành như sau:

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

8
chỉ một chút sửa đổi str == 'true' || str = '1'
AMTourky

30
có lẽ str.downcase == 'true' cho đầy đủ
JEMaddux

@AMTourky không nên là str == 'true' || str == '1' với hai "=="?
Pascal

@Lowryder Có! nếu sử dụng hộp kiểm mặc định.
7urkm3n

49

ActiveRecord cung cấp một cách làm rõ ràng để thực hiện việc này.

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

ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES có tất cả các đại diện rõ ràng của giá trị True dưới dạng chuỗi.


16
Đơn giản hơn nữa, chỉ cần sử dụng ActiveRecord::ConnectionAdapters::Column.value_to_boolean(string)(nguồn) apidock.com/rails/v3.0.9/ActiveRecord/ConnectionAdapters/Column/…
Mike Atlas

Có, trong các phiên bản mới nhất!
Satya Kalluri

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

Danh sách giá trị False đã được chuyển đến ActiveModel :: Loại :: Boolean trong Rails 5
divideByZero

ActiveModel::Type::Booleancó vẻ như là một con đường phù hợp hơn nhiều - trong khi ActiveRecord::ConnectionAdapters::Column::TRUE_VALUESchứa các giá trị "chân lý", người ta có thể lập luận rằng đó chỉ là trường hợp ngẫu nhiên và rằng các giá trị nên được coi là trung thực cho trường hợp sử dụng cụ thể đó chứ không thể đưa vào các giá trị khác. Mặt khác, ActiveModel::Type::Booleannó dường như được thiết kế để sử dụng một cách chung chung.
Lyndsy Simon

24

Thông báo bảo mật

Lưu ý rằng câu trả lời ở dạng trần chỉ thích hợp cho trường hợp sử dụng khác được liệt kê bên dưới hơn là câu trả lời trong câu hỏi. Mặc dù hầu hết đã được khắc phục, vẫn có rất nhiều lỗ hổng bảo mật liên quan đến YAML do tải đầu vào của người dùng dưới dạng YAML.


Một thủ thuật tôi sử dụng để chuyển đổi chuỗi thành bools YAML.load, ví dụ:

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

YAML bool chấp nhận khá nhiều chuỗi true / falsy:

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

Một trường hợp sử dụng khác

Giả sử rằng bạn có một đoạn mã cấu hình như sau:

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

Và trong dòng lệnh:

$ export ETC_SOMETHING=false

Bây giờ vì ENVvars là các chuỗi một khi bên trong mã, config.etc.somethinggiá trị của sẽ là chuỗi "false"và nó sẽ đánh giá sai thành true. Nhưng nếu bạn làm như thế này:

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

tất cả sẽ ổn thôi. Điều này cũng tương thích với việc tải cấu hình từ các tệp .yml.


1
Điều đó tốt nếu chuỗi được truyền qua nằm trong tầm kiểm soát của bạn. Trong trường hợp của câu hỏi này, các giá trị được cung cấp đến từ trình duyệt của người dùng và do đó, chúng nên được coi là không an toàn. YAML cho phép bạn tuần tự hóa / giải mã hóa bất kỳ đối tượng Ruby nào và điều này có khả năng nguy hiểm. Đã có nhiều sự cố xảy ra: google.com/webhp?q=rails+yaml+vulnerability
Teoulas

1
@Teoulas, tôi hoàn toàn đồng ý với bạn. Trên thực tế, tôi đang thêm một thông báo để mọi người không sử dụng thông báo này theo cách không an toàn.
Halil Özgür

16

Không có bất kỳ cách tích hợp nào để xử lý điều này (mặc dù actionpack có thể có một trình trợ giúp cho việc đó). Tôi sẽ khuyên một cái gì đó như thế này

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

Những gì hoạt động tốt là sử dụng 0 và khác 0 thay vì các ký tự sai / đúng:

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

3
bạn không cần bảo vệ "s và ..." nếu bạn sử dụng "!! (s = ~ / regex_here /)" bởi vì "nil = ~ / anything /" trả về nil.
Pavling

Đúng vậy. Tôi đã thêm nó vào nhưng vẫn giữ cái cũ, vì tôi nghĩ .matchnó dễ đọc hơn một chút.
Marcel Jackwerth,

7

ActiveRecord::Type::Boolean.new.type_cast_from_userthực hiện điều này theo ánh xạ nội bộ của Rails ConnectionAdapters::Column::TRUE_VALUESConnectionAdapters::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

Vì vậy, bạn có thể tạo phương thức to_b(hoặc to_boolhoặc to_boolean) của riêng mình trong một trình khởi tạo như sau:

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

2
Đó là ActiveRecord :: Loại :: Boolean.new.cast (giá trị) trong Rails 5 (xem CWitty dưới đây)
Dave Burt


6

Trong Rails 5, bạn có thể sử dụng ActiveRecord::Type::Boolean.new.cast(value)để truyền nó tới boolean.


1
được mặc dù đã biết, ActiveRecord :: Loại :: Boolean.new.cast ( "42") trả về true
divideByZero

3

Tôi không nghĩ rằng bất cứ thứ gì như vậy đều được tích hợp sẵn trong Ruby. Bạn có thể mở lại lớp String và thêm phương thức to_bool vào đó:

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

Sau đó, bạn có thể sử dụng nó ở bất kỳ đâu trong dự án của mình, như sau: params[:internal].to_bool


2
Tôi chắc chắn sẽ không muốn có một to_boolhàm trả về nil; điều đó có vẻ sai. Chức năng chuyển đổi khác không làm điều này: "a".to_ilợi nhuận 0, khôngnil
Krease

3

Có lẽ str.to_s.downcase == 'true'để hoàn chỉnh. Sau đó, không có gì có thể sụp đổ ngay cả khi strlà 0 hoặc 0.


2

Nhìn vào mã nguồn của Virtus , tôi có thể làm một cái gì đó như sau:

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

Bạn có thể chỉ xem xét việc thêm internalvào url của mình nếu nó là đúng, sau đó nếu hộp kiểm không được chọn và bạn không thêm vào thì nó params[:internal]sẽ được nilđánh giá là false trong Ruby.

Tôi không quen với jQuery cụ thể mà bạn đang sử dụng, nhưng có cách nào rõ ràng hơn để gọi những gì bạn muốn hơn là xây dựng một chuỗi URL theo cách thủ công không? Bạn đã xem qua $get$ajax?


1

Bạn có thể thêm vào lớp String để có phương thức to_boolean. Sau đó, bạn có thể làm 'true'.to_boolean hoặc' 1'.to_boolean

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

-5

Tôi ngạc nhiên là không có ai đăng giải pháp đơn giản này. Đó là nếu chuỗi của bạn là "true" hoặc "false".

def to_boolean(str)
    eval(str)
end

4
Đó là bởi vì giải pháp này là một Desaster an ninh. : D
davidb

1
Vấn đề với giải pháp này là đầu vào của người dùng - nếu ai đó nhập to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')"), nó sẽ phá hủy cơ sở dữ liệu của bạn (và trả về true!). Hãy vui vẻ: D
Bến Aubin

Điểm tốt. Tôi không nghĩ đến bối cảnh. Tôi đang nghĩ số lượng ký tự ít nhất để thực hiện. :)
povess

Một bản sửa lỗi dễ dàng cho lỗ hổng nói trên: bool = nil; bool = eval(str) if ["true", "false"].include?(str)Tôi chỉ nghĩ rằng tôi nên thêm vào để làm rõ.
Fernando Cordeiro
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.