All threads / Trouble With Form Objects

Ask A Question

Notifications

You’re not receiving notifications from this thread.

Trouble With Form Objects

Chris Zempel asked in Rails

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?

Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 38,558+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2020 GoRails, LLC. All rights reserved.