【问题标题】:String "true" and "false" to boolean将字符串“true”和“false”转为布尔值
【发布时间】:2011-12-28 12:44:15
【问题描述】:

我有一个 Rails 应用程序,我正在使用 jQuery 在后台查询我的搜索视图。有字段q(搜索词)、start_dateend_dateinternalinternal 字段是一个复选框,我正在使用 is(:checked) 方法构建被查询的 url:

$.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'ish 的方式来处理这个问题!那里没有……?

【问题讨论】:

    标签: jquery ruby-on-rails ruby ruby-on-rails-3


    【解决方案1】:

    据我所知,没有内置的将字符串转换为布尔值的方法, 但如果您的字符串仅由 'true''false' 组成,您可以将您的方法缩短为以下内容:

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

    【讨论】:

    • 稍作修改 str == 'true' || str = '1'
    • 也许 str.downcase == 'true' 为了完整性
    • @AMTourky 不应该是 str == 'true' || str == '1' 带有两个 "==" ?
    • @Lowryder 是的!如果使用默认复选框。
    【解决方案2】:

    ActiveRecord 提供了一种干净的方式来执行此操作。

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

    ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES 将所有明显的 True 值表示为字符串。

    【讨论】:

    • 更简单,只需使用ActiveRecord::ConnectionAdapters::Column.value_to_boolean(string)(来源)apidock.com/rails/v3.0.9/ActiveRecord/ConnectionAdapters/Column/…
    • 是的,在最新版本中!
    • ActiveRecord::Type::Boolean.new.type_cast_from_user("true") => 真 ActiveRecord::Type::Boolean.new.type_cast_from_user("T") => 真
    • 假值列表已移至 Rails 5 中的 ActiveModel::Type::Boolean
    • ActiveModel::Type::Boolean 似乎是一条更合适的路径 - 虽然 ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES 包含“真实”值,但有人可能会争辩说这只是偶然的情况,并且对于该特定值应该被视为真实的值可以包括用例,但不能包括其他用例。另一方面,ActiveModel::Type::Boolean 显然被设计为以通用方式使用。
    【解决方案3】:

    安全通知

    请注意,此答案仅适用于下面列出的其他用例,而不适用于问题中的用例。虽然大部分是固定的,但有许多 YAML related security vulnerabilities 是由将用户输入加载为 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.etc.something = ENV['ETC_SOMETHING']
    

    在命令行中:

    $ export ETC_SOMETHING=false
    

    现在由于ENV vars 是代码中的字符串,config.etc.something 的值将是字符串"false",它会错误地计算为true。但如果你这样做:

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

    一切都会好的。这也与从 .yml 文件加载配置兼容。

    【讨论】:

    • 如果传递的字符串在您的控制之下,那就太好了。在这个问题的情况下,提供的值来自用户的浏览器,因此,它们应该被认为是不安全的。 YAML 允许您序列化/反序列化任何 Ruby 对象,这有潜在的危险。发生了很多事件:google.com/webhp?q=rails+yaml+vulnerability
    • @Teoulas,我完全同意你的看法。事实上,我正在添加一个通知,这样人们就不会以不安全的方式使用它。
    【解决方案4】:

    没有任何内置的方法来处理这个问题(尽管 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
    

    【讨论】:

    • 如果你使用 "!!(s =~ /regex_here/)",则不需要保护 "s and ...",因为 "nil =~ /anything/" 返回 nil。
    • 确实如此。我添加了它,但也保留了旧的,因为我认为.match 更容易阅读。
    【解决方案5】:

    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
    

    【讨论】:

    • 它是 Rails 5 中的 ActiveRecord::Type::Boolean.new.cast(value)(参见下面的 CWitty)
    【解决方案6】:

    您可以使用wannabe_bool gem。 https://github.com/prodis/wannabe_bool

    此 gem 为 String、Integer、Symbol 和 NilClass 类实现了 #to_b 方法。

    params[:internal].to_b
    

    【讨论】:

      【解决方案7】:

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

      【讨论】:

      • 请注意,ActiveRecord::Type::Boolean.new.cast("42") 返回 true
      【解决方案8】:

      为了完整起见,可能是str.to_s.downcase == 'true'。那么即使str 为 nil 或 0,也不会崩溃。

      【讨论】:

        【解决方案9】:

        我不认为 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

        【讨论】:

        • 我绝对不想让to_bool 函数返回nil;这似乎是错误的。其他转换函数不这样做:"a".to_i 返回0,而不是nil
        【解决方案10】:

        查看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
        

        【讨论】:

          【解决方案11】:

          您可以考虑仅将internal 附加到您的网址(如果它为真),那么如果未选中复选框并且您不附加它params[:internal] 将是nil,它在Ruby 中的计算结果为假。

          我对您使用的特定 jQuery 不是很熟悉,但是有没有比手动构建 URL 字符串更简洁的方法来调用您想要的内容?你看过$get$ajax吗?

          【讨论】:

            【解决方案12】:

            您可以添加到 String 类以具有 to_boolean 的方法。然后你可以做 'true'.to_boolean 或 '1'.to_boolean

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

            【讨论】:

              【解决方案13】:

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

              def to_boolean(str)
                  eval(str)
              end
              

              【讨论】:

              • 那是因为这个解决方案是一个安全灾难。 :D
              • 这个解决方案的问题是用户输入 - 如果有人输入 to_boolean("ActiveRecord::Base.connection.execute('DROP TABLE *')") ,它会破坏你的数据库(并返回真!)。玩得开心:D
              • 好点。我没有考虑上下文。我在想最少数量的字符来实现。 :)
              • 上述漏洞的简单修复:bool = nil; bool = eval(str) if ["true", "false"].include?(str) 只是想为了澄清起见我应该添加。
              猜你喜欢
              • 2011-04-27
              • 2017-01-08
              • 1970-01-01
              • 2020-03-08
              • 2021-11-28
              • 2020-06-02
              • 1970-01-01
              • 2016-04-05
              • 2018-03-10
              相关资源
              最近更新 更多