字符串“ true”和“ false”为布尔值


86

我有一个Rails应用程序,并且正在使用jQuery在后台查询我的搜索视图。有字段q(搜索词)start_dateend_dateinternal。该internal字段是一个复选框,我正在使用该is(: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]因为有一个包含“ true”或“ false”的字符串,我需要将其强制转换为布尔值。我当然可以这样:

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

但是我认为必须有一种更Ruby的方式来解决这个问题!没有...吗?

Answers:


133

据我所知,没有内置的铸造字符串布尔值的方式,但如果你的字符串只包含'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'和两个“ ==”?
帕斯卡

@Lowryder是的!如果使用默认的check_box。
7urkm3n

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)2014年

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

虚假值列表已在Rails 5中移动到ActiveModel :: Type :: Boolean
DivingByZero

ActiveModel::Type::Boolean似乎是一条更合适的路径-虽然ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES包含“真实的”值,但有人可能会说只是偶然的情况,对于特定的用例,应该认为这些值是真实的,但不能包括其他值。另一方面,ActiveModel::Type::Boolean显然被设计为以通用方式使用。
Lyndsy Simon

24

安全须知

请注意,此答案以其裸露的形式仅适用于下面列出的其他用例,而不是问题中的用例。尽管大多数情况下是固定的,但由于将用户输入加载为YAML导致了许多与YAML相关的安全漏洞


我用于将字符串转换为bool的技巧是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.etc.something = ENV['ETC_SOMETHING']

并在命令行中:

$ export ETC_SOMETHING=false

现在,由于ENVvars曾经是代码config.etc.something中的字符串"false",因此其值将是字符串,并且错误地将其评估为true。但是,如果您这样做:

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

没关系。这也与从.yml文件加载配置兼容。


1
如果传递的字符串在您的控制之下,那很好。在此问题的情况下,提供的值来自用户的浏览器,因此,应将其视为不安全的。YAML允许您序列化/反序列化任何Ruby对象,这有潜在的危险。发生了很多事件:google.com/webhp?
q=rails+yaml+漏洞

1
@Teoulas,我完全同意你的看法。实际上,我要添加一条通知,以便人们不要以不安全的方式使用它。
HalilÖzgür15年

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代替false / true文字:

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

3
如果您使用“ !!(s =〜/ regex_here /)”,则不需要保护符“ s and ...”,因为“ nil =〜/ anything /”返回nil。
2011年

的确是啊。我添加了它,但也保留了旧的,因为我认为它.match更容易阅读。
Marcel Jackwerth 2011年

7

ActiveRecord::Type::Boolean.new.type_cast_from_user根据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

所以,你可以让你自己to_b(或to_boolto_boolean)方法在这样的初始化:

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

2
在Rails 5中是ActiveRecord :: Type :: Boolean.new.cast(value)(请参见下面的CWitty)
Dave Burt


6

在Rails 5中,可以使用ActiveRecord::Type::Boolean.new.cast(value)将其强制转换为布尔值。


1
请注意,ActiveRecord :: Type :: Boolean.new.cast(“ 42”)返回true
DividByZero

3

我不认为Ruby内置了类似的功能。您可以重新打开String类并在其中添加to_bool方法:

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'为了完整性。这样即使strnil或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

你可以只考虑追加internal到您的网址,如果这是真的,那么,如果未选中该复选框,你不追加它params[:internal]nil,它评估为false红宝石。

我对您使用的特定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

我很惊讶没有人发布这个简单的解决方案。也就是说,如果您的字符串将为“ true”或“ false”。

def to_boolean(str)
    eval(str)
end

4
那是因为此解决方案是一个安全灾难。:D
davidb

1
此解决方案的问题是用户输入-如果有人输入to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')"),它将破坏您的数据库(并返回true!)。玩得开心:D
Ben Aubin 2015年

好点。我没有考虑上下文。我在考虑最少要实施的字符。:)
19年

修复该漏洞的简单方法:bool = nil; bool = eval(str) if ["true", "false"].include?(str)只是为了说明我应该添加。
Fernando Cordeiro
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.