Changeset 1014
- Timestamp:
- 11/17/07 08:10:24 (1 year ago)
- Files:
-
- trunk/lib/merb/mixins/render.rb (modified) (1 diff)
- trunk/lib/merb/mixins/responder.rb (modified) (12 diffs)
- trunk/spec/fixtures/controllers/render_spec_controllers.rb (modified) (2 diffs)
- trunk/spec/merb/render_spec.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/lib/merb/mixins/render.rb
r964 r1014 146 146 if transform_method = Merb.mime_transform_method(fmt) 147 147 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 149 161 end 150 162 end trunk/lib/merb/mixins/responder.rb
r807 r1014 19 19 ResponderMixin::Rest::TYPES.update(key => values) 20 20 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) 22 22 end 23 23 … … 26 26 end 27 27 28 # Return the method name (if any) for the mimetype 28 29 def mime_transform_method(key) 29 30 ResponderMixin::Rest::TRANSFORM_METHODS[key] 30 31 end 31 32 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 32 43 33 44 # Adds outgoing headers to a mime type. This can be done with the Merb.add_mime_type method … … 166 177 def self.included(base) # :nodoc: 167 178 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 170 184 end 171 185 … … 175 189 # default list of provided_formats. These will apply to 176 190 # 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| 179 196 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 181 199 end 182 200 … … 185 203 # in the action. 186 204 def only_provides(*formats) 187 self.class_provided_formats = formats 205 clear_provides 206 provides(*formats) 188 207 end 189 208 190 209 # Removes formats from the controller's 191 # default list of provided_formats. These will apply to210 # default list of provided_formats. These will apply to 192 211 # every action in the controller, unless modified in the action. 193 212 def does_not_provide(*formats) 194 213 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 196 239 end 197 240 … … 200 243 # (or :html by default) but can be modifed on a per-action basis. 201 244 def provided_formats 202 @_provided_formats ||= class_provided_formats 245 @_provided_formats ||= class_provided_formats.dup 203 246 end 204 247 … … 206 249 # use a combination of +provides+, +only_provides+ and +does_not_provide+ 207 250 # 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) 211 269 end 212 270 … … 214 272 # request. Usually used to add formats to a single action. See also 215 273 # 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 220 281 end 221 282 … … 224 285 # the controller-level provides that affects all actions in a controller. 225 286 def only_provides(*formats) 226 self. provided_formats = formats.flatten287 self.set_provided_formats(*formats) 227 288 end 228 289 … … 232 293 # controller. 233 294 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) } 235 298 end 236 299 … … 298 361 end 299 362 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 300 369 module Rest 301 370 … … 303 372 RESPONSE_HEADERS = {} 304 373 TRANSFORM_METHODS = {} 374 TRANSFORM_METHOD_DEFAULTS = {} 305 375 306 376 class Responder trunk/spec/fixtures/controllers/render_spec_controllers.rb
r986 r1014 22 22 class FakeModel 23 23 24 def to_json 24 def to_json(*args) 25 25 "{'foo':'bar'}" 26 26 end 27 27 28 def to_xml 28 def to_xml(*args) 29 29 "<foo>bar</foo>" 30 30 end 31 31 end 32 32 33 class 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 47 end 48 33 49 class RenderObjectController < Merb::Controller 34 35 50 36 51 def render_object … … 46 61 end 47 62 63 end 64 65 class 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 82 end 83 84 class 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 48 106 end 49 107 trunk/spec/merb/render_spec.rb
r958 r1014 369 369 end 370 370 371 describe "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 399 end 400 401 describe "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 429 end 430 371 431 def new_spec_controller(options={}) 372 432 params = {:controller => 'ExtensionTemplateController'}
