From 01f032f256f96f65e154061b582fbb4b32e4a692 Mon Sep 17 00:00:00 2001 From: Yehuda Katz + Carl Lerche Date: Wed, 20 May 2009 15:33:08 -0700 Subject: [PATCH] Added responds_to to new base. --- .../lib/action_controller/abstract/layouts.rb | 7 +- .../action_controller/base/mime_responds.rb | 215 +++++++++--------- actionpack/lib/action_controller/new_base.rb | 1 + .../lib/action_controller/new_base/base.rb | 1 + .../new_base/compatibility.rb | 11 +- .../action_controller/new_base/renderer.rb | 2 +- .../lib/action_controller/new_base/testing.rb | 2 +- .../lib/action_controller/testing/process.rb | 1 + actionpack/lib/action_view/template/text.rb | 7 +- actionpack/test/controller/caching_test.rb | 2 + .../test/controller/mime_responds_test.rb | 24 +- 11 files changed, 148 insertions(+), 125 deletions(-) diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index 35d5e85ed9652..96cb05972f329 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -66,7 +66,12 @@ def _layout_for_name(name) raise ArgumentError, "String, false, or nil expected; you passed #{name.inspect}" end - name && view_paths.find_by_parts(name, {:formats => formats}, "layouts") + name && view_paths.find_by_parts(name, {:formats => formats}, _layout_prefix(name)) + end + + # TODO: Decide if this is the best hook point for the feature + def _layout_prefix(name) + "layouts" end def _default_layout(require_layout = false) diff --git a/actionpack/lib/action_controller/base/mime_responds.rb b/actionpack/lib/action_controller/base/mime_responds.rb index 1003e61a0b190..e560376e0d909 100644 --- a/actionpack/lib/action_controller/base/mime_responds.rb +++ b/actionpack/lib/action_controller/base/mime_responds.rb @@ -1,111 +1,103 @@ module ActionController #:nodoc: module MimeResponds #:nodoc: - def self.included(base) - base.module_eval do - include ActionController::MimeResponds::InstanceMethods - end - end - - module InstanceMethods - # Without web-service support, an action which collects the data for displaying a list of people - # might look something like this: - # - # def index - # @people = Person.find(:all) - # end - # - # Here's the same action, with web-service support baked in: - # - # def index - # @people = Person.find(:all) - # - # respond_to do |format| - # format.html - # format.xml { render :xml => @people.to_xml } - # end - # end - # - # What that says is, "if the client wants HTML in response to this action, just respond as we - # would have before, but if the client wants XML, return them the list of people in XML format." - # (Rails determines the desired response format from the HTTP Accept header submitted by the client.) - # - # Supposing you have an action that adds a new person, optionally creating their company - # (by name) if it does not already exist, without web-services, it might look like this: - # - # def create - # @company = Company.find_or_create_by_name(params[:company][:name]) - # @person = @company.people.create(params[:person]) - # - # redirect_to(person_list_url) - # end - # - # Here's the same action, with web-service support baked in: - # - # def create - # company = params[:person].delete(:company) - # @company = Company.find_or_create_by_name(company[:name]) - # @person = @company.people.create(params[:person]) - # - # respond_to do |format| - # format.html { redirect_to(person_list_url) } - # format.js - # format.xml { render :xml => @person.to_xml(:include => @company) } - # end - # end - # - # If the client wants HTML, we just redirect them back to the person list. If they want Javascript - # (format.js), then it is an RJS request and we render the RJS template associated with this action. - # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also - # include the person's company in the rendered XML, so you get something like this: - # - # - # ... - # ... - # - # ... - # ... - # ... - # - # - # - # Note, however, the extra bit at the top of that action: - # - # company = params[:person].delete(:company) - # @company = Company.find_or_create_by_name(company[:name]) - # - # This is because the incoming XML document (if a web-service request is in process) can only contain a - # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded): - # - # person[name]=...&person[company][name]=...&... - # - # And, like this (xml-encoded): - # - # - # ... - # - # ... - # - # - # - # In other words, we make the request so that it operates on a single entity's person. Then, in the action, - # we extract the company data from the request, find or create the company, and then create the new person - # with the remaining data. - # - # Note that you can define your own XML parameter parser which would allow you to describe multiple entities - # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow - # and accept Rails' defaults, life will be much easier. - # - # If you need to use a MIME type which isn't supported by default, you can register your own handlers in - # environment.rb as follows. - # - # Mime::Type.register "image/jpg", :jpg - def respond_to(*types, &block) - raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block - block ||= lambda { |responder| types.each { |type| responder.send(type) } } - responder = Responder.new(self) - block.call(responder) - responder.respond - end + # Without web-service support, an action which collects the data for displaying a list of people + # might look something like this: + # + # def index + # @people = Person.find(:all) + # end + # + # Here's the same action, with web-service support baked in: + # + # def index + # @people = Person.find(:all) + # + # respond_to do |format| + # format.html + # format.xml { render :xml => @people.to_xml } + # end + # end + # + # What that says is, "if the client wants HTML in response to this action, just respond as we + # would have before, but if the client wants XML, return them the list of people in XML format." + # (Rails determines the desired response format from the HTTP Accept header submitted by the client.) + # + # Supposing you have an action that adds a new person, optionally creating their company + # (by name) if it does not already exist, without web-services, it might look like this: + # + # def create + # @company = Company.find_or_create_by_name(params[:company][:name]) + # @person = @company.people.create(params[:person]) + # + # redirect_to(person_list_url) + # end + # + # Here's the same action, with web-service support baked in: + # + # def create + # company = params[:person].delete(:company) + # @company = Company.find_or_create_by_name(company[:name]) + # @person = @company.people.create(params[:person]) + # + # respond_to do |format| + # format.html { redirect_to(person_list_url) } + # format.js + # format.xml { render :xml => @person.to_xml(:include => @company) } + # end + # end + # + # If the client wants HTML, we just redirect them back to the person list. If they want Javascript + # (format.js), then it is an RJS request and we render the RJS template associated with this action. + # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also + # include the person's company in the rendered XML, so you get something like this: + # + # + # ... + # ... + # + # ... + # ... + # ... + # + # + # + # Note, however, the extra bit at the top of that action: + # + # company = params[:person].delete(:company) + # @company = Company.find_or_create_by_name(company[:name]) + # + # This is because the incoming XML document (if a web-service request is in process) can only contain a + # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded): + # + # person[name]=...&person[company][name]=...&... + # + # And, like this (xml-encoded): + # + # + # ... + # + # ... + # + # + # + # In other words, we make the request so that it operates on a single entity's person. Then, in the action, + # we extract the company data from the request, find or create the company, and then create the new person + # with the remaining data. + # + # Note that you can define your own XML parameter parser which would allow you to describe multiple entities + # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow + # and accept Rails' defaults, life will be much easier. + # + # If you need to use a MIME type which isn't supported by default, you can register your own handlers in + # environment.rb as follows. + # + # Mime::Type.register "image/jpg", :jpg + def respond_to(*types, &block) + raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block + block ||= lambda { |responder| types.each { |type| responder.send(type) } } + responder = Responder.new(self) + block.call(responder) + responder.respond end class Responder #:nodoc: @@ -127,8 +119,15 @@ def custom(mime_type, &block) @order << mime_type @responses[mime_type] ||= Proc.new do - @controller.template.formats = [mime_type.to_sym] - @response.content_type = mime_type.to_s + # TODO: Remove this when new base is merged in + if defined?(Http) + @controller.formats = [mime_type.to_sym] + @controller.template.formats = [mime_type.to_sym] + else + @controller.template.formats = [mime_type.to_sym] + @response.content_type = mime_type.to_s + end + block_given? ? block.call : @controller.send(:render, :action => @controller.action_name) end end diff --git a/actionpack/lib/action_controller/new_base.rb b/actionpack/lib/action_controller/new_base.rb index 078f66ced131e..3fc5d82d01b5e 100644 --- a/actionpack/lib/action_controller/new_base.rb +++ b/actionpack/lib/action_controller/new_base.rb @@ -15,6 +15,7 @@ module ActionController # require 'action_controller/routing' autoload :Caching, 'action_controller/caching' autoload :Dispatcher, 'action_controller/dispatch/dispatcher' + autoload :MimeResponds, 'action_controller/base/mime_responds' autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes' autoload :RecordIdentifier, 'action_controller/record_identifier' autoload :Resources, 'action_controller/routing/resources' diff --git a/actionpack/lib/action_controller/new_base/base.rb b/actionpack/lib/action_controller/new_base/base.rb index 7f830307b67f8..3d8d46c9c2c9e 100644 --- a/actionpack/lib/action_controller/new_base/base.rb +++ b/actionpack/lib/action_controller/new_base/base.rb @@ -18,6 +18,7 @@ class Base < Http include SessionManagement include ActionDispatch::StatusCodes include ActionController::Caching + include ActionController::MimeResponds # Rails 2.x compatibility include ActionController::Rails2Compatibility diff --git a/actionpack/lib/action_controller/new_base/compatibility.rb b/actionpack/lib/action_controller/new_base/compatibility.rb index 0098c0747ea95..c861af2fa269f 100644 --- a/actionpack/lib/action_controller/new_base/compatibility.rb +++ b/actionpack/lib/action_controller/new_base/compatibility.rb @@ -59,6 +59,11 @@ module Rails2Compatibility def initialize_template_class(*) end def assign_shortcuts(*) end + # TODO: Remove this after we flip + def template + _action_view + end + module ClassMethods def protect_from_forgery() end def consider_all_requests_local() end @@ -95,10 +100,8 @@ def method_for_action(action_name) super || (respond_to?(:method_missing) && "_handle_method_missing") end - def _layout_for_name(name) - name &&= name.sub(%r{^/?layouts/}, '') - super + def _layout_prefix(name) + super unless name =~ /\blayouts/ end - end end \ No newline at end of file diff --git a/actionpack/lib/action_controller/new_base/renderer.rb b/actionpack/lib/action_controller/new_base/renderer.rb index 9253df53a1d3f..276816a6f55b7 100644 --- a/actionpack/lib/action_controller/new_base/renderer.rb +++ b/actionpack/lib/action_controller/new_base/renderer.rb @@ -18,7 +18,7 @@ def render_to_body(options) _process_options(options) if options.key?(:text) - options[:_template] = ActionView::TextTemplate.new(_text(options)) + options[:_template] = ActionView::TextTemplate.new(_text(options), formats.first) template = nil elsif options.key?(:inline) handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb") diff --git a/actionpack/lib/action_controller/new_base/testing.rb b/actionpack/lib/action_controller/new_base/testing.rb index b39d8d539d98d..0659d81710823 100644 --- a/actionpack/lib/action_controller/new_base/testing.rb +++ b/actionpack/lib/action_controller/new_base/testing.rb @@ -7,7 +7,7 @@ def process_with_test(request, response) @_response = response @_response.request = request ret = process(request.parameters[:action]) - @_response.body = self.response_body || " " + @_response.body ||= self.response_body || " " @_response.prepare! set_test_assigns ret diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb index 8831ff57e25b2..9647f8ce454e3 100644 --- a/actionpack/lib/action_controller/testing/process.rb +++ b/actionpack/lib/action_controller/testing/process.rb @@ -41,6 +41,7 @@ def assign_parameters(controller_path, action, parameters) end def recycle! + @formats = nil @env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ } @env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ } @env['action_dispatch.request.query_parameters'] = {} diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb index a777021a128c0..927a0d7a72e77 100644 --- a/actionpack/lib/action_view/template/text.rb +++ b/actionpack/lib/action_view/template/text.rb @@ -1,11 +1,16 @@ module ActionView #:nodoc: class TextTemplate < String #:nodoc: + def initialize(string, content_type = Mime[:html]) + super(string) + @content_type = Mime[content_type] + end + def identifier() self end def render(*) self end - def mime_type() Mime::HTML end + def mime_type() @content_type end def partial?() false end end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 560c09509b663..c286976315489 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -48,6 +48,8 @@ def setup super ActionController::Base.perform_caching = true + ActionController::Routing::Routes.clear! + ActionController::Routing::Routes.draw do |map| map.main '', :controller => 'posts' map.formatted_posts 'posts.:format', :controller => 'posts' diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index 7cd5145a2fef7..3b8babb84c58e 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -437,7 +437,7 @@ def test_render_action_for_html @controller.instance_eval do def render(*args) unless args.empty? - @action = args.first[:action] + @action = args.first[:action] || action_name end response.body = "#{@action} - #{@template.formats}" end @@ -490,14 +490,15 @@ def index end end - protected - def with_iphone - Mime::Type.register_alias("text/html", :iphone) - request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" - yield - ensure - Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } - end +protected + + def with_iphone + Mime::Type.register_alias("text/html", :iphone) + request.format = "iphone" if request.env["HTTP_ACCEPT"] == "text/iphone" + yield + ensure + Mime.module_eval { remove_const :IPHONE if const_defined?(:IPHONE) } + end end class SuperPostController < PostController @@ -509,6 +510,11 @@ def index end end +if ENV["new_base"] + PostController._write_layout_method + SuperPostController._write_layout_method +end + class MimeControllerLayoutsTest < ActionController::TestCase tests PostController