Converting from HABTM to has_many :through

Hello,

I have a has_and_belongs_to_many relationship that I’ve realized I need
to add additional attributes to. So I’m converting it to a has_many
:through (HMT) relationship. This is the first time I’ve used an HMT,
and it seems like I’m using too much code. I couldn’t figure out how to
get the join model objects to update automatically as a result of
updating the primary object, so I’ve ended up doing individual
saves/updates on each join model object.

The story is a regatta can include many boat classes, and a boat class
can be included in many regattas.

class Regatta < ActiveRecord::Base
has_many :inclusions
has_many :boat_classes, :through => :inclusions
has_and_belongs_to_many :users
end

class BoatClass < ActiveRecord::Base
has_many :inclusions
has_many :regattas, :through => :inclusions
end

class Inclusion < ActiveRecord::Base
belongs_to :regatta
belongs_to :boat_class
end

Eventually, I’ll attach a fee to each boat_class when it gets included
in a regatta. But for now I’m just trying to get back to the same
functionality I had when it was a HABTM relationship, and there was no
“include” model. So, here is what the update method looks like now:

@regatta = Regatta.find(params[:id])
Inclusion.find_all_by_regatta_id(params[:id]).each { |i|
    boat_class = i.boat_class
    if params[:boat_classes]
        i.included = params[:boat_classes].include? boat_class.id
        params[:boat_classes].delete boat_class.id
    else
        i.included = false
    end
    flash[:notice] = 'inclusion failure' if !

i.update_attribute(:included, i.included)
}
params[:boat_classes].each { |boat_class_id|
boat_class = BoatClass.find boat_class_id
logger.info “*** boat class #{boat_class.name} inclusion not
found”
@regatta.inclusions << Inclusion.new { |i|
i.boat_class_id = boat_class_id
i.included = true
flash[:notice] = ‘inclusion failure’ if ! i.save
}
} if params[:boat_classes]
@regatta.users = User.find(params[:users]) if params[:users]
if @regatta.update_attributes(params[:regatta])
flash[:notice] = ‘Regatta was successfully updated.’
redirect_to :action => ‘show’, :id => @regatta
else
render :action => ‘edit’
end

It seems to me that I shouldn’t have to individually update all the
existing inclusions, or manually create & save the new ones. I’d like
suggestions for how I can reduce the amount of code here. Considering
that the first 20 lines used to simply be:

@regatta = Regatta.find(params[:id])
@regatta.boat_classes = User.find(params[:boat_classes]) if

params[:boat_classes]

Additional background info: a) this keeps the join model objects around
even when a boat_class gets excluded, so that it’s attributes will be
saved. That’s not critical, if I can have it be much tighter, I’m happy
to give that up. b) there is a bug in this that I haven’t found yet, but
is irrelevant to this question: this code creates a new inclusion even
when there is an existing one found already. It seems params[…].delete
is not working the way I expect it to.

Thanks for any help,
Avram

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs