John Pollard
John Pollard

Reputation: 3899

Is there a better way to check child params?

Is there a better way to check for items_attributes than the following code? I have to check params[:order] first since sometimes that may not exist, but I hate the look of the long conditional.

if params[:order] and params[:order][:items_attributes]

UPDATE for ruby 2.3, now you can use the dig method

params.dig(:order, :item_attributes)

Upvotes: 5

Views: 2936

Answers (6)

Vladan Markovic
Vladan Markovic

Reputation: 354

You can create helper method for easier work with nested hashes. Create ruby_ext.rb file in your lib folder, and write this function:

module RubyExt
  module SafeHashChain
    def safe(*args)
      if args.size == 0 then self # Called without args ...
      elsif args.size == 1 then self[args[0]] # Reached end
      elsif self[args[0]].is_a?(Hash) then self[args[0]].safe(*args[1..-1])
      else nil end # Reached end-value too soon
    end
  end
end

class Hash; include RubyExt::SafeHashChain; end

After this you can call safe method on nested hashes like this:

params.safe(:order,:items_attributes)

It will return value from items_attributes. If order or items_attributes don`t exist it will return nil.

Upvotes: 1

Julien Portalier
Julien Portalier

Reputation: 2999

You'll may like fetch with a default?

order = params.fetch(:order, {})

if order[:item_attributes]
  # ...
end

Upvotes: 0

Mikael Hellman
Mikael Hellman

Reputation: 2724

Params is actually an instance of ActionController::Parameters and has built in white list filtering features.

This makes it possible to do stuff like this:

# White list for Order params
def order_params
    params.require(:order).permit(:items_attributes)
end

// If :order is missing exception, and then filter only permitted.
valid_params = order_params()

// Also make these calls safe without risk for unwanted params
order = Order.new(order_params())
order.save!    

Upvotes: 0

gotva
gotva

Reputation: 5998

If I have such problem I would think about extending basic ruby class Hash with something like this (this is just an idea)

class Hash

  def has_nested_values?(*args)
    current_value = self.dup
    args.each do |arg|
      current_value = current_value[arg]
      break unless current_value
    end
    !!current_value
  end

end

and the result is

h[:a] = {b: {c: {d: 1}}}

h.has_nested_values?(:a, :b, :c)
=> true

h.has_nested_values?(:a, :b, :cc)
=> false

PS I don't like dup in implementation but it works

Upvotes: 0

Zoran
Zoran

Reputation: 4226

You can use try, like so:

params[:order].try(:[], :items_attributes)

The try method returns nil if the receiver does not respond to it, instead of raising an exception.

Hope it's helpful!

Upvotes: 0

Dave Newton
Dave Newton

Reputation: 160181

If you're using the andand gem:

if params[:order].andand[:items_attributes]

You could also use try.

Upvotes: 0

Related Questions