First ... a word of caution: That railscast is very old. There may be syntactical things in that episode that have been dated by new versions of rails.
Question 1
If you are using the has_many through method then you have to have an id column in the join model because you are using a full blown model. As Ryan mentions in the episode, you'll choose this method if you need to track additional information. If you use the has_and_belongs_to_many method, you will not have an id column in your table.
If you want to achieve a check where you do not allow duplicates in your many-to-many association (ie allow the pairing of item a with item b and again allowing another record of item a to item b), you can use a simple validates line with a scope:
validates_uniqueness_of :model_a_id, :scope => [:model_b_id]
Question 2
You can add indices in your migrations with this code
add_index :table_name, [ :join_a_id, :join_b_id ], :unique => true, :name => 'by_a_and_b'
This would be inserted into the change block below your create_table statement (but not in that create_table block). Check out this question for some more details: In a join table, what's the best workaround for Rails' absence of a composite key?
Question 3
I'm not completely clear on what you're looking to accomplish but if you want to take some action every time a new record is inserted into the join model I would use the after_create active record hook. That would look something like this.
class YourJoinModel < ActiveRecord::Base
after_create :do_something
def do_something
puts "hello world"
end
end
That function, do_something, will be called each time a new record is created.
Question 4
Using the has_many through method will give you access to the additional attributes that you defined in that model on both sides of the relationship. For example, if you have this setup:
class Factory < ActiveRecord::Base
has_many :widgets, :through => :showcases
end
class Widget < ActiveRecord::Base
has_many :factories, :through => :showcases
end
class Showcases < ActiveRecord::Base
belongs_to :factory
belongs_to :widget
attr_accessiable :factory_id, :widget_id, :visible
end
You could say something like
widget = Widget.first
shown = widget.showcases
shown.first.visible
or
shown = widget.showcases.where( :visible=> true )
You can also reach to the other association:
shown.first.factory