merb-resource-scope Brought to you by Dynamic50

What the heck is this? It’s a way of decorating your restful routes with a specification. This specification can provide some useful information when the request has hit a certain route. THIS MEANS THAT YOUR CONTROLLERS WILL BECOME DRY AS A BONE! AND THEY WILL KNOW HOW TO FIND ITS RESOURCE AND ENCLOSING RESOURCE, without before filters. ALSO YOUR RESOURCE WILL BE AUTOMATICALLY SCOPED, ie users/1/posts/1/comments/3 So the controller will have access to find_resource method and that resource will be found from users.get(1).posts.get(1).comments.get(3) Not comment.get(3) the comments has to belong to the post and the post has to belong_to the user! A route of users/1/posts/1 would have 2 specifications. one for the ‘users’ and one for the ‘posts’ and the order of the specifications is quite important. So once that route has been hit we have 2 specs “users” and “posts”, the posts controller is now aware of that the current request is nested inside user, so you could call /user/1 the ‘enclosing resource’ The controller can now determine its “resource_scope”. which can then be used to find resources and resources in a DRY way. With the extra bonus of getting automatic scoping. Note this is being tested using a test-stack that has been tested on stack —edge and latest stable datamapper Nothing better then examples to see how stuff works, so lets have a look at them now.

Examples

Ever seen “lets pretend”, well today we have [users, posts, comments, profile_image, :images]

user has 1, :profile_image
user has n, :posts
user has n, :comments
post has n, :comments
image has n, :comments
There are 2 steps to resource_scope enlightenment;
  1. Setting up your resourceful routes
  2. Making your controller resource_scope aware

Setting Up the Routes Example 1

What on earth are the options and what do they do? see router/resource_specification resource_with_spec is exactly the same as the “resource” method except a specification is added to the request resources_with_spec is exactly the same as the “resources” method except a specification is added to the request You Specify spec option with the :spec => {}, all other arguments will be parsed to the resource method ie. resources_with_spec :posts, :spec => {:permalink => :title}, :collection => {:recent => :get}

resource_with_spec :myhome, :spec => {:class_name => 

Setting Up the Controllers Example 1

lets make Comments Controller be resource_scope aware so lets just make the comments controller do exactly that

class Comments < Application
build_resource_scope
end
yep thats it!! so what does that do??? this will add a before filter that will do exactly that, along with some included actions. This is just a module and in the config we can set to use our own default actions ok now lets pretend we have just requested “/myhome/posts/1/comments” this will hit the default index action which looks something a bit like this

def index
self.current_resources = find_resources
display current_resources
end
what is happening here? find_resources – is essentially doing this. session.authentication.user.posts.get(1).comments Now you can override this method in your controller change the default behavior

def find_resources
@resource_scope.all :conditions => {:pending => false}
end
@resource_scope what exactly is that? Its the resource_scope which for this particular instance is session.authentication.user.posts.get(1).comments we would also have access to some included url helpers NOTE resource_url is not the same as resource method resources_url == "/myhome/posts/1/comments" enclosing_resource_url == "/myhome/posts/1" enclosing_resources_url == "/myhome/posts" You can now extend your enclosing_resource_url, resource_url with a block, why? Well if you want to get to link to post/1/tags when you are on the post/1/comments page for instance

Example

enclosing_resource_url{|route| route.add(:tags)} So what you do is use a block and you will have the route_generate object available to you which means you can use the add method!!! look at controllers/helpers for more info on the add methods, But essentially it takes up to 3 parameters add(:comment, :new) add(:comment, :edit, @comment) add(:comment, @comment) see URL helper for more info on these lets a heading below just in case ok now lets pretend we have just requested “/myhome/posts/1”

def show
self.current_resource = find_resource
raise ::Merb::ControllerExceptions::NotFound unless current_resource
display current_resource
end
find_resource here would be essential resource_scope.get(1) which would mean – session.authentication.user.posts.get(1) resource_url(current_resource) == "/myhome/posts/1" resource_url(current_resource, :edit) == "/myhome/posts/1/edit" resource_url(current_resource, :edit, :a => "w") == "/myhome/posts/1/edit?a=w" new_resource_url == "/myhome/posts/new" enclosing_resource_url(:edit) == "/myhome/edit" new_enclosing_resource_url == "/myhome/new" If you want to see more look at the test-stack | and the controller/actions | and specs There are more specs to come with more examples but for now that is all she wrote. Also need to add slice support which I have stubbed

url helpers

The url helper basically generate a name route using the specifications and the captured name_prefix in the route behaviors, so in essence they create a name_route. This is important to remember when you are just using the specification route extension method as you will need to give the route the correct name, basically needs to match the resource_name, or you change the resource_name so yeah remember its looking for NAMED ROUTES

new_resources_url – Just pass in params hash

will generate a scope url like /posts/1/comments/new new_resource_url new_resource_url(:paramexample => "whatver")

resource_url – Pass in a the current_resource OR a symbol for singletons and then params hash

resource_url(current_resource) resource_url(current_resource, :edit) resource_url(current_resource, :whatever => "whatberparams") resource_url(:myhome) resource_url(:myhome, :edit)

resources_url – Pass in custom routes and then params hash

resources_url resources_url(:pending) resources_url(:parms => "whateverparams")

enclosing_resource_url – Pass in custom routes and then params hash

enclosing_resource_url enclosing_resource_url(:edit) enclosing_resource_url(:parms => "whateverparams")

enclosing_resources_url – Pass in custom routes and then params hash

enclosing_resources_url enclosing_resources_url(:pending) enclosing_resources_url(:pending, :q => {"Asdf dsaf"}) enclosing_resources_url(:a => "whateverparams")

new_enclosing_resource_url – Just params hash

will generate a scope url like /posts/1/comments/new new_enclosing_resource_url new_enclosing_resource_url(:paramexample => "whatver")

build_resource_scope options

- :actions => => [:action_names], :exclude => [:actions_name] # include what actions you want - :singleton => true # makes sure that only singleton actions are included i.e not index - :build_scope => take the same options that you would pass to a before filter i.e :only, :if etc!!!

config for my own actions

Merb::Plugins.config[:merb_resource_scope][:actions] = MyOwnModuleForMyDefaultCoolActions Merb::Plugins.config[:merb_resource_scope][:singleton_actions] = MyReallyCoolDefaultSingletonActions

Resources

GOOGLE GROUP http://groups.google.com/group/merb-resource-scope LIGHTHOUSE http://dynamic50.lighthouseapp.com/projects/18588-merb-resource-scope/overview Inspiration And Thanks ====== (RC) resources_controller from my old rails world, respect goes out to our good friend Ian White http://github.com/ianwhite/resources_controller/tree/master Thanks == Gfriends Aimee for putting up with me, and Megan my special daughter License -—— (The MIT License) Copyright © 2008 Dynamic50 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ‘Software’), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED ‘AS IS’, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.