Ok, I'll explain it a little more detailed and give my current solution which works the way I want it to. I know the code I wrote is absolutely beginner style and the algorithms are not efficient. But unfortunately I am a beginner in Ruby on Rails. I'll try to optimize this in the next days
The idea is that a manager can invite volunteers to an event. The invitation can be accepted or rejected by the volunteers. The models:
class Event < ActiveRecord::Base
has_many :invitations
has_many :volunteers, :through => :invitations
end
class Volunteer < ActiveRecord::Base
has_many :invitations
has_many :events, :through => :invitations
end
class Invitation < ActiveRecord::Base
belongs_to :event
belongs_to :volunteer
end
update method of events_controller.rb
def update
@event = Event.find(params[:id])
respond_to do |format|
if @event.update_attributes(params[:event])
format.html { redirect_to(@event, :notice => 'Event was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => @event.errors, :status => :unprocessable_entity }
end
end
if params[:volunteers] && !params[:volunteers].empty?
@volunteers = Volunteer.find(params[:volunteers][:ids])
@volunteerIds = @volunteers.map(&:id)
# cancel invitations
@current = @event.invitations.find(:all).map(&:volunteer_id)
@current.each do |vId|
if @volunteerIds.find{|e| e==vId}.nil?
logger.info("DESTROYING #{vId}")
Invitation.find(:first, :conditions => "volunteer_id = #{vId}").destroy
end
end
#handle new invitations
@volunteers.each do |v|
if @event.invitations.find(:all, :conditions => "volunteer_id = #{v.id}").empty?
v.events << @event
logger.info("ADDING #{v.id}")
@event.invitations.find_by_volunteer_id(v.id).update_attributes :accepted => 1
end
end
end
end
Two additional functions (which should definitly be merged) in volunteers_controller
def accept
@volunteer = Volunteer.find_by_id(params[:id])
@volunteer.invitations.find_by_event_id(params[:event_id]).update_attributes(:accepted => 2)
redirect_to :action => "show", :id => params[:id]
end
def cancel
@volunteer = Volunteer.find_by_id(params[:id])
@volunteer.invitations.find_by_event_id(params[:event_id]).update_attributes(:accepted => 3)
redirect_to :action => "show", :id => params[:id]
end
the checkboxes, which the manager uses to invite the volunteers in the event edit view
<p>
<% for volunteer in Volunteer.find(:all) %>
<%= check_box_tag "volunteers[ids][]" ,volunteer.id, @event.volunteers.include?(volunteer)%>
<%= volunteer.first_name %>
<% end %>
</p>
and the two links for the volunteer to reject or accept in the show view
<p>
<b>Events</b>
<ul>
<% @volunteer.events.each do |event| %>
<li>
<%= link_to event.title, event %>
(<%= link_to "Accept", {:controller => "volunteers", :action => "accept", :id => @volunteer.id, :event_id => event.id} %>,
<%= link_to "Reject", {:controller => "volunteers", :action => "cancel", :id => @volunteer.id, :event_id => event.id} %>)
</li>
<% end %>
</ul>
</p>
I know there is a lot to do concerning code optimization and making the user interface more comfortable but right now I am happy that I now manage the relationships of the models correctly. Maybe this beginner-style code helps other beginners like me to understand the way the has_many => :through relation works