Latest Tweets

James on the Web

Since last week, I've had some really great user feedback from the community. From that feedback, four new features made it in to this week's release. Here they are.

Support for Custom Route Names

Osh let me know that r_c didn't really support non-standard resource names. That is, it wasn't possible to have a controller whose name didn't match its model. Now, not only does r_c support non-standard resource names, but it supports every configuration of non-standard resources imaginable.

Osh's example was the simplest, and probably the most common. He has a resource with a name that doesnt't match the model. In that case, all you have to do is override model_name.

All of those new helpers default to the value of the resource_name helper, which is derived from the name of the controller.

New Urligence Syntax

In order to support non-standard routes in resource_controller, Urligence has gained some new syntax. smart_url infers the name of a resource from an object's class name, so a non-standard route name was impossible. Now, if you provide a 2-element array parameter to smart_url, the first element (a symbol) is used as the resource name, and the second element is the object that is passed to the url helper. It looks like this...

smart_url([:tag,@photo_tag])

All of the old syntax is still, of course, valid, and can be mixed and matched with the new syntax.

hash_for, path, and url

On the topic of Urligence, Hsiu-Fan raised the issue that smart_url would only return paths. Not really an accurate method name, and not convenient if you want a URL, or a hash. Hsiu-Fan was also nice enough to send in a patch, which I refactored a bit, and now we have four methods.

smart_urlsmart_pathhash_for_smart_urlhash_for_smart_path

Important: The smart_url method now outputs URLs, not paths. If you are depending on paths coming out of smart_url anywhere in your code, or tests, this may be a code breaking change.

New Instantiation Syntax

Another thing Osh brought up with me, during our back and forth, was his concern with r_c's inheritance syntax. Since there isn't any particular reason for that syntax other than the fact that I like it, I have added an alternative. Just say resource_controller, and you'll get the exact same effect as inheriting from ResourceController::Base.

class PhotosController<ApplicationControllerresource_controllerend

Just make sure that you call the resource_controller method before you use any other r_c features.

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::Baseend

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.

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.

Helpers

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.

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::Basebelongs_to:postend

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).

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.

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.

If you've ever written a rails app with a lot of polymorphic resources, you've probably noticed that generating urls for your polymorphic routes is a real pain. In edge rails, some of the problems have been solved by the new polymorphic url methods. Unfortunately, those solutions are both inadequate, and "not recommended for use by end users" (see DHH's comment at the bottom). The problem is that if you want to use the same views and controller code without a lot of ugly conditionals to generate your urls, you're out of luck.

The Problem

## routes.rbmap.resources:photosmap.resources:users,:has_many=>:photosmap.resources:tags,:has_many=>:photos## photos_controller.rbclass PhotosController## in the create methodwants.html{redirect_tourl_for(url_helper_params)}# I know DHH doesn't want us to use this, but how else can I get this effect?## in the destroy methodwants.html{redirect_tophotos_url}# unfortunately, this will drop any parent paths we may have# a user who called DELETE /users/james/photos/1 ends up at /photos instead of /users/james/photos)private# Note: I actually use a modification to make_resourceful to do this far more cleanly, but wanted to be as complete as possible in this example.def parent_objectcasewhenparams[:user_id]thenUser.find_by_id(params[:article_id])whenparams[:tag_id]thenTag.find_by_id(params[:news_id])endenddef url_helper_params[parent_object,@photo].reject{|obj|obj.nil?}# we have to get rid of any the parent_object if it's nil, or else this will fail :(endend# index.rhtml<%= link_to "New Photo", new_photo_path %> # same problem as our destroy redirect

We have routes like /users/james/photos, and /tags/montrealonrails/photos. We want to seamlessly maintain the top level of the path without resorting to conditionals or repetition. That is, if you browse to /users/james/photos, and click on the show link, you should go to /users/james/photos/1, not /photos/1. There should also be a polymorphic plural url helper. We have url_for for members (/photos/1), but for collections (/photos), we're stuck resorting to calling the url helpers themselves. So, to create a collection url, we'd have to check what the parent object was, and then call the appropriate helper based on that. And, all of this is further complicated when there are namespaces (/cms/products). Wouldn't it be great if there was a better way?

Stupid Simple Urls

Try saying that three times fast. Done? Good, because it's not called that - I was just messing with you.

It's pretty straightforward. Just call smart_url with any parameters that might be there, including ones that might be nil, and symbols at the beginning for namespaces (simple_url/cms/products), member actions (/products/1/edit), or both (/cms/products/1/edit), objects in the middle, and a symbol at the end for a resource's index method (/products). That's it.