3
votes

I am building a rails4 api where i am posting a nested hash of attributes. however, the model does not actually have nested attributes or an association. Attempting to make a cleaner post params hash by combining some attributes into a group, but have the api handle the unwrapping and strong parameters validation.

## Example POST params
params[:item][:group] = {
  a: true,
  b: false
}

But the model does not actually have a group column, the attributes a and b are attributes directly on the model.

to handle this without the group wrapper would simply be

params.require(:item).permit(:a, :b)

But I would like to have it pass strong_parameters submitted with the group wrapper. How can I achieve this but having the POST params as mentioned above?

3
i ended up making the group a single hstore column and permitting each hash key individually instead of each attribute be its own column.skilleo

3 Answers

1
votes

You could strip all intermediate levels before your call the action:

class YourController < ApplicationController
  before_action :flatten_item_params

  private

  def flatten_item_params
    params[:item] = params.require(:item).each_with_object({}) do |(key, value), result|
      result.merge!(value)
    end
  end

  def item_params
    params[:item].permit(:a, :b)
  end
end
1
votes

In regards the passing of "nested" parameters, the docs are pretty explicit; as Hoang Phan recommended, you can pass a nested hash to your controller:

params: {
   object: {
      title: "Test",
      group: {
         a: "test", 
         b: "test"
      }
   }
}

This can be accepted with:

def params
   params.require(:object).permit(:title, group: [:a, :b])
end

--

The problem with this is that I believe all keys have to be present in the model. To remedy this, you can use a virtual attribute:

#app/models/your_model.rb
class YourModel < ActiveRecord::Base
   attr_accessor :group, :a, :b
end

Virtual attributes are not saved, but are instance methods appended to the class, allowing you to populate it with data which you don't need to put into the database.

Using the above should give you the ability to use the nested params hash; the only downside would be that you have to stick to group: [:a, :b] as a structure for your params.

0
votes

like most ruby/rails situations there's likely a better way of doing this. grabbing from the responses i end up with this seemingly hack approach. the important thing is that it does what i'm looking for, but it feels icky so if someone has a more elegant way please share!

class YourController < ApplicationController

private

  ## POST { item: { group: { a: true, b: false } } }
  def item_params
    group = params[:item][:group].presence

    if group
      ## Unwrap nesting and place at root of params[:item]
      ## Probably should explicitly handle this instead of iterating
      group.each {|k,v| params[:item][k] = v }
      params[:item].delete :group
    end

    params[:item].permit(:a, :b)
  end
end