On Apr 26, 2005, at 1:54 PM, Edgardo Hames wrote:
> In case this solution is way too far from being done, how can I handle
> checking and unchecking the different authors in that table on the
> Controller side? What I would need to do is remove the unchecked
> authors and add the checked ones. [...] How do you all usually solve 
> this problem?

The functionality of checkboxes is the same as that of a 
multiple-select field, though the UI is different.

I have two tables in a habtm relationship; following is the 
(hacked-together) code that I've used (which happens to use selects, 
but which would be easy to modify for checkboxes).

The short answer is:
When you are saving, get a list of all the items currently stored for 
the relationship into an array, get the list of all the new items 
desired into a new array, and then do a two-way difference to figure 
out which relationships must be removed and which must be added.


Here's the relevant parts of my code:

# POSTGRESQL SCHEMA
create table roles (
   id serial primary key,
   name varchar(100) not null
);

create table acts (
   id serial primary key,
   action_path varchar(100) not null
);

create table acts_roles (
   act_id integer references acts on delete cascade,
   role_id integer references roles on delete cascade,
   primary key ( act_id, role_id )
);


#FILE app/model/act.rb
class Act < ActiveRecord::Base
   has_and_belongs_to_many :roles
end


#FILE app/model/role.rb
class Role < ActiveRecord::Base
   has_and_belongs_to_many :acts
   has_and_belongs_to_many :users

   #This really should be done for all habtm, IMO
   def users=( new_array )
     self.remove_users( self.users - new_array )
     self.users << ( new_array - self.users )
     self.users
   end

   #This really should be done for all habtm, IMO
   def acts=( new_array )
     self.remove_acts( self.acts - new_array )
     self.acts << ( new_array - self.acts )
     self.acts
   end
end


#FILE app/controllers/role.rb
class RoleController < ApplicationController
   def edit
     @role = Role.find_by_id @params[ :id ]
     @page_title = "Edit Role '#{@role.name}'"

     @all_actions = Act.find_all.sort_by{ |act| act.action_path }
     @actions_allowed = @role.acts
   end
        def update
     role = Role.find( role_atts["id"] )
     role.acts  = @params['role']['acts'].collect{ |id| Act.find id }
     role.save
   end
end


#FILE app/views/role/edit.rhtml
<!-- snip -->
<fieldset id="actions" class="two_column">
   <legend><strong>Actions</strong> permitted by <%=@role.name%></legend>
   <select name="role[acts][]" multiple="multiple" 
size="<%=select_length%>">
   <%=options_from_collection @all_actions, @role.acts, :action_path %>
   </select>
</fieldset>


#FILE app/helpers/application.rb
module ApplicationHelper

   # Creates a list of HTML option tags based on a collection of model 
instances
   # with the ability to provide a second collection of elements which 
should be selected
   def options_from_collection( all_items, selected, text_method=:name, 
value_method=:id )
     use_include = selected.respond_to?( :include? ) && !selected.is_a?( 
String )

     all_items.inject([]) do |options, element|
       val = element.send( value_method )
       text = element.send( text_method )
       is_selected = use_include ? selected.include?( element ) : val == 
selected
       if is_selected
         options << "<option value=\"#{html_escape(val.to_s)}\" 
selected=\"selected\">#{html_escape(text.to_s)}</option>"
       else
         options << "<option 
value=\"#{html_escape(val.to_s)}\">#{html_escape(text.to_s)}</option>"
       end
     end.join( "\n" )
   end
end