Skip to main content

Trouble With Form Objects

Rails • Asked by Chris Zempel

I decided to experiment with a form object.

class Invitation
  include ActiveModel::Model

  attr_accessor(
    :first_name,
    :last_name,
    :email,
    :firm_id,
    :role
  )

  validates :first_name, presence: true
  validates :last_name, presence: true
  validates :email, presence: true
  validates :firm_id, presence: true
  validates :role, presence: true

  def invite
    if valid?

    end
  end

  def firms_for(user)
    if user.admin?
      Firm.all
    else
      user.firms
    end
  end

  def invitable_roles_for(user)
    current_role = User::ROLES.index("#{user.role}")
    last_role = -1

    User::ROLES.slice(current_role..last_role)
  end

  private

    def create_firm_connection

    end
end
in the controller:

  def new
    @invitation = Invitation.new
  end
        <%= form_for @invitation do |f| %>
          <%= f.label :first_name %>
          <%= f.text_field :first_name %>
          <br />

          <%= f.label :last_name %>
          <%= f.text_field :last_name %>
          <br />

          <%= f.label :email %>
          <%= f.email_field :email %>
          <br />

          <%= f.label :firm_id, "Firm" %>
          <%= f.select :firm_id, options_from_collection_for_select(@invitation.firms_for(current_user), 'to_param', 'short_name') %>
          <br />

          <%= f.label :role %>
          <%= f.select :role, options_for_select(@invitation.invitable_roles_for(current_user)) %>
          <br />

          <%= f.submit %>
        <% end %>

I've run into this error:

undefined method `model_name' for ActionController::Parameters:Class

I can replicate this through calling @invitation.class.model_name, so here are my attempts at fixing it:

  def self.model_name
    "invitation"
  end

  def self.model_name
    ActiveModel::Name.new(self, nil, "Invitation")
  end

However, neither of these work. What's actually going on here?


Ah shoot, I thought I was inside the new controller action. I was actually inside create. Working now:

  def create
    authorize :invitation
    @invitation = Invitation.new invitation_params

    if @invitation.nil?
      redirect_to new_invitation_path, notice: "Invite successfully sent!"
    else
      render "new", notice: "Invite couldn't be sent!"
    end
  end

The correct way to define model_name is:

  def self.model_name
    ActiveModel::Name.new(self, nil, "ModelNameHere")
  end

Why does ActiveModel not automatically define this for you? The method is thrown on an instance of ActionController::Parameters which is what the params hash is, right? Are you sure you're instanciating the new model instance in the controller correctly?


Another code smell of the real problem is that you're doing

if @invitation.nil?

Which means that you're not creating an Invitation correctly. If you were, you'd be checking if it was valid or not. So you're not running validations and you're not creating the object in memory. There's something else going on here.


Correct on both counts. hadn't fully coded through the create action and didn't realize that's where I actually was - thought all the errors were being thrown on new


Does that mean you're able to remove the model_name method?



Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 27,623+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.