Strange behavior of expire_fragment


#1

I’m following railscasts tutorial on fragment caching, everything works
great except for the sweepers. When i put expire_fragment(%r{.*})
directly in my controller, i can expire my fragments with wonderful
ease. Yet when i write a nice little sweeper like this code block,
nothing gets expired (the puts have been added in for troubleshooting):

class PhraseSweeper < ActionController::Caching::Sweeper
observe Phrase

def after_update(phrase)
expire_cache(phrase)
end

def expire_cache(phrase)
puts "=====This Is Activated Via expire_cache(phrase) "
puts expire_fragment(%r{.}).class
expire_fragment(%r{.
})
end
end

I put this put it in apps/sweepers, and initialize the folder under
environment.rb. When i update my phrase model I get this in my console:

=====This Is Activated Via expire_cache(phrase)
NilClass
=====This Is Activated Via expire_cache(phrase)
NilClass

Yet when i have the expire_fragment called via my controller i get this
in the console:

======This is called via the controller======
Hash

So, my sweeper folder is getting initialized, and the code is running on
update, but for some reason the puts are called twice, and the
expire_fragment doesn’t work at all. Rails 2.1.0 Ruby 1.8.6. Any
suggestions??


#2

Have you determined what

%r{.*}

evaluates to in the context of your Sweeper?


#3

Ar Chron wrote:

Have you determined what

%r{.*}

evaluates to in the context of your Sweeper?

%r{.*} will delete absoloutly everything in my cache, i think i found my
error after reading this forum thread
http://www.ruby-forum.com/topic/145163#643061 .

In my controller i had:
phrase_sweeper :phrase_sweeper, :only => [:create, :update, :destroy]

when i was actually using an action called :rank_up, for some reason i
just assumed if i was updating a model, the :update action would cover
it, although i wasn’t using :update in my controller. I’ve yet to try
the changes, but i’ll let you know the outcome. The confusing part was
that the sweeper gets called no mater what, but the expire_fragment only
works on the specified actions.


#4

Richard S. wrote:

The confusing part was
that the sweeper gets called no mater what, but the expire_fragment only
works on the specified actions.

I actually gave up on the sweepers in my app, as they were far too
general, and expired more than was necessary in some cases, and given
the filters and sorting that can be done, it’s all fragment caching.

Now the models decide which of their own fragments to expire (via
Rails.cache.delete) depending on what was changed.


#5

Now the models decide which of their own fragments to expire (via
Rails.cache.delete) depending on what was changed.

Sounds interesting do you have a code snippet or tutorial you can point
me to, before i get too entrenched in this sweeper mess??


#6

Basically, all my controllers inherit from GenericController, all my
models from GenericModel, so I can single source stuff like these
behaviors, but override if necessary…

GenericController:

def create
model = params[:controller].singularize.downcase
@object =
params[:controller].singularize.camelcase.constantize.new(params[model])
if @object.save
# expire caches invalidated by the creation of this object
@object.expire_caches ‘create’
end
end

def update
model = params[:controller].singularize.downcase
@object =
params[:controller].singularize.camelcase.constantize.find(params[:id])
orig_name = @object.name
if @object.update_attributes(params[model])
expire_type = (@object.name == orig_name ? ‘update’ : ‘rename’)
# expire caches invalidated by just an update, or an update
# including a name change
@object.expire_caches expire_type
end
end

Generic Model:

models have fragments in basically

modelname.id.row,

modelname.id.show,

modelname.id.related, and a bunch of other ‘sub-fragments’

that make up the ‘related’ items navigation sidebar

def expire_caches(method)
model = self.class.table_name.singularize
case method
when ‘update’
# just expire the ‘show’ fragment for this model
expire_base_fragment model, self.id
when ‘destroy’
# destroy all the fragments for this model, and all other
# fragments that include this model - this is driven by
# an attribute of each model, which tells what the related
# models are
destroy_caches model, self.id
when ‘rename’
# just like destroy
destroy_caches model, self.id
when ‘create’
# this case looks at models related to the current
# one to see if those caches should be invalidated. i.e.,
# creating this model as a child of another invalidates
# that models “children” cache
… ugly code omitted…
else
# there are other actions that can cause different fragments
# to be expired - relating 2 models will cause 4 fragments to
# be expired:
# this model’s list of that related type,
# that model’s list of this related type,
# and both model’s aggregate ‘related’ fragment
… ugly code omitted…
end
end

The various “expire” or “destroy” methods are all variants of:

def expire_base_fragment(model, id)
Rails.cache.delete(‘views/’+model+’/’+id.to_s+’/show’)
end