Skip to content

Commit

Permalink
Added responds_to to new base.
Browse files Browse the repository at this point in the history
  • Loading branch information
Yehuda Katz + Carl Lerche committed May 20, 2009
1 parent c03b0ca commit 01f032f
Show file tree
Hide file tree
Showing 11 changed files with 148 additions and 125 deletions.
7 changes: 6 additions & 1 deletion actionpack/lib/action_controller/abstract/layouts.rb
Expand Up @@ -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)
Expand Down
215 changes: 107 additions & 108 deletions 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:
#
# <person>
# <id>...</id>
# ...
# <company>
# <id>...</id>
# <name>...</name>
# ...
# </company>
# </person>
#
# 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):
#
# <person>
# <name>...</name>
# <company>
# <name>...</name>
# </company>
# </person>
#
# 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:
#
# <person>
# <id>...</id>
# ...
# <company>
# <id>...</id>
# <name>...</name>
# ...
# </company>
# </person>
#
# 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):
#
# <person>
# <name>...</name>
# <company>
# <name>...</name>
# </company>
# </person>
#
# 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:
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions actionpack/lib/action_controller/new_base.rb
Expand Up @@ -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'
Expand Down
1 change: 1 addition & 0 deletions actionpack/lib/action_controller/new_base/base.rb
Expand Up @@ -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
Expand Down
11 changes: 7 additions & 4 deletions actionpack/lib/action_controller/new_base/compatibility.rb
Expand Up @@ -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
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion actionpack/lib/action_controller/new_base/renderer.rb
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion actionpack/lib/action_controller/new_base/testing.rb
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions actionpack/lib/action_controller/testing/process.rb
Expand Up @@ -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'] = {}
Expand Down
7 changes: 6 additions & 1 deletion 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
Expand Down
2 changes: 2 additions & 0 deletions actionpack/test/controller/caching_test.rb
Expand Up @@ -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'
Expand Down
24 changes: 15 additions & 9 deletions actionpack/test/controller/mime_responds_test.rb
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand Down

0 comments on commit 01f032f

Please sign in to comment.