Inherited Resources is scopes and responder fluent

First, what is Inherited Resources?

Inherited Resources is a gem/plugin that allows you to speed up development by making your controllers inherit all restful actions so you just have to focus on what is important. A Rails scaffold controller which responds to html, xml and json is as simple as:

class ProjectsController < InheritedResources::Base
  respond_to :html, :xml, :json
end

However all actions are still customizable! For example, you can change the behavior of the create action on success just doing:

class ProjectsController < InheritedResouces::Base
  respond_to :html, :xml, :json

  def create
    create! do |success, failure|
      success.html { redirect_to edit_project_url(@project) }
    end
  end
end

It also supports I18n (all flash messages are set with I18n), belongs to association (like a task belongs to project), nested belongs to (task belongs to project which belongs to company), polymorphic belongs to (comments belong to task or file or message) and optional belongs to (the same as previously, but you can request the resource with or without parents).

As you noticed, besides simplifying your controllers, InheritedResouces makes easy to reproduce your models behavior and relationship in controllers. And right now, it's also scopes and responder fluent!

has_scope: named_scope twin brother

Let's suppose that we have a ProjectsController and at some point you want to add some filters on the index action like showing just featured projects, selecting projects by methodology or even let the user choose how many projects he can see per page. The first thing to do? Add named scopes to your model:

class Project < ActiveRecord::Base
  named_scope :featured, :conditions => { :featured => true }
  named_scope :by_methodology, proc {|methodology| { :conditions => { :methodology => methodology } } }
  named_scope :limit, proc{|limit| :limit => limit.to_i }
end

The next step would be to add a lot of code in your controllers that check which named scopes you should call, based on the parameters sent right? Well, not anymore. Your controller can be as simple as:

class ProjectsController < InheritedResources::Base
  has_scope :featured, :boolean => true
  has_scope :by_methodology
  has_scope :limit, :default => 10, :only => :index
end

Then for each request:

/projects
#=> acts like a normal request, but returning only 10 projects

/projects?featured=true
#=> calls the featured named scope and bring 10 featured projects

/projects?featured=true&by_methodology=agile&limit=20
#=> brings 20 featured projects with methodology agile

If you configure your routes, you could even have pretty urls with it:

/projects/agile/featured
#=> brings 10 featured projects with methodology agile

Yay!

Responder

A couple weeks ago, we wrote about ActionController::Responder. But you don't have to wait Rails 3 to be released to play with it, you can start now by using Inherited Resources.

Quick redirect

After using Inherited Resouces, I realized that most of the times I overwrite a create, update or destroy actions is to change the redirect destination. In our ProjectsController above, if we want to redirect to the edit page after create, we would have to do:

class ProjectsController < InheritedResouces::Base
  respond_to :html, :xml, :json

  def create
    create! do |success, failure|
      success.html { redirect_to edit_project_url(@project) }
    end
  end
end

It wouldn't be cool if it have a shortcut? Now it has:

class ProjectsController < InheritedResouces::Base
  respond_to :html, :xml, :json

  def create
    create! { edit_project_url(@project) }
  end
end

That simple, just give the url as a proc and since the proc does not expect any parameters, it assumes that you want to overwrite the redirect url.

Finally some DSL?

Last weeks, there was a discussion on Boston.rb group about different resource controllers gems. Reading some of the feedback there, I've decided to stab a DSL to Inherited Resources. It will mainly remove the duplication when you have to give a block, the previous example above could be written as:

class ProjectsController < InheritedResouces::Base
  respond_to :html, :xml, :json

  create! do |success, failure|
    success.html { redirect_to edit_project_url(@project) }
  end
end

Or even in the compact form:

class ProjectsController < InheritedResouces::Base
  respond_to :html, :xml, :json
  create! { redirect_to edit_project_url(@project) }
end

Those changes were not applied yet, it depends mainly on you. If you like or not, let us know in the comments!

  • http://1up4dev.org Roger Leite

    Great features!
    has_scope and responder would be a wonderful help!
    About the DSL … IMHO, i couldn’t use.

    Success!

  • http://1up4dev.org Roger Leite

    Great features!
    has_scope and responder would be a wonderful help!
    About the DSL … IMHO, i couldn’t use.

    Success!

  • http://blogbangbang.com jblanche

    That’s just awesome !
    I want all of it, even if the DSL part looks less interresting to me than the rest.

  • http://blogbangbang.com jblanche

    That’s just awesome !
    I want all of it, even if the DSL part looks less interresting to me than the rest.

  • http://- Diego Caliri

    Looks nice! Will try it. I have a project that could use right now the feature

  • http://www.urubatan.com.br Rodrigo Urubatan

    It looks very pretty, I liked it a lot!!
    Mainly the integration between the controlers and the models

  • http://www.urubatan.com.br Rodrigo Urubatan

    It looks very pretty, I liked it a lot!!
    Mainly the integration between the controlers and the models

  • http://twitter.com/ivanyv2 Ivan

    That’s some awesome work, thanks José!

    Would it be too difficult to make has_scope calls accept an :as parameter? For cases when you prefer to code in english but expose to the user in another language. Something like this:

    has_scope :by_methodology, :as => ‘por_metodologia’, :boolean => true

    Or:

    has_scope :by_methodology => ‘por_metodologia’, :boolean => true

  • http://twitter.com/ivanyv2 Ivan

    That’s some awesome work, thanks José!

    Would it be too difficult to make has_scope calls accept an :as parameter? For cases when you prefer to code in english but expose to the user in another language. Something like this:

    has_scope :by_methodology, :as => ‘por_metodologia’, :boolean => true

    Or:

    has_scope :by_methodology => ‘por_metodologia’, :boolean => true

  • http://iain.nl iain

    Looks great! I will certainly take a look at it. Two questions though: Can you alter certain behavior globally? I remember that being a pain in the ass to do with other gems (but that was almost a year ago). And do you have a neat trick on how to test the results? Maybe in combination with Remarkable?

  • http://iain.nl iain

    Looks great! I will certainly take a look at it. Two questions though: Can you alter certain behavior globally? I remember that being a pain in the ass to do with other gems (but that was almost a year ago). And do you have a neat trick on how to test the results? Maybe in combination with Remarkable?

  • http://matflores.com Matías Flores

    Awesome! Thanks!!

  • http://matflores.com Matías Flores

    Awesome! Thanks!!

  • José Valim

    @Ivan there is already an options called :key that does what you suggested. But :as is definitely a better name! I’m going to change that! :)

    @iain Whenever I’m using Inherited Resources, I do not write controller specs, just integration ones (unless a controller does not follow IR pattern). So I test such behavior just in integration specs!

  • José Valim

    @Ivan there is already an options called :key that does what you suggested. But :as is definitely a better name! I’m going to change that! :)

    @iain Whenever I’m using Inherited Resources, I do not write controller specs, just integration ones (unless a controller does not follow IR pattern). So I test such behavior just in integration specs!

  • http://bendyworks.com bendycode

    +1 for the DSL. It looks like a nice step towards even more declarative power.

  • http://bendyworks.com bendycode

    +1 for the DSL. It looks like a nice step towards even more declarative power.

  • http://www.jackhq.com jackhq

    +1 for the DSL, very well put together, can’t wait to give it a try!

  • http://www.jackhq.com jackhq

    +1 for the DSL, very well put together, can’t wait to give it a try!

  • http://esopp.us Richie Vos

    Has scope seems pretty phenomenal. I’m not using inherited resources but am definitely going to have to track down how you’re implementing that.

  • http://esopp.us Richie Vos

    Has scope seems pretty phenomenal. I’m not using inherited resources but am definitely going to have to track down how you’re implementing that.

  • malkomalko

    Jose,

    Great job on the has_scope. Is this actually looking for a named_scope or would it work also for a method defined on the class?

  • malkomalko

    Jose,

    Great job on the has_scope. Is this actually looking for a named_scope or would it work also for a method defined on the class?

  • José Valim

    @malkomalko it also works for methods defined on the class!

  • José Valim

    @malkomalko it also works for methods defined on the class!

  • malkomalko

    Great!

  • malkomalko

    Great!

  • http://rrn.dk Rasmus

    Looks great. I have a question though.

    Is inhering from InheritedResource::Base just a transition thing while we’re waiting for Rails 3?

  • http://rrn.dk Rasmus

    Looks great. I have a question though.

    Is inhering from InheritedResource::Base just a transition thing while we’re waiting for Rails 3?

  • José Valim

    @Rasmus, what do you mean?

    Inherited Resources is a plugin and there is no plan to be merged with Rails 3. So that means you still need to inherit from InheritedResources::Base. :)

  • José Valim

    @Rasmus, what do you mean?

    Inherited Resources is a plugin and there is no plan to be merged with Rails 3. So that means you still need to inherit from InheritedResources::Base. :)

  • http://rrn.dk Rasmus

    José,

    Thank you for the clarification. I somehow managed to misunderstand that this was a plugin. May I suggest that you write that InheritedResources is a plugin, in the section called “First, what is Inherited Resources?” so that slow people like me understand faster ;)

    Also, reading through the source of the plugin I realized it contains a version of ActionController::Responder. This led me to understand how (and why) the plugin functions with both Rails 2 and 3.

  • http://rrn.dk Rasmus

    José,

    Thank you for the clarification. I somehow managed to misunderstand that this was a plugin. May I suggest that you write that InheritedResources is a plugin, in the section called “First, what is Inherited Resources?” so that slow people like me understand faster ;)

    Also, reading through the source of the plugin I realized it contains a version of ActionController::Responder. This led me to understand how (and why) the plugin functions with both Rails 2 and 3.

  • José Valim

    Thanks Rasmus! Already updated the post!

  • José Valim

    Thanks Rasmus! Already updated the post!

  • Pingback: Kai’s daily tech #28 … | Kai Richard König

  • http://catzo.com Aaron Renoir

    Very cool. I am going to use this with all my projects.

    I am having an issue unrelated to has_scope and responder.
    I am using the begin_of_association_chain method to set the current_user and current_domain. It works for a single resource but once I start nesting them it fails to set the domain_id or user_id. I am currently overriding all the update and create actions to add the domain_id and user_id in? Am I missing something?

  • http://catzo.com Aaron Renoir

    Very cool. I am going to use this with all my projects.

    I am having an issue unrelated to has_scope and responder.
    I am using the begin_of_association_chain method to set the current_user and current_domain. It works for a single resource but once I start nesting them it fails to set the domain_id or user_id. I am currently overriding all the update and create actions to add the domain_id and user_id in? Am I missing something?

  • José Valim

    @Aaron, I need to see some code in order to reply you. ;)

  • José Valim

    @Aaron, I need to see some code in order to reply you. ;)

  • http://catzo.com Aaron Renoir
  • http://catzo.com Aaron Renoir
  • José Valim

    Aaron, looking to the code at the pastie, the begin of association chain can only set the @current_user OR the @current_domain. So you don’t need to set the one of them in your actions, but you will need to overwrite create and update to set the other.

  • José Valim

    Aaron, looking to the code at the pastie, the begin of association chain can only set the @current_user OR the @current_domain. So you don’t need to set the one of them in your actions, but you will need to overwrite create and update to set the other.

  • Oleh

    Thanks for great plugin!
    I have a question: what approach would you suggest if i want to inherit some of my controllers from SecureController with, say, permissions check in before_filter?

  • Oleh

    Thanks for great plugin!
    I have a question: what approach would you suggest if i want to inherit some of my controllers from SecureController with, say, permissions check in before_filter?

  • José Valim

    @Oleh, you have two options: 1) Move SecureController actions to a module or 2) use your SecureController and call inherit_resources inside your new controller:

    class MyController < SecureController
    inherit_resources
    end

  • José Valim

    @Oleh, you have two options: 1) Move SecureController actions to a module or 2) use your SecureController and call inherit_resources inside your new controller:

    class MyController < SecureController
    inherit_resources
    end

  • Oleh

    Thanx! I must read docs more carefully :)

  • Oleh

    Thanx! I must read docs more carefully :)