aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/metal
diff options
context:
space:
mode:
authorJosé Valim <jose.valim@gmail.com>2009-08-06 23:48:48 +0200
committerJosé Valim <jose.valim@gmail.com>2009-08-07 17:17:51 +0200
commitaed135d3e261cbee153a35fcfbeb47e2e02b12e4 (patch)
treeab3b3ff0b8cbd632f34d938bf1117b075f4813a7 /actionpack/lib/action_controller/metal
parent1fd65c80fcdc6080b9efa27f41dbb7f7d95a17c6 (diff)
downloadrails-aed135d3e261cbee153a35fcfbeb47e2e02b12e4.tar.gz
rails-aed135d3e261cbee153a35fcfbeb47e2e02b12e4.tar.bz2
rails-aed135d3e261cbee153a35fcfbeb47e2e02b12e4.zip
Renamed presenter to renderer, added some documentation and defined its API.
Diffstat (limited to 'actionpack/lib/action_controller/metal')
-rw-r--r--actionpack/lib/action_controller/metal/layouts.rb2
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb162
-rw-r--r--actionpack/lib/action_controller/metal/render_options.rb10
-rw-r--r--actionpack/lib/action_controller/metal/renderer.rb215
-rw-r--r--actionpack/lib/action_controller/metal/rendering_controller.rb66
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/verification.rb4
7 files changed, 250 insertions, 211 deletions
diff --git a/actionpack/lib/action_controller/metal/layouts.rb b/actionpack/lib/action_controller/metal/layouts.rb
index 365351b421..cac529b1ae 100644
--- a/actionpack/lib/action_controller/metal/layouts.rb
+++ b/actionpack/lib/action_controller/metal/layouts.rb
@@ -158,7 +158,7 @@ module ActionController
module Layouts
extend ActiveSupport::Concern
- include ActionController::Renderer
+ include ActionController::RenderingController
include AbstractController::Layouts
module ClassMethods
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index d43f940774..d823dd424a 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -1,146 +1,4 @@
module ActionController #:nodoc:
-
- # Presenter is responsible to expose a resource for different mime requests,
- # usually depending on the HTTP verb. The presenter is triggered when
- # respond_with is called. The simplest case to study is a GET request:
- #
- # class PeopleController < ApplicationController
- # respond_to :html, :xml, :json
- #
- # def index
- # @people = Person.find(:all)
- # respond_with(@people)
- # end
- # end
- #
- # When a request comes, for example with format :xml, three steps happen:
- #
- # 1) respond_with searches for a template at people/index.xml;
- #
- # 2) if the template is not available, it will create a presenter, passing
- # the controller and the resource, and invoke :to_xml on it;
- #
- # 3) if the presenter does not respond_to :to_xml, call to_format on it.
- #
- # === Builtin HTTP verb semantics
- #
- # Rails default presenter holds semantics for each HTTP verb. Depending on the
- # content type, verb and the resource status, it will behave differently.
- #
- # Using Rails default presenter, a POST request could be written as:
- #
- # def create
- # @user = User.new(params[:user])
- # flash[:notice] = 'User was successfully created.' if @user.save
- # respond_with(@user)
- # end
- #
- # Which is exactly the same as:
- #
- # def create
- # @user = User.new(params[:user])
- #
- # respond_to do |format|
- # if @user.save
- # flash[:notice] = 'User was successfully created.'
- # format.html { redirect_to(@user) }
- # format.xml { render :xml => @user, :status => :created, :location => @user }
- # else
- # format.html { render :action => "new" }
- # format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
- # end
- # end
- # end
- #
- # The same happens for PUT and DELETE requests. By default, it accepts just
- # :location as parameter, which is used as redirect destination, in both
- # POST, PUT, DELETE requests for HTML mime, as in the example below:
- #
- # def destroy
- # @person = Person.find(params[:id])
- # @person.destroy
- # respond_with(@person, :location => root_url)
- # end
- #
- # === Nested resources
- #
- # You can given nested resource as you do in form_for and polymorphic_url.
- # Consider the project has many tasks example. The create action for
- # TasksController would be like:
- #
- # def create
- # @project = Project.find(params[:project_id])
- # @task = @project.comments.build(params[:task])
- # flash[:notice] = 'Task was successfully created.' if @task.save
- # respond_with([@project, @task])
- # end
- #
- # Given a nested resource, you ensure that the presenter will redirect to
- # project_task_url instead of task_url.
- #
- # Namespaced and singleton resources requires a symbol to be given, as in
- # polymorphic urls. If a project has one manager which has many tasks, it
- # should be invoked as:
- #
- # respond_with([@project, :manager, @task])
- #
- # Check polymorphic_url documentation for more examples.
- #
- class Presenter
- attr_reader :controller, :request, :format, :resource, :resource_location, :options
-
- def initialize(controller, resource, options)
- @controller = controller
- @request = controller.request
- @format = controller.formats.first
- @resource = resource.is_a?(Array) ? resource.last : resource
- @resource_location = options[:location] || resource
- @options = options
- end
-
- delegate :head, :render, :redirect_to, :to => :controller
- delegate :get?, :post?, :put?, :delete?, :to => :request
-
- # Undefine :to_json since it's defined on Object
- undef_method :to_json
-
- def to_html
- if get?
- render
- elsif has_errors?
- render :action => default_action
- else
- redirect_to resource_location
- end
- end
-
- def to_format
- return render unless resourceful?
-
- if get?
- render format => resource
- elsif has_errors?
- render format => resource.errors, :status => :unprocessable_entity
- elsif post?
- render format => resource, :status => :created, :location => resource_location
- else
- head :ok
- end
- end
-
- def resourceful?
- resource.respond_to?(:"to_#{format}")
- end
-
- def has_errors?
- resource.respond_to?(:errors) && !resource.errors.empty?
- end
-
- def default_action
- request.post? ? :new : :edit
- end
- end
-
module MimeResponds #:nodoc:
extend ActiveSupport::Concern
@@ -339,10 +197,10 @@ module ActionController #:nodoc:
end
end
- # respond_with wraps a resource around a presenter for default representation.
+ # respond_with wraps a resource around a renderer for default representation.
# First it invokes respond_to, if a response cannot be found (ie. no block
# for the request was given and template was not available), it instantiates
- # an ActionController::Presenter with the controller and resource.
+ # an ActionController::Renderer with the controller and resource.
#
# ==== Example
#
@@ -363,19 +221,19 @@ module ActionController #:nodoc:
# end
# end
#
- # All options given to respond_with are sent to the underlying presenter.
+ # All options given to respond_with are sent to the underlying renderer,
+ # except for the option :renderer itself. Since the renderer interface
+ # is quite simple (it just needs to respond to call), you can even give
+ # a proc to it.
#
def respond_with(resource, options={}, &block)
respond_to(&block)
rescue ActionView::MissingTemplate
- presenter = ActionController::Presenter.new(self, resource, options)
- format_method = :"to_#{self.formats.first}"
+ (options.delete(:renderer) || renderer).call(self, resource, options)
+ end
- if presenter.respond_to?(format_method)
- presenter.send(format_method)
- else
- presenter.to_format
- end
+ def renderer
+ ActionController::Renderer
end
protected
diff --git a/actionpack/lib/action_controller/metal/render_options.rb b/actionpack/lib/action_controller/metal/render_options.rb
index c6a965472f..0d69ca10df 100644
--- a/actionpack/lib/action_controller/metal/render_options.rb
+++ b/actionpack/lib/action_controller/metal/render_options.rb
@@ -47,7 +47,7 @@ module ActionController
end
end
- module Renderers
+ module RenderOptions
module Json
extend RenderOption
register_renderer :json
@@ -94,10 +94,10 @@ module ActionController
module All
extend ActiveSupport::Concern
- include ActionController::Renderers::Json
- include ActionController::Renderers::Js
- include ActionController::Renderers::Xml
- include ActionController::Renderers::RJS
+ include ActionController::RenderOptions::Json
+ include ActionController::RenderOptions::Js
+ include ActionController::RenderOptions::Xml
+ include ActionController::RenderOptions::RJS
end
end
end
diff --git a/actionpack/lib/action_controller/metal/renderer.rb b/actionpack/lib/action_controller/metal/renderer.rb
index 31ba7e582a..39ab2407aa 100644
--- a/actionpack/lib/action_controller/metal/renderer.rb
+++ b/actionpack/lib/action_controller/metal/renderer.rb
@@ -1,66 +1,181 @@
-module ActionController
- module Renderer
- extend ActiveSupport::Concern
+module ActionController #:nodoc:
+ # Renderer is responsible to expose a resource for different mime requests,
+ # usually depending on the HTTP verb. The renderer is triggered when
+ # respond_with is called. The simplest case to study is a GET request:
+ #
+ # class PeopleController < ApplicationController
+ # respond_to :html, :xml, :json
+ #
+ # def index
+ # @people = Person.find(:all)
+ # respond_with(@people)
+ # end
+ # end
+ #
+ # When a request comes, for example with format :xml, three steps happen:
+ #
+ # 1) respond_with searches for a template at people/index.xml;
+ #
+ # 2) if the template is not available, it will create a renderer, passing
+ # the controller and the resource and invoke :to_xml on it;
+ #
+ # 3) if the renderer does not respond_to :to_xml, call to_format on it.
+ #
+ # === Builtin HTTP verb semantics
+ #
+ # Rails default renderer holds semantics for each HTTP verb. Depending on the
+ # content type, verb and the resource status, it will behave differently.
+ #
+ # Using Rails default renderer, a POST request for creating an object could
+ # be written as:
+ #
+ # def create
+ # @user = User.new(params[:user])
+ # flash[:notice] = 'User was successfully created.' if @user.save
+ # respond_with(@user)
+ # end
+ #
+ # Which is exactly the same as:
+ #
+ # def create
+ # @user = User.new(params[:user])
+ #
+ # respond_to do |format|
+ # if @user.save
+ # flash[:notice] = 'User was successfully created.'
+ # format.html { redirect_to(@user) }
+ # format.xml { render :xml => @user, :status => :created, :location => @user }
+ # else
+ # format.html { render :action => "new" }
+ # format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
+ # end
+ # end
+ # end
+ #
+ # The same happens for PUT and DELETE requests.
+ #
+ # === Nested resources
+ #
+ # You can given nested resource as you do in form_for and polymorphic_url.
+ # Consider the project has many tasks example. The create action for
+ # TasksController would be like:
+ #
+ # def create
+ # @project = Project.find(params[:project_id])
+ # @task = @project.comments.build(params[:task])
+ # flash[:notice] = 'Task was successfully created.' if @task.save
+ # respond_with([@project, @task])
+ # end
+ #
+ # Giving an array of resources, you ensure that the renderer will redirect to
+ # project_task_url instead of task_url.
+ #
+ # Namespaced and singleton resources requires a symbol to be given, as in
+ # polymorphic urls. If a project has one manager which has many tasks, it
+ # should be invoked as:
+ #
+ # respond_with([@project, :manager, @task])
+ #
+ # Check polymorphic_url documentation for more examples.
+ #
+ class Renderer
+ attr_reader :controller, :request, :format, :resource, :resource_location, :options
- include AbstractController::Renderer
+ def initialize(controller, resource, options={})
+ @controller = controller
+ @request = controller.request
+ @format = controller.formats.first
+ @resource = resource.is_a?(Array) ? resource.last : resource
+ @resource_location = options[:location] || resource
+ @options = options
+ end
+
+ delegate :head, :render, :redirect_to, :to => :controller
+ delegate :get?, :post?, :put?, :delete?, :to => :request
- def process_action(*)
- self.formats = request.formats.map {|x| x.to_sym}
- super
+ # Undefine :to_json since it's defined on Object
+ undef_method :to_json
+
+ # Initializes a new renderer an invoke the proper format. If the format is
+ # not defined, call to_format.
+ #
+ def self.call(*args)
+ renderer = new(*args)
+ method = :"to_#{renderer.format}"
+ renderer.respond_to?(method) ? renderer.send(method) : renderer.to_format
end
- def render(options)
- super
- self.content_type ||= begin
- mime = options[:_template].mime_type
- formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first)
- end.to_s
- response_body
+ # HTML format does not render the resource, it always attempt to render a
+ # template.
+ #
+ def to_html
+ if get?
+ render
+ elsif has_errors?
+ render :action => default_action
+ else
+ redirect_to resource_location
+ end
end
- def render_to_body(options)
- _process_options(options)
+ # All others formats try to render the resource given instead. For this
+ # purpose a helper called display as a shortcut to render a resource with
+ # the current format.
+ #
+ def to_format
+ return render unless resourceful?
- if options.key?(:partial)
- options[:partial] = action_name if options[:partial] == true
- options[:_details] = {:formats => formats}
+ if get?
+ display resource
+ elsif has_errors?
+ display resource.errors, :status => :unprocessable_entity
+ elsif post?
+ display resource, :status => :created, :location => resource_location
+ else
+ head :ok
end
-
- super
end
- private
- def _prefix
- controller_path
- end
+ protected
- def _determine_template(options)
- if options.key?(:text)
- options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first)
- elsif options.key?(:inline)
- handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
- template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
- options[:_template] = template
- elsif options.key?(:template)
- options[:_template_name] = options[:template]
- elsif options.key?(:file)
- options[:_template_name] = options[:file]
- elsif !options.key?(:partial)
- options[:_template_name] = (options[:action] || action_name).to_s
- options[:_prefix] = _prefix
- end
+ # Checks whether the resource responds to the current format or not.
+ #
+ def resourceful?
+ resource.respond_to?(:"to_#{format}")
+ end
- super
- end
+ # display is just a shortcut to render a resource with the current format.
+ #
+ # display @user, :status => :ok
+ #
+ # For xml request is equivalent to:
+ #
+ # render :xml => @user, :status => :ok
+ #
+ # Options sent by the user are also used:
+ #
+ # respond_with(@user, :status => :created)
+ # display(@user, :status => :ok)
+ #
+ # Results in:
+ #
+ # render :xml => @user, :status => :created
+ #
+ def display(resource, given_options={})
+ render given_options.merge!(options).merge!(format => resource)
+ end
- def _render_partial(partial, options)
- end
+ # Check if the resource has errors or not.
+ #
+ def has_errors?
+ resource.respond_to?(:errors) && !resource.errors.empty?
+ end
- def _process_options(options)
- status, content_type, location = options.values_at(:status, :content_type, :location)
- self.status = status if status
- self.content_type = content_type if content_type
- self.headers["Location"] = url_for(location) if location
- end
+ # By default, render the :edit action for html requests with failure, unless
+ # the verb is post.
+ #
+ def default_action
+ request.post? ? :new : :edit
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/rendering_controller.rb b/actionpack/lib/action_controller/metal/rendering_controller.rb
new file mode 100644
index 0000000000..c8922290f3
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/rendering_controller.rb
@@ -0,0 +1,66 @@
+module ActionController
+ module RenderingController
+ extend ActiveSupport::Concern
+
+ include AbstractController::RenderingController
+
+ def process_action(*)
+ self.formats = request.formats.map {|x| x.to_sym}
+ super
+ end
+
+ def render(options)
+ super
+ self.content_type ||= begin
+ mime = options[:_template].mime_type
+ formats.include?(mime && mime.to_sym) || formats.include?(:all) ? mime : Mime::Type.lookup_by_extension(formats.first)
+ end.to_s
+ response_body
+ end
+
+ def render_to_body(options)
+ _process_options(options)
+
+ if options.key?(:partial)
+ options[:partial] = action_name if options[:partial] == true
+ options[:_details] = {:formats => formats}
+ end
+
+ super
+ end
+
+ private
+ def _prefix
+ controller_path
+ end
+
+ def _determine_template(options)
+ if options.key?(:text)
+ options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first)
+ elsif options.key?(:inline)
+ handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
+ template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
+ options[:_template] = template
+ elsif options.key?(:template)
+ options[:_template_name] = options[:template]
+ elsif options.key?(:file)
+ options[:_template_name] = options[:file]
+ elsif !options.key?(:partial)
+ options[:_template_name] = (options[:action] || action_name).to_s
+ options[:_prefix] = _prefix
+ end
+
+ super
+ end
+
+ def _render_partial(partial, options)
+ end
+
+ def _process_options(options)
+ status, content_type, location = options.values_at(:status, :content_type, :location)
+ self.status = status if status
+ self.content_type = content_type if content_type
+ self.headers["Location"] = url_for(location) if location
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index f0317c6e99..57318e8747 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -6,7 +6,7 @@ module ActionController #:nodoc:
module Streaming
extend ActiveSupport::Concern
- include ActionController::Renderer
+ include ActionController::RenderingController
DEFAULT_SEND_FILE_OPTIONS = {
:type => 'application/octet-stream'.freeze,
diff --git a/actionpack/lib/action_controller/metal/verification.rb b/actionpack/lib/action_controller/metal/verification.rb
index 951ae1bee1..d3d78e3749 100644
--- a/actionpack/lib/action_controller/metal/verification.rb
+++ b/actionpack/lib/action_controller/metal/verification.rb
@@ -2,7 +2,7 @@ module ActionController #:nodoc:
module Verification #:nodoc:
extend ActiveSupport::Concern
- include AbstractController::Callbacks, Session, Flash, Renderer
+ include AbstractController::Callbacks, Session, Flash, RenderingController
# This module provides a class-level method for specifying that certain
# actions are guarded against being called without certain prerequisites
@@ -127,4 +127,4 @@ module ActionController #:nodoc:
end
end
end
-end \ No newline at end of file
+end