192
votes

I'm using an IF statement in Ruby on Rails to try and test if request parameters are set. Regardless of whether or not both parameters are set, the first part of the following if block gets triggered. How can I make this part ONLY get triggered if both params[:one] and params[:two] is set?

if (defined? params[:one]) && (defined? params[:two])
 ... do something ...
elsif (defined? params[:one])
 ... do something ...
end
15
@Nakilon: Given that params is a Rails controller method (that happens to return a HashWithIndifferentAccess), it is about Rails.mu is too short

15 Answers

377
votes

You want has_key?:

if(params.has_key?(:one) && params.has_key?(:two))

Just checking if(params[:one]) will get fooled by a "there but nil" and "there but false" value and you're asking about existence. You might need to differentiate:

  • Not there at all.
  • There but nil.
  • There but false.
  • There but an empty string.

as well. Hard to say without more details of your precise situation.

96
votes

I am a fan of

params[:one].present?

Just because it keeps the params[sym] form so it's easier to read.

23
votes

use blank? http://api.rubyonrails.org/classes/Object.html#method-i-blank-3F

unless params[:one].blank? && params[:two].blank?

will return true if its empty or nil

also... that will not work if you are testing boolean values.. since

>> false.blank?
=> true

in that case you could use

unless params[:one].to_s.blank? && params[:two].to_s.blank?
19
votes

You can write it more succinctly like the following:

required = [:one, :two, :three]
if required.all? {|k| params.has_key? k}
  # here you know params has all the keys defined in required array
else
  ...
end
10
votes

Simple as pie:

if !params[:one].nil? and !params[:two].nil?
  #do something...
elsif !params[:one].nil?
  #do something else...
elsif !params[:two].nil?
  #do something extraordinary...
end
5
votes
if params[:one] && params[:two]
 ... do something ...
elsif params[:one]
 ... do something ...
end
5
votes

A very simple way to provide default values to your params: params[:foo] ||= 'default value'

5
votes

I just read this on RubyInRails classes http://api.rubyonrails.org/classes/Object.html#method-i-blank-3F

you can use blank? method which is equivalent to params[:one].nil? || params[:one].empty?

(e.g)

if params[:one].blank? 
  # do something if not exist
else
  # do something if exist
end
4
votes

You can also do the following:

unless params.values_at(:one, :two, :three, :four).includes?(nil)
 ... excute code ..
end 

I tend to use the above solution when I want to check to more then one or two params.

.values_at returns and array with nil in the place of any undefined param key. i.e:

some_hash = {x:3, y:5}
some_hash.values_at(:x, :random, :y}

will return the following:

[3,nil,5] 

.includes?(nil) then checks the array for any nil values. It will return true is the array includes nil.

In some cases you may also want to check that params do not contain and empty string on false value.

You can handle those values by adding the following code above the unless statement.

params.delete_if{|key,value| value.blank?}

all together it would look like this:

 params.delete_if{|key,value| value.blank?}
 unless params.values_at(:one, :two, :three, :four).includes?(nil)
   ... excute code ..
  end

It is important to note that delete_if will modify your hash/params, so use with caution.

The above solution clearly takes a bit more work to set up but is worth it if you are checking more then just one or two params.

3
votes

In addition to previous answers: has_key? and has_value? have shorter alternatives in form of key? and value?. Ruby team also suggests using shorter alternatives, but for readability some might still prefer longer versions of these methods.

Therefore in your case it would be something like

if params.key?(:one) && params.key?(:two)
  ... do something ...
elsif params.key?(:one)
  ... do something ...
end

NB! .key? will just check if the key exists and ignores the whatever possible value. For ex:

2.3.3 :016 > a = {first: 1, second: nil, third: ''}
  => {:first=>1, :second=>nil, :third=>""}
2.3.3 :017 > puts "#{a.key?(:first)}, #{a.key?(:second)}, #{a.key?(:third), #{a.key?(:fourth)}}"
true, true, true, false
1
votes

Just pieced this together for the same problem:

before_filter :validate_params

private

def validate_params
  return head :bad_request unless params_present?
end

def params_present?  
  Set.new(%w(one two three)) <= (Set.new(params.keys)) &&
  params.values.all?
end

the first line checks if our target keys are present in the params' keys using the <= subset? operator. Enumerable.all? without block per default returns false if any value is nil or false.

1
votes

Here's what I do,

before_action :validate_presence

and then following methods:

    def check_presence
  params[:param1].present? && params[:param2].present?
 end

 def validate_presence
  if !check_presence
    render json:  {
                      error:  {
                                message: "Bad Request, parameters missing.",
                                status: 500
                              }
                    }
  end
 end
0
votes
if params[:one] && param[:two]
  ... excute code ..
end

You can also check if the parameters are empty by using params[:two].empty

0
votes

I try a late, but from far sight answer:

If you want to know if values in a (any) hash are set, all above answers a true, depending of their point of view.

If you want to test your (GET/POST..) params, you should use something more special to what you expect to be the value of params[:one], something like

if params[:one]~=/   / and  params[:two]~=/[a-z]xy/

ignoring parameter (GET/POST) as if they where not set, if they dont fit like expected

just a if params[:one] with or without nil/true detection is one step to open your page for hacking, because, it is typically the next step to use something like select ... where params[:one] ..., if this is intended or not, active or within or after a framework.

an answer or just a hint

0
votes

If you want to be able to return an error based on the specific missing parameter without having to switch through all of them:

required_params = [:one, :two, :three]
required_params.each do |param|
  if params[param].blank?
    render json: { errors: "Missing parameter #{param.to_s}." }, :status => :bad_request 
    return
  end
end