Sweeper outside?
Sometimes we modify some stuff without controllers (for example we publish some stuff via rake task ignited by cron). Unfortunately Rails does not handle it "out of the box", so we need to do it by ourselfs.
BaseSweeper
Let's create a base sweeper class where all the "magic" will happen:
class BaseSweeper < ActionController::Caching::Sweeper def after_save(data) expire_data(data) end def after_destroy(data) expire_data(data) end def expire_data @controller ||= ActionController::Base.new end end
There's nothing special about this code, except:
@controller ||= ActionController::Base.new
This line will emulate (if needed) controller outside of standard Rails flow. Thanks to this we can sweep stuff whenever we need. Example:
class NewsSweeper < BaseSweeper observe News def expire_data(data) super expire_fragment /news_#{data.id}/ end end
There's one more thing to do. We need to add sweeper to observers in application.rb file (so our sweeper will react on any change in observed model):
application.rb:
config.active_record.observers = "CommentObserver", "JakistamInnyObserver", # Sweepery "NewsSweeper"
March 20, 2012 — 18:33
In Rails 3.1.3, if you instantiate the controller and then try calling expire_fragment, you’ll get errors about trying to call host on NilClass.
After some experimenting, I remembered that functional tests can instantiate your controller. So I changed the instantiation code to:
@controller ||= ApplicationController.new
if @controller.request.nil?
@controller.request = ActionDispatch::TestRequest.new
end
This seems to work, even in production, even using rails console.
May 9, 2012 — 23:51
Dzięki :),
Ratujesz mi kilka godzin grzebania!
August 15, 2012 — 13:09
I couldn’t get this to work so made this ugly kludge in the BaseSweeper:
def expire_page(hash)
@controller ||= ApplicationController.new
@controller.request = ActionDispatch::TestRequest.new if @controller.request.nil?
hash[:only_path] = true
hash[:format] = :html
path = Rails.root.join(“public”).to_s + @controller.url_for(hash)
File.delete(path) if File.exists?(path)
end