Default Association Attributes in ActiveRecord
I cringe a little bit when I see a controller like this:
class Admin::Subscribers::InvitationsController < Admin::ApplicationController
before_filter :load_subscriber
before_filter :build_invitation, :only => [:new, :create]
def build_invitation
@invitation = @subscriber.invitations.build(params[:invitation])
@invitation.email = @subscriber.email # *shudder*
end
end
While it’s almost okay, it irks me to have to explicitly set the Invitations’s email address in the controller12.
An Insight
What I really want is to set the email address when I make an Invitation for a Subscriber. There should only be two ways to say that:
@subscriber.invitations.build
@subscriber.invitations.create
A Solution
So, let’s place the responsibility for setting the Invitation’s email address directly inside the Subscriber’s has_many
association:
class Subscriber < ActiveRecord::Base
has_many :invitations, :as => :source, :extend => DefaultAssociationAttributes do
def default_attributes
{ :email => proxy_owner.email }
end
end
end
module DefaultAssociationAttributes
def build_record(attrs, &block)
super(with_default_attributes(attrs), &block)
end
def create_record(attrs, &block)
super(with_default_attributes(attrs), &block)
end
def with_default_attributes(attrs)
default_attributes.merge((attrs || {}).symbolize_keys)
end
end
And then clean up our controller:
class Admin::Subscribers::InvitationsController < Admin::ApplicationController
before_filter :load_subscriber
before_filter :build_invitation, :only => [:new, :create]
def build_invitation
@invitation = @subscriber.invitations.build(params[:invitation])
end
end
Feedback?
I’m feeling pretty happy with this idea, so I’m thinking I’ll keep the code around and maybe throw a small gem up on GitHub.
UPDATE: The gem’s now in my GitHub account as default_association_attributes.
I may have missed another solution though, so I’d love to hear from you—how have you solved this problem? How have you felt about your solution?
-
Having Invitation hang onto its own email address allows it to be re-used in non-Subscriber contexts—like when a User wants to create an Invitation for a friend. ↩
-
Somehow, using a custom method like
Subscriber#build_invitation_with_email
doesn’t feel much better—then I have to remember to use it instead of the default. ↩