About

James Golick

James Golick is an engineer, entrepreneur, speaker, and above all else, a grinder.

As CTO (or something?) of BitLove, he scaled FetLife.com's traffic by more than an order of magnitude (and counting).

James spends most of his time writing ruby and scala, building infrastructure, and extinguishing fires.

He speaks regularly at conferences and blogs periodically, but James values shipping code over just about anything else.

Latest Tweets

follow me on Twitter

James on the Web

resource_controller 0.1.5: Another Week, Another Release

Nov 01 2007

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.

class TagsController < ResourceController::Base
  private
    def model_name
      'photo_tag'
    end
end

In that example, the variables, and form_builders available in your views would be named photo_tag(s). If you wanted to change that to match the name of your model, you'd override object_name.

class TagsController < ResourceController::Base
  private
    def model_name
      'photo_tag'
    end
    
    def object_name
      'photo_tag'
    end
end

Your controller might have some non-standard name, too. If it does, just override route_name.

## routes.rb
map.resources :tags, :controller => "photo_tags"

## photo_tags_controller.rb
class PhotoTagsController < ResourceController::Base
  private
    def route_name
      'tag'
    end
end

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_url
smart_path
hash_for_smart_url
hash_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.

r_c has also gained helpers for all of these methods...

# object
[new_|edit_]object_url
[new_|edit_]object_path
hash_for_[new_|edit_]object_url
hash_for_[new_|edit_]object_path

# collection
collection_url
collection_path
hash_for_collection_url
hash_for_collection_path

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 < ApplicationController
  resource_controller
end

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

Get It!

1.2.3+ Compatible

svn export http://svn.jamesgolick.com/resource_controller/tags/stable vendor/plugins/resource_controller

Edge/Rails 2.0 Compatible

svn export http://svn.jamesgolick.com/resource_controller/tags/edge_compatible/stable vendor/plugins/resource_controller

If you're looking for older versions, try browsing http://svn.jamesgolick.com/resource_controller/tags.

For more info, see the resource_controller section of my blog.

Come join the discussion on the mailing list.


Introducing resource_controller: Focus on what makes your controller special.

Oct 19 2007

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!


Rails polymorphic url generation sucks. Here's something better.

Oct 11 2007

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

map.resources :photos
map.resources :users, :has_many => :photos
map.resources :tags, :has_many => :photos

## photos_controller.rb

class PhotosController
  ## in the create method
  wants.html { redirect_to url_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 method
  wants.html { redirect_to photos_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_object
      case
        when params[:user_id] then User.find_by_id(params[:article_id])
        when params[:tag_id] then Tag.find_by_id(params[:news_id])
      end    
    end
  
    def 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 :(
    end
end

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

URLigence

Intelligent url building.
## routes.rb

map.resources :photos
map.resources :users, :has_many => :photos
map.resources :tags, :has_many => :photos

map.namespace :admin do |admin|
  admin.resources :products, :has_many => :options
  admin.resources :options
end

##

assert_equal "/photos", smart_url(:photos)
assert_equal "/photos", smart_url(nil, nil, nil, :photos) # in case your parent objects aren't there
assert_equal "/users/james/photos", smart_url(User.find_by_name(:james), :photos)
assert_equal "/users/james/photos/1", smart_url(User.find_by_name(:james), Photo.find_by_id(1))
assert_equal "/photos/1", smart_url(nil, Photo.find_by_id(1)) # in the case that your parent object isn't there
assert_equal "/photos/1/edit", smart_url(:edit, Photo.find_by_id(1))

assert_equal "/admin/products", smart_url(:admin, :products)
assert_equal "/admin/products/1", smart_url(:admin, Product.find_by_id(1))
assert_equal "/admin/products/1/options", smart_url(:admin, Product.find_by_id(1), :options)
assert_equal "/admin/products/1/edit", smart_url(:edit, :admin, Product.find_by_id(1))
assert_equal "/admin/options", smart_url(:admin, nil, :options) # in the case that your parent object isn't there
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.

Get It

Get version 0.1 at http://svn.jamesgolick.com/urligence/tags/stable. Oh, and don't take the name of the tag too seriously - I just wrote it tonight.

svn export http://svn.jamesgolick.com/urligence/tags/stable vendor/plugins/urligence

Let me know what you think of it.