Ruby Weekly is a weekly newsletter covering the latest Ruby and Rails news.

MeantimeFilter: Wrap your Rails controller actions with scopes

By Peter Cooper / July 6, 2006

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.

Comments

  1. Josh says:

    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

  2. Peter Cooper says:

    It can get a lot more complex than that. Consider when you nest scopes. His demonstration is a little simplistic, of course :)

  3. Tony says:

    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?

  4. Tony says:

    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

Other Posts to Enjoy

Twitter Mentions