Error while generating sweeper in rails - ruby-on-rails

Everyone is like this is sweeper. But can anyone please tell me how to create sweeper file for a particular controller. I copy pasted the code but its not working.

Sweepers are the terminators of the caching world and responsible for expiring caches when model objects change. They do this by being half-observers, half-filters and implementing callbacks for both roles. A Sweeper example:
class ImageSweeper < ActionController::Caching::Sweeper
observe Image
def after_save(record)
def after_save(image)
expire_cache(image)
end
def after_destroy(image)
expire_cache(image)
end
def expire_cache(image)
##expire_fragment #'image'
expire_cache(image)
end
end
The sweeper is assigned in the controllers that wish to have its job performed using the cache_sweeper class method:
class ImageController < ApplicationController
cache_sweeper :image_sweeper, :only => [ :edit, :destroy, :share ]
end
In the example above, three actions are responsible for expiring those caches
Above will only work if you have config.action_controller.perform_caching = true in development.rb

I got it working. Sweeper file will be in app/sweepers/controllername(without s)_sweeper.rb

Related

in Rails where do you put your Sweepers?

Is there a convention in Rails to put Sweeper classes in a particular directory location?
UPDATE: Since observers are put into app/models, I'm assuming sweepers are no different, as long as the name always ends with "sweeper".
I like to put them in the app/sweepers directory.
I also put Presenters in the app/presenters directory...and Observers in the app/observers directory.
Try putting them in the app/models directory.
Sweepers
Cache sweeping is a mechanism which allows you to get around having a ton of expire_{page,action,fragment} calls in your code. It does this by moving all the work required to expire cached content into
na ActionController::Caching::Sweeper class. This class is an Observer that looks for changes to an object via callbacks, and when a change occurs it expires the caches associated with that object in an around or after filter.
Continuing with our Product controller example, we could rewrite it with a sweeper like this:
class StoreSweeper < ActionController::Caching::Sweeper
# This sweeper is going to keep an eye on the Product model
observe Product
# If our sweeper detects that a Product was created call this
def after_create(product)
expire_cache_for(product)
end
# If our sweeper detects that a Product was updated call this
def after_update(product)
expire_cache_for(product)
end
# If our sweeper detects that a Product was deleted call this
def after_destroy(product)
expire_cache_for(product)
end
private
def expire_cache_for(record)
# Expire the list page now that we added a new product
expire_page(:controller => '#{record}', :action => 'list')
# Expire a fragment
expire_fragment(:controller => '#{record}',
:action => 'recent', :action_suffix => 'all_products')
end
end
The sweeper has to be added to the controller that will use it. So, if we wanted to expire the cached content for the list and edit actions when the create action was called, we could do the following:
class ProductsController < ActionController
before_filter :authenticate, :only => [ :edit, :create ]
caches_page :list
caches_action :edit
cache_sweeper :store_sweeper, :only => [ :create ]
def list; end
def create
expire_page :action => :list
expire_action :action => :edit
end
def edit; end
end
source rails guide

Rails3 filter is being skipped in production

I have a strange behavior of Rails 3.0.10. Got this application controller:
app/controllers/application_controller.rb
require 'api/api_controller'
# rest not important
app/controllers/api/api_controller.rb
class Api::ApiController < ActionController::Base
before_filter :require_user
def require_user
#user = User.find(xxx, yyy)
end
end
and then this controller:
app/controllers/api/ac_controller.rb
class Api::AcController < Api::ApiController
before_filter :find_pool, :only => [:add_pool, :remove_pool]
def add_pool
# some logic that needs #user to be set
end
def remove_pool
# some logic that needs #user to be set
end
def find_pool
# some logic here
end
end
My problem is when I run this in production mode the require_user filter is NOT called. When I try this in the development mode, it works.
Now, I understand in development mode classes are being reloaded, but the question is why the require_user filter does NOT get called?
Edit: Please note AC controller is before API controller lexicographically.
It looks like order of required files problem or the ApiController being loaded twice. Once before AcController and one more time after AcController being loaded. This could cause, that find_pool filter will get evaluated before require_user. ApiController is alse after AcController in lex order.
The problem might be caused by require "api_controller" being present somewhere - it should be handled by Rails and does not need to be put down explicitly. So if there is such a line, removing it could help.
Typically the methods called by filters should NOT be public. Public methods in controllers are treated as actions. Have you tried making require_user a private method?

ActiveResource models + Sweepers

I've just started working with ActiveResource, and decided to cache a few bits of the model so I'm not hitting the api incessantly. Ok, fine.
I've looked into expiring caches, and decided to implement a sweeper (which I've not had to do yet). This isn't working.
AR model:
class Myresource < ActiveResource::Base
extend ActiveModel::Callbacks
define_model_callbacks :update
"stuff"
def current
Rails.cache.fetch("/key/#{self.id}", :expires_in => 5.minutes) do
Myresource.find(ID)
end
end
end
Sweeper:
class MyresourceSweeper < ActionController::Caching::Sweeper
observe Myresource
def after_update(myresource)
expire_cache_for_myresource
end
private
def expire_cache_for_myresource
Rails.cache.delete '/key/myresource.id'
end
end
Controller:
cache_sweeper :myresource_sweeper
So having worked with AR and caches and Sweepers only a bit, I can't figure out where to look after trying various combos of things. I can set and expire from the console for a resource, but within the app, the cache gets set, but nothing I've done is triggering a delete.
Suggestions?
The code that you posted has a typo. I kinda doubt this is your real problem, but for what it's worth, I think you meant to write your sweeper as follows:
class MyresourceSweeper < ActionController::Caching::Sweeper
observe Myresource
def after_update(myresource)
expire_cache_for_myresource(myresource)
end
private
def expire_cache_for_myresource(myresource)
Rails.cache.delete "/key/#{myresource.id}"
end
end
E.g. in the original code you posted, you weren't passing the resource to the expire_cache_for_my_resource method, and therefore were expiring the same static key over and over again.

Can Rails sweepers work across different controllers?

I have action caching working on my Sites index, and set up a SiteSweeper that works fine:
# app/controllers/admin/sites_controller.rb
class Admin::SitesController < Admin::BaseController
cache_sweeper :site_sweeper, :only => [:create, :update, :destroy]
caches_action :index, :cache_path => '/admin/sites'
...
# app/sweepers/site_sweeper.rb
class SiteSweeper < ActionController::Caching::Sweeper
observe Site
def after_save(site)
expire_cache(site)
end
def after_destroy(site)
expire_cache(site)
end
def expire_cache(site)
expire_action '/admin/sites'
end
end
But I also want to expire /admin/sites whenever any Publishers are saved or destroyed. Is it possible to have a PublisherSweeper expire the Sites index with something like this?
# app/sweepers/publisher_sweeper.rb
class PublisherSweeper < ActionController::Caching::Sweeper
observe Publisher
def after_save(publisher)
expire_cache(publisher)
end
def after_destroy(publisher)
expire_cache(publisher)
end
def expire_cache(publisher)
expire_action '/admin/sites'
end
end
I know I can just call expire_action '/admin/sites' within the various Publisher actions. I'm just wondering if sweepers have this capability (to keep my controllers a bit cleaner).
One sweeper can observe many Models, and any controller can have multiple sweepers.
I think you should change your logic to use something like that:
class SiteSweeper < ActionController::Caching::Sweeper
observe Site, Publisher
(…)
end
On PublishersController
cache_sweeper :site_sweeper, :admin_sweeper
So you don't repeat the logic of cleaning the /admin/site. Call it AdminSweeper, so when something goes wrong you know the only one place that expired the "/admin/sites" action.

How do I limit the accessing of a method across an app?

So I have a method and corresponding partial for including a set of random photos in the sidebar of certain areas of our site.
Right now I have a random_photos method in ApplicationController set with a before_filter.
That works in the sense that it makes the contents of the random_photos method available wherever I need it, but it also unnecessarily executes some complex SQL queries when I don't know it too (ie, when I don't need to access those random photos).
So, how can I limit the accessing of the random_photos method to only when I really need it?
You can add an :if condition to the before_filter call, like so:
class ApplicationController < ActiveController::Base
before_filter :random_photos, :if => is_it_the_right_time?
Yet another option is to use skip_before_filter. It just depends in how many controllers you want to be different. Use skip_before_filter if there are only a handful of controllers you want to be the exception. Use one of the other suggestions if there are many controllers where you want to bypass the filter.
class ApplicationController < ActiveController::Base
before_filter :random_photos
def random_photos
#photos = Photo.random
end
end
class OtherController < ApplicationController
skip_before_filter :random_photos
...
end
You can keep the random_photos method in ApplicationController, and put the before_filters in your other controllers.
class ApplicationController < ActiveController::Base
...
def random_photos
#photos = Photo.random
end
end
class OtherController < ApplicationController
before_filter :random_photos, :only => 'show'
...
end
It depends on how many functions are making use of random_photos...
If a handful then use vrish88's approach but with an after_filter:
class ApplicationController < ActiveController::Base
after_filter :random_photos, :if => is_it_the_right_time?
...
private
def is_it_the_right_time?
return #get_random_photos
end
end
class SomeController < ApplicationController
def show
#get_random_photos = true
...
end
end
If every function in a controller will make use of it then use the skip_before_filter or move the before_filter in the controller and out of the application controller.
Many ways to get it done, and none is more correct then the next. Just try to keep it as simple and transparent as possible so you don't recreate the functionality months down the road because you forgot where all the pieces are located.

Resources