Note that there are some explanatory texts on larger screens.

plurals
  1. PORails3 Cache Sweeper for has_and_belongs_to_many association
    primarykey
    data
    text
    <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>
    singulars
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload