Changeset 1014

Show
Ignore:
Timestamp:
11/17/07 08:10:24 (1 year ago)
Author:
in..@loobmedia.com
Message:

added arguments/proc logic to provides with transform_method object rendering (eg. obj.to_xml(:include => ...)

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/lib/merb/mixins/render.rb

    r964 r1014  
    146146          if transform_method = Merb.mime_transform_method(fmt) 
    147147            set_response_headers fmt 
    148             return obj.send(transform_method) 
     148            transform_args = provided_format_arguments_for(fmt) 
     149            return case transform_args 
     150              when Hash   then obj.send(transform_method, transform_args) 
     151              when Array  then obj.send(transform_method, *transform_args) 
     152              when Proc   then 
     153                case transform_args.arity 
     154                  when 3 then transform_args.call(obj, self, transform_method) 
     155                  when 2 then transform_args.call(obj, self) 
     156                  when 1 then transform_args.call(obj) 
     157                  else transform_args.call 
     158                end 
     159              else obj.send(transform_method) 
     160            end 
    149161          end   
    150162        end 
  • trunk/lib/merb/mixins/responder.rb

    r807 r1014  
    1919      ResponderMixin::Rest::TYPES.update(key => values) 
    2020      add_response_headers!(key, new_response_headers)    
    21       ResponderMixin::Rest::TRANSFORM_METHODS.merge!(key => transform_method)  
     21      ResponderMixin::Rest::TRANSFORM_METHODS.merge!(key => transform_method) 
    2222    end     
    2323     
     
    2626    end 
    2727     
     28    # Return the method name (if any) for the mimetype 
    2829    def mime_transform_method(key) 
    2930      ResponderMixin::Rest::TRANSFORM_METHODS[key] 
    3031    end 
    3132     
     33    # Return default arguments for transform method (if any) 
     34    def mime_transform_method_defaults(key) 
     35      ResponderMixin::Rest::TRANSFORM_METHOD_DEFAULTS[key] 
     36    end 
     37     
     38    # Set default arguments for transform method 
     39    def set_mime_transform_method_defaults(key, *args) 
     40      raise "Unknown mimetype #{key}" unless ResponderMixin::Rest::TRANSFORM_METHODS[key] 
     41      ResponderMixin::Rest::TRANSFORM_METHOD_DEFAULTS[key] = args unless args.empty? 
     42    end 
    3243     
    3344    # Adds outgoing headers to a mime type.  This can be done with the Merb.add_mime_type method 
     
    166177    def self.included(base) # :nodoc: 
    167178      base.extend(ClassMethods) 
    168       base.class_eval { class_inheritable_accessor :class_provided_formats } 
    169       base.class_provided_formats = [:html] 
     179      base.class_eval do 
     180        class_inheritable_accessor :class_provided_formats 
     181        class_inheritable_accessor :class_provided_format_arguments 
     182      end 
     183      base.reset_provides 
    170184    end 
    171185 
     
    175189      # default list of provided_formats.  These will apply to 
    176190      # every action in the controller, unless modified in the action. 
    177       def provides(*formats) 
    178         formats.each {|fmt| 
     191      # If the last argument is a Hash or an Array, these are regarded 
     192      # as arguments to pass to the to_<mime_type> method as needed. 
     193      def provides(*formats, &block) 
     194        options = extract_provides_options(formats, &block) 
     195        formats.each do |fmt| 
    179196          self.class_provided_formats << fmt unless class_provided_formats.include?(fmt) 
    180         } 
     197          self.class_provided_format_arguments[fmt] = options unless options.nil? 
     198        end 
    181199      end 
    182200       
     
    185203      # in the action. 
    186204      def only_provides(*formats) 
    187         self.class_provided_formats = formats 
     205        clear_provides 
     206        provides(*formats) 
    188207      end 
    189208       
    190209      # Removes formats from the controller's 
    191       # default list of provided_formats. These will apply to 
     210      # default list of provided_formats. These will apply to 
    192211      # every action in the controller, unless modified in the action. 
    193212      def does_not_provide(*formats) 
    194213        self.class_provided_formats -= formats 
    195       end 
     214        formats.each { |fmt| self.class_provided_format_arguments.delete(fmt) } 
     215      end 
     216       
     217      # Clear any formats and their options 
     218      def clear_provides 
     219        self.class_provided_formats = [] 
     220        self.class_provided_format_arguments = {} 
     221      end 
     222       
     223      # Reset to the default list of formats 
     224      def reset_provides 
     225        only_provides(:html) 
     226      end 
     227       
     228      # Extract arguments for provided format methods 
     229      def extract_provides_options(args, &block) 
     230        return block if block_given? 
     231        case args.last 
     232          when Hash   then [args.pop] 
     233          when Array  then args.pop 
     234          when Proc   then args.pop 
     235          else nil 
     236        end 
     237      end 
     238             
    196239    end 
    197240     
     
    200243    # (or :html by default) but can be modifed on a per-action basis. 
    201244    def provided_formats 
    202       @_provided_formats ||= class_provided_formats 
     245      @_provided_formats ||= class_provided_formats.dup 
    203246    end 
    204247     
     
    206249    # use a combination of +provides+, +only_provides+ and +does_not_provide+ 
    207250    # to manage this, but you can set it directly. 
    208     def provided_formats=(*formats) 
    209       raise "Cannot modify provided_formats because content_type has already been set" if content_type_set? 
    210       @_provided_formats = formats.flatten 
     251    # If the last argument is a Hash or an Array, these are regarded 
     252    # as arguments to pass to the to_<mime_type> method as needed.  
     253    def set_provided_formats(*formats, &block) 
     254      raise_if_content_type_already_set! 
     255      @_provided_formats = [] 
     256      @_provided_format_arguments = {} 
     257      provides(*formats.flatten, &block) 
     258    end 
     259    alias :provided_formats= :set_provided_formats 
     260     
     261    # Returns a Hash of arguments for format methods 
     262    def provided_format_arguments 
     263      @_provided_format_arguments ||= Hash.new.replace(class_provided_format_arguments) 
     264    end 
     265     
     266    # Returns the arguments (if any) for the mime_transform_method call 
     267    def provided_format_arguments_for(fmt) 
     268      self.provided_format_arguments[fmt] || Merb.mime_transform_method_defaults(fmt) 
    211269    end 
    212270     
     
    214272    # request.  Usually used to add formats to a single action.  See also 
    215273    # the controller-level provides that affects all actions in a controller. 
    216     def provides(*formats) 
    217       formats.each {|fmt| 
    218         self.provided_formats += [fmt] unless provided_formats.include?(fmt) 
    219       } 
     274    def provides(*formats, &block) 
     275      raise_if_content_type_already_set! 
     276      options = self.class.extract_provides_options(formats, &block) 
     277      formats.each do |fmt| 
     278        self.provided_formats << fmt unless provided_formats.include?(fmt) 
     279        self.provided_format_arguments[fmt] = options unless options.nil? 
     280      end 
    220281    end 
    221282     
     
    224285    # the controller-level provides that affects all actions in a controller. 
    225286    def only_provides(*formats) 
    226       self.provided_formats = formats.flatten 
     287      self.set_provided_formats(*formats) 
    227288    end 
    228289     
     
    232293    # controller. 
    233294    def does_not_provide(*formats) 
    234       self.provided_formats -= formats.flatten 
     295      formats.flatten! 
     296      self.provided_formats -= formats 
     297      formats.each { |fmt| self.provided_format_arguments.delete(fmt) } 
    235298    end 
    236299     
     
    298361    end 
    299362 
     363    private 
     364     
     365    def raise_if_content_type_already_set! 
     366      raise "Cannot modify provided_formats because content_type has already been set" if content_type_set? 
     367    end 
     368 
    300369    module Rest 
    301370       
     
    303372      RESPONSE_HEADERS = {} 
    304373      TRANSFORM_METHODS = {} 
     374      TRANSFORM_METHOD_DEFAULTS = {} 
    305375 
    306376      class Responder 
  • trunk/spec/fixtures/controllers/render_spec_controllers.rb

    r986 r1014  
    2222class FakeModel 
    2323   
    24   def to_json 
     24  def to_json(*args) 
    2525    "{'foo':'bar'}" 
    2626  end 
    2727   
    28   def to_xml 
     28  def to_xml(*args) 
    2929    "<foo>bar</foo>" 
    3030  end 
    3131end 
    3232 
     33class FakeModelWithArguments 
     34   
     35  def to_json(*args) 
     36    options = args.last.is_a?(Hash) ? args.pop : {} 
     37    '[' + args.map { |arg| "'#{arg}'" }.join(',') + ']' 
     38  end 
     39   
     40  def to_xml(*args) 
     41    options = args.last.is_a?(Hash) ? args.pop.stringify_keys : {} 
     42    options.keys.sort.inject('') do |str, tag| 
     43      str << "<#{tag}>#{options[tag]}</#{tag}>" 
     44    end 
     45  end 
     46   
     47end 
     48 
    3349class RenderObjectController < Merb::Controller 
    34    
    3550   
    3651  def render_object 
     
    4661  end 
    4762     
     63end 
     64 
     65class RenderObjectWithArgumentsController < Merb::Controller 
     66   
     67  provides :xml, :foo => 'bar' 
     68  provides :json, ['foo', 'bar'] 
     69   
     70  def render_standard 
     71    @foo = FakeModelWithArguments.new 
     72    render @foo 
     73  end 
     74   
     75  def render_specific 
     76    provides :xml, :foo => 'bar', :biz => 'baz' 
     77    provides :json, ['foo', 'bar', 'baz'] 
     78    @foo = FakeModelWithArguments.new 
     79    render @foo 
     80  end 
     81       
     82end 
     83 
     84class RenderObjectWithBlockController < Merb::Controller 
     85   
     86  provides :xml do |obj, controller| 
     87    obj.to_xml(:foo => controller.class.name) 
     88  end 
     89   
     90  provides :json do |obj| 
     91    obj.to_json('foo') 
     92  end 
     93   
     94  def render_standard 
     95    @foo = FakeModelWithArguments.new 
     96    render @foo 
     97  end 
     98   
     99  def render_specific 
     100    callback = lambda { |obj, controller, method| obj.send(method, controller.class.name, 'foo', 'bar', 'baz', :foo => controller.class.name.upcase) } 
     101    provides :xml, :json, callback 
     102    @foo = FakeModelWithArguments.new 
     103    render @foo 
     104  end 
     105       
    48106end 
    49107 
  • trunk/spec/merb/render_spec.rb

    r958 r1014  
    369369end 
    370370 
     371describe "Merb rendering with an object calls to_json or to_xml on the object (using specified arguments)" do 
     372   
     373  it "render @foo should call @foo.to_json when json is requested (using default options)" do 
     374    c = new_spec_controller(:format => 'json', :controller => 'RenderObjectWithArgumentsController') 
     375    c.provided_format_arguments_for(:json).should == ["foo", "bar"] 
     376    c.dispatch(:render_standard) 
     377    c.body.should == "['foo','bar']" 
     378  end 
     379   
     380  it "render @foo should call @foo.to_json when json is requested (using action options)" do 
     381    c = new_spec_controller(:format => 'json', :controller => 'RenderObjectWithArgumentsController') 
     382    c.dispatch(:render_specific) 
     383    c.body.should == "['foo','bar','baz']" 
     384  end 
     385   
     386  it "render @foo should call @foo.to_xml when xml is requested (using default options)" do 
     387    c = new_spec_controller(:format => 'xml', :controller => 'RenderObjectWithArgumentsController') 
     388    c.provided_format_arguments_for(:xml).should == [{:foo=>"bar"}] 
     389    c.dispatch(:render_standard) 
     390    c.body.should == "<foo>bar</foo>" 
     391  end 
     392   
     393  it "render @foo should call @foo.to_xml when xml is requested (using action options)" do 
     394    c = new_spec_controller(:format => 'xml', :controller => 'RenderObjectWithArgumentsController') 
     395    c.dispatch(:render_specific) 
     396    c.body.should == "<biz>baz</biz><foo>bar</foo>" 
     397  end 
     398   
     399end 
     400 
     401describe "Merb rendering with an object and using a block/lambda for provides" do 
     402   
     403  it "render @foo should use the default block when xml is requested" do 
     404    c = new_spec_controller(:format => 'xml', :controller => 'RenderObjectWithBlockController') 
     405    c.provided_format_arguments_for(:xml).should be_kind_of(Proc) 
     406    c.dispatch(:render_standard) 
     407    c.body.should == "<foo>RenderObjectWithBlockController</foo>" 
     408  end 
     409   
     410  it "render @foo should use the default block when json is requested" do 
     411    c = new_spec_controller(:format => 'json', :controller => 'RenderObjectWithBlockController') 
     412    c.provided_format_arguments_for(:json).should be_kind_of(Proc) 
     413    c.dispatch(:render_standard) 
     414    c.body.should == "['foo']" 
     415  end 
     416   
     417  it "render @foo should use the specific proc when xml is requested" do 
     418    c = new_spec_controller(:format => 'xml', :controller => 'RenderObjectWithBlockController') 
     419    c.dispatch(:render_specific) 
     420    c.body.should == "<foo>RENDEROBJECTWITHBLOCKCONTROLLER</foo>" 
     421  end 
     422   
     423  it "render @foo should use the specific proc when json is requested" do 
     424    c = new_spec_controller(:format => 'json', :controller => 'RenderObjectWithBlockController') 
     425    c.dispatch(:render_specific) 
     426    c.body.should == "['RenderObjectWithBlockController','foo','bar','baz']" 
     427  end 
     428   
     429end 
     430 
    371431def new_spec_controller(options={}) 
    372432  params = {:controller => 'ExtensionTemplateController'}