Populating model attribute from one of many lists


#1

I have a model named Project which contains a name attribute, and I
have a second model named Booking which has, among other things, a
date and a Project. (has_many :bookings, belongs_to :project).

When a user creates a new Booking, they have the option of using an
existing Project or creating a new Project. As an added complexity,
when choosing from existing Projects, they can either choose from a
list of all Projects or they can choose from a list of “recent”
projects (Projects associated with Bookings dated within the past N
days). I settled upon the following as a way to present the different
lists of projects:

I added three attributes to the Booking model:

attr_accessor :recent_project, :all_project, :new_project

The view for creating new Bookings is provided two lists of Projects
(@projects and @recent_projects) which are used to create
corresponding select lists, essentially:

<%= select :booking, :recent_project, @recent_projects,
:include_blank => true %>
<%= select :booking, :all_project, @projects, :include_blank => true
%>
<%= text_field :booking, :new_project %>

There’s some validation going on to make sure one of the auxiliary
Projects is set, and finally, the booking model uses a before_save
hook to set the project_id attribute based on these auxiliary
attributes (simplified version below):

def before_save
if not self.recent_project.nil?
self.project = Project.find(self.recent_project)
elsif not self.all_project.nil?
self.project = Project.find(self.all_project)
else
self.project = Project.find_or_create_by_name(self.new_project)
end
end

To make matter trickier, I mangle Project.name before saving new
Projects. In order to make them easier to sort I move leading articles
to the end of the name, e.g., “The Widget” becomes “Widget, The”. This
is accomplished using a before_validation hook in the Project model.
(You may have spotted the bug here: if a user enters a new Project
title which happens to already exist and which starts with an article
find_or_create_by_name() gets fooled since the pre-validation mangling
kicks in after the find() but before the create().)

Now I have two questions:

  1. Is there a better or standard way of using one of multiple inputs
    to populate an attribute of an ActiveRecord model?

  2. What would be a smart way to fix the bug described at the end? I
    haven’t tried overwriting Project.find_or_create_by_name() yet, and
    I’m guessing I can just do that, but I thought I’d ask while I’m here.

Thanks for any advice.

-J