0

I'm developing a REST Api for an app's factory generators to allow model instances to be also be created using REST, and I'm wondering how can I permit the nested attributes of the model without hardcoding the attributes.

Suppose I have a model called Restaurant

class Restaurant < ApplicationRecord
  has_one :cost, dependent: :destroy, inverse_of: :grant
  has_one :address, dependent: :destroy, inverse_of: :grant
  has_one :owner, dependent: :destroy, inverse_of: :grant

  accepts_nested_attributes_for :cost, :address, :owner
  ...

where its associations also have its own model attributes, and a factory

FactoryGirl.define do factory :restaurant, class Restaurant do

  after(:create) do |restaurant|
    restaurant.cost = assign_attributes(attributse_for(:cost_factory))
    restuarnat.address = assign_attributes(attributes_for(:address_factory))
    restaurant.owner = assign_attributes(attributes_for(:owner_factory))
  end
 ...
end

where its nested associations also have its own factory. I am passing a json body through REST api in this format

{
  "restaurant": {
    "cost_attributes": {
     "staff": 123
     "tables": 123
    }
}

I know I can permit the attributes these way

params.permit(:restaurant)
      .permit(cost_attributes: [:staff, :tables],
              address_attributes: [:address_attr_1],
              owner_attributes: [:owner_attr_1]]

but the actual model I'm working on has many associations. It will be painful to hardcode everything. Is there a way i can permit the parameters coming through instead of hardcoding in my controller? Currently this is what im thinking

params.permit(Restaurant.nested_attributes_options.keys)

but obviously this does not work out.

2 Answers 2

1

Here's one way to collect nested attribute names:

mod = Restaurant

nested_attributes = mod.nested_attributes_options.keys.each_with_object({}) do |association, hash|
  hash[:"#{association}_attributes"] = association.
                                       to_s.
                                       classify.
                                       constantize.
                                       send(:attribute_names).
                                       map(&:to_sym)
end
params.permit(:restaurant).permit(nested_attributes)

This code is kinda generic, do not hesitate to adapt it to your specific use-case.

Sign up to request clarification or add additional context in comments.

Comments

0

To improve on yoones answer, here is a way to collect all deeply nested attribute names recursively using reflections :

  def get_nested_attributes(model, _attributes = [])

    # base case
    if model.nested_attributes_options.empty?
      nil

    # recurse deeper
    else
      associations_list = model.reflect_on_all_associations(:has_one)
      associations_list = associations_list.push(model.reflect_on_all_associations(:has_many)).flatten

      nested_attributes = associations_list.each_with_object({}) do |association , hash|

        association_model = association.klass.nil? ? association.name.to_s.classify.constantize : association.klass

        # include attributes of association model
        # { _attributes => [attributes_1, attribute_2] }
        attribute_name = :"#{association.name.to_s}_attributes"
        hash[attribute_name] = association_model
                               .send(:attribute_names)
                               .map(&:to_sym)

        # recurse deeper into tree, get the result and append
        # { _attributes => [attributes_1, attribute_2, { } ] }
        result = get_nested_attributes(association_model, hash[attribute_name])
        if !result.nil?
          hash[attribute_name].push(result)
        end
      end
      nested_attributes
    end
  end

model = Restaurant
nested_attributes = get_nested_attributes(model)
params.permit(nested_attributes)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.