aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r--actionpack/lib/action_controller/abstract.rb2
-rw-r--r--actionpack/lib/action_controller/abstract/base.rb73
-rw-r--r--actionpack/lib/action_controller/abstract/callbacks.rb10
-rw-r--r--actionpack/lib/action_controller/abstract/exceptions.rb2
-rw-r--r--actionpack/lib/action_controller/abstract/helpers.rb20
-rw-r--r--actionpack/lib/action_controller/abstract/layouts.rb28
-rw-r--r--actionpack/lib/action_controller/abstract/logger.rb2
-rw-r--r--actionpack/lib/action_controller/abstract/renderer.rb37
-rw-r--r--actionpack/lib/action_controller/base/base.rb29
-rw-r--r--actionpack/lib/action_controller/dispatch/dispatcher.rb2
-rw-r--r--actionpack/lib/action_controller/dispatch/middlewares.rb2
-rw-r--r--actionpack/lib/action_controller/new_base.rb30
-rw-r--r--actionpack/lib/action_controller/new_base/base.rb103
-rw-r--r--actionpack/lib/action_controller/new_base/compatibility.rb58
-rw-r--r--actionpack/lib/action_controller/new_base/conditional_get.rb131
-rw-r--r--actionpack/lib/action_controller/new_base/hide_actions.rb13
-rw-r--r--actionpack/lib/action_controller/new_base/http.rb64
-rw-r--r--actionpack/lib/action_controller/new_base/layouts.rb16
-rw-r--r--actionpack/lib/action_controller/new_base/renderer.rb52
-rw-r--r--actionpack/lib/action_controller/new_base/testing.rb25
-rw-r--r--actionpack/lib/action_controller/new_base/url_for.rb6
-rw-r--r--actionpack/lib/action_controller/routing/route_set.rb2
-rw-r--r--actionpack/lib/action_controller/testing/integration.rb15
-rw-r--r--actionpack/lib/action_controller/testing/process.rb5
-rw-r--r--actionpack/lib/action_controller/testing/process2.rb69
25 files changed, 614 insertions, 182 deletions
diff --git a/actionpack/lib/action_controller/abstract.rb b/actionpack/lib/action_controller/abstract.rb
index 9442d4559f..3f5c4a185f 100644
--- a/actionpack/lib/action_controller/abstract.rb
+++ b/actionpack/lib/action_controller/abstract.rb
@@ -7,4 +7,4 @@ module AbstractController
autoload :Renderer, "action_controller/abstract/renderer"
# === Exceptions
autoload :ActionNotFound, "action_controller/abstract/exceptions"
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb
index 39e9ad0440..ab9aed0b26 100644
--- a/actionpack/lib/action_controller/abstract/base.rb
+++ b/actionpack/lib/action_controller/abstract/base.rb
@@ -1,38 +1,85 @@
module AbstractController
class Base
+
attr_internal :response_body
attr_internal :response_obj
attr_internal :action_name
- def self.process(action)
- new.process(action)
- end
+ class << self
+ attr_reader :abstract
+
+ def abstract!
+ @abstract = true
+ end
+
+ alias_method :abstract?, :abstract
+
+ def internal_methods
+ controller = self
+ controller = controller.superclass until controller.abstract?
+ controller.public_instance_methods(true)
+ end
+
+ def process(action)
+ new.process(action.to_s)
+ end
- def self.inherited(klass)
+ def hidden_actions
+ []
+ end
+
+ def action_methods
+ @action_methods ||=
+ # All public instance methods of this class, including ancestors
+ public_instance_methods(true).map { |m| m.to_s }.to_set -
+ # Except for public instance methods of Base and its ancestors
+ internal_methods.map { |m| m.to_s } +
+ # Be sure to include shadowed public instance methods of this class
+ public_instance_methods(false).map { |m| m.to_s } -
+ # And always exclude explicitly hidden actions
+ hidden_actions
+ end
end
-
+
+ abstract!
+
def initialize
self.response_obj = {}
end
-
+
def process(action_name)
unless respond_to_action?(action_name)
raise ActionNotFound, "The action '#{action_name}' could not be found"
end
-
+
@_action_name = action_name
process_action
- self.response_obj[:body] = self.response_body
self
end
-
+
private
+
+ def action_methods
+ self.class.action_methods
+ end
+
+ # It is possible for respond_to?(action_name) to be false and
+ # respond_to?(:action_missing) to be false if respond_to_action?
+ # is overridden in a subclass. For instance, ActionController::Base
+ # overrides it to include the case where a template matching the
+ # action_name is found.
def process_action
- respond_to?(action_name) ? send(action_name) : send(:action_missing, action_name)
+ if respond_to?(action_name) then send(action_name)
+ elsif respond_to?(:action_missing, true) then action_missing(action_name)
+ end
end
-
+
+ # Override this to change the conditions that will raise an
+ # ActionNotFound error. If you accept a difference case,
+ # you must handle it by also overriding process_action and
+ # handling the case.
def respond_to_action?(action_name)
- respond_to?(action_name) || respond_to?(:action_missing, true)
+ action_methods.include?(action_name) || respond_to?(:action_missing, true)
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb
index cb8aade0be..6e15b3e81b 100644
--- a/actionpack/lib/action_controller/abstract/callbacks.rb
+++ b/actionpack/lib/action_controller/abstract/callbacks.rb
@@ -13,19 +13,19 @@ module AbstractController
super
end
end
-
+
module ClassMethods
def _normalize_callback_options(options)
if only = options[:only]
- only = Array(only).map {|o| "action_name == :#{o}"}.join(" || ")
+ only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
options[:per_key] = {:if => only}
end
if except = options[:except]
- except = Array(except).map {|e| "action_name == :#{e}"}.join(" || ")
+ except = Array(except).map {|e| "action_name == '#{e}'"}.join(" || ")
options[:per_key] = {:unless => except}
end
end
-
+
[:before, :after, :around].each do |filter|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{filter}_filter(*names, &blk)
@@ -40,4 +40,4 @@ module AbstractController
end
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/abstract/exceptions.rb b/actionpack/lib/action_controller/abstract/exceptions.rb
index a7d07868bb..ec4680629b 100644
--- a/actionpack/lib/action_controller/abstract/exceptions.rb
+++ b/actionpack/lib/action_controller/abstract/exceptions.rb
@@ -1,3 +1,3 @@
module AbstractController
class ActionNotFound < StandardError ; end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/abstract/helpers.rb b/actionpack/lib/action_controller/abstract/helpers.rb
index 38e3ce8127..968d3080c1 100644
--- a/actionpack/lib/action_controller/abstract/helpers.rb
+++ b/actionpack/lib/action_controller/abstract/helpers.rb
@@ -9,13 +9,6 @@ module AbstractController
self.master_helper_module = Module.new
end
- # def self.included(klass)
- # klass.class_eval do
- # extlib_inheritable_accessor :master_helper_module
- # self.master_helper_module = Module.new
- # end
- # end
-
def _action_view
@_action_view ||= begin
av = super
@@ -23,19 +16,19 @@ module AbstractController
av
end
end
-
+
module ClassMethods
def inherited(klass)
klass.master_helper_module = Module.new
klass.master_helper_module.__send__ :include, master_helper_module
-
+
super
end
-
+
def add_template_helper(mod)
master_helper_module.module_eval { include mod }
end
-
+
def helper_method(*meths)
meths.flatten.each do |meth|
master_helper_module.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
@@ -45,7 +38,7 @@ module AbstractController
ruby_eval
end
end
-
+
def helper(*args, &blk)
args.flatten.each do |arg|
case arg
@@ -56,5 +49,6 @@ module AbstractController
master_helper_module.module_eval(&blk) if block_given?
end
end
+
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb
index 69fe4efc19..e48b8b2b4b 100644
--- a/actionpack/lib/action_controller/abstract/layouts.rb
+++ b/actionpack/lib/action_controller/abstract/layouts.rb
@@ -9,18 +9,18 @@ module AbstractController
unless [String, Symbol, FalseClass, NilClass].include?(layout.class)
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
end
-
+
@_layout = layout || false # Converts nil to false
_write_layout_method
end
-
+
def _implied_layout_name
name.underscore
end
-
+
# Takes the specified layout and creates a _layout method to be called
# by _default_layout
- #
+ #
# If the specified layout is a:
# String:: return the string
# Symbol:: call the method specified by the symbol
@@ -49,22 +49,26 @@ module AbstractController
end
end
end
-
+
def _render_template(template, options)
- _action_view._render_template_with_layout(template, options[:_layout])
+ _action_view._render_template_with_layout(template, options[:_layout], options)
end
-
+
private
+
def _layout() end # This will be overwritten
-
+
+ # :api: plugin
+ # ====
+ # Override this to mutate the inbound layout name
def _layout_for_name(name)
unless [String, FalseClass, NilClass].include?(name.class)
raise ArgumentError, "String, false, or nil expected; you passed #{name.inspect}"
end
-
+
name && view_paths.find_by_parts(name, {:formats => formats}, "layouts")
end
-
+
def _default_layout(require_layout = false)
if require_layout && !_layout
raise ArgumentError,
@@ -74,9 +78,9 @@ module AbstractController
begin
layout = _layout_for_name(_layout)
rescue NameError => e
- raise NoMethodError,
+ raise NoMethodError,
"You specified #{@_layout.inspect} as the layout, but no such method was found"
end
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/abstract/logger.rb b/actionpack/lib/action_controller/abstract/logger.rb
index c3bccd7778..5fb78f1755 100644
--- a/actionpack/lib/action_controller/abstract/logger.rb
+++ b/actionpack/lib/action_controller/abstract/logger.rb
@@ -6,4 +6,4 @@ module AbstractController
cattr_accessor :logger
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/action_controller/abstract/renderer.rb
index b5c31a27ee..b58688c9da 100644
--- a/actionpack/lib/action_controller/abstract/renderer.rb
+++ b/actionpack/lib/action_controller/abstract/renderer.rb
@@ -1,6 +1,15 @@
require "action_controller/abstract/logger"
module AbstractController
+ class AbstractControllerError < StandardError; end
+ class DoubleRenderError < AbstractControllerError
+ DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
+
+ def initialize(message = nil)
+ super(message || DEFAULT_MESSAGE)
+ end
+ end
+
module Renderer
extend ActiveSupport::DependencyModule
@@ -15,31 +24,36 @@ module AbstractController
end
def _action_view
- @_action_view ||= ActionView::Base.new(self.class.view_paths, {}, self)
+ @_action_view ||= ActionView::Base.new(self.class.view_paths, {}, self)
end
-
+
def render(options = {})
+ if response_body
+ raise AbstractController::DoubleRenderError, "OMG"
+ end
+
self.response_body = render_to_body(options)
end
-
+
# Raw rendering of a template to a Rack-compatible body.
# ====
# @option _prefix<String> The template's path prefix
# @option _layout<String> The relative path to the layout template to use
- #
+ #
# :api: plugin
def render_to_body(options = {})
name = options[:_template_name] || action_name
-
- template = options[:_template] || view_paths.find_by_parts(name.to_s, {:formats => formats}, options[:_prefix])
- _render_template(template, options)
+
+ options[:_template] ||= view_paths.find_by_parts(name.to_s, {:formats => formats}, options[:_prefix])
+
+ _render_template(options[:_template], options)
end
# Raw rendering of a template to a string.
# ====
# @option _prefix<String> The template's path prefix
# @option _layout<String> The relative path to the layout template to use
- #
+ #
# :api: plugin
def render_to_string(options = {})
AbstractController::Renderer.body_to_s(render_to_body(options))
@@ -48,7 +62,7 @@ module AbstractController
def _render_template(template, options)
_action_view._render_template_with_layout(template)
end
-
+
def view_paths() _view_paths end
# Return a string representation of a Rack-compatible response body.
@@ -64,14 +78,15 @@ module AbstractController
end
module ClassMethods
+
def append_view_path(path)
self.view_paths << path
end
-
+
def view_paths
self._view_paths
end
-
+
def view_paths=(paths)
self._view_paths = paths.is_a?(ActionView::PathSet) ?
paths : ActionView::Base.process_view_paths(paths)
diff --git a/actionpack/lib/action_controller/base/base.rb b/actionpack/lib/action_controller/base/base.rb
index 7bbde519cc..d25801b17b 100644
--- a/actionpack/lib/action_controller/base/base.rb
+++ b/actionpack/lib/action_controller/base/base.rb
@@ -365,17 +365,23 @@ module ActionController #:nodoc:
attr_reader :template
- class << self
- def call(env)
- new.call(env)
- end
+ def action(name, env)
+ # HACK: For global rescue to have access to the original request and response
+ request = env["action_controller.rescue.request"] ||= ActionDispatch::Request.new(env)
+ response = env["action_controller.rescue.response"] ||= ActionDispatch::Response.new
+ self.action_name = name && name.to_s
+ process(request, response).to_a
+ end
- # Factory for the standard create, process loop where the controller is discarded after processing.
- def process(request, response) #:nodoc:
- ActiveSupport::Deprecation.warn("Controller.process has been deprecated. Use Controller.call instead", caller)
- new.process(request, response)
- end
+ class << self
+ def action(name = nil)
+ @actions ||= {}
+ @actions[name] ||= proc do |env|
+ new.action(name, env)
+ end
+ end
+
# Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
def controller_class_name
@controller_class_name ||= name.demodulize
@@ -518,7 +524,6 @@ module ActionController #:nodoc:
assign_shortcuts(request, response)
initialize_template_class(response)
initialize_current_url
- assign_names
log_processing
send(method, *arguments)
@@ -882,10 +887,6 @@ module ActionController #:nodoc:
@performed_render || @performed_redirect
end
- def assign_names
- @action_name = (params['action'] || 'index')
- end
-
def reset_variables_added_to_assigns
@template.instance_variable_set("@assigns_added", nil)
end
diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb
index cce3b6175d..63866caed9 100644
--- a/actionpack/lib/action_controller/dispatch/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatch/dispatcher.rb
@@ -48,7 +48,7 @@ module ActionController
cattr_accessor :middleware
self.middleware = ActionDispatch::MiddlewareStack.new do |middleware|
middlewares = File.join(File.dirname(__FILE__), "middlewares.rb")
- middleware.instance_eval(File.read(middlewares))
+ middleware.instance_eval(File.read(middlewares), middlewares, 1)
end
include ActiveSupport::Callbacks
diff --git a/actionpack/lib/action_controller/dispatch/middlewares.rb b/actionpack/lib/action_controller/dispatch/middlewares.rb
index 31a7b00d28..f99637b109 100644
--- a/actionpack/lib/action_controller/dispatch/middlewares.rb
+++ b/actionpack/lib/action_controller/dispatch/middlewares.rb
@@ -14,4 +14,4 @@ use lambda { ActionController::Base.session_store },
use "ActionDispatch::ParamsParser"
use "Rack::MethodOverride"
-use "Rack::Head"
+use "Rack::Head" \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base.rb b/actionpack/lib/action_controller/new_base.rb
index 29dc3aaddc..8a7de1476c 100644
--- a/actionpack/lib/action_controller/new_base.rb
+++ b/actionpack/lib/action_controller/new_base.rb
@@ -1,7 +1,27 @@
module ActionController
- autoload :AbstractBase, "action_controller/new_base/base"
- autoload :HideActions, "action_controller/new_base/hide_actions"
- autoload :Layouts, "action_controller/new_base/layouts"
- autoload :Renderer, "action_controller/new_base/renderer"
- autoload :UrlFor, "action_controller/new_base/url_for"
+ autoload :Base, "action_controller/new_base/base"
+ autoload :ConditionalGet, "action_controller/new_base/conditional_get"
+ autoload :HideActions, "action_controller/new_base/hide_actions"
+ autoload :Http, "action_controller/new_base/http"
+ autoload :Layouts, "action_controller/new_base/layouts"
+ autoload :Rails2Compatibility, "action_controller/new_base/compatibility"
+ autoload :Renderer, "action_controller/new_base/renderer"
+ autoload :Testing, "action_controller/new_base/testing"
+ autoload :UrlFor, "action_controller/new_base/url_for"
+
+ # Ported modules
+ # require 'action_controller/routing'
+ autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
+ autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes'
+ autoload :RecordIdentifier, 'action_controller/record_identifier'
+ autoload :Resources, 'action_controller/routing/resources'
+ autoload :SessionManagement, 'action_controller/base/session_management'
+ autoload :TestCase, 'action_controller/testing/test_case'
+ autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter'
+ autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter'
+
+ require 'action_controller/routing'
end
+
+require 'action_dispatch'
+require 'action_view' \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base/base.rb b/actionpack/lib/action_controller/new_base/base.rb
index 2dd5390c99..4892886341 100644
--- a/actionpack/lib/action_controller/new_base/base.rb
+++ b/actionpack/lib/action_controller/new_base/base.rb
@@ -1,59 +1,62 @@
module ActionController
- class AbstractBase < AbstractController::Base
- # :api: public
- attr_internal :request, :response, :params
-
- # :api: public
- def self.controller_name
- @controller_name ||= controller_path.split("/").last
+ class Base < Http
+ abstract!
+
+ include AbstractController::Callbacks
+ include AbstractController::Helpers
+ include AbstractController::Logger
+
+ include ActionController::HideActions
+ include ActionController::UrlFor
+ include ActionController::Renderer
+ include ActionController::Layouts
+ include ActionController::ConditionalGet
+
+ # Legacy modules
+ include SessionManagement
+ include ActionDispatch::StatusCodes
+
+ # Rails 2.x compatibility
+ include ActionController::Rails2Compatibility
+
+ def self.inherited(klass)
+ ::ActionController::Base.subclasses << klass.to_s
+ super
end
-
- # :api: public
- def controller_name() self.class.controller_name end
-
- # :api: public
- def self.controller_path
- @controller_path ||= self.name.sub(/Controller$/, '').underscore
+
+ def self.subclasses
+ @subclasses ||= []
end
-
- # :api: public
- def controller_path() self.class.controller_path end
-
- # :api: private
- def self.action_methods
- @action_names ||= Set.new(self.public_instance_methods - self::CORE_METHODS)
+
+ def self.app_loaded!
+ @subclasses.each do |subclass|
+ subclass.constantize._write_layout_method
+ end
end
-
- # :api: private
- def self.action_names() action_methods end
-
- # :api: private
- def action_methods() self.class.action_names end
-
- # :api: private
- def action_names() action_methods end
-
- # :api: plugin
- def self.call(env)
- controller = new
- controller.call(env).to_rack
+
+ def render(action = action_name, options = {})
+ if action.is_a?(Hash)
+ options, action = action, nil
+ else
+ options.merge! :action => action
+ end
+
+ super(options)
end
-
- # :api: plugin
- def response_body=(body)
- @_response.body = body
+
+ def render_to_body(options = {})
+ options = {:template => options} if options.is_a?(String)
+ super
end
-
- # :api: private
- def call(env)
- @_request = ActionDispatch::Request.new(env)
- @_response = ActionDispatch::Response.new
- process(@_request.parameters[:action])
+
+ def process_action
+ ret = super
+ render if response_body.nil?
+ ret
end
-
- # :api: private
- def to_rack
- response.to_a
+
+ def respond_to_action?(action_name)
+ super || view_paths.find_by_parts?(action_name.to_s, {:formats => formats, :locales => [I18n.locale]}, controller_path)
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base/compatibility.rb b/actionpack/lib/action_controller/new_base/compatibility.rb
new file mode 100644
index 0000000000..4655a94923
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/compatibility.rb
@@ -0,0 +1,58 @@
+module ActionController
+ module Rails2Compatibility
+ extend ActiveSupport::DependencyModule
+
+ # Temporary hax
+ included do
+ ::ActionController::UnknownAction = ::AbstractController::ActionNotFound
+ ::ActionController::DoubleRenderError = ::AbstractController::DoubleRenderError
+
+ cattr_accessor :session_options
+ self.send(:class_variable_set, "@@session_options", {})
+
+ cattr_accessor :allow_concurrency
+ self.send(:class_variable_set, "@@allow_concurrency", false)
+
+ cattr_accessor :param_parsers
+ self.send(:class_variable_set, "@@param_parsers", { Mime::MULTIPART_FORM => :multipart_form,
+ Mime::URL_ENCODED_FORM => :url_encoded_form,
+ Mime::XML => :xml_simple,
+ Mime::JSON => :json })
+
+ cattr_accessor :relative_url_root
+ self.send(:class_variable_set, "@@relative_url_root", ENV['RAILS_RELATIVE_URL_ROOT'])
+
+ cattr_accessor :default_charset
+ self.send(:class_variable_set, "@@default_charset", "utf-8")
+
+ cattr_reader :protected_instance_variables
+ self.send(:class_variable_set, "@@protected_instance_variables", %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
+ @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params
+ @_flash @_response))
+ end
+
+ module ClassMethods
+ def protect_from_forgery() end
+ def consider_all_requests_local() end
+ def rescue_action(env)
+ raise env["action_dispatch.rescue.exception"]
+ end
+ end
+
+ def render_to_body(options)
+ if options.is_a?(Hash) && options.key?(:template)
+ options[:template].sub!(/^\//, '')
+ end
+
+ options[:text] = nil if options[:nothing] == true
+
+ super
+ end
+
+ def _layout_for_name(name)
+ name &&= name.sub(%r{^/?layouts/}, '')
+ super
+ end
+
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base/conditional_get.rb b/actionpack/lib/action_controller/new_base/conditional_get.rb
new file mode 100644
index 0000000000..e1407e671a
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/conditional_get.rb
@@ -0,0 +1,131 @@
+module ActionController
+ module ConditionalGet
+
+ # Sets the etag, last_modified, or both on the response and renders a
+ # "304 Not Modified" response if the request is already fresh.
+ #
+ # Parameters:
+ # * <tt>:etag</tt>
+ # * <tt>:last_modified</tt>
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
+ #
+ # Example:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true)
+ # end
+ #
+ # This will render the show template if the request isn't sending a matching etag or
+ # If-Modified-Since header and just a "304 Not Modified" response if there's a match.
+ #
+ def fresh_when(options)
+ options.assert_valid_keys(:etag, :last_modified, :public)
+
+ response.etag = options[:etag] if options[:etag]
+ response.last_modified = options[:last_modified] if options[:last_modified]
+
+ if options[:public]
+ cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip }
+ cache_control.delete("private")
+ cache_control.delete("no-cache")
+ cache_control << "public"
+ response.headers["Cache-Control"] = cache_control.join(', ')
+ end
+
+ if request.fresh?(response)
+ head :not_modified
+ end
+ end
+
+ # Return a response that has no content (merely headers). The options
+ # argument is interpreted to be a hash of header names and values.
+ # This allows you to easily return a response that consists only of
+ # significant headers:
+ #
+ # head :created, :location => person_path(@person)
+ #
+ # It can also be used to return exceptional conditions:
+ #
+ # return head(:method_not_allowed) unless request.post?
+ # return head(:bad_request) unless valid_request?
+ # render
+ def head(*args)
+ if args.length > 2
+ raise ArgumentError, "too many arguments to head"
+ elsif args.empty?
+ raise ArgumentError, "too few arguments to head"
+ end
+ options = args.extract_options!
+ status = interpret_status(args.shift || options.delete(:status) || :ok)
+
+ options.each do |key, value|
+ headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
+ end
+
+ render :nothing => true, :status => status
+ end
+
+ # Sets the etag and/or last_modified on the response and checks it against
+ # the client request. If the request doesn't match the options provided, the
+ # request is considered stale and should be generated from scratch. Otherwise,
+ # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent.
+ #
+ # Parameters:
+ # * <tt>:etag</tt>
+ # * <tt>:last_modified</tt>
+ # * <tt>:public</tt> By default the Cache-Control header is private, set this to true if you want your application to be cachable by other devices (proxy caches).
+ #
+ # Example:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ #
+ # if stale?(:etag => @article, :last_modified => @article.created_at.utc)
+ # @statistics = @article.really_expensive_call
+ # respond_to do |format|
+ # # all the supported formats
+ # end
+ # end
+ # end
+ def stale?(options)
+ fresh_when(options)
+ !request.fresh?(response)
+ end
+
+ # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
+ # intermediate caches shouldn't cache the response.
+ #
+ # Examples:
+ # expires_in 20.minutes
+ # expires_in 3.hours, :public => true
+ # expires in 3.hours, 'max-stale' => 5.hours, :public => true
+ #
+ # This method will overwrite an existing Cache-Control header.
+ # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
+ def expires_in(seconds, options = {}) #:doc:
+ cache_control = response.headers["Cache-Control"].split(",").map {|k| k.strip }
+
+ cache_control << "max-age=#{seconds}"
+ cache_control.delete("no-cache")
+ if options[:public]
+ cache_control.delete("private")
+ cache_control << "public"
+ else
+ cache_control << "private"
+ end
+
+ # This allows for additional headers to be passed through like 'max-stale' => 5.hours
+ cache_control += options.symbolize_keys.reject{|k,v| k == :public || k == :private }.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
+
+ response.headers["Cache-Control"] = cache_control.join(', ')
+ end
+
+ # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
+ # intermediate caches (like caching proxy servers).
+ def expires_now #:doc:
+ response.headers["Cache-Control"] = "no-cache"
+ end
+
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base/hide_actions.rb b/actionpack/lib/action_controller/new_base/hide_actions.rb
index 422ea180c4..d1857a9169 100644
--- a/actionpack/lib/action_controller/new_base/hide_actions.rb
+++ b/actionpack/lib/action_controller/new_base/hide_actions.rb
@@ -1,25 +1,28 @@
module ActionController
module HideActions
+ extend ActiveSupport::DependencyModule
+
included do
extlib_inheritable_accessor :hidden_actions
self.hidden_actions ||= Set.new
end
def action_methods() self.class.action_names end
- def action_names() action_methods end
-
+ def action_names() action_methods end
+
private
+
def respond_to_action?(action_name)
!hidden_actions.include?(action_name) && (super || respond_to?(:method_missing))
end
-
+
module ClassMethods
def hide_action(*args)
args.each do |arg|
self.hidden_actions << arg.to_s
end
end
-
+
def action_methods
@action_names ||= Set.new(super.reject {|name| self.hidden_actions.include?(name.to_s)})
end
@@ -27,4 +30,4 @@ module ActionController
def self.action_names() action_methods end
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base/http.rb b/actionpack/lib/action_controller/new_base/http.rb
new file mode 100644
index 0000000000..f269fe70db
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/http.rb
@@ -0,0 +1,64 @@
+module ActionController
+ class Http < AbstractController::Base
+ abstract!
+
+ # :api: public
+ attr_internal :request, :response, :params
+
+ # :api: public
+ def self.controller_name
+ @controller_name ||= controller_path.split("/").last
+ end
+
+ # :api: public
+ def controller_name() self.class.controller_name end
+
+ # :api: public
+ def self.controller_path
+ @controller_path ||= self.name.sub(/Controller$/, '').underscore
+ end
+
+ # :api: public
+ def controller_path() self.class.controller_path end
+
+ # :api: private
+ def self.internal_methods
+ ActionController::Http.public_instance_methods(true)
+ end
+
+ # :api: private
+ def self.action_names() action_methods end
+
+ # :api: private
+ def action_names() action_methods end
+
+ # :api: plugin
+ def self.call(env)
+ controller = new
+ controller.call(env).to_rack
+ end
+
+ # :api: private
+ def call(name, env)
+ @_request = ActionDispatch::Request.new(env)
+ @_response = ActionDispatch::Response.new
+ @_response.request = request
+ process(name)
+ @_response.body = response_body
+ @_response.prepare!
+ to_rack
+ end
+
+ def self.action(name)
+ @actions ||= {}
+ @actions[name] ||= proc do |env|
+ new.call(name, env)
+ end
+ end
+
+ # :api: private
+ def to_rack
+ @_response.to_a
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/new_base/layouts.rb b/actionpack/lib/action_controller/new_base/layouts.rb
index 228162421d..e851eb5f9a 100644
--- a/actionpack/lib/action_controller/new_base/layouts.rb
+++ b/actionpack/lib/action_controller/new_base/layouts.rb
@@ -4,34 +4,36 @@ module ActionController
depends_on ActionController::Renderer
depends_on AbstractController::Layouts
-
+
module ClassMethods
def _implied_layout_name
controller_path
end
end
-
+
def render_to_body(options)
# render :text => ..., :layout => ...
# or
# render :anything_else
- if !options.key?(:text) || options.key?(:layout)
+ if (!options.key?(:text) && !options.key?(:inline)) || options.key?(:layout)
options[:_layout] = options.key?(:layout) ? _layout_for_option(options[:layout]) : _default_layout
end
-
+
super
end
-
+
private
+
def _layout_for_option(name)
case name
when String then _layout_for_name(name)
when true then _default_layout(true)
when false, nil then nil
else
- raise ArgumentError,
- "String, true, or false, expected for `layout'; you passed #{name.inspect}"
+ raise ArgumentError,
+ "String, true, or false, expected for `layout'; you passed #{name.inspect}"
end
end
+
end
end
diff --git a/actionpack/lib/action_controller/new_base/renderer.rb b/actionpack/lib/action_controller/new_base/renderer.rb
index d7ea9ec4a5..41e3dfbe23 100644
--- a/actionpack/lib/action_controller/new_base/renderer.rb
+++ b/actionpack/lib/action_controller/new_base/renderer.rb
@@ -3,48 +3,44 @@ module ActionController
extend ActiveSupport::DependencyModule
depends_on AbstractController::Renderer
-
+
def initialize(*)
self.formats = [:html]
super
end
-
- def render(action, options = {})
- # TODO: Move this into #render_to_body
- if action.is_a?(Hash)
- options, action = action, nil
- else
- options.merge! :action => action
- end
-
+
+ def render(options = {})
_process_options(options)
-
- self.response_body = render_to_body(options)
+
+ super(options)
end
def render_to_body(options)
- unless options.is_a?(Hash)
- options = {:action => options}
- end
-
if options.key?(:text)
options[:_template] = ActionView::TextTemplate.new(_text(options))
template = nil
+ 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?(:action)
- options[:_template_name] = options[:action].to_s
- options[:_prefix] = _prefix
+ else
+ options[:_template_name] = (options[:action] || action_name).to_s
+ options[:_prefix] = _prefix
end
-
- super(options)
+
+ ret = super(options)
+ response.content_type ||= options[:_template].mime_type
+ ret
end
-
+
private
+
def _prefix
controller_path
- end
-
+ end
+
def _text(options)
text = options[:text]
@@ -53,11 +49,11 @@ module ActionController
else text.to_s
end
end
-
+
def _process_options(options)
- if status = options[:status]
- response.status = status.to_i
- end
+ status, content_type = options.values_at(:status, :content_type)
+ response.status = status.to_i if status
+ response.content_type = content_type if content_type
end
end
end
diff --git a/actionpack/lib/action_controller/new_base/testing.rb b/actionpack/lib/action_controller/new_base/testing.rb
new file mode 100644
index 0000000000..106990b9ba
--- /dev/null
+++ b/actionpack/lib/action_controller/new_base/testing.rb
@@ -0,0 +1,25 @@
+module ActionController
+ module Testing
+
+ # OMG MEGA HAX
+ def process_with_test(request, response)
+ @_request = request
+ @_response = response
+ @_response.request = request
+ ret = process(request.parameters[:action])
+ @_response.body = self.response_body
+ @_response.prepare!
+ set_test_assigns
+ ret
+ end
+
+ def set_test_assigns
+ @assigns = {}
+ (instance_variable_names - self.class.protected_instance_variables).each do |var|
+ name, value = var[1..-1], instance_variable_get(var)
+ @assigns[name] = value
+ end
+ end
+
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/new_base/url_for.rb b/actionpack/lib/action_controller/new_base/url_for.rb
index 3179c8cd5b..af5b21012b 100644
--- a/actionpack/lib/action_controller/new_base/url_for.rb
+++ b/actionpack/lib/action_controller/new_base/url_for.rb
@@ -16,7 +16,7 @@ module ActionController
# by this method.
def default_url_options(options = nil)
end
-
+
def rewrite_options(options) #:nodoc:
if defaults = default_url_options(options)
defaults.merge(options)
@@ -24,7 +24,7 @@ module ActionController
options
end
end
-
+
def url_for(options = {})
options ||= {}
case options
@@ -37,4 +37,4 @@ module ActionController
end
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb
index 172b867bf0..45ad8a3a3b 100644
--- a/actionpack/lib/action_controller/routing/route_set.rb
+++ b/actionpack/lib/action_controller/routing/route_set.rb
@@ -430,7 +430,7 @@ module ActionController
def call(env)
request = ActionDispatch::Request.new(env)
app = Routing::Routes.recognize(request)
- app.call(env)
+ app.action(request.parameters[:action] || 'index').call(env)
end
def recognize(request)
diff --git a/actionpack/lib/action_controller/testing/integration.rb b/actionpack/lib/action_controller/testing/integration.rb
index 4f39ee6a01..d6991ab4f5 100644
--- a/actionpack/lib/action_controller/testing/integration.rb
+++ b/actionpack/lib/action_controller/testing/integration.rb
@@ -309,24 +309,21 @@ module ActionController
def self.included(base)
base.extend(ClassMethods)
base.class_eval do
- class << self
- alias_method_chain :new, :capture
- end
+ alias_method_chain :initialize, :capture
end
end
+ def initialize_with_capture(*args)
+ initialize_without_capture
+ self.class.last_instantiation ||= self
+ end
+
module ClassMethods #:nodoc:
mattr_accessor :last_instantiation
def clear_last_instantiation!
self.last_instantiation = nil
end
-
- def new_with_capture(*args)
- controller = new_without_capture(*args)
- self.last_instantiation ||= controller
- controller
- end
end
end
diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb
index 49e8322491..21023ac101 100644
--- a/actionpack/lib/action_controller/testing/process.rb
+++ b/actionpack/lib/action_controller/testing/process.rb
@@ -131,6 +131,9 @@ module ActionController #:nodoc:
@request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
build_request_uri(action, parameters)
+ @request.env["action_controller.rescue.request"] = @request
+ @request.env["action_controller.rescue.response"] = @response
+
Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
env = @request.env
@@ -139,7 +142,7 @@ module ActionController #:nodoc:
# TODO: Enable Lint
# app = Rack::Lint.new(app)
- status, headers, body = app.call(env)
+ status, headers, body = app.action(action, env)
response = Rack::MockResponse.new(status, headers, body)
@response.request, @response.template = @request, @controller.template
diff --git a/actionpack/lib/action_controller/testing/process2.rb b/actionpack/lib/action_controller/testing/process2.rb
new file mode 100644
index 0000000000..18b7335450
--- /dev/null
+++ b/actionpack/lib/action_controller/testing/process2.rb
@@ -0,0 +1,69 @@
+require "action_controller/testing/process"
+
+module ActionController
+ module TestProcess
+
+ # Executes a request simulating GET HTTP method and set/volley the response
+ def get(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "GET")
+ end
+
+ # Executes a request simulating POST HTTP method and set/volley the response
+ def post(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "POST")
+ end
+
+ # Executes a request simulating PUT HTTP method and set/volley the response
+ def put(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "PUT")
+ end
+
+ # Executes a request simulating DELETE HTTP method and set/volley the response
+ def delete(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "DELETE")
+ end
+
+ # Executes a request simulating HEAD HTTP method and set/volley the response
+ def head(action, parameters = nil, session = nil, flash = nil)
+ process(action, parameters, session, flash, "HEAD")
+ end
+
+ def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
+ # Sanity check for required instance variables so we can give an
+ # understandable error message.
+ %w(@controller @request @response).each do |iv_name|
+ if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
+ raise "#{iv_name} is nil: make sure you set it in your test's setup method."
+ end
+ end
+
+ @request.recycle!
+ @response.recycle!
+ @controller.response_body = nil
+
+ @html_document = nil
+ @request.env['REQUEST_METHOD'] = http_method
+
+ parameters ||= {}
+ @request.assign_parameters(@controller.class.controller_path, action.to_s, parameters)
+
+ @request.session = ActionController::TestSession.new(session) unless session.nil?
+ @request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
+ build_request_uri(action, parameters)
+ @controller.params.merge!(parameters)
+ # Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
+ @controller.process_with_test(@request, @response)
+ end
+
+ def build_request_uri(action, parameters)
+ unless @request.env['REQUEST_URI']
+ options = @controller.__send__(:rewrite_options, parameters)
+ options.update(:only_path => true, :action => action)
+
+ url = ActionController::UrlRewriter.new(@request, parameters)
+ @request.request_uri = url.rewrite(options)
+ end
+ end
+
+ end
+end \ No newline at end of file