我正在提交show_all
带有值的参数true
。该值与模型无关。
我的控制器正在将此参数分配给实例变量:
@show_all = params[:show_all]
但是,@show_all.is_a? String
并且if @show_all == true
总是失败。
Rails将哪些值解析为布尔值?如何明确指定我的参数是布尔值而不是字符串?
我正在提交show_all
带有值的参数true
。该值与模型无关。
我的控制器正在将此参数分配给实例变量:
@show_all = params[:show_all]
但是,@show_all.is_a? String
并且if @show_all == true
总是失败。
Rails将哪些值解析为布尔值?如何明确指定我的参数是布尔值而不是字符串?
TrueClass
或FalseClass
。知道我如何才能做到这一点吗?
Answers:
我想对zetetic答案发表评论,但由于目前还无法完成,我将其发布为答案。
如果您使用
@show_all = params[:show_all] == "1"
那么您可以删除,? true : false
因为params[:show_all] == "1"
语句本身将评估为true或false,因此不需要三元运算符。
更新:Rails 5:
ActiveRecord::Type::Boolean.new.deserialize('0')
更新:Rails 4.2为此提供了公共API:
ActiveRecord::Type::Boolean.new.type_cast_from_user("0") # false
上一个答案:
ActiveRecord在https://github.com/rails/rails/blob/master/activerecord/lib/active_record/connection_adapters/column.rb中维护true / false表示形式的列表
2.0.0-p247 :005 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean("ON")
2.0.0-p247 :006 > ActiveRecord::ConnectionAdapters::Column.value_to_boolean("F")
这不是Rails的公共API的一部分,因此我将其包装到一个辅助方法中:
class ApplicationController < ActionController::Base
private
def parse_boolean(value)
ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
end
并添加了基本测试:
class ApplicationControllerTest < ActionController::TestCase
test "parses boolean params" do
refute ApplicationController.new.send(:parse_boolean, "OFF")
assert ApplicationController.new.send(:parse_boolean, "T")
end
end
ActiveRecord::Type::Boolean.new.type_cast_from_user("0")
返回的结果false
与您提到的一样,但是在Rails 5中它将开始返回true:DEPRECATION警告:您试图分配的值不是显式的,true
也不false
是布尔值。目前,此值转换为false
。这将进行更改以匹配Ruby的语义,并将true
在Rails 5中强制转换为。如果您希望保持当前行为,则应显式处理要转换为的值false
。
ActiveRecord::Type::Boolean
只是的别名ActiveModel::Type::Boolean
。使用原始类可能要好一点。
这个问题比较老,但是由于我遇到过几次这个问题,并且不喜欢提出的任何解决方案,所以我自己修改了一些东西,允许使用多个字符串表示“ true ”,例如“ yes”,“ on” ,“ t”,反之则为false。
Monkey修补了String类,并添加了一种将其转换为boolean的方法,然后/config/initializers
按照此处的建议将其放入:Rails 3中的Monkey Patching
class String
def to_bool
return true if ['true', '1', 'yes', 'on', 't'].include? self
return false if ['false', '0', 'no', 'off', 'f'].include? self
return nil
end
end
请注意,如果该值不是true或false的有效值,则返回nil。搜索?paid=false
(返回所有未付款的记录)和?paid=
(我未指定是否必须付款-因此将其丢弃)搜索(返回所有未付款的记录)是不同的。
然后,按照此示例,控制器中的逻辑将如下所示:
Something.where(:paid => params[:paid].to_bool) unless params[:paid].try(:to_bool).nil?
它非常整洁,有助于保持控制器/模型的清洁。
String#to_bool
可以返回nil
。请对此进行一段时间的沉思,然后将方法重命名为String#maybe_bool?
或诉诸ArgumentError
。
def to_bool; ['true', '1', 'yes', 'on', 't'].include? self; end
TRUE_VALUES = ['true'.freeze, '1'.freeze, 'yes'.freeze, 'on'.freeze, 't'.freeze]
。这a)节省了内存,b)防止了字符串的运行时更改。
ActiveRecord::ConnectionAdapters::Column.value_to_boolean('')
它将返回nil
。
我认为最简单的解决方案是针对其字符串表示形式测试“布尔”参数。
@show_all = params[:show_all]
if @show_all.to_s == "true"
# do stuff
end
不管Rails提供的参数为字符串“ true”还是“ false”,还是实际的TrueClass或FalseClass,此测试始终有效。
另一种方法是只传递没有值的密钥。虽然使用ActiveRecord::Type::Boolean.new.type_cast_from_user(value)
很简洁,但是在给param键赋值时可能会有些多余。
请考虑以下因素:默认情况下,在我的产品索引视图中,我只想显示产品范围内的产品集合(例如,库存中的产品)。也就是说,如果我想退回所有产品,我可以发送myapp.com/products?show_all=true
并打字show_all
参数为布尔值。
但是相反的选择-myapp.com/products?show_all=false
没有意义,因为它将返回与返回的产品集合相同的产品myapp.com/products
。
替代:
如果我想返回整个无作用域的集合,那么我发送myapp.com/products?all
并在控制器中定义
private
def show_all?
params.key?(:all)
end
如果键存在于params中,那么无论其值如何,我都会知道我需要返回所有产品,而无需键入值。
您可以像这样将所有布尔参数转换为真实的布尔值:
%w(show_all, show_featured).each do |bool_param|
params[bool_param.to_sym] = params[bool_param.to_sym] == "true"
end
在此解决方案中,nil参数将变为false。
值得注意的是,如果您要在Rails> 5.2中将值传递给ActiveModel,更简单的解决方案是使用attribute
,
class Model
include ActiveModel::Attributes
attribute :show_all, :boolean
end
Model.new(show_all: '0').show_all # => false
从这里可以看出。
在5.2之前,我使用:
class Model
include ActiveModel::Attributes
attribute_reader :show_all
def show_all=(value)
@show_all = ActiveModel::Type::Boolean.new.cast(value)
end
end
Model.new(show_all: '0').show_all # => false
TrueClass
和FalseClass