If you're using rails, and you've kept up with what's happening in the community, and in the framework, you're probably writing RESTful apps. You may have noticed, like I did, that most of your controllers follow the same basic pattern - the one that the scaffold_resource (just scaffold in edge/2.0) generator spits out.
I wanted a great way to hide that pattern, and describe the unique features of my controllers through a more domain-specific API. Since I tend to write a lot of nested, and polymorphic resources, I also wanted to DRY up all of that code, and make generating urls a lot more intuitive. Finally, I wanted all of that code to be well-tested, so that I could count on hacking it up when I needed new features, without breaking things.
So, I created it, and here it is.
resource_controller
A basic, out of the generator resource just requires inheriting from ResourceController::Base.class PostsController < ResourceController::Base
end
API
Before/after callbacks, responses, and flash messages are managed through an extremely flexible API. I tried my best, here, to make syntax succinct when your needs are succinct, and allow API calls to be organized in an intuitive way. What I came up with was a scoping system, where calls to the various API methods can be chained, or scoped with a block.Take the create action, for example. You might want to add a before block, to assign the author attribute of your post model to the current user.
class PostsController < ResourceController::Base
create.before do
@post.author << current_user
end
end
As your application matures, you may want to add an RJS response to your create method, for some AJAXy goodness. Easy.
class PostsController < ResourceController::Base
create.before do
@post.author << current_user
response do |wants|
wants.html
wants.js
end
end
end
Actually, there's even a shortcut for adding responses. You can omit the response block, if you just want to add responses to what's already provided by resource_controller (just HTML) or has been added elsewhere.
class PostsController < ResourceController::Base
create do
before do
@post.author << current_user
end
wants.js
end
end
Since I commonly use the same RJS template for several actions, I've found this short-form really handy, because I can use a one-liner to add the same response to several actions.
class PostsController < ResourceController::Base
[create, update].each { |action| action.wants.js {render :template => "post.rjs"} }
end
Oh yeah, and you can change the flash too.
class PostsController < ResourceController::Base
create.flash "Wow! RESTful controllers are so easy with resource_controller!"
end
For more API details, and examples, see the RDocs.
Helpers
Helpers are used internally to manage objects, generate urls, and manage parent resource associations.If you want to customize certain controller behaviour, like member-object, and collection fetching, overriding helper methods is all it takes.
Note:For certain resource_controller functionality to work properly, user-defined helpers must make use of the other r_c helpers. The following examples do not follow that convention for clarity purposes - see the docs for more details.
If you wanted, for example, to use a permalink for your post, you'd need to alter the way that posts are fetched. Just override the object method.
class PostsController < ResourceController::Base
private
def object
@object ||= Post.find_by_permalink(param[:id])
end
end
You'll probably also want to add pagination to your index method. In the same way we altered member object fetching, we can change collection fetching behavior.
class PostsController < ResourceController::Base
private
def collection
@collection ||= Post.find(:all, :page => {:size => 10, :current => params[:page]})
end
end
Details and examples in the RDocs.
Namespaced Resources
...are handled automatically, and any namespaces are available, symbolized, in array form from the namespaces helper method.Nested Resources
Again, handled automatically. This can be a real pain, and it's a lot easier with r_c. With an ActiveRecord-like syntax, just say belongs_to :model, and resource_controller takes care of the associations for you.class CommentsController < ResourceController::Base
belongs_to :post
end
Polymorphic Resources
This is a concept that can be found in a lot of my apps. Prior to resource_controller, it was a real pain on various levels. I solved some of my problems with urligence, and took things the rest of the way with resource_controller. It really does pretty much everything for you.Just use the belongs_to syntax in your controller, and r_c infers whichever association (if any) is present when an action is called. The arguments passed to belongs_to are single possible parents; there is no support for deeply nested resources (although, I'm not necessarily opposed to adding it, if there is demandwe).
class CommentsController < ResourceController::Base
belongs_to :post, :product
end
In the above example, the controller will automatically infer the presence of either a parent Post, or Product, and scope all the comments to that parent. The controller will also respond without a parent if you have that in your routes.
Thanks to urligence, generating urls in your polymorphic controller's views is really easy. The object_url, and collection_url helpers will maintain your parent resource's scope automatically.
# /posts/1/comments
object_url # => /posts/1/comments/#{@comment.to_param}
object_url(comment) # => /posts/1/comments/#{comment.to_param}
edit_object_url # => /posts/1/comments/#{@comment.to_param}/edit
collection_url # => /posts/1/comments
# /products/1/comments
object_url # => /products/1/comments/#{@comment.to_param}
object_url(comment) # => /products/1/comments/#{comment.to_param}
edit_object_url # => /products/1/comments/#{@comment.to_param}/edit
collection_url # => /products/1/comments
# /comments
object_url # => /comments/#{@comment.to_param}
object_url(comment) # => /comments/#{comment.to_param}
edit_object_url # => /comments/#{@comment.to_param}/edit
collection_url # => /comments
More in the RDocs.
Getting it
resource_controller is available under the MIT License.
It is currently available in two streams:
1.2.3+ Compatible
Install it:
svn export http://svn.jamesgolick.com/resource_controller/tags/stable vendor/plugins/resource_controller
SVN (stable): http://svn.jamesgolick.com/resource_controller/tags/stable
SVN (ongoing): http://svn.jamesgolick.com/resource_controller/trunk
Note: If you want to run the tests, cd in to the test directory, and type rake test.
Edge/Rails 2.0 Compatible
Install it:
svn export http://svn.jamesgolick.com/resource_controller/tags/edge_compatible/stable vendor/plugins/resource_controller
SVN (stable): http://svn.jamesgolick.com/resource_controller/tags/edge_compatible/stable
SVN (ongoing): http://svn.jamesgolick.com/resource_controller/branches/edge_compatible
Note: If you want to run the tests in the edge-compatible version, cd in to the test directory, and type rake rails:freeze:edge. I didn't want people to have to download all of edge rails just to get the plugin.
Also, check out the rdoc
Feedback
Drop me a line and let me know what you think. I'd love to hear your suggestions for API or implementation improvements, or anything else!


Nice work James!
I like the API. I might give it a try on my next project.
How did you test it? I saw you setup a full Rails app under test/ interesting!
Interesting.
What's the difference between this and the make_resourceful plugin you presented at montreal on rails (except from a different api)?
I like your objecturl helper and using belongsto to enumerate parent resources.
I've hacked together some stuff for handling polymorphic resources (might tidy up and release at some point), but it's not as nice. I've been waiting for something better to come along, and perhaps be merged into Rails, so I don't have to bother. ;) This seems like a step in the right direction.
Have you found yourself wanting to render in the parent's layout? E.g. /posts/1/comments with the "posts" layout; /products/1/comments with the "products" layout. I'm currently doing something like http://pastie.textmate.org/private/4qzqfi2ji0eybu4dj3nhgw which works well enough for my purposes, but I don't really like it. I'm not sure if rendering in the parent layout is a good idea at all – there could be issues with not having access to the parent's helpers, the layout may rely on data that the parent controller would set in filters (e.g. find_post)...
Since you seem to have good ideas in other polymorphic resource matters, do you have any here? :)
jfcouture: I don't see a lot of difference in purpose, but the API is a good bit different (e.g., you inherit from a class rather than some metaprogramming, which is sure to boost performance, the callbacks are structured differently, etc.).
The big win I see here, though, is the formats. I don't think you can easily do that with make_resourceful IIRC.
Differences between this and m_r:
So you've not seen this then: http://agilewebdevelopment.com/plugins/resources_controller
Almost exactly the same name and functionality made by ian white a few months ago.
Hi James,
Great plugin! Have you seen resources_controller ?
http://svn.ardes.com/railsplugins/resourcescontroller/
plugin to facilitate inheritance and DRYness in your resources controllers.
Nope, I had not heard of that... very very similar functionality. His looks like a great plugin too!
Maybe resource_controller (mine) needs a name change. Anybody have any ideas?
Just to be clear...
Anyhow, this isn't an mr/rc pissing contest. Different styles, different strengths.
I do have one major concern.... what if you only want a few actions? I find myself often just wanting a "show" action and nothing else. Is this possible?
Hampton is 100% right; it's not a pissing contest. Just two great plugins with different styles and strengths.
You can specify certain actions by saying:
actions :all, :except => [:show, :index] or actions :show, :index
It's a bit broken right now, though. I will correct that with a maintenance release before the end of the week.
The URL helpers in helpers.rb and urligence seem to assume the object's class name is always the correct name to use within the URL path. What is the best way to override this with a custom resource name?
Example: We have a model SpecialPost, and a controller SpecialPostsController. We want to expose paths such as, "/user/1/posts/", rather than "/user/1/special_posts/". So in routes.rb we do: map.resources :users do |user| user.resources :posts, :controller => 'specialposts', :nameprefix => 'user_' end
Osh: Sorry your comment didn't appear for so long. It got stuck in my filter. More and more seem tobe getting stuck in there.
The url issue is due to a current limitation with Urligence. The way smart_url works is by serializing all of the parameters, and calling the url helper that it thinks is responsible for generating that route. There's one last bit of syntax to add to urligence that will solve the problem, and I can bake it in to r_c, so that the free urls will work for non-standard controller names.
Shoot me an email, or send a post to the mailing list (see my latest blog entry), and we can chat further about this. I'll have you a fix very, very soon (tomorrow, hopefully).
+1 for developing the ability to handle deeply nested resources. I am just starting REST and deeply nested resources and was almost tearing my hair out (messy messy messy) before discovering these plugins existed.
Sadly, none of the available resource controller plugins seem to help much with the hardest part of polymorphic/nested controllers: Different views/layouts and different before_filters depending on what the resource is nested under. /users/3/posts is going to look and work a lot differently than /forums/3/posts, for example.
Do deeply nested resources work now? I see Jeff gave you a +1 for it, but I wasn't sure if that was to encourage you to do it or a reward for doign it. I could really use it.
What about the model class name vs. route path limitation? Has that been accounted for since the this blog post was made? Also important for me.
Frankly, I'm almost starting to regret this huge overhaul of one of my applications to be totally (or as much as possible) RESTful. Multiplexing (nested) controllers is kind of a pain in the butt, even with the plugins.
Think I found a little missing piece...
The ResourceController::Controller self.included method calls helpermethod, but it doesn't include ":parentparam". I added it in my code and works as I'd except.
Thank you for this wonderful plugin! Matt
I love the plugin, thanks for releasing it!
Very often, I run into a situation where I want to scope a resource by the current_user. For instance, for a 'new' action, I might do:
def new current_user.resources.build end
In rc, I can't just use belongsto :user because the user is not explicitly included in my route, it's assumed. How can I easily change the base object so that when I use the endof_associationchain method or similar methods, it uses my properly scoped resource?
Thanks!
First of all,
Congratulations for your work, but I wold like to install resource_controller in Aptana. Is it possible?
Hi James,
Great plugin! I changed something in the plugin and thought I would make you aware and get your thoughts.
def namespaces names = self.class.name.split("::") names.pop
names.map(&:underscore).map(&:to_sym) end
I change the map function to :underscore instead of :downcase. For example, I had a my_account name space. It was trying to create urls like the following:
newmyaccountaddress_path
instead of the correct route
newmy_accountaddress_path
Not sure if this was intentional or not, but thought I'd let you know.