"has_many :through =>" and checkboxes

Posted by: Jeff Kupperman Sat, 29 Sep 2007 03:51:24 GMT

I was working with Jaime on the Fair Labor simulation, and we ran into this problem:

There will be multiple games, each with multiple users, but a user may belong to more than one game. She wanted to use a checkbox list to add and remove users from a game. So far so good -- it's a classic "has_and_belongs_to_many" problem, and the code is very concise. We made a join table, games_models, using the migration in my previous post. Then:

In the Game model:

has_and_belongs_to_many :users


In the User model

has_and_belongs_to_many :games


In the view (edit_game.rhtml):

<% form_tag :controller=>'main', :action=>'update_game', :id=>@game.id do %>

 <% for user in User.find(:all) %>

   <%= check_box_tag "game[user_ids][]", user.id, @game.users.include?(user) %>

   <%= user.name %>

 <% end %>

 <P><%= submit_tag 'Add' %></P>

<% end %>


In the controller:

	
def edit_game
  @game = Game.find(params[:id])
  @users = User.find(:all)
end
	
def update_game
  @game = Game.find(params[:id])
  @game.update_attributes(params[:game])
  redirect_to :action=>'edit_game', :id=>@game
end



So we made a join table the usual way and used "has_and_belongs_to_many" in the models, and everything was fine and dandy...except she needed to identify some of the roles as "leader" roles -- in other words, the HABTM relationship needed to contain additional information.

So we made a model called Member to hold the join table, and that was fine, but we no longer could use the magic that comes with HABTM. Instead, we needed to set up the relationships in the models manually, like this:

class Game < ActiveRecord::Base
	has_many :members, :dependent => true
	has_many :users, :through => :members
end

class User < ActiveRecord::Base
	has_many :members, :dependent => true
	has_many :games, :through => :members
end

class Member < ActiveRecord::Base
	belongs_to :user
	belongs_to :game 
end


That worked, sort of, but it turns out that the "game[user_ids][]" nomenclature goes with HABTM, and when we did it manually, it choked. Thankfully, we're not the first people to get stuck on this (see http://railsforum.com/viewtopic.php?id=803). We can write our own user_ids method, as follows, which we put in the Game model declaration (game.rb):

def user_ids=(user_ids)
  members.each do |mb|
    mb.destroy unless user_ids.include? mb.user_id
  end
     
  user_ids.each do |user_id|
     self.members.create(:user_id => user_id) unless members.any? { |d| d.user_id == user_id }
  end
end 


Whew!

Posted in  | no comments

Migration example for a join table

Posted by: Jeff Kupperman Fri, 28 Sep 2007 15:15:24 GMT

... because I will forget again:

class GamesUsers < ActiveRecord::Migration

	def self.up		
		create_table :games_users, :id => false do |t|
			t.column :user_id, :integer
			t.column :game_id, :integer
		end
		add_index :games_users, [:user_id]
		add_index :games_users, [:game_id]
	end
	
	def self.down	
		drop_table :games_users
	end
	
end

Posted in  | no comments

Using Lightbox.js with Rails AJAX

Posted by: Jeff Kupperman Mon, 23 Apr 2007 16:11:54 GMT

We couldn't figure out why the lightbox.js effects were working perfectly in parts of our site and not at all in others, until we figured out that it never worked on elements that were created through an AJAX call.

Normally when we load a page, the function "initLightbox" is called automatically, and it "loops through anchor tags looking for 'lightbox' references and applies onclick events to appropriate links."

Problem was, the anchor tags for the images in the response didn't exist when the page finished loading initially -- they only came into being after the AJAX call. The solution? Add this line at the end of each RJS file where we might need Lightbox:

page.call 'initLightbox'

Posted in  | no comments