How do I submit a boolean parameter in Rails?

2019-02-01 15:36发布

I'm submitting a parameter show_all with the value true. This value isn't associated with a model.

My controller is assigning this parameter to an instance variable:

@show_all = params[:show_all]

However, @show_all.is_a? String, and if @show_all == true always fails.

What values does Rails parse as booleans? How can I explicitly specify that my parameter is a boolean, and not a string?

9条回答
戒情不戒烟
2楼-- · 2019-02-01 16:08

You can add the following to your model:

def show_all= value
  @show_all = ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
end
查看更多
欢心
3楼-- · 2019-02-01 16:12
@show_all = params[:show_all] == "1" ? true : false

This should work nicely if you're passing the value in from a checkbox -- a missing key in a hash generates nil, which evaluates to false in a conditional.

EDIT

As pointed out here, the ternary operator is not necessary, so this can just be:

@show_all = params[:show_all] == "1"

查看更多
我命由我不由天
4楼-- · 2019-02-01 16:14

You could convert all your boolean params to real booleans like this:

%w(show_all, show_featured).each do |bool_param|
  params[bool_param.to_sym] = params[bool_param.to_sym] == "true"
end

In this solution, nil parameters would become false.

查看更多
萌系小妹纸
5楼-- · 2019-02-01 16:18

Another approach is to pass only the key without a value. Although using ActiveRecord::Type::Boolean.new.type_cast_from_user(value) is pretty neat, there might be a situation when assigning a value to the param key is redundant.

Consider the following: On my products index view by default I want to show only scoped collection of products (e.g. those that are in the stock). That is if I want to return all the products, I may send myapp.com/products?show_all=true and typecast the show_all parameter for a boolean value.

However the opposite option - myapp.com/products?show_all=false just makes no sense since it will return the same product collection as myapp.com/products would have returned.

An alternative:

if I want to return the whole unscoped collection, then I send myapp.com/products?all and in my controller define

private

def show_all?
  params.key?(:all)
end

If the key is present in params, then regardless of its value, I will know that I need to return all products, no need to typecast value.

查看更多
家丑人穷心不美
6楼-- · 2019-02-01 16:19

I wanted to comment on zetetic answer but as I can't do that yet I'll post this as an answer.

If you use

@show_all = params[:show_all] == "1"

then you can drop ? true : false because params[:show_all] == "1" statement itself will evaluate to true or false and thus ternary operator is not needed.

查看更多
等我变得足够好
7楼-- · 2019-02-01 16:20

This question is rather old, but since I came across this issue a couple of times, and didn't like any of the solutions proposed, I hacked something myself which allows to use multiple strings for true such as 'yes', 'on', 't' and the opposite for false.

Monkey patch the class String, and add a method to convert them to boolean, and put this file in /config/initializers as suggested here: Monkey Patching in Rails 3

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

Notice that if the value is none of the valid ones either for true or false, then it returns nil. It's not the same to search for ?paid=false (return all records not paid) than ?paid= (I don't specify if it has to be paid or not -- so discard this).

Then, following this example, the logic in your controller would look like this:

Something.where(:paid => params[:paid].to_bool) unless params[:paid].try(:to_bool).nil?

It's pretty neat, and helps to keep controllers/models clean.

查看更多
登录 后发表回答