# = Inheriting # # To use InheritedResources you have to inherit from InheritedResources::Base # class. This class have all Rails REST actions defined (index, show, new, edit # update, create and destroy). The following definition is the same as a Rails # scaffolded controller: # # class ProjectController < InheritedResources::Base # end # # All actions are defined, check it! # # The next step is to define which mime types this controller provides: # # class ProjectController < InheritedResources::Base # respond_to :html, :xml, :json # end # # You just said that this controller will respond to :html, :xml and :json. You # can also specify it based on actions: # # class ProjectController < InheritedResources::Base # respond_to :html, :xml, :json # respond_to :js, :only => :create # respond_to :csv, :except => [ :destroy ] # end # # How it works is simple. Let's suppose you have a json request on the action # show. It will first try to render "projects/show.json.something". If it can't # be found, it will call :to_json in the resource, which in this case is # @project. # # If the resource @project doesn't respond to :to_json, we will render a 404 # Not Found. # # If you don't want to inherit all actions from InheritedResources::Base, call # actions method with the actions you want to inherit: # # class ProjectController < InheritedResources::Base # actions :index, :show, :new, :create, :edit, :update # end # # Or: # # class ProjectController < InheritedResources::Base # actions :all, :except => :destroy # end # # = Extending the default behaviour # # Let's suppose that after destroying a project you want to redirect to your # root url instead of redirecting to projects url. You just have to do: # # class ProjectController < InheritedResources::Base # def destroy # super do |format| # format.html { redirect_to projects_url } # end # end # end # # super? Yes, we agree that calling super is the right thing but it does not # look nice. That's why all methods have aliases. So this is equivalent: # # class ProjectController < InheritedResources::Base # def destroy # destroy! do |format| # format.html { redirect_to projects_url } # end # end # end # # Since this is actually Ruby (and not a new DSL), if you want to do something # before creating the project that is to small to deserve a before_filter, you # could simply do: # # class ProjectController < InheritedResources::Base # def create # # do something different! # create! # end # end # # And as instance variables are shared you can do more nice things. # Let's suppose you want to create a project based on the current user: # # class ProjectController < InheritedResources::Base # def create # @project = @current_user.projects.build(params[:project]) # create! # end # end # # When you call create! the instance variable @project is already defined, # so the method won't instanciate it again. # # The great thing is that we are not using blocks or nothing in special. We are # just inheriting and calling the parent (super). You can extend even more # without using blocks, please check helpers.rb for more info. # # = Flash and I18n # # Flash messages are changed through I18n API. If you have a ProjectsController, # when a resource is updated with success, it will search for messages in the # following order: # # 'flash.projects.update.notice' # 'flash.actions.update.notice' # # If none of them are not available, it will show the default message: # # Project was successfully updated. # # The message will be set into flash[:notice]. # Messages can be interpolated, so you can do the following in your I18n files: # # flash: # actions: # update: # notice: "Hooray! {{resource_name}} was updated with success!" # # It will replace {{resource_name}} by Project.human_name, which is also localized # (check http://rails-i18n.org/wiki/pages/i18n-rails-guide for more info). # # But sometimes, flash messages are not that simple. You might want to say the # the name of the project when it's updated. Well, that's easy also: # # flash: # projects: # update: # notice: "Dear manager, {{project_name}} was successfully updated!" # # Since :project_name is not available for interpolation by default, you # have to overwrite interpolation_options method on your controller. # # def interpolation_options # { :project_name => @project.quoted_name } # end # # Then you will finally have: # # 'Dear manager, "Make Rails Scale" was successfully updated!' # # Success messages appear on :create, :update and :destroy actions. Failure # messages appear only on :create and :update. # # = Changing assumptions # # When you inherit from InheritedResources::Base, we make some assumptions on # what is your resource_class, instance_name and collection_name. # # You can change those values by calling the class method defaults: # # class PeopleController < InheritedResources::Base # defaults :resource_class => User, :instance_name => 'user', :collection_name => 'users' # end # # Namespaced controllers work out of the box, but if you need to specify a # different route prefix, you can do the following: # # class Administrators::PeopleController < InheritedResources::Base # defaults :route_prefix => 'admin' # end # # Then your named routes will be: 'admin_people_url', 'admin_person_url' and # so on. # # Further customizations can be done replacing some methods. Check # base_helpers.rb file for more information. # Let's require all needed files here. We are still on time to eager load # everything on multithreaded environments. require File.dirname(__FILE__) + '/base_helpers.rb' require File.dirname(__FILE__) + '/belongs_to_helpers.rb' require File.dirname(__FILE__) + '/class_methods.rb' require File.dirname(__FILE__) + '/dumb_responder.rb' require File.dirname(__FILE__) + '/polymorphic_helpers.rb' require File.dirname(__FILE__) + '/singleton_helpers.rb' require File.dirname(__FILE__) + '/url_helpers.rb' module InheritedResources RESOURCES_ACTIONS = [ :index, :show, :new, :edit, :create, :update, :destroy ] unless self.const_defined? "RESOURCES_ACTIONS" class Base < ::ApplicationController unloadable include InheritedResources::BaseHelpers extend InheritedResources::ClassMethods helper_method :collection_url, :collection_path, :resource_url, :resource_path, :new_resource_url, :new_resource_path, :edit_resource_url, :edit_resource_path, :resource, :collection, :resource_class, :parent? def self.inherited(base) base.class_eval do # Make all resources actions public public *RESOURCES_ACTIONS end # Call super to register class in ApplicationController super # Creates and sets class accessors default values initialize_resources_class_accessors!(base) end protected # GET /resources def index(&block) respond_to(:with => collection, &block) end alias :index! :index # GET /resources/1 def show(&block) respond_to(:with => resource, &block) end alias :show! :show # GET /resources/new def new(&block) respond_to(:with => build_resource, &block) end alias :new! :new # GET /resources/1/edit def edit(&block) respond_to(:with => resource, &block) end alias :edit! :edit # POST /resources def create(&block) object = build_resource(params[resource_instance_name]) if object.save set_flash_message!(:notice, '{{resource_name}} was successfully created.') location_url = resource_url rescue nil # Sometimes resource_url is undefined respond_to(:with => object, :status => :created, :location => location_url) do |format| block.call(args_for_block(block, format, true)) if block_given? format.html { redirect_to(resource_url) } end else set_flash_message!(:error) respond_to(:with => object.errors, :status => :unprocessable_entity) do |format| block.call(args_for_block(block, format, false)) if block_given? format.html { render :action => 'new' } end end end alias :create! :create # PUT /resources/1 def update(&block) object = resource if object.update_attributes(params[resource_instance_name]) set_flash_message!(:notice, '{{resource_name}} was successfully updated.') respond_to do |format| block.call(args_for_block(block, format, true)) if block_given? format.html { redirect_to(resource_url) } format.all { head :ok } end else set_flash_message!(:error) respond_to(:with => object.errors, :status => :unprocessable_entity) do |format| block.call(args_for_block(block, format, false)) if block_given? format.html { render :action => 'edit' } end end end alias :update! :update # DELETE /resources/1 def destroy resource.destroy set_flash_message!(:notice, '{{resource_name}} was successfully destroyed.') respond_to do |format| yield(format) if block_given? format.html { redirect_to(collection_url) } format.all { head :ok } end end alias :destroy! :destroy end end