Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Rails RESTful controller abstraction plugin.
Pull request Compare This branch is even with jamesgolick:deep_nesting.

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.
generators/scaffold_resource
lib
test
.gitignore
LICENSE
README
Rakefile
TODO
init.rb
install.rb
uninstall.rb

README

= Resource Controller

resource_controller makes RESTful controllers easier, more maintainable, and super readable.  With the RESTful controller pattern hidden away, you can focus on what makes your controller special.

== Get 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}[http://svn.jamesgolick.com/resource_controller/tags/stable]

SVN (ongoing): {http://svn.jamesgolick.com/resource_controller/trunk}[http://svn.jamesgolick.com/resource_controller/trunk]

= Usage

Creating a basic RESTful controller is as easy as...

  class PostsController < ResourceController::Base
  end
  
...or if you prefer, you can use the method-call syntax.  If you need to inherit from some other class, this syntax is definitely for you:

  class PostsController < ApplicationController
    resource_controller
  end

Both syntaxes are identical in their behavior.  Just make sure you call resource_controller before you use any other r_c functionality in your controller.
  
  
Nobody just uses the default RESTful controller, though.  resource_controller provides a simple API for customizations.

== Action Lifecycle

It's really easy to make changes to the lifecycle of your actions.

  Note: We had to call the new accessor "new_action", since new is somewhat reserved in ruby.

=== Before and After

  class ProjectsController < ResourceController::Base
    
    new_action.before do
      3.times { object.tasks.build }
    end
    
    create.after do
      object.creator = current_user
    end
    
  end
  
=== Flash

  class ProjectsController < ResourceController::Base
    create.flash "Can you believe how easy it is to use resource_controller?  Neither could I!"
  end

=== respond_to

You can add to what's already there...
  
  class ProjectsController < ResourceController::Base      
    create.wants.js { render :template => "show.rjs" }
  end
  
Or you can create a whole new block.  This syntax destroys everything that's there, and starts again...

  class ProjectsController < ResourceController::Base      
    create.response do |wants|
      wants.html
      wants.js { render :template => "show.rjs" }
    end
  end
  
=== Scoping

Because sometimes you want to make a bunch of customizations at once, most of the helpers accept blocks that make grouping calls really easy.  Is it a DSL?  Maybe; maybe not.  But, it's definitely awesome.

With actions that can fail, the scoping defaults to success.  That means that create.flash == create.success.flash.

  class ProjectsController < ResourceController::Base
    
    create do
      flash "Object successfully created!"
      wants.js { render :template => "show.rjs" }
      
      failure.wants.js { render :template => "display_errors.rjs" }
    end
    
    destroy do
      flash "You destroyed your project.  Good work."
      
      failure do
        flash "You cannot destroy that project.  Stop trying!"
        wants.js { render :template => "display_errors.rjs" }
      end
    end
    
  end

== Helpers (ResourceController::Helpers)

=== Loading objects

You want to add something like pagination to your controller...

  class PostsController < ResourceController::Base
    private
      def collection
        @collection ||= end_of_association_chain.find(:all, :page => {:size => 10, :current => params[:page]})
      end
  end
  
Or maybe you used a permalink...

  class PostsController < ResourceController::Base
    private
      def object
        @object ||= end_of_association_chain.find_by_permalink(param)
      end
  end

=== Building objects

Maybe you have some alternative way of building objects...

  class PostsController < ResourceController::Base
    private
      def build_object
        @object ||= end_of_association_chain.build_my_object_some_funky_way object_params
      end
  end
  
...and there are tons more helpers in the ResourceController::Helpers

== Nested Resources

Nested controllers can be a pain, especially if routing is such that you may or may not have a parent.  Not so with Resource Controller.

  class CommentsController < ResourceController::Base
    belongs_to :post
  end
  
All of the finding, and creation, and everything will be done at the scope of the post automatically.

== Namespaced Resources

...are handled automatically, and any namespaces are always available, symbolized, in array form @ ResourceController::Helpers#namespaces

== Polymorphic Resources

Everything, including url generation is handled completely automatically.  Take this example...
  
  ## comment.rb
  class Comment
    belongs_to :commentable, :polymorphic => true
  end
  
  ## comments_controller.rb
  class CommentsController < ResourceController::Base
    belongs_to :post, :product, :user
  end
  *Note:* Your model doesn't have to be polymorphic in the ActiveRecord sense.  It can be associated in whichever way you want.
  
  ## routes.rb
  map.resources :posts, :has_many => :comments
  map.resources :products, :has_many => :comments
  map.resources :users, :has_many => :comments

All you have to do is that, and r_c will infer whichever relationship is present, and perform all the actions at the scope of the parent object.

=== Parent Helpers

You also get some helpers for reflecting on your parent.

  parent?       # => true/false is there a parent present?
  parent_type   # => :post
  parent_model  # => Post
  parent_object # => @post

=== Non-standard resource names

resource_controller supports overrides for every non-standard configuration of resources.

The most common example is where the resource has a different name than the associated model.  Simply overriding the model_name helper will get resource_controller working with your model.

  map.resources :tags
  ...
  class PhotoTag < ActiveRecord::Base
  ...
  class TagsController < ResourceController::Base
    private
      def model_name
        'photo_tag'
      end
  end
  
In the above example, the variable, and params will be set to @tag, @tags, and params[:tag].  If you'd like to change that, override object_name.

  def object_name
    'photo_tag'
  end

If you're using a non-standard controller name, but everything else is standard, overriding resource_name will propagate through all of the other helpers.

  map.resources :tags, :controller => "somethings"
  ...
  class Tag < ActiveRecord::Base
  ...
  class SomethingsController < ResourceController::Base
    private
      def resource_name
        'tag'
      end
  end
  
Finally, the route_name helper is used by Urligence to determine which url helper to call, so if you have non-standard route names, override it.

  map.resources :tags, :controller => "taggings"
  ...
  class Taggings < ActiveRecord::Base
  ...
  class TaggingsController < ResourceController::Base
    private
      def route_name
        'tag'
      end
  end

== Url Helpers

Thanks to Urligence, you also get some free url helpers.

No matter what your controller looks like...

  [edit_|new_]object_url # is the equivalent of saying [edit_|new_]post_url(@post)
  [edit_|new_]object_url(some_other_object) # allows you to specify an object, but still maintain any paths or namespaces that are present
  
  collection_url # is like saying posts_url

Url helpers are especially useful when working with polymorphic controllers.

  # /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
  
Or with namespaced, nested controllers...

  # /admin/products/1/options
  object_url          # => /admin/products/1/options/#{@option.to_param}
  object_url(option)  # => /admin/products/1/options/#{option.to_param}
  edit_object_url     # => /admin/products/1/options/#{@option.to_param}/edit
  collection_url      # => /admin/products/1/options
  
You get the idea.  Everything is automagical!  All parameters are inferred.

== Credits

resource_controller was created, and is maintained by {James Golick}[http://jamesgolick.com].

== License

resource_controller is available under the {MIT License}[http://en.wikipedia.org/wiki/MIT_License]
Something went wrong with that request. Please try again.