26

7 Patterns to Refactor Fat ActiveRecord Models - here is a great article about different refactoring approaches using PORO. Under the 3rd caption there is a Form Object pattern, which I really liked and already implemented in one of the projects. There is only an example using one nested resource, but I would like to implement this pattern for multiple nested resources. Maybe someone here had already dealt with this? I don't necessarily need any code examples, just the basic idea would be fine.

Update

Consider this example. I have two models.

class Company
  has_many :users

  accepts_nested_attributes_for :users
end

class User
  belongs_to :company
end

In case of one nested user for company using Form Object Pattern I would write the following:

<%= form_for @company_form do |f| %>
  <%= f.text_field :name %>
  <%= f.text_field :user_name %>
  <%= f.submit %>
<% end %>     

Form Object

class CompanyForm
  include Virtus

  extend ActiveModel::Naming
  include ActiveModel::Conversion
  include ActiveModel::Validations

  attr_accessor :company, :user

  def user
    @user ||= company.users.build
  end

  def company
    @company ||= Company.new
  end

  def submit(params={})
    company.name = params[:name]
    user.name = params[:user_name]
    persist!
  end

  private

  def persist!
    company.save!
    user.save!
  end
end 

But what if I have a form, where a company with multiple users can be created. The usual approach is to write it like this, using nested_form:

<%= nested_form_for @company do |f| %>
  <%= f.text_field :name %>
  <%= fields_for :users, do |user_form| %>
    <%= user.form.text_field :name %>
  <% end %>
  <%= f.link_to_add "Add a user", :users %>
  <%= f.submit %>
<% end %>

What I am asking is how do I implement that Form Object Pattern in this case?

3

1 Answer 1

19

the rails fields_for helper checks for a method in this format: #{association_name}_attributes=

so, if you add this method to CompanyForm:

def users_attributes=(users_attributes)
  # manipulate attributes as desired...
  @company.users_attributes= users_attributes
end

def users
  company.users
end

the fields_for generators will generate the nested users fields for a CompanyForm as if it were a Company. the above could be rewritten as a delegation since nothing is happening in the methods:

  delegate :users, :users_attributes=, :to => :company, :prefix => false, :allow_nil => false
Sign up to request clarification or add additional context in comments.

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.