Trouble With Form Objects
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
Yep!
relevant links for this:
https://www.reinteractive.net/posts/158-form-objects-in-rails
http://robots.thoughtbot.com/activemodel-form-objects