title: Rails3 Cache Sweeper for has_and_belongs_to_many association
body: <p>I have the following relationships modeled in a Rails3 application:</p>
  
  <pre><code>class User &lt; ActiveRecord::Base
    has_and_belongs_to_many :skills
  end
  
  class SkillsUser &lt; ActiveRecord::Base
  end
  
  class Skill &lt; ActiveRecord::Base
    has_and_belongs_to_many :users
  end  
  </code></pre>
  
  <p>The "SkillsUser" model represents the many-to-many association between users and skills.  In this way, when a User adds a new Skill, and said Skill already exists in the "skills" table (i.e "Java"), I simply create the relationship between the existing skill and the user in the skills_users table.  All good.</p>
  
  <p>Within the User's view, I display a list of Skills.  And I have a fragment caching block wrapped around those skills.  </p>
  
  <pre><code>&lt;% cache([user,"skills"]) do %&gt;
  &lt;div id="skills-grid"&gt;
        &lt;% user.sorted_skills.each do |s| %&gt;
          ...
    &lt;% end %&gt;
  &lt;/div&gt;
  &lt;% end %&gt;
  </code></pre>
  
  <p>On a separate edit page, a User can add or delete a Skill.  This action simply creates or removes a skills_users record.  And when this happens, I need to invalidate the fragment cache so that the skills render appropriately on the User view.  </p>
  
  <p>So I created a CacheSweeper who's purpose in life is to observe the skills_users relationship.  Here's the controller:</p>
  
  <pre><code>class SkillsController &lt; ApplicationController
    autocomplete :skill, :name
    cache_sweeper :skill_user_sweeper
  
    def create
      @user  = User.find(params[:user_id])
      #Make sure the current user has access to 
      #associate a skill to the user in the request
      if(@user.id = current_user.id)
        SkillsHelper.associate_skill(@user,params[:skill][:name])
        @skill  = Skill.find_by_name(params[:skill][:name])
      end
      respond_to do |format|
        format.js
      end
    end
  
    def destroy
      @skill = Skill.find_by_id(params[:id])
      @user  = User.find_by_id(params[:user_id])
      #Destroy the relationship, not the skill
      @user.skills.delete(@skill) if(@skill.can_be_tweaked_by?(current_user))
  
      respond_to do |format|
        format.js
      end
     end
  end
  </code></pre>
  
  <p>And here's the sweeper:</p>
  
  <pre><code>class SkillUserSweeper &lt; ActionController::Caching::Sweeper
    observe SkillsUser
  
    def after_create(skill_user)
      expire_cache_for(skill_user)
    end
  
    def after_update(skill_user)
      expire_cache_for(skill_user)
    end
  
    def after_destroy(skill_user)
      expire_cache_for(skill_user)
    end
  
    private
    def expire_cache_for(skill_user)
      expire_fragment([skill_user.user,"skills"])
    end  
  end
  </code></pre>
  
  <p>The problem is, after adding or removing a skills_users record (after "create" or "destroy" on the SkillsController), the sweeper is never invoked.  I have other sweepers working within my project, but none of them observe many-to-many associations.</p>
  
  <p>My question, then, is how does one create a CacheSweeper to observe a "has_and_belongs_to_many" association?</p>