MeantimeFilter: Wrap your Rails controller actions with scopes
MeantimeFilter is an interesting new plugin for Rails by Roman Le Negrate. It's a little like around_filter, but rather than using a class with 'before' and 'after' methods, it uses a single method (like the other types of filter) and passes in the method to wrap 'around' as a code block. You can then yield to this or pass it into anything you like. An example:
class PostsController < ApplicationController before_filter :authenticate meantime_filter :scope_posts_to_user # Displays the posts of the logged in user. def show @posts = Post.find(:all) end def create # ... @post = Post.create(params[:post]) # Automatically associated to @user # ... end private # Sets @user to the logged in user or redirects to :index. def authenticate # ... end def scope_posts_to_user(&block) Post.with_scope({ :find => { :conditions => [ 'user_id = ?', @user.id ]}, :create => { :user => @user } }, &block) end # Or: def scope_posts_to_user Post.with_scope({ :find => { :conditions => [ 'user_id = ?', @user.id ]}, :create => { :user => @user } }) { yield } end end
Hot! This could make your code a lot cleaner by allowing you to wrap any methods you want in a scope automatically.
July 6, 2006 at 12:57 pm
I'm confused. Why is this cleaner than @user.posts.create(params[:post])? I like yielding as much as the next guy, but I dungettit
July 6, 2006 at 1:22 pm
It can get a lot more complex than that. Consider when you nest scopes. His demonstration is a little simplistic, of course :)
July 7, 2006 at 6:21 pm
This looks really usefull, but I'm having trouble wrapping my head around how to use this for setting the same scope for a number of models? Short of doing this:
meantime_filter :scope_x_to_foo, :scope_y_to_foo, :scope_z_to_foo...
def scope_x_to_foo
x.with_scope({...})
end
def scope_y_to_foo
y.with_scope({...})
end
...
I guess I'm not grokking the way yield works... is there a more DRY way?
July 7, 2006 at 9:26 pm
In case anyone is curious I figured out how to do it pretty cleanly by passing a class instance. Basically I did this:
# Intranet.current_intranet is set by a before_filter...
class IntranetScopeFilter
def initialize(model_name)
@model_name = model_name
@model = Object.const_get(model_name)
end
def filter(controller)
if Intranet.current_intranet.nil?
yield
else
@model.with_scope({
:find => {:conditions => ["#{@model_name.downcase.pluralize}.intranet_id = ?",Intranet.current_intranet.id]},
:create => {:intranet_id => Intranet.current_intranet.id}
}) { yield }
end
end
end
meantime_filter IntranetScopeFilter.new("User")
meantime_filter IntranetScopeFilter.new("Contact")
meantime_filter IntranetScopeFilter.new("Matter")
...etc