module ShouldBeRestful # :nodoc: # Formats tested by #should_be_restful. Defaults to [:html, :xml] VALID_FORMATS = Dir.glob(File.join(File.dirname(__FILE__), 'formats', '*.rb')).map { |f| File.basename(f, '.rb') }.map(&:to_sym) # :doc: VALID_FORMATS.each {|f| require "should_be_restful/formats/#{f}"} # Actions tested by #should_be_restful VALID_ACTIONS = [:index, :show, :new, :edit, :create, :update, :destroy] # :doc: # A ResourceOptions object is passed into should_be_restful in order to configure the tests for your controller. # # Example: # class UsersControllerTest < Test::Unit::TestCase # fixtures :all # # def setup # ...normal setup code... # @user = User.find(:first) # end # # should_be_restful do |resource| # resource.identifier = :id # resource.klass = User # resource.object = :user # resource.parent = [] # resource.actions = [:index, :show, :new, :edit, :update, :create, :destroy] # resource.formats = [:html, :xml] # # resource.create.params = { :name => "bob", :email => 'bob@bob.com', :age => 13} # resource.update.params = { :name => "sue" } # # resource.create.redirect = "user_url(@user)" # resource.update.redirect = "user_url(@user)" # resource.destroy.redirect = "users_url" # # resource.create.flash = /created/i # resource.update.flash = /updated/i # resource.destroy.flash = /removed/i # end # end # # Whenever possible, the resource attributes will be set to sensible defaults. # class ResourceOptions # Configuration options for the create, update, destroy actions under should_be_restful class ActionOptions # String evaled to get the target of the redirection. # All of the instance variables set by the controller will be available to the # evaled code. # # Example: # resource.create.redirect = "user_url(@user.company, @user)" # # Defaults to a generated url based on the name of the controller, the action, and the resource.parents list. attr_accessor :redirect # String or Regexp describing a value expected in the flash. Will match against any flash key. # # Defaults: # destroy:: /removed/ # create:: /created/ # update:: /updated/ attr_accessor :flash # Hash describing the params that should be sent in with this action. attr_accessor :params end # Configuration options for the denied actions under should_be_restful # # Example: # context "The public" do # setup do # @request.session[:logged_in] = false # end # # should_be_restful do |resource| # resource.parent = :user # # resource.denied.actions = [:index, :show, :edit, :new, :create, :update, :destroy] # resource.denied.flash = /get outta here/i # resource.denied.redirect = 'new_session_url' # end # end # class DeniedOptions # String evaled to get the target of the redirection. # All of the instance variables set by the controller will be available to the # evaled code. # # Example: # resource.create.redirect = "user_url(@user.company, @user)" attr_accessor :redirect # String or Regexp describing a value expected in the flash. Will match against any flash key. # # Example: # resource.create.flash = /created/ attr_accessor :flash # Actions that should be denied (only used by resource.denied). Note that these actions will # only be tested if they are also listed in +resource.actions+ # The special value of :all will deny all of the REST actions. attr_accessor :actions end # Name of key in params that references the primary key. # Will almost always be :id (default), unless you are using a plugin or have patched rails. attr_accessor :identifier # Name of the ActiveRecord class this resource is responsible for. Automatically determined from # test class if not explicitly set. UserTest => "User" attr_accessor :klass # Name of the instantiated ActiveRecord object that should be used by some of the tests. # Defaults to the underscored name of the AR class. CompanyManager => :company_manager attr_accessor :object # Name of the parent AR objects. Can be set as parent= or parents=, and can take either # the name of the parent resource (if there's only one), or an array of names (if there's # more than one). # # Example: # # in the routes... # map.resources :companies do # map.resources :people do # map.resources :limbs # end # end # # # in the tests... # class PeopleControllerTest < Test::Unit::TestCase # should_be_restful do |resource| # resource.parent = :companies # end # end # # class LimbsControllerTest < Test::Unit::TestCase # should_be_restful do |resource| # resource.parents = [:companies, :people] # end # end attr_accessor :parent alias parents parent alias parents= parent= # Actions that should be tested. Must be a subset of VALID_ACTIONS (default). # Tests for each actionw will only be generated if the action is listed here. # The special value of :all will test all of the REST actions. # # Example (for a read-only controller): # resource.actions = [:show, :index] attr_accessor :actions # Formats that should be tested. Must be a subset of VALID_FORMATS (default). # Each action will be tested against the formats listed here. The special value # of :all will test all of the supported formats. # # Example: # resource.actions = [:html, :xml] attr_accessor :formats # ActionOptions object specifying options for the create action. attr_accessor :create # ActionOptions object specifying options for the update action. attr_accessor :update # ActionOptions object specifying options for the desrtoy action. attr_accessor :destroy # DeniedOptions object specifying which actions should return deny a request, and what should happen in that case. attr_accessor :denied def initialize # :nodoc: @create = ActionOptions.new @update = ActionOptions.new @destroy = ActionOptions.new @denied = DeniedOptions.new @create.flash ||= /created/i @update.flash ||= /updated/i @destroy.flash ||= /removed/i @denied.flash ||= /denied/i @create.params ||= {} @update.params ||= {} @actions = VALID_ACTIONS @formats = VALID_FORMATS @denied.actions = [] end def normalize!(target) # :nodoc: @denied.actions = VALID_ACTIONS if @denied.actions == :all @actions = VALID_ACTIONS if @actions == :all @formats = VALID_FORMATS if @formats == :all @denied.actions = @denied.actions.map(&:to_sym) @actions = @actions.map(&:to_sym) @formats = @formats.map(&:to_sym) ensure_valid_members(@actions, VALID_ACTIONS, 'actions') ensure_valid_members(@denied.actions, VALID_ACTIONS, 'denied.actions') ensure_valid_members(@formats, VALID_FORMATS, 'formats') @identifier ||= :id @klass ||= target.name.gsub(/ControllerTest$/, '').singularize.constantize @object ||= @klass.name.tableize.singularize @parent ||= [] @parent = [@parent] unless @parent.is_a? Array collection_helper = [@parent, @object.to_s.pluralize, 'url'].flatten.join('_') collection_args = @parent.map {|n| "@#{object}.#{n}"}.join(', ') @destroy.redirect ||= "#{collection_helper}(#{collection_args})" member_helper = [@parent, @object, 'url'].flatten.join('_') member_args = [@parent.map {|n| "@#{object}.#{n}"}, "@#{object}"].flatten.join(', ') @create.redirect ||= "#{member_helper}(#{member_args})" @update.redirect ||= "#{member_helper}(#{member_args})" @denied.redirect ||= "new_session_url" end private def ensure_valid_members(ary, valid_members, name) # :nodoc: invalid = ary - valid_members raise ArgumentError, "Unsupported #{name}: #{invalid.inspect}" unless invalid.empty? end end end