A multitude of method_missing monstrosities
Friday, December 15th, 2006I read a blog entry that dinged with_scope in controllers and especially in around_filters. My first reaction to reading this post was quite positive: “Here are some techniques to help move more logic to the model” which is high on my list of objectives. But I was dismayed to see the trendy fervor building up to hobble with_scope (mostly on other sites). Permit me to outline some excellent uses for with_scope in a controller….
First and most topically is the issue of nested RESTful resources. Imagine a MessagesController that deals with Messages. Messages can be viewed as a resource in their own right and RESTfully represented as http://somesite/messages. But they can also be considered child resources of two other resources, dogs and cats, and consequently messages can also be represented as http://somesite/dog/1/messages or http://somesite/cat/26/messages. In each action of my MessagesController, I need to check for the parameter ‘dog_id’ or ‘cat_id’ (or none) and ensure that proper restrictions are in place for every model query. I could perform this test in every action and call a custom model method that implements the appropriate scope. Or I could send a programatically constructed method name. But duplicating the test/construction in every action is not very DRY (my controller gets complicated with cats and dogs before I even start thinking about Messages) and my Message model now has some complex method_missing code, or unDRY method definitions. And please don’t suggest I pass a parameter to a custom finder -the M-C coupling will hurt badly when my second scenario is considered.
I would argue that the decoupled nature of with_scope in an around filter suits the explicit scoping implied by nested resources very nicely. It would be slick as snot if a REST resource definition could also support automatically defining a model scope for that resource. Something like
map.resources :cat {|r| r.messages, :scope_model => :message }
would automatically Message.with_scope(cat=cat_id) within the resource controller. OK, so this might be going a bit too far…
Secondly, with_scope has the nifty ability to merge independently defined scopes. This allows for a very clean decoupling of orthogonal concepts that would otherwise result in a multitude of custom finders with garishly coupled names. If I need need restrictions for authorization (get authorized data), and I need restrictions for currency (get current data) and I need restrictions for RESTful nesting (get data belonging to parent resource), and I need restrictions for active data (get data that has not been logically deleted) there might be hundreds of different custom model finders needed. Obviously method_missing could DRY that up quickly, but the method constructor would be horrendously complicated that could build Message.find_active_owner-authorized_current_dog and Message.find_deleted_view-authorized_old_cat. A better solution would be to decouple the overarching restrictions of authorization and explicit parent resources into around_filters leaving a custom finder to deal with currency, logical deletion and, of course, run-time user-defined filter conditions. Now my method_missing only has to build Message.find_active_current and Message.find_deleted_old.
To summarize, with_scope in a controller has two excellent applications: 1. The controller’s entry points are explicitly or consistently scoped, such as with nested RESTful resource controllers, and in many cases, authorization. 2. Numerous orthogonal restrictions that need to be combined, such as (logical deletion) AND (authorizations) AND (belongs_to_parent). In these cases, I believe controller-defined with_scope is The Right Stuff.
Technorati Tags: with_scope method_missing around_filter rails rest