SimpleScope =========== SimpleScope is a DRY version, both in source and DSL, that derives ultimately from ScopedAccess and various other scoping solutions. It utilizes the in-built ActiveRecord::Base.with_scope method and relies on a number of the ActiveRecord staple protected/private routines. It allows you to make a with_scope around filter from simple attribute hashes and find options, meaning you can do some seriously snazzy DRYing up with just a line or two of code at the top of your controller. But first, how this plugin differs from its predecessors: - Many previous scoping solutions are burdened by how to handle conditions or options for :find - Some solutions had an excess of metalanguage, with filters, method symbols, parsers and class instantiations to do just this: extract conditions, make a hash, scope it to class. Their usage, then, became somewhat cumbersome and, while the ideas were brilliant, I would like something a little more terse. I felt the verbosity of some were so much that it was actually more code to use than to just implement with_scope by hand. That's no good. - Other solutions, except ScopedAccess, had no facility for scoping a scope to a different class. For example, the following is very useful: Article.with_scope(:find => { :conditions => ["comments.visible = ?", true], :include => :comments}) - My last qualm, and perhaps the biggest, was the hackish way I needed to disable caching and rewrite a few ways other filters operated because they didn't play well in tests. I fixed this by making the parsing non-existent and consequently no longer need to cache the result. The only extra step for testing is to clear the scope in setup (see NOTICE FOR TESTERS below). Scoping makes the biggest difference when you can use both the class and instance scope of your controller. ScopedAccess did this through methods. Other solutions use association proxies. SimpleScope uses the wonderful before_filter idea of passing blocks or methods. Really, SimpleScope is a scope_filter, a wrapper around around_filter to do some extra, but minimal, processing. And so, that's why I call its class method: scope_filter USAGE Not only should you consider using the scope_filter here and there, but you should use it in any place where you are specifying find or create options. Take advantage of with_scope's power to DRY up any ActiveRecord.find() or ActiveRecord.create(), especially includes, required attributes and special conditions. Syntax: scope_filter ActiveRecordModel [method] [options hash] [{ |controller| block }] scope_filter has a few options beyond the required class constant and method/block to execute: :only goes straight to the around filter :except goes straight to the around filter :class scope a different class from the model (for associations) EXAMPLES Below are some example usages and the scoping hash that will be passed to with_scope. Using a method on the controller (which can be inherited from parent classes, just use protected) to define the scoping hash, which takes the form of { :find => {...}, :create => {...} }. def custom_scope { :find => { :conditions => ["activated_at IS NOT ?", nil] } } end scope_filter User, :custom_scope, :only => :edit => { :find => { :conditions => ["activated_at IS NOT ?", nil] } } scope_filter(User, :only => :edit) { |c| { :id => c.current_user } } => { :find => { :conditions => ["id = ?", 1] }, :create => { :id => 1 } } scope_filter(User, :class => LoginKey) { |c| { :user_id => c.current_user } } => { :find => { :conditions => ["login_keys.user_id = ?", 1] }, :create => { :user_id => 1 } } def request_comments [1,3,4,5,20] end scope_filter Comment do |c| { :id => c.request_comments, :include => :ratings } end => { :find => { :include => :ratings, :conditions => ["id IN (?)", [1,3,4,5,20]] } :create => { } } (SimpleScope uses ActiveRecord Column definitions to ensure :create has only valid values) REMEMBER: When you are using inline blocks (using '{}'), you should put parentheses around the arguments and place the block on the outside. Otherwise Ruby will interpret it as part of the arguments to scope_filter; blocks are parallel, not within, the call, so you'll get syntax errors if you don't adhere to this rule. You have two alternatives to make it 'prettier'. You can use parentheses around the arguments and make a normal block outside of it or you can use an inline do...end block without parentheses. The last example above demonstrates this syntax and it is the one I personally prefer because the hash which it returns is easier to read. NOTICE TO TESTERS When you are doing testing, you will probably encounter the one unavoidable instance when scope filtering creates lots of errors. The errors are a result of the way the test environment treats filters for each test case. The after_filter on any test case will not be called if it encounters an error or, more importantly, a failure. This behaviour screws up the scoping of classes. Its scope leaks onto the next test case and, consequently, you will receive a failure or error followed by a cascade of errors. It may also be the result of exceptions being thrown which you do expect, but prevent the after_filter. In any case, whatever disrupts after_filters will seriously disrupt scope_filters and subsequent test cases. To fix this problem, you need to clear the scopes on all the models affected by that controller or group of controllers. A test helper module is included in Simple Scope which defines a couple of methods for you in Test::Unit::TestCase: clear_all_scopes - remove all scoping on all ActiveRecord::Base classes clear_scope_for(*args) - give a list of classes to clear their scope If you put clear_all_scopes in your setup, you should be fine. It can also go in your teardown method, but I prefer setup because any error in teardown will, yet again, result in errors.