aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller.rb28
-rw-r--r--actionpack/lib/abstract_controller/base.rb15
-rw-r--r--actionpack/lib/abstract_controller/exceptions.rb12
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb2
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb2
-rw-r--r--actionpack/lib/abstract_controller/logger.rb28
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb (renamed from actionpack/lib/abstract_controller/rendering_controller.rb)37
-rw-r--r--actionpack/lib/action_controller.rb122
-rw-r--r--actionpack/lib/action_controller/base.rb72
-rw-r--r--actionpack/lib/action_controller/caching.rb13
-rw-r--r--actionpack/lib/action_controller/dispatch/dispatcher.rb5
-rw-r--r--actionpack/lib/action_controller/metal.rb9
-rw-r--r--actionpack/lib/action_controller/metal/benchmarking.rb1
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb7
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb2
-rw-r--r--actionpack/lib/action_controller/metal/cookies.rb170
-rw-r--r--actionpack/lib/action_controller/metal/exceptions.rb11
-rw-r--r--actionpack/lib/action_controller/metal/flash.rb58
-rw-r--r--actionpack/lib/action_controller/metal/head.rb7
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb2
-rw-r--r--actionpack/lib/action_controller/metal/layouts.rb2
-rw-r--r--actionpack/lib/action_controller/metal/logger.rb34
-rw-r--r--actionpack/lib/action_controller/metal/rack_delegation.rb (renamed from actionpack/lib/action_controller/metal/rack_convenience.rb)10
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb90
-rw-r--r--actionpack/lib/action_controller/metal/redirector.rb22
-rw-r--r--actionpack/lib/action_controller/metal/render_options.rb103
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb91
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb (renamed from actionpack/lib/action_controller/metal/rendering_controller.rb)4
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb32
-rw-r--r--actionpack/lib/action_controller/metal/rescue.rb (renamed from actionpack/lib/action_controller/metal/rescuable.rb)0
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb20
-rw-r--r--actionpack/lib/action_controller/metal/session.rb15
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/testing.rb2
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb2
-rw-r--r--actionpack/lib/action_controller/metal/verification.rb70
-rw-r--r--actionpack/lib/action_controller/notifications.rb10
-rw-r--r--actionpack/lib/action_controller/rails.rb102
-rw-r--r--actionpack/lib/action_controller/test_case.rb (renamed from actionpack/lib/action_controller/testing/test_case.rb)3
-rw-r--r--actionpack/lib/action_controller/testing/process.rb111
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner.rb28
-rw-r--r--actionpack/lib/action_dispatch.rb55
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb14
-rw-r--r--actionpack/lib/action_dispatch/http/response.rb41
-rw-r--r--actionpack/lib/action_dispatch/http/status_codes.rb42
-rw-r--r--actionpack/lib/action_dispatch/http/utils.rb20
-rw-r--r--actionpack/lib/action_dispatch/middleware/cascade.rb29
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb47
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/abstract_store.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb9
-rw-r--r--actionpack/lib/action_dispatch/middleware/show_exceptions.rb16
-rw-r--r--actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb2
-rw-r--r--actionpack/lib/action_dispatch/routing.rb107
-rw-r--r--actionpack/lib/action_dispatch/routing/deprecated_mapper.rb5
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb616
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb201
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/dom.rb4
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb2
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb48
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/selector.rb22
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/tag.rb21
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb12
-rw-r--r--actionpack/lib/action_dispatch/testing/performance_test.rb24
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb42
-rw-r--r--actionpack/lib/action_view.rb56
-rw-r--r--actionpack/lib/action_view/base.rb2
-rw-r--r--actionpack/lib/action_view/helpers/sanitize_helper.rb1
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb2
-rw-r--r--actionpack/lib/action_view/paths.rb2
-rw-r--r--actionpack/lib/action_view/render/partials.rb20
-rw-r--r--actionpack/lib/action_view/render/rendering.rb22
-rw-r--r--actionpack/lib/action_view/safe_buffer.rb1
-rw-r--r--actionpack/lib/action_view/template.rb (renamed from actionpack/lib/action_view/template/template.rb)32
-rw-r--r--actionpack/lib/action_view/template/error.rb156
-rw-r--r--actionpack/lib/action_view/template/handler.rb47
-rw-r--r--actionpack/lib/action_view/template/handlers.rb96
-rw-r--r--actionpack/lib/action_view/template/handlers/builder.rb4
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb7
-rw-r--r--actionpack/lib/action_view/template/handlers/rjs.rb4
-rw-r--r--actionpack/lib/action_view/template/resolver.rb6
-rw-r--r--actionpack/lib/action_view/template/text.rb70
-rw-r--r--actionpack/lib/action_view/test_case.rb5
82 files changed, 1770 insertions, 1499 deletions
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index 1a6c4278c9..237ab577ba 100644
--- a/actionpack/lib/abstract_controller.rb
+++ b/actionpack/lib/abstract_controller.rb
@@ -1,16 +1,18 @@
-require "active_support/core_ext/module/attr_internal"
-require "active_support/core_ext/module/delegation"
+activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
+$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
+
+require 'active_support/ruby/shim'
+require 'active_support/core_ext/module/attr_internal'
+require 'active_support/core_ext/module/delegation'
module AbstractController
- autoload :Base, "abstract_controller/base"
- autoload :Callbacks, "abstract_controller/callbacks"
- autoload :Helpers, "abstract_controller/helpers"
- autoload :Layouts, "abstract_controller/layouts"
- autoload :LocalizedCache, "abstract_controller/localized_cache"
- autoload :Logger, "abstract_controller/logger"
- autoload :RenderingController, "abstract_controller/rendering_controller"
- # === Exceptions
- autoload :ActionNotFound, "abstract_controller/exceptions"
- autoload :DoubleRenderError, "abstract_controller/exceptions"
- autoload :Error, "abstract_controller/exceptions"
+ extend ActiveSupport::Autoload
+
+ autoload :Base
+ autoload :Callbacks
+ autoload :Helpers
+ autoload :Layouts
+ autoload :LocalizedCache
+ autoload :Logger
+ autoload :Rendering
end
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index f5b1c9e4d1..efea81aa71 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -1,8 +1,11 @@
module AbstractController
+ class Error < StandardError; end
+ class ActionNotFound < StandardError; end
class Base
attr_internal :response_body
attr_internal :action_name
+ attr_internal :formats
class << self
attr_reader :abstract
@@ -74,21 +77,23 @@ module AbstractController
abstract!
# Calls the action going through the entire action dispatch stack.
- #
+ #
# The actual method that is called is determined by calling
# #method_for_action. If no method can handle the action, then an
# ActionNotFound error is raised.
#
# ==== Returns
# self
- def process(action)
+ def process(action, *args)
@_action_name = action_name = action.to_s
unless action_name = method_for_action(action_name)
raise ActionNotFound, "The action '#{action}' could not be found"
end
- process_action(action_name)
+ @_response_body = nil
+
+ process_action(action_name, *args)
end
private
@@ -108,8 +113,8 @@ module AbstractController
# Call the action. Override this in a subclass to modify the
# behavior around processing an action. This, and not #process,
# is the intended way to override action dispatching.
- def process_action(method_name)
- send_action(method_name)
+ def process_action(method_name, *args)
+ send_action(method_name, *args)
end
# Actually call the method associated with the action. Override
diff --git a/actionpack/lib/abstract_controller/exceptions.rb b/actionpack/lib/abstract_controller/exceptions.rb
deleted file mode 100644
index b671516de1..0000000000
--- a/actionpack/lib/abstract_controller/exceptions.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-module AbstractController
- class Error < StandardError; end
- class ActionNotFound < StandardError; end
-
- class DoubleRenderError < Error
- 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
-end
diff --git a/actionpack/lib/abstract_controller/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index d3b492ad09..1d898d1a4c 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -4,7 +4,7 @@ module AbstractController
module Helpers
extend ActiveSupport::Concern
- include RenderingController
+ include Rendering
def self.next_serial
@helper_serial ||= 0
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index c71cef42b2..46760bba7c 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -2,7 +2,7 @@ module AbstractController
module Layouts
extend ActiveSupport::Concern
- include RenderingController
+ include Rendering
included do
extlib_inheritable_accessor(:_layout_conditions) { Hash.new }
diff --git a/actionpack/lib/abstract_controller/logger.rb b/actionpack/lib/abstract_controller/logger.rb
index 27ba5be45f..e3bcd28da7 100644
--- a/actionpack/lib/abstract_controller/logger.rb
+++ b/actionpack/lib/abstract_controller/logger.rb
@@ -29,33 +29,5 @@ module AbstractController
@str.send(*args, &block)
end
end
-
- # Override process_action in the AbstractController::Base
- # to log details about the method.
- def process_action(action)
- result = ActiveSupport::Notifications.instrument(:process_action,
- :controller => self, :action => action) do
- super
- end
-
- if logger
- log = DelayedLog.new do
- "\n\nProcessing #{self.class.name}\##{action_name} " \
- "to #{request.formats} (for #{request_origin}) " \
- "[#{request.method.to_s.upcase}]"
- end
-
- logger.info(log)
- end
-
- result
- end
-
- private
- # Returns the request origin with the IP and time. This needs to be cached,
- # otherwise we would get different results for each time it calls.
- def request_origin
- @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}"
- end
end
end
diff --git a/actionpack/lib/abstract_controller/rendering_controller.rb b/actionpack/lib/abstract_controller/rendering.rb
index 7054b9cf26..64a8a5f241 100644
--- a/actionpack/lib/abstract_controller/rendering_controller.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -1,13 +1,21 @@
+require "abstract_controller/base"
require "abstract_controller/logger"
module AbstractController
- module RenderingController
+ class DoubleRenderError < Error
+ 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 Rendering
extend ActiveSupport::Concern
include AbstractController::Logger
included do
- attr_internal :formats
extlib_inheritable_accessor :_view_paths
self._view_paths ||= ActionView::PathSet.new
end
@@ -21,7 +29,7 @@ module AbstractController
# An instance of a view class. The default view class is ActionView::Base
#
# The view class must have the following methods:
- # View.for_controller[controller] Create a new ActionView instance for a
+ # View.for_controller[controller] Create a new ActionView instance for a
# controller
# View#render_partial[options]
# - responsible for setting options[:_template]
@@ -71,7 +79,7 @@ module AbstractController
#
# :api: plugin
def render_to_string(options = {})
- AbstractController::RenderingController.body_to_s(render_to_body(options))
+ AbstractController::Rendering.body_to_s(render_to_body(options))
end
# Renders the template from an object.
@@ -115,10 +123,10 @@ module AbstractController
# _partial<TrueClass, FalseClass>:: Whether or not the file to look up is a partial
def _determine_template(options)
if options.key?(:text)
- options[:_template] = ActionView::TextTemplate.new(options[:text], format_for_text)
+ options[:_template] = ActionView::Template::Text.new(options[:text], format_for_text)
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, {})
+ handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
+ template = ActionView::Template.new(options[:inline], "inline template", handler, {})
options[:_template] = template
elsif options.key?(:template)
options[:_template_name] = options[:template]
@@ -152,12 +160,12 @@ module AbstractController
module ClassMethods
def clear_template_caches!
end
-
+
# Append a path to the list of view paths for this controller.
#
# ==== Parameters
- # path<String, ViewPath>:: If a String is provided, it gets converted into
- # the default view path. You may also provide a custom view path
+ # path<String, ViewPath>:: If a String is provided, it gets converted into
+ # the default view path. You may also provide a custom view path
# (see ActionView::ViewPathSet for more information)
def append_view_path(path)
self.view_paths << path
@@ -166,8 +174,8 @@ module AbstractController
# Prepend a path to the list of view paths for this controller.
#
# ==== Parameters
- # path<String, ViewPath>:: If a String is provided, it gets converted into
- # the default view path. You may also provide a custom view path
+ # path<String, ViewPath>:: If a String is provided, it gets converted into
+ # the default view path. You may also provide a custom view path
# (see ActionView::ViewPathSet for more information)
def prepend_view_path(path)
clear_template_caches!
@@ -186,9 +194,8 @@ module AbstractController
# otherwise, process the parameter into a ViewPathSet.
def view_paths=(paths)
clear_template_caches!
- self._view_paths = paths.is_a?(ActionView::PathSet) ?
- paths : ActionView::Base.process_view_paths(paths)
+ self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths)
end
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 03a40e4fce..26a85d4de8 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -1,66 +1,78 @@
+activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
+$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
+require 'active_support/ruby/shim'
+
module ActionController
- autoload :Base, "action_controller/base"
- autoload :Benchmarking, "action_controller/metal/benchmarking"
- autoload :ConditionalGet, "action_controller/metal/conditional_get"
- autoload :Configuration, "action_controller/metal/configuration"
- autoload :Head, "action_controller/metal/head"
- autoload :Helpers, "action_controller/metal/helpers"
- autoload :HideActions, "action_controller/metal/hide_actions"
- autoload :Layouts, "action_controller/metal/layouts"
- autoload :Metal, "action_controller/metal"
- autoload :Middleware, "action_controller/middleware"
- autoload :RackConvenience, "action_controller/metal/rack_convenience"
- autoload :Rails2Compatibility, "action_controller/metal/compatibility"
- autoload :Redirector, "action_controller/metal/redirector"
- autoload :RenderingController, "action_controller/metal/rendering_controller"
- autoload :RenderOptions, "action_controller/metal/render_options"
- autoload :Rescue, "action_controller/metal/rescuable"
- autoload :Responder, "action_controller/metal/responder"
- autoload :Session, "action_controller/metal/session"
- autoload :Testing, "action_controller/metal/testing"
- autoload :UrlFor, "action_controller/metal/url_for"
+ extend ActiveSupport::Autoload
- autoload :Caching, 'action_controller/caching'
- autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
- autoload :Integration, 'action_controller/deprecated/integration_test'
- autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
- autoload :MimeResponds, 'action_controller/metal/mime_responds'
- autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
- autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes'
- autoload :RecordIdentifier, 'action_controller/record_identifier'
- autoload :Routing, 'action_controller/deprecated'
- autoload :SessionManagement, 'action_controller/metal/session_management'
- autoload :TestCase, 'action_controller/testing/test_case'
- autoload :TestProcess, 'action_controller/testing/process'
- autoload :UrlRewriter, 'action_controller/url_rewriter'
- autoload :UrlWriter, 'action_controller/url_rewriter'
+ autoload :Base
+ autoload :Caching
+ autoload :PolymorphicRoutes
+ autoload :Translation
+ autoload :Metal
+ autoload :Middleware
- autoload :Verification, 'action_controller/metal/verification'
- autoload :Flash, 'action_controller/metal/flash'
- autoload :RequestForgeryProtection, 'action_controller/metal/request_forgery_protection'
- autoload :Streaming, 'action_controller/metal/streaming'
- autoload :HttpAuthentication, 'action_controller/metal/http_authentication'
- autoload :FilterParameterLogging, 'action_controller/metal/filter_parameter_logging'
- autoload :Translation, 'action_controller/translation'
- autoload :Cookies, 'action_controller/metal/cookies'
+ autoload_under "metal" do
+ autoload :Benchmarking
+ autoload :ConditionalGet
+ autoload :Configuration
+ autoload :Head
+ autoload :Helpers
+ autoload :HideActions
+ autoload :Layouts
+ autoload :Logger
+ autoload :MimeResponds
+ autoload :RackDelegation
+ autoload :Compatibility
+ autoload :Redirecting
+ autoload :Rendering
+ autoload :Renderers
+ autoload :Rescue
+ autoload :Responder
+ autoload :SessionManagement
+ autoload :UrlFor
+ autoload :Verification
+ autoload :Flash
+ autoload :RequestForgeryProtection
+ autoload :Streaming
+ autoload :HttpAuthentication
+ autoload :FilterParameterLogging
+ autoload :Cookies
+ end
- autoload :ActionControllerError, 'action_controller/metal/exceptions'
- autoload :RenderError, 'action_controller/metal/exceptions'
- autoload :RoutingError, 'action_controller/metal/exceptions'
- autoload :MethodNotAllowed, 'action_controller/metal/exceptions'
- autoload :NotImplemented, 'action_controller/metal/exceptions'
- autoload :UnknownController, 'action_controller/metal/exceptions'
- autoload :MissingFile, 'action_controller/metal/exceptions'
- autoload :RenderError, 'action_controller/metal/exceptions'
- autoload :SessionOverflowError, 'action_controller/metal/exceptions'
- autoload :UnknownHttpMethod, 'action_controller/metal/exceptions'
-end
+ autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
+ autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
+ autoload :Routing, 'action_controller/deprecated'
+ autoload :Integration, 'action_controller/deprecated/integration_test'
+ autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
-autoload :HTML, 'action_controller/vendor/html-scanner'
-autoload :AbstractController, 'abstract_controller'
+ eager_autoload do
+ autoload :RecordIdentifier
+ autoload :UrlRewriter
+ autoload :UrlWriter, 'action_controller/url_rewriter'
+
+ # TODO: Don't autoload exceptions, setup explicit
+ # requires for files that need them
+ autoload_at "action_controller/metal/exceptions" do
+ autoload :ActionControllerError
+ autoload :RenderError
+ autoload :RoutingError
+ autoload :MethodNotAllowed
+ autoload :NotImplemented
+ autoload :UnknownController
+ autoload :MissingFile
+ autoload :RenderError
+ autoload :SessionOverflowError
+ autoload :UnknownHttpMethod
+ end
+ end
+end
+# All of these simply register additional autoloads
+require 'abstract_controller'
require 'action_dispatch'
require 'action_view'
+require 'action_controller/vendor/html-scanner'
# Common ActiveSupport usage in ActionController
require "active_support/concern"
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 4c026fe5f7..dbba69f637 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -8,26 +8,25 @@ module ActionController
include ActionController::Helpers
include ActionController::HideActions
include ActionController::UrlFor
- include ActionController::Redirector
- include ActionController::RenderingController
- include ActionController::RenderOptions::All
+ include ActionController::Redirecting
+ include ActionController::Rendering
+ include ActionController::Renderers::All
include ActionController::Layouts
include ActionController::ConditionalGet
- include ActionController::RackConvenience
+ include ActionController::RackDelegation
+ include ActionController::Logger
include ActionController::Benchmarking
include ActionController::Configuration
# Legacy modules
include SessionManagement
- include ActionDispatch::StatusCodes
include ActionController::Caching
include ActionController::MimeResponds
# Rails 2.x compatibility
- include ActionController::Rails2Compatibility
+ include ActionController::Compatibility
include ActionController::Cookies
- include ActionController::Session
include ActionController::Flash
include ActionController::Verification
include ActionController::RequestForgeryProtection
@@ -91,7 +90,7 @@ module ActionController
end
if options[:status]
- options[:status] = interpret_status(options[:status]).to_i
+ options[:status] = Rack::Utils.status_code(options[:status])
end
options[:update] = blk if block_given?
@@ -107,62 +106,5 @@ module ActionController
options = _normalize_options(action, options, &blk)
super(options)
end
-
- # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
- #
- # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
- # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
- # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
- # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
- # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
- # Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
- #
- # Examples:
- # redirect_to :action => "show", :id => 5
- # redirect_to post
- # redirect_to "http://www.rubyonrails.org"
- # redirect_to "/images/screenshot.jpg"
- # redirect_to articles_url
- # redirect_to :back
- #
- # The redirection happens as a "302 Moved" header unless otherwise specified.
- #
- # Examples:
- # redirect_to post_url(@post), :status=>:found
- # redirect_to :action=>'atom', :status=>:moved_permanently
- # redirect_to post_url(@post), :status=>301
- # redirect_to :action=>'atom', :status=>302
- #
- # When using <tt>redirect_to :back</tt>, if there is no referrer,
- # RedirectBackError will be raised. You may specify some fallback
- # behavior for this case by rescuing RedirectBackError.
- def redirect_to(options = {}, response_status = {}) #:doc:
- raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
-
- status = if options.is_a?(Hash) && options.key?(:status)
- interpret_status(options.delete(:status))
- elsif response_status.key?(:status)
- interpret_status(response_status[:status])
- else
- 302
- end
-
- url = case options
- # The scheme name consist of a letter followed by any combination of
- # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
- # characters; and is terminated by a colon (":").
- when %r{^\w[\w\d+.-]*:.*}
- options
- when String
- request.protocol + request.host_with_port + options
- when :back
- raise RedirectBackError unless refer = request.headers["Referer"]
- refer
- else
- url_for(options)
- end
-
- super(url, status)
- end
end
end
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 3caf759032..d784138ebe 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -30,12 +30,15 @@ module ActionController #:nodoc:
# config.action_controller.cache_store = MyOwnStore.new("parameter")
module Caching
extend ActiveSupport::Concern
+ extend ActiveSupport::Autoload
- autoload :Actions, 'action_controller/caching/actions'
- autoload :Fragments, 'action_controller/caching/fragments'
- autoload :Pages, 'action_controller/caching/pages'
- autoload :Sweeper, 'action_controller/caching/sweeping'
- autoload :Sweeping, 'action_controller/caching/sweeping'
+ eager_autoload do
+ autoload :Actions
+ autoload :Fragments
+ autoload :Pages
+ autoload :Sweeper, 'action_controller/caching/sweeping'
+ autoload :Sweeping, 'action_controller/caching/sweeping'
+ end
included do
@@cache_store = nil
diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb
index e04da42637..cf02757cf6 100644
--- a/actionpack/lib/action_controller/dispatch/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatch/dispatcher.rb
@@ -13,11 +13,6 @@ module ActionController
# Run prepare callbacks before every request in development mode
self.prepare_each_request = true
- # Development mode callbacks
- ActionDispatch::Callbacks.before_dispatch do |app|
- ActionController::Routing::Routes.reload
- end
-
ActionDispatch::Callbacks.after_dispatch do
# Cleanup the application before processing the current request.
ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord)
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 60b3f9a89b..b436de9878 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -45,7 +45,7 @@ module ActionController
# The details below can be overridden to support a specific
# Request and Response object. The default ActionController::Base
- # implementation includes RackConvenience, which makes a request
+ # implementation includes RackDelegation, which makes a request
# and response object available. You might wish to control the
# environment and response manually for performance reasons.
@@ -57,7 +57,7 @@ module ActionController
end
# Basic implementations for content_type=, location=, and headers are
- # provided to reduce the dependency on the RackConvenience module
+ # provided to reduce the dependency on the RackDelegation module
# in Renderer and Redirector.
def content_type=(type)
@@ -68,6 +68,10 @@ module ActionController
headers["Location"] = url
end
+ def status=(status)
+ @_status = Rack::Utils.status_code(status)
+ end
+
# :api: private
def dispatch(name, env)
@_env = env
@@ -92,6 +96,7 @@ module ActionController
def initialize(controller, action)
@controller, @action = controller, action
+ @_formats = [Mime::HTML]
end
def call(env)
diff --git a/actionpack/lib/action_controller/metal/benchmarking.rb b/actionpack/lib/action_controller/metal/benchmarking.rb
index e58df69172..f73f635b0d 100644
--- a/actionpack/lib/action_controller/metal/benchmarking.rb
+++ b/actionpack/lib/action_controller/metal/benchmarking.rb
@@ -53,7 +53,6 @@ module ActionController #:nodoc:
log_message << " [#{complete_request_uri rescue "unknown"}]"
logger.info(log_message)
- response.headers["X-Runtime"] = "%.0f" % ms
else
super
end
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index c251d79f4e..a90f798cd5 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -1,5 +1,5 @@
module ActionController
- module Rails2Compatibility
+ module Compatibility
extend ActiveSupport::Concern
class ::ActionController::ActionControllerError < StandardError #:nodoc:
@@ -46,11 +46,8 @@ module ActionController
cattr_accessor :use_accept_header
self.use_accept_header = true
- cattr_accessor :page_cache_directory
self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
- cattr_reader :cache_store
-
cattr_accessor :consider_all_requests_local
self.consider_all_requests_local = true
@@ -116,7 +113,7 @@ module ActionController
details[:prefix] = nil if name =~ /\blayouts/
super
end
-
+
# Move this into a "don't run in production" module
def _default_layout(details, require_layout = false)
super
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 5156fbc1d5..61e7ece90d 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -2,7 +2,7 @@ module ActionController
module ConditionalGet
extend ActiveSupport::Concern
- include RackConvenience
+ include RackDelegation
include Head
# Sets the etag, last_modified, or both on the response and renders a
diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb
index 6855ca1478..8d5f0d7199 100644
--- a/actionpack/lib/action_controller/metal/cookies.rb
+++ b/actionpack/lib/action_controller/metal/cookies.rb
@@ -46,60 +46,152 @@ module ActionController #:nodoc:
module Cookies
extend ActiveSupport::Concern
- include RackConvenience
+ include RackDelegation
included do
helper_method :cookies
+ cattr_accessor :cookie_verifier_secret
end
- protected
- # Returns the cookie container, which operates as described above.
- def cookies
- @cookies ||= CookieJar.build(request, response)
+ protected
+ # Returns the cookie container, which operates as described above.
+ def cookies
+ @cookies ||= CookieJar.build(request, response)
+ end
end
- end
- class CookieJar < Hash #:nodoc:
- def self.build(request, response)
- new.tap do |hash|
- hash.update(request.cookies)
- hash.response = response
+ class CookieJar < Hash #:nodoc:
+ def self.build(request, response)
+ new.tap do |hash|
+ hash.update(request.cookies)
+ hash.response = response
+ end
end
- end
- attr_accessor :response
+ attr_accessor :response
- # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
- def [](name)
- super(name.to_s)
- end
+ # Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
+ def [](name)
+ super(name.to_s)
+ end
+
+ # Sets the cookie named +name+. The second argument may be the very cookie
+ # value, or a hash of options as documented above.
+ def []=(key, options)
+ if options.is_a?(Hash)
+ options.symbolize_keys!
+ value = options[:value]
+ else
+ value = options
+ options = { :value => value }
+ end
- # Sets the cookie named +name+. The second argument may be the very cookie
- # value, or a hash of options as documented above.
- def []=(key, options)
- if options.is_a?(Hash)
+ super(key.to_s, value)
+
+ options[:path] ||= "/"
+ response.set_cookie(key, options)
+ end
+
+ # Removes the cookie on the client machine by setting the value to an empty string
+ # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
+ # an options hash to delete cookies with extra data such as a <tt>:path</tt>.
+ def delete(key, options = {})
options.symbolize_keys!
- value = options[:value]
- else
- value = options
- options = { :value => value }
+ options[:path] ||= "/"
+ value = super(key.to_s)
+ response.delete_cookie(key, options)
+ value
end
- super(key.to_s, value)
-
- options[:path] ||= "/"
- response.set_cookie(key, options)
+ # Returns a jar that'll automatically set the assigned cookies to have an expiration date 20 years from now. Example:
+ #
+ # cookies.permanent[:prefers_open_id] = true
+ # # => Set-Cookie: prefers_open_id=true; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
+ #
+ # This jar is only meant for writing. You'll read permanent cookies through the regular accessor.
+ #
+ # This jar allows chaining with the signed jar as well, so you can set permanent, signed cookies. Examples:
+ #
+ # cookies.permanent.signed[:remember_me] = current_user.id
+ # # => Set-Cookie: discount=BAhU--848956038e692d7046deab32b7131856ab20e14e; path=/; expires=Sun, 16-Dec-2029 03:24:16 GMT
+ def permanent
+ @permanent ||= PermanentCookieJar.new(self)
+ end
+
+ # Returns a jar that'll automatically generate a signed representation of cookie value and verify it when reading from
+ # the cookie again. This is useful for creating cookies with values that the user is not supposed to change. If a signed
+ # cookie was tampered with by the user (or a 3rd party), an ActiveSupport::MessageVerifier::InvalidSignature exception will
+ # be raised.
+ #
+ # This jar requires that you set a suitable secret for the verification on ActionController::Base.cookie_verifier_secret.
+ #
+ # Example:
+ #
+ # cookies.signed[:discount] = 45
+ # # => Set-Cookie: discount=BAhpMg==--2c1c6906c90a3bc4fd54a51ffb41dffa4bf6b5f7; path=/
+ #
+ # cookies.signed[:discount] # => 45
+ def signed
+ @signed ||= SignedCookieJar.new(self)
+ end
end
-
- # Removes the cookie on the client machine by setting the value to an empty string
- # and setting its expiration date into the past. Like <tt>[]=</tt>, you can pass in
- # an options hash to delete cookies with extra data such as a <tt>:path</tt>.
- def delete(key, options = {})
- options.symbolize_keys!
- options[:path] ||= "/"
- value = super(key.to_s)
- response.delete_cookie(key, options)
- value
+
+ class PermanentCookieJar < CookieJar #:nodoc:
+ def initialize(parent_jar)
+ @parent_jar = parent_jar
+ end
+
+ def []=(key, options)
+ if options.is_a?(Hash)
+ options.symbolize_keys!
+ else
+ options = { :value => options }
+ end
+
+ options[:expires] = 20.years.from_now
+ @parent_jar[key] = options
+ end
+
+ def signed
+ @signed ||= SignedCookieJar.new(self)
+ end
+
+ def controller
+ @parent_jar.controller
+ end
+
+ def method_missing(method, *arguments, &block)
+ @parent_jar.send(method, *arguments, &block)
+ end
end
+
+ class SignedCookieJar < CookieJar #:nodoc:
+ def initialize(parent_jar)
+ unless ActionController::Base.cookie_verifier_secret
+ raise "You must set ActionController::Base.cookie_verifier_secret to use signed cookies"
+ end
+
+ @parent_jar = parent_jar
+ @verifier = ActiveSupport::MessageVerifier.new(ActionController::Base.cookie_verifier_secret)
+ end
+
+ def [](name)
+ @verifier.verify(@parent_jar[name])
+ end
+
+ def []=(key, options)
+ if options.is_a?(Hash)
+ options.symbolize_keys!
+ options[:value] = @verifier.generate(options[:value])
+ else
+ options = { :value => @verifier.generate(options) }
+ end
+
+ @parent_jar[key] = options
+ end
+
+ def method_missing(method, *arguments, &block)
+ @parent_jar.send(method, *arguments, &block)
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/exceptions.rb b/actionpack/lib/action_controller/metal/exceptions.rb
index b9d23da3e0..07024d0a9a 100644
--- a/actionpack/lib/action_controller/metal/exceptions.rb
+++ b/actionpack/lib/action_controller/metal/exceptions.rb
@@ -18,18 +18,9 @@ module ActionController
def initialize(*allowed_methods)
super("Only #{allowed_methods.to_sentence(:locale => :en)} requests are allowed.")
- @allowed_methods = allowed_methods
- end
-
- def allowed_methods_header
- allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
- end
-
- def handle_response!(response)
- response.headers['Allow'] ||= allowed_methods_header
end
end
-
+
class NotImplemented < MethodNotAllowed #:nodoc:
end
diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb
index f43900faa0..581ff6109e 100644
--- a/actionpack/lib/action_controller/metal/flash.rb
+++ b/actionpack/lib/action_controller/metal/flash.rb
@@ -28,7 +28,9 @@ module ActionController #:nodoc:
module Flash
extend ActiveSupport::Concern
- include Session
+ included do
+ helper_method :alert, :notice
+ end
class FlashNow #:nodoc:
def initialize(flash)
@@ -133,10 +135,44 @@ module ActionController #:nodoc:
Array(key || keys).each { |k| used ? @used << k : @used.delete(k) }
return key ? self[key] : self
end
+ end
+
+ # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
+ # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
+ # to put a new one.
+ def flash #:doc:
+ unless @_flash
+ @_flash = session["flash"] || FlashHash.new
+ @_flash.sweep
end
+ @_flash
+ end
+
+ # Convenience accessor for flash[:alert]
+ def alert
+ flash[:alert]
+ end
+
+ # Convenience accessor for flash[:alert]=
+ def alert=(message)
+ flash[:alert] = message
+ end
+
+ # Convenience accessor for flash[:notice]
+ def notice
+ flash[:notice]
+ end
+
+ # Convenience accessor for flash[:notice]=
+ def notice=(message)
+ flash[:notice] = message
+ end
+
+
protected
def process_action(method_name)
+ @_flash = nil
super
@_flash.store(session) if @_flash
@_flash = nil
@@ -147,16 +183,20 @@ module ActionController #:nodoc:
@_flash = nil
end
- # Access the contents of the flash. Use <tt>flash["notice"]</tt> to
- # read a notice you put there or <tt>flash["notice"] = "hello"</tt>
- # to put a new one.
- def flash #:doc:
- unless @_flash
- @_flash = session["flash"] || FlashHash.new
- @_flash.sweep
+ def redirect_to(options = {}, response_status_and_flash = {}) #:doc:
+ if alert = response_status_and_flash.delete(:alert)
+ flash[:alert] = alert
+ end
+
+ if notice = response_status_and_flash.delete(:notice)
+ flash[:notice] = notice
+ end
+
+ if other_flashes = response_status_and_flash.delete(:flash)
+ flash.update(other_flashes)
end
- @_flash
+ super(options, response_status_and_flash)
end
end
end
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index 68fa0a0402..c82d9cf369 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -1,5 +1,7 @@
module ActionController
module Head
+ include UrlFor
+
# 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
@@ -21,7 +23,10 @@ module ActionController
headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
end
- render :nothing => true, :status => status, :location => location
+ self.status = status
+ self.location = url_for(location) if location
+ self.content_type = Mime[formats.first]
+ self.response_body = " "
end
end
end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index b4325e24ad..d0402e5bad 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -52,7 +52,7 @@ module ActionController
included do
# Set the default directory for helpers
extlib_inheritable_accessor(:helpers_dir) do
- defined?(Rails) ? "#{Rails.root}/app/helpers" : "app/helpers"
+ defined?(Rails.root) ? "#{Rails.root}/app/helpers" : "app/helpers"
end
end
diff --git a/actionpack/lib/action_controller/metal/layouts.rb b/actionpack/lib/action_controller/metal/layouts.rb
index cc7088248a..f44498a884 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::RenderingController
+ include ActionController::Rendering
include AbstractController::Layouts
module ClassMethods
diff --git a/actionpack/lib/action_controller/metal/logger.rb b/actionpack/lib/action_controller/metal/logger.rb
new file mode 100644
index 0000000000..956d7dd371
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/logger.rb
@@ -0,0 +1,34 @@
+require 'abstract_controller/logger'
+
+module ActionController
+ module Logger
+ # Override process_action in the AbstractController::Base
+ # to log details about the method.
+ def process_action(action)
+ result = ActiveSupport::Notifications.instrument(:process_action,
+ :controller => self, :action => action) do
+ super
+ end
+
+ if logger
+ log = AbstractController::Logger::DelayedLog.new do
+ "\n\nProcessing #{self.class.name}\##{action_name} " \
+ "to #{request.formats} (for #{request_origin}) " \
+ "[#{request.method.to_s.upcase}]"
+ end
+
+ logger.info(log)
+ end
+
+ result
+ end
+
+ private
+
+ # Returns the request origin with the IP and time. This needs to be cached,
+ # otherwise we would get different results for each time it calls.
+ def request_origin
+ @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}"
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/rack_convenience.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb
index 131d20114d..bb55383631 100644
--- a/actionpack/lib/action_controller/metal/rack_convenience.rb
+++ b/actionpack/lib/action_controller/metal/rack_delegation.rb
@@ -1,8 +1,12 @@
+require 'action_dispatch/http/request'
+require 'action_dispatch/http/response'
+
module ActionController
- module RackConvenience
+ module RackDelegation
extend ActiveSupport::Concern
included do
+ delegate :session, :to => "@_request"
delegate :headers, :status=, :location=, :content_type=,
:status, :location, :content_type, :to => "@_response"
attr_internal :request
@@ -23,5 +27,9 @@ module ActionController
response.body = body if response
super
end
+
+ def reset_session
+ @_request.reset_session
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
new file mode 100644
index 0000000000..7a2f9a6fc5
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -0,0 +1,90 @@
+module ActionController
+ class RedirectBackError < AbstractController::Error #:nodoc:
+ DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
+
+ def initialize(message = nil)
+ super(message || DEFAULT_MESSAGE)
+ end
+ end
+
+ module Redirecting
+ extend ActiveSupport::Concern
+ include AbstractController::Logger
+
+ # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
+ #
+ # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
+ # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
+ # * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
+ # * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
+ # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
+ # Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
+ #
+ # Examples:
+ # redirect_to :action => "show", :id => 5
+ # redirect_to post
+ # redirect_to "http://www.rubyonrails.org"
+ # redirect_to "/images/screenshot.jpg"
+ # redirect_to articles_url
+ # redirect_to :back
+ #
+ # The redirection happens as a "302 Moved" header unless otherwise specified.
+ #
+ # Examples:
+ # redirect_to post_url(@post), :status => :found
+ # redirect_to :action=>'atom', :status => :moved_permanently
+ # redirect_to post_url(@post), :status => 301
+ # redirect_to :action=>'atom', :status => 302
+ #
+ # It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
+ # +alert+ and +notice+ as well as a general purpose +flash+ bucket.
+ #
+ # Examples:
+ # redirect_to post_url(@post), :alert => "Watch it, mister!"
+ # redirect_to post_url(@post), :status=> :found, :notice => "Pay attention to the road"
+ # redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id }
+ # redirect_to { :action=>'atom' }, :alert => "Something serious happened"
+ #
+ # When using <tt>redirect_to :back</tt>, if there is no referrer,
+ # RedirectBackError will be raised. You may specify some fallback
+ # behavior for this case by rescuing RedirectBackError.
+ def redirect_to(options = {}, response_status = {}) #:doc:
+ raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
+ raise AbstractController::DoubleRenderError if response_body
+
+ self.status = _extract_redirect_to_status(options, response_status)
+ self.location = _compute_redirect_to_location(options)
+ self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(location)}\">redirected</a>.</body></html>"
+
+ logger.info("Redirected to #{location}") if logger && logger.info?
+ end
+
+ private
+ def _extract_redirect_to_status(options, response_status)
+ status = if options.is_a?(Hash) && options.key?(:status)
+ Rack::Utils.status_code(options.delete(:status))
+ elsif response_status.key?(:status)
+ Rack::Utils.status_code(response_status[:status])
+ else
+ 302
+ end
+ end
+
+ def _compute_redirect_to_location(options)
+ case options
+ # The scheme name consist of a letter followed by any combination of
+ # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
+ # characters; and is terminated by a colon (":").
+ when %r{^\w[\w\d+.-]*:.*}
+ options
+ when String
+ request.protocol + request.host_with_port + options
+ when :back
+ raise RedirectBackError unless refer = request.headers["Referer"]
+ refer
+ else
+ url_for(options)
+ end.gsub(/[\r\n]/, '')
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/redirector.rb b/actionpack/lib/action_controller/metal/redirector.rb
deleted file mode 100644
index b55f5e7bfc..0000000000
--- a/actionpack/lib/action_controller/metal/redirector.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-module ActionController
- class RedirectBackError < AbstractController::Error #:nodoc:
- DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
-
- def initialize(message = nil)
- super(message || DEFAULT_MESSAGE)
- end
- end
-
- module Redirector
- extend ActiveSupport::Concern
- include AbstractController::Logger
-
- def redirect_to(url, status) #:doc:
- raise AbstractController::DoubleRenderError if response_body
- logger.info("Redirected to #{url}") if logger && logger.info?
- self.status = status
- self.location = url.gsub(/[\r\n]/, '')
- self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(url)}\">redirected</a>.</body></html>"
- end
- end
-end
diff --git a/actionpack/lib/action_controller/metal/render_options.rb b/actionpack/lib/action_controller/metal/render_options.rb
deleted file mode 100644
index 0d69ca10df..0000000000
--- a/actionpack/lib/action_controller/metal/render_options.rb
+++ /dev/null
@@ -1,103 +0,0 @@
-module ActionController
- module RenderOptions
- extend ActiveSupport::Concern
-
- included do
- extlib_inheritable_accessor :_renderers
- self._renderers = []
- end
-
- module ClassMethods
- def _write_render_options
- renderers = _renderers.map do |r|
- <<-RUBY_EVAL
- if options.key?(:#{r})
- _process_options(options)
- return render_#{r}(options[:#{r}], options)
- end
- RUBY_EVAL
- end
-
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def _handle_render_options(options)
- #{renderers.join}
- end
- RUBY_EVAL
- end
-
- def _add_render_option(name)
- _renderers << name
- _write_render_options
- end
- end
-
- def render_to_body(options)
- _handle_render_options(options) || super
- end
- end
-
- module RenderOption #:nodoc:
- def self.extended(base)
- base.extend ActiveSupport::Concern
- base.send :include, ::ActionController::RenderOptions
-
- def base.register_renderer(name)
- included { _add_render_option(name) }
- end
- end
- end
-
- module RenderOptions
- module Json
- extend RenderOption
- register_renderer :json
-
- def render_json(json, options)
- json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str)
- json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
- self.content_type ||= Mime::JSON
- self.response_body = json
- end
- end
-
- module Js
- extend RenderOption
- register_renderer :js
-
- def render_js(js, options)
- self.content_type ||= Mime::JS
- self.response_body = js.respond_to?(:to_js) ? js.to_js : js
- end
- end
-
- module Xml
- extend RenderOption
- register_renderer :xml
-
- def render_xml(xml, options)
- self.content_type ||= Mime::XML
- self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml
- end
- end
-
- module RJS
- extend RenderOption
- register_renderer :update
-
- def render_update(proc, options)
- generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc)
- self.content_type = Mime::JS
- self.response_body = generator.to_s
- end
- end
-
- module All
- extend ActiveSupport::Concern
-
- 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/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
new file mode 100644
index 0000000000..c1ba47927a
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -0,0 +1,91 @@
+module ActionController
+ def self.add_renderer(key, &block)
+ Renderers.add(key, &block)
+ end
+
+ module Renderers
+ extend ActiveSupport::Concern
+
+ included do
+ extlib_inheritable_accessor :_renderers
+ self._renderers = {}
+ end
+
+ module ClassMethods
+ def _write_render_options
+ renderers = _renderers.map do |name, value|
+ <<-RUBY_EVAL
+ if options.key?(:#{name})
+ _process_options(options)
+ return _render_option_#{name}(options[:#{name}], options)
+ end
+ RUBY_EVAL
+ end
+
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
+ def _handle_render_options(options)
+ #{renderers.join}
+ end
+ RUBY_EVAL
+ end
+
+ def use_renderers(*args)
+ args.each do |key|
+ _renderers[key] = RENDERERS[key]
+ end
+ _write_render_options
+ end
+ alias use_renderer use_renderers
+ end
+
+ def render_to_body(options)
+ _handle_render_options(options) || super
+ end
+
+ RENDERERS = {}
+ def self.add(key, &block)
+ define_method("_render_option_#{key}", &block)
+ RENDERERS[key] = block
+ All._write_render_options
+ end
+
+ module All
+ extend ActiveSupport::Concern
+ include Renderers
+
+ INCLUDED = []
+ included do
+ self._renderers = RENDERERS
+ _write_render_options
+ INCLUDED << self
+ end
+
+ def self._write_render_options
+ INCLUDED.each(&:_write_render_options)
+ end
+ end
+
+ add :json do |json, options|
+ json = ActiveSupport::JSON.encode(json) unless json.respond_to?(:to_str)
+ json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
+ self.content_type ||= Mime::JSON
+ self.response_body = json
+ end
+
+ add :js do |js, options|
+ self.content_type ||= Mime::JS
+ self.response_body = js.respond_to?(:to_js) ? js.to_js : js
+ end
+
+ add :xml do |xml, options|
+ self.content_type ||= Mime::XML
+ self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml : xml
+ end
+
+ add :update do |proc, options|
+ generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc)
+ self.content_type = Mime::JS
+ self.response_body = generator.to_s
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/metal/rendering_controller.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 237299cd30..20eb524e50 100644
--- a/actionpack/lib/action_controller/metal/rendering_controller.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -1,9 +1,9 @@
module ActionController
- module RenderingController
+ module Rendering
extend ActiveSupport::Concern
included do
- include AbstractController::RenderingController
+ include AbstractController::Rendering
include AbstractController::LocalizedCache
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 173df79ee7..2826b1e34c 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -5,7 +5,7 @@ module ActionController #:nodoc:
module RequestForgeryProtection
extend ActiveSupport::Concern
- include AbstractController::Helpers, Session
+ include AbstractController::Helpers
included do
# Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
@@ -19,31 +19,31 @@ module ActionController #:nodoc:
helper_method :form_authenticity_token
helper_method :protect_against_forgery?
end
-
- # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current
- # web application, not a forged link from another site, is done by embedding a token based on a random
+
+ # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current
+ # web application, not a forged link from another site, is done by embedding a token based on a random
# string stored in the session (which an attacker wouldn't know) in all forms and Ajax requests generated
- # by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript
- # requests are checked, so this will not protect your XML API (presumably you'll have a different
- # authentication scheme there anyway). Also, GET requests are not protected as these should be
+ # by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript
+ # requests are checked, so this will not protect your XML API (presumably you'll have a different
+ # authentication scheme there anyway). Also, GET requests are not protected as these should be
# idempotent anyway.
#
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
- # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the
+ # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the
# error message in production by editing public/422.html. A call to this method in ApplicationController is
# generated by default in post-Rails 2.0 applications.
#
- # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form
- # manually (without the use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to
- # include a hidden field named like that and set its value to what is returned by
+ # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form
+ # manually (without the use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to
+ # include a hidden field named like that and set its value to what is returned by
# <tt>form_authenticity_token</tt>.
#
- # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails
+ # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails
# 1.x, add this to config/environments/test.rb:
#
# # Disable request forgery protection in test environment
# config.action_controller.allow_forgery_protection = false
- #
+ #
# == Learn more about CSRF (Cross-Site Request Forgery) attacks
#
# Here are some resources:
@@ -52,11 +52,11 @@ module ActionController #:nodoc:
#
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
# There are a few guidelines you should follow:
- #
+ #
# * Keep your GET requests safe and idempotent. More reading material:
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
- # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look
+ # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look
# for "Expires: at end of session"
#
module ClassMethods
@@ -92,7 +92,7 @@ module ActionController #:nodoc:
# * is it a GET request? Gets should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
def verified_request?
- !protect_against_forgery? || request.forgery_whitelisted? ||
+ !protect_against_forgery? || request.forgery_whitelisted? ||
form_authenticity_token == params[request_forgery_protection_token]
end
diff --git a/actionpack/lib/action_controller/metal/rescuable.rb b/actionpack/lib/action_controller/metal/rescue.rb
index bbca1b2179..bbca1b2179 100644
--- a/actionpack/lib/action_controller/metal/rescuable.rb
+++ b/actionpack/lib/action_controller/metal/rescue.rb
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index e8e88e7479..cb0e600871 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -80,6 +80,11 @@ module ActionController #:nodoc:
class Responder
attr_reader :controller, :request, :format, :resource, :resources, :options
+ ACTIONS_FOR_VERBS = {
+ :post => :new,
+ :put => :edit
+ }
+
def initialize(controller, resources, options={})
@controller = controller
@request = controller.request
@@ -102,9 +107,14 @@ module ActionController #:nodoc:
# not defined, call to_format.
#
def self.call(*args)
- responder = new(*args)
- method = :"to_#{responder.format}"
- responder.respond_to?(method) ? responder.send(method) : responder.to_format
+ new(*args).respond
+ end
+
+ # Main entry point for responder responsible to dispatch to the proper format.
+ #
+ def respond
+ method = :"to_#{format}"
+ respond_to?(method) ? send(method) : to_format
end
# HTML format does not render the resource, it always attempt to render a
@@ -133,7 +143,7 @@ module ActionController #:nodoc:
def navigation_behavior(error)
if get?
raise error
- elsif has_errors?
+ elsif has_errors? && default_action
render :action => default_action
else
redirect_to resource_location
@@ -204,7 +214,7 @@ module ActionController #:nodoc:
# the verb is post.
#
def default_action
- @action || (request.post? ? :new : :edit)
+ @action ||= ACTIONS_FOR_VERBS[request.method]
end
end
end
diff --git a/actionpack/lib/action_controller/metal/session.rb b/actionpack/lib/action_controller/metal/session.rb
deleted file mode 100644
index bcedd6e1c7..0000000000
--- a/actionpack/lib/action_controller/metal/session.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-module ActionController
- module Session
- extend ActiveSupport::Concern
-
- include RackConvenience
-
- def session
- @_request.session
- end
-
- def reset_session
- @_request.reset_session
- end
- end
-end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 43c661bef4..288b5d7c99 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -4,7 +4,7 @@ module ActionController #:nodoc:
module Streaming
extend ActiveSupport::Concern
- include ActionController::RenderingController
+ include ActionController::Rendering
DEFAULT_SEND_FILE_OPTIONS = {
:type => 'application/octet-stream'.freeze,
diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb
index a4a1116d9e..c193a5eff4 100644
--- a/actionpack/lib/action_controller/metal/testing.rb
+++ b/actionpack/lib/action_controller/metal/testing.rb
@@ -2,7 +2,7 @@ module ActionController
module Testing
extend ActiveSupport::Concern
- include RackConvenience
+ include RackDelegation
# OMG MEGA HAX
def process_with_new_base_test(request, response)
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 14c6523045..8c3810ebcb 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -2,7 +2,7 @@ module ActionController
module UrlFor
extend ActiveSupport::Concern
- include RackConvenience
+ include RackDelegation
# Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
# the form of a hash, just like the one you would use for url_for directly. Example:
diff --git a/actionpack/lib/action_controller/metal/verification.rb b/actionpack/lib/action_controller/metal/verification.rb
index 500cced539..bce942b588 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, RenderingController
+ include AbstractController::Callbacks, Flash, Rendering
# This module provides a class-level method for specifying that certain
# actions are guarded against being called without certain prerequisites
@@ -35,7 +35,7 @@ module ActionController #:nodoc:
# :add_flash => { "alert" => "Failed to create your message" },
# :redirect_to => :category_url
#
- # Note that these prerequisites are not business rules. They do not examine
+ # Note that these prerequisites are not business rules. They do not examine
# the content of the session or the parameters. That level of validation should
# be encapsulated by your domain model or helper methods in the controller.
module ClassMethods
@@ -43,40 +43,40 @@ module ActionController #:nodoc:
# the user is redirected to a different action. The +options+ parameter
# is a hash consisting of the following key/value pairs:
#
- # <tt>:params</tt>::
- # a single key or an array of keys that must be in the <tt>params</tt>
+ # <tt>:params</tt>::
+ # a single key or an array of keys that must be in the <tt>params</tt>
# hash in order for the action(s) to be safely called.
- # <tt>:session</tt>::
- # a single key or an array of keys that must be in the <tt>session</tt>
+ # <tt>:session</tt>::
+ # a single key or an array of keys that must be in the <tt>session</tt>
# in order for the action(s) to be safely called.
- # <tt>:flash</tt>::
- # a single key or an array of keys that must be in the flash in order
+ # <tt>:flash</tt>::
+ # a single key or an array of keys that must be in the flash in order
# for the action(s) to be safely called.
- # <tt>:method</tt>::
- # a single key or an array of keys--any one of which must match the
- # current request method in order for the action(s) to be safely called.
- # (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for
+ # <tt>:method</tt>::
+ # a single key or an array of keys--any one of which must match the
+ # current request method in order for the action(s) to be safely called.
+ # (The key should be a symbol: <tt>:get</tt> or <tt>:post</tt>, for
# example.)
- # <tt>:xhr</tt>::
- # true/false option to ensure that the request is coming from an Ajax
- # call or not.
- # <tt>:add_flash</tt>::
- # a hash of name/value pairs that should be merged into the session's
+ # <tt>:xhr</tt>::
+ # true/false option to ensure that the request is coming from an Ajax
+ # call or not.
+ # <tt>:add_flash</tt>::
+ # a hash of name/value pairs that should be merged into the session's
# flash if the prerequisites cannot be satisfied.
- # <tt>:add_headers</tt>::
- # a hash of name/value pairs that should be merged into the response's
+ # <tt>:add_headers</tt>::
+ # a hash of name/value pairs that should be merged into the response's
# headers hash if the prerequisites cannot be satisfied.
- # <tt>:redirect_to</tt>::
- # the redirection parameters to be used when redirecting if the
- # prerequisites cannot be satisfied. You can redirect either to named
+ # <tt>:redirect_to</tt>::
+ # the redirection parameters to be used when redirecting if the
+ # prerequisites cannot be satisfied. You can redirect either to named
# route or to the action in some controller.
- # <tt>:render</tt>::
+ # <tt>:render</tt>::
# the render parameters to be used when the prerequisites cannot be satisfied.
- # <tt>:only</tt>::
- # only apply this verification to the actions specified in the associated
+ # <tt>:only</tt>::
+ # only apply this verification to the actions specified in the associated
# array (may also be a single value).
- # <tt>:except</tt>::
- # do not apply this verification to the actions specified in the associated
+ # <tt>:except</tt>::
+ # do not apply this verification to the actions specified in the associated
# array (may also be a single value).
def verify(options={})
before_filter :only => options[:only], :except => options[:except] do
@@ -94,31 +94,31 @@ module ActionController #:nodoc:
apply_remaining_actions(options) unless performed?
end
end
-
+
def prereqs_invalid?(options) # :nodoc:
- verify_presence_of_keys_in_hash_flash_or_params(options) ||
- verify_method(options) ||
+ verify_presence_of_keys_in_hash_flash_or_params(options) ||
+ verify_method(options) ||
verify_request_xhr_status(options)
end
-
+
def verify_presence_of_keys_in_hash_flash_or_params(options) # :nodoc:
[*options[:params] ].find { |v| v && params[v.to_sym].nil? } ||
[*options[:session]].find { |v| session[v].nil? } ||
[*options[:flash] ].find { |v| flash[v].nil? }
end
-
+
def verify_method(options) # :nodoc:
[*options[:method]].all? { |v| request.method != v.to_sym } if options[:method]
end
-
+
def verify_request_xhr_status(options) # :nodoc:
request.xhr? != options[:xhr] unless options[:xhr].nil?
end
-
+
def apply_redirect_to(redirect_to_option) # :nodoc:
(redirect_to_option.is_a?(Symbol) && redirect_to_option != :back) ? self.__send__(redirect_to_option) : redirect_to_option
end
-
+
def apply_remaining_actions(options) # :nodoc:
case
when options[:render] ; render(options[:render])
diff --git a/actionpack/lib/action_controller/notifications.rb b/actionpack/lib/action_controller/notifications.rb
deleted file mode 100644
index 1a4f29e0e2..0000000000
--- a/actionpack/lib/action_controller/notifications.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-require 'active_support/notifications'
-
-ActiveSupport::Notifications.subscribe(/(read|write|cache|expire|exist)_(fragment|page)\??/) do |*args|
- event = ActiveSupport::Notifications::Event.new(*args)
-
- if logger = ActionController::Base.logger
- human_name = event.name.to_s.humanize
- logger.info("#{human_name} (%.1fms)" % event.duration)
- end
-end
diff --git a/actionpack/lib/action_controller/rails.rb b/actionpack/lib/action_controller/rails.rb
new file mode 100644
index 0000000000..36a52b3149
--- /dev/null
+++ b/actionpack/lib/action_controller/rails.rb
@@ -0,0 +1,102 @@
+module ActionController
+ class Plugin < Rails::Plugin
+ plugin_name :action_controller
+
+ initializer "action_controller.set_configs" do |app|
+ app.config.action_controller.each do |k,v|
+ ActionController::Base.send "#{k}=", v
+ end
+ end
+
+ # TODO: ActionController::Base.logger should delegate to its own config.logger
+ initializer "action_controller.logger" do
+ ActionController::Base.logger ||= Rails.logger
+ end
+
+ # Routing must be initialized after plugins to allow the former to extend the routes
+ # ---
+ # If Action Controller is not one of the loaded frameworks (Configuration#frameworks)
+ # this does nothing. Otherwise, it loads the routing definitions and sets up
+ # loading module used to lazily load controllers (Configuration#controller_paths).
+ initializer "action_controller.initialize_routing" do |app|
+ app.route_configuration_files << app.config.routes_configuration_file
+ app.route_configuration_files << app.config.builtin_routes_configuration_file
+ app.reload_routes!
+ end
+
+ # Include middleware to serve up static assets
+ initializer "action_controller.initialize_static_server" do |app|
+ if app.config.serve_static_assets
+ app.config.middleware.use(ActionDispatch::Static, Rails.public_path)
+ end
+ end
+
+ initializer "action_controller.initialize_middleware_stack" do |app|
+ middleware = app.config.middleware
+ middleware.use(::Rack::Lock, :if => lambda { ActionController::Base.allow_concurrency })
+ middleware.use(::Rack::Runtime)
+ middleware.use(ActionDispatch::ShowExceptions, lambda { ActionController::Base.consider_all_requests_local })
+ middleware.use(ActionDispatch::Callbacks, lambda { ActionController::Dispatcher.prepare_each_request })
+ middleware.use(lambda { ActionController::Base.session_store }, lambda { ActionController::Base.session_options })
+ middleware.use(ActionDispatch::ParamsParser)
+ middleware.use(::Rack::MethodOverride)
+ middleware.use(::Rack::Head)
+ middleware.use(ActionDispatch::StringCoercion)
+ end
+
+ initializer "action_controller.initialize_framework_caches" do
+ ActionController::Base.cache_store ||= RAILS_CACHE
+ end
+
+ # Sets +ActionController::Base#view_paths+ and +ActionMailer::Base#template_root+
+ # (but only for those frameworks that are to be loaded). If the framework's
+ # paths have already been set, it is not changed, otherwise it is
+ # set to use Configuration#view_path.
+ initializer "action_controller.initialize_framework_views" do |app|
+ # TODO: this should be combined with the logic for default config.action_controller.view_paths
+ view_path = ActionView::PathSet.type_cast(app.config.view_path, app.config.cache_classes)
+ ActionController::Base.view_paths = view_path if ActionController::Base.view_paths.blank?
+ end
+
+ initializer "action_controller.initialize_metal" do |app|
+ Rails::Rack::Metal.requested_metals = app.config.metals
+
+ app.config.middleware.insert_before(:"ActionDispatch::ParamsParser",
+ Rails::Rack::Metal, :if => Rails::Rack::Metal.metals.any?)
+ end
+
+ # # Prepare dispatcher callbacks and run 'prepare' callbacks
+ initializer "action_controller.prepare_dispatcher" do |app|
+ # TODO: This used to say unless defined?(Dispatcher). Find out why and fix.
+ require 'rails/dispatcher'
+
+ Dispatcher.define_dispatcher_callbacks(app.config.cache_classes)
+
+ unless app.config.cache_classes
+ # Setup dev mode route reloading
+ routes_last_modified = app.routes_changed_at
+ reload_routes = lambda do
+ unless app.routes_changed_at == routes_last_modified
+ routes_last_modified = app.routes_changed_at
+ app.reload_routes!
+ end
+ end
+ ActionDispatch::Callbacks.before_dispatch { |callbacks| reload_routes.call }
+ end
+ end
+
+ initializer "action_controller.notifications" do |app|
+ require 'active_support/notifications'
+
+ ActiveSupport::Notifications.subscribe(/(read|write|cache|expire|exist)_(fragment|page)\??/) do |*args|
+ event = ActiveSupport::Notifications::Event.new(*args)
+
+ if logger = ActionController::Base.logger
+ human_name = event.name.to_s.humanize
+ logger.info("#{human_name} (%.1fms)" % event.duration)
+ end
+ end
+ end
+
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 01a55fe930..398ea52495 100644
--- a/actionpack/lib/action_controller/testing/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,5 +1,6 @@
require 'active_support/test_case'
require 'rack/session/abstract/id'
+require 'action_controller/metal/testing'
module ActionController
class TestRequest < ActionDispatch::TestRequest #:nodoc:
@@ -180,7 +181,7 @@ module ActionController
#
# assert_redirected_to page_url(:title => 'foo')
class TestCase < ActiveSupport::TestCase
- include TestProcess
+ include ActionDispatch::TestProcess
# Executes a request simulating GET HTTP method and set/volley the response
def get(action, parameters = nil, session = nil, flash = nil)
diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb
deleted file mode 100644
index 323cce6a2f..0000000000
--- a/actionpack/lib/action_controller/testing/process.rb
+++ /dev/null
@@ -1,111 +0,0 @@
-require 'active_support/core_ext/object/conversions'
-require "rack/test"
-
-module ActionController #:nodoc:
- # Essentially generates a modified Tempfile object similar to the object
- # you'd get from the standard library CGI module in a multipart
- # request. This means you can use an ActionController::TestUploadedFile
- # object in the params of a test request in order to simulate
- # a file upload.
- #
- # Usage example, within a functional test:
- # post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png')
- #
- # Pass a true third parameter to ensure the uploaded file is opened in binary mode (only required for Windows):
- # post :change_avatar, :avatar => ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + '/files/spongebob.png', 'image/png', :binary)
- TestUploadedFile = Rack::Test::UploadedFile
-
- module TestProcess
- def assigns(key = nil)
- assigns = {}
- @controller.instance_variable_names.each do |ivar|
- next if ActionController::Base.protected_instance_variables.include?(ivar)
- assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar)
- end
-
- key.nil? ? assigns : assigns[key.to_s]
- end
-
- def session
- @request.session
- end
-
- def flash
- @request.flash
- end
-
- def cookies
- @request.cookies.merge(@response.cookies)
- end
-
- def redirect_to_url
- @response.redirect_url
- end
-
- def html_document
- xml = @response.content_type =~ /xml$/
- @html_document ||= HTML::Document.new(@response.body, false, xml)
- end
-
- def find_tag(conditions)
- html_document.find(conditions)
- end
-
- def find_all_tag(conditions)
- html_document.find_all(conditions)
- end
-
- def method_missing(selector, *args, &block)
- if @controller && ActionController::Routing::Routes.named_routes.helpers.include?(selector)
- @controller.send(selector, *args, &block)
- else
- super
- end
- end
-
- # Shortcut for <tt>ActionController::TestUploadedFile.new(ActionController::TestCase.fixture_path + path, type)</tt>:
- #
- # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
- #
- # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
- # This will not affect other platforms:
- #
- # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary)
- def fixture_file_upload(path, mime_type = nil, binary = false)
- fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path)
- ActionController::TestUploadedFile.new("#{fixture_path}#{path}", mime_type, binary)
- end
-
- # A helper to make it easier to test different route configurations.
- # This method temporarily replaces ActionController::Routing::Routes
- # with a new RouteSet instance.
- #
- # The new instance is yielded to the passed block. Typically the block
- # will create some routes using <tt>map.draw { map.connect ... }</tt>:
- #
- # with_routing do |set|
- # set.draw do |map|
- # map.connect ':controller/:action/:id'
- # assert_equal(
- # ['/content/10/show', {}],
- # map.generate(:controller => 'content', :id => 10, :action => 'show')
- # end
- # end
- # end
- #
- def with_routing
- real_routes = ActionController::Routing::Routes
- ActionController::Routing.module_eval { remove_const :Routes }
-
- temporary_routes = ActionController::Routing::RouteSet.new
- ActionController::Routing.module_eval { const_set :Routes, temporary_routes }
-
- yield temporary_routes
- ensure
- if ActionController::Routing.const_defined? :Routes
- ActionController::Routing.module_eval { remove_const :Routes }
- end
- ActionController::Routing.const_set(:Routes, real_routes) if real_routes
- end
- end
-end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner.rb b/actionpack/lib/action_controller/vendor/html-scanner.rb
index f622d195ee..879b31e60e 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner.rb
@@ -1,16 +1,20 @@
$LOAD_PATH << "#{File.dirname(__FILE__)}/html-scanner"
module HTML
- autoload :CDATA, 'html/node'
- autoload :Document, 'html/document'
- autoload :FullSanitizer, 'html/sanitizer'
- autoload :LinkSanitizer, 'html/sanitizer'
- autoload :Node, 'html/node'
- autoload :Sanitizer, 'html/sanitizer'
- autoload :Selector, 'html/selector'
- autoload :Tag, 'html/node'
- autoload :Text, 'html/node'
- autoload :Tokenizer, 'html/tokenizer'
- autoload :Version, 'html/version'
- autoload :WhiteListSanitizer, 'html/sanitizer'
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :CDATA, 'html/node'
+ autoload :Document, 'html/document'
+ autoload :FullSanitizer, 'html/sanitizer'
+ autoload :LinkSanitizer, 'html/sanitizer'
+ autoload :Node, 'html/node'
+ autoload :Sanitizer, 'html/sanitizer'
+ autoload :Selector, 'html/selector'
+ autoload :Tag, 'html/node'
+ autoload :Text, 'html/node'
+ autoload :Tokenizer, 'html/tokenizer'
+ autoload :Version, 'html/version'
+ autoload :WhiteListSanitizer, 'html/sanitizer'
+ end
end
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 259814a322..0696cb017c 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -21,6 +21,10 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
+activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
+$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
+require 'active_support/ruby/shim'
+
require 'rack'
module Rack
@@ -28,29 +32,25 @@ module Rack
end
module ActionDispatch
- autoload :Request, 'action_dispatch/http/request'
- autoload :Response, 'action_dispatch/http/response'
- autoload :StatusCodes, 'action_dispatch/http/status_codes'
- autoload :Utils, 'action_dispatch/http/utils'
-
- autoload :Callbacks, 'action_dispatch/middleware/callbacks'
- autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
- autoload :ParamsParser, 'action_dispatch/middleware/params_parser'
- autoload :Rescue, 'action_dispatch/middleware/rescue'
- autoload :ShowExceptions, 'action_dispatch/middleware/show_exceptions'
- autoload :Static, 'action_dispatch/middleware/static'
- autoload :StringCoercion, 'action_dispatch/middleware/string_coercion'
+ extend ActiveSupport::Autoload
- autoload :Routing, 'action_dispatch/routing'
+ autoload_under 'http' do
+ autoload :Request
+ autoload :Response
+ end
- autoload :Assertions, 'action_dispatch/testing/assertions'
- autoload :Integration, 'action_dispatch/testing/integration'
- autoload :IntegrationTest, 'action_dispatch/testing/integration'
- autoload :PerformanceTest, 'action_dispatch/testing/performance_test'
- autoload :TestRequest, 'action_dispatch/testing/test_request'
- autoload :TestResponse, 'action_dispatch/testing/test_response'
+ autoload_under 'middleware' do
+ autoload :Callbacks
+ autoload :Cascade
+ autoload :ParamsParser
+ autoload :Rescue
+ autoload :ShowExceptions
+ autoload :Static
+ autoload :StringCoercion
+ end
- autoload :HTML, 'action_controller/vendor/html-scanner'
+ autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
+ autoload :Routing
module Http
autoload :Headers, 'action_dispatch/http/headers'
@@ -58,13 +58,18 @@ module ActionDispatch
module Session
autoload :AbstractStore, 'action_dispatch/middleware/session/abstract_store'
- autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
+ autoload :CookieStore, 'action_dispatch/middleware/session/cookie_store'
autoload :MemCacheStore, 'action_dispatch/middleware/session/mem_cache_store'
end
+
+ autoload_under 'testing' do
+ autoload :Assertions
+ autoload :Integration
+ autoload :PerformanceTest
+ autoload :TestProcess
+ autoload :TestRequest
+ autoload :TestResponse
+ end
end
autoload :Mime, 'action_dispatch/http/mime_type'
-
-activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
-$:.unshift activesupport_path if File.directory?(activesupport_path)
-require 'active_support'
diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 6a52854961..6e8a5dcb8a 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -6,6 +6,7 @@ require 'active_support/memoizable'
require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/hash/indifferent_access'
require 'active_support/core_ext/string/access'
+require 'action_dispatch/http/headers'
module ActionDispatch
class Request < Rack::Request
@@ -17,7 +18,7 @@ module ActionDispatch
HTTP_ACCEPT HTTP_ACCEPT_CHARSET HTTP_ACCEPT_ENCODING
HTTP_ACCEPT_LANGUAGE HTTP_CACHE_CONTROL HTTP_FROM
- HTTP_NEGOTIATE HTTP_PRAGMA HTTP_REFERER HTTP_USER_AGENT ].each do |env|
+ HTTP_NEGOTIATE HTTP_PRAGMA ].each do |env|
define_method(env.sub(/^HTTP_/n, '').downcase) do
@env[env]
end
@@ -117,7 +118,7 @@ module ActionDispatch
end
end
end
-
+
def if_modified_since
if since = env['HTTP_IF_MODIFIED_SINCE']
Time.rfc2822(since) rescue nil
@@ -464,6 +465,15 @@ EOM
session['flash'] || {}
end
+ # Returns the authorization header regardless of whether it was specified directly or through one of the
+ # proxy alternatives.
+ def authorization
+ @env['HTTP_AUTHORIZATION'] ||
+ @env['X-HTTP_AUTHORIZATION'] ||
+ @env['X_HTTP_AUTHORIZATION'] ||
+ @env['REDIRECT_X_HTTP_AUTHORIZATION']
+ end
+
# Receives an array of mimes and return the first user sent mime that
# matches the order array.
#
diff --git a/actionpack/lib/action_dispatch/http/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index b3ed7c9d1a..8524bbd993 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -33,7 +33,6 @@ module ActionDispatch # :nodoc:
# end
class Response < Rack::Response
attr_accessor :request, :blank
- attr_reader :cache_control
attr_writer :header, :sending_file
alias_method :headers=, :header=
@@ -50,6 +49,9 @@ module ActionDispatch # :nodoc:
@body, @cookie = [], []
@sending_file = false
+ @blank = false
+ @etag = nil
+
yield self if block_given?
end
@@ -57,14 +59,8 @@ module ActionDispatch # :nodoc:
@cache_control ||= {}
end
- def write(str)
- s = str.to_s
- @writer.call s
- str
- end
-
def status=(status)
- @status = status.to_i
+ @status = Rack::Utils.status_code(status)
end
# The response code of the request
@@ -78,7 +74,7 @@ module ActionDispatch # :nodoc:
end
def message
- StatusCodes::STATUS_CODES[@status]
+ Rack::Utils::HTTP_STATUS_CODES[@status]
end
alias_method :status_message, :message
@@ -149,18 +145,6 @@ module ActionDispatch # :nodoc:
cattr_accessor(:default_charset) { "utf-8" }
- def assign_default_content_type_and_charset!
- return if headers[CONTENT_TYPE].present?
-
- @content_type ||= Mime::HTML
- @charset ||= self.class.default_charset
-
- type = @content_type.to_s.dup
- type << "; charset=#{@charset}" unless @sending_file
-
- headers[CONTENT_TYPE] = type
- end
-
def to_a
assign_default_content_type_and_charset!
handle_conditional_get!
@@ -263,6 +247,18 @@ module ActionDispatch # :nodoc:
!@blank && @body.respond_to?(:all?) && @body.all? { |part| part.is_a?(String) }
end
+ def assign_default_content_type_and_charset!
+ return if headers[CONTENT_TYPE].present?
+
+ @content_type ||= Mime::HTML
+ @charset ||= self.class.default_charset
+
+ type = @content_type.to_s.dup
+ type << "; charset=#{@charset}" unless @sending_file
+
+ headers[CONTENT_TYPE] = type
+ end
+
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
def set_conditional_cache_control!
@@ -277,14 +273,13 @@ module ActionDispatch # :nodoc:
max_age = control[:max_age]
options = []
- options << "max-age=#{max_age}" if max_age
+ options << "max-age=#{max_age.to_i}" if max_age
options << (control[:public] ? "public" : "private")
options << "must-revalidate" if control[:must_revalidate]
options.concat(extras) if extras
headers["Cache-Control"] = options.join(", ")
end
-
end
end
end
diff --git a/actionpack/lib/action_dispatch/http/status_codes.rb b/actionpack/lib/action_dispatch/http/status_codes.rb
deleted file mode 100644
index 5bac842ec1..0000000000
--- a/actionpack/lib/action_dispatch/http/status_codes.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require 'active_support/inflector'
-
-module ActionDispatch
- module StatusCodes #:nodoc:
- STATUS_CODES = Rack::Utils::HTTP_STATUS_CODES.merge({
- 102 => "Processing",
- 207 => "Multi-Status",
- 226 => "IM Used",
- 422 => "Unprocessable Entity",
- 423 => "Locked",
- 424 => "Failed Dependency",
- 426 => "Upgrade Required",
- 507 => "Insufficient Storage",
- 510 => "Not Extended"
- }).freeze
-
- # Provides a symbol-to-fixnum lookup for converting a symbol (like
- # :created or :not_implemented) into its corresponding HTTP status
- # code (like 200 or 501).
- SYMBOL_TO_STATUS_CODE = STATUS_CODES.inject({}) { |hash, (code, message)|
- hash[ActiveSupport::Inflector.underscore(message.gsub(/ /, "")).to_sym] = code
- hash
- }.freeze
-
- private
- # Given a status parameter, determine whether it needs to be converted
- # to a string. If it is a fixnum, use the STATUS_CODES hash to lookup
- # the default message. If it is a symbol, use the SYMBOL_TO_STATUS_CODE
- # hash to convert it.
- def interpret_status(status)
- case status
- when Fixnum then
- "#{status} #{STATUS_CODES[status]}".strip
- when Symbol then
- interpret_status(SYMBOL_TO_STATUS_CODE[status] ||
- "500 Unknown Status #{status.inspect}")
- else
- status.to_s
- end
- end
- end
-end
diff --git a/actionpack/lib/action_dispatch/http/utils.rb b/actionpack/lib/action_dispatch/http/utils.rb
deleted file mode 100644
index e04a39935e..0000000000
--- a/actionpack/lib/action_dispatch/http/utils.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-module ActionDispatch
- module Utils
- # TODO: Pull this into rack core
- # http://github.com/halorgium/rack/commit/feaf071c1de743fbd10bc316830180a9af607278
- def parse_config(config)
- if config =~ /\.ru$/
- cfgfile = ::File.read(config)
- if cfgfile[/^#\\(.*)/]
- opts.parse! $1.split(/\s+/)
- end
- inner_app = eval "Rack::Builder.new {( " + cfgfile + "\n )}.to_app",
- nil, config
- else
- require config
- inner_app = Object.const_get(::File.basename(config, '.rb').capitalize)
- end
- end
- module_function :parse_config
- end
-end
diff --git a/actionpack/lib/action_dispatch/middleware/cascade.rb b/actionpack/lib/action_dispatch/middleware/cascade.rb
new file mode 100644
index 0000000000..9f5c9891f0
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/cascade.rb
@@ -0,0 +1,29 @@
+module ActionDispatch
+ class Cascade
+ def self.new(*apps)
+ apps = apps.flatten
+
+ case apps.length
+ when 0
+ raise ArgumentError, "app is required"
+ when 1
+ apps.first
+ else
+ super(apps)
+ end
+ end
+
+ def initialize(apps)
+ @apps = apps
+ end
+
+ def call(env)
+ result = nil
+ @apps.each do |app|
+ result = app.call(env)
+ break unless result[1]["X-Cascade"] == "pass"
+ end
+ result
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 32ccb5c931..534390d4aa 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -1,4 +1,5 @@
require 'active_support/json'
+require 'action_dispatch/http/request'
module ActionDispatch
class ParamsParser
@@ -31,41 +32,39 @@ module ActionDispatch
return false unless strategy
case strategy
- when Proc
- strategy.call(request.raw_post)
- when :xml_simple, :xml_node
- request.body.size == 0 ? {} : Hash.from_xml(request.body).with_indifferent_access
- when :yaml
- YAML.load(request.body)
- when :json
- if request.body.size == 0
- {}
- else
- data = ActiveSupport::JSON.decode(request.body)
- data = {:_json => data} unless data.is_a?(Hash)
- data.with_indifferent_access
- end
+ when Proc
+ strategy.call(request.raw_post)
+ when :xml_simple, :xml_node
+ request.body.size == 0 ? {} : Hash.from_xml(request.body).with_indifferent_access
+ when :yaml
+ YAML.load(request.body)
+ when :json
+ if request.body.size == 0
+ {}
else
- false
+ data = ActiveSupport::JSON.decode(request.body)
+ data = {:_json => data} unless data.is_a?(Hash)
+ data.with_indifferent_access
+ end
+ else
+ false
end
rescue Exception => e # YAML, XML or Ruby code block errors
logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}"
raise
- { "body" => request.raw_post,
- "content_type" => request.content_type,
+ { "body" => request.raw_post,
+ "content_type" => request.content_type,
"content_length" => request.content_length,
- "exception" => "#{e.message} (#{e.class})",
- "backtrace" => e.backtrace }
+ "exception" => "#{e.message} (#{e.class})",
+ "backtrace" => e.backtrace }
end
def content_type_from_legacy_post_data_format_header(env)
if x_post_format = env['HTTP_X_POST_DATA_FORMAT']
case x_post_format.to_s.downcase
- when 'yaml'
- return Mime::YAML
- when 'xml'
- return Mime::XML
+ when 'yaml' then return Mime::YAML
+ when 'xml' then return Mime::XML
end
end
@@ -76,4 +75,4 @@ module ActionDispatch
defined?(Rails.logger) ? Rails.logger : Logger.new($stderr)
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
index c5c06f74a2..7d4f0998ce 100644
--- a/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/abstract_store.rb
@@ -1,4 +1,5 @@
require 'rack/utils'
+require 'rack/request'
module ActionDispatch
module Session
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index bd552b458a..f27f22c7e7 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -1,4 +1,5 @@
-require "active_support/core_ext/hash/keys"
+require 'active_support/core_ext/hash/keys'
+require 'rack/request'
module ActionDispatch
module Session
@@ -49,7 +50,7 @@ module ActionDispatch
:expire_after => nil,
:httponly => true
}.freeze
-
+
class OptionsHash < Hash
def initialize(by, env, default_options)
@session_data = env[CookieStore::ENV_SESSION_KEY]
@@ -60,7 +61,7 @@ module ActionDispatch
key == :id ? @session_data[:session_id] : super(key)
end
end
-
+
ENV_SESSION_KEY = "rack.session".freeze
ENV_SESSION_OPTIONS_KEY = "rack.session.options".freeze
HTTP_SET_COOKIE = "Set-Cookie".freeze
@@ -102,7 +103,7 @@ module ActionDispatch
def call(env)
env[ENV_SESSION_KEY] = AbstractStore::SessionHash.new(self, env)
env[ENV_SESSION_OPTIONS_KEY] = OptionsHash.new(self, env, @default_options)
-
+
status, headers, body = @app.call(env)
session_data = env[ENV_SESSION_KEY]
diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
index 036deec6d2..4ebc8a2ab9 100644
--- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
+++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb
@@ -1,9 +1,8 @@
-require "active_support/core_ext/exception"
+require 'active_support/core_ext/exception'
+require 'action_dispatch/http/request'
module ActionDispatch
class ShowExceptions
- include StatusCodes
-
LOCALHOST = '127.0.0.1'.freeze
RESCUES_TEMPLATE_PATH = File.join(File.dirname(__FILE__), 'templates')
@@ -12,8 +11,7 @@ module ActionDispatch
@@rescue_responses = Hash.new(:internal_server_error)
@@rescue_responses.update({
'ActionController::RoutingError' => :not_found,
- # TODO: Clean this up after the switch
- ActionController::UnknownAction.name => :not_found,
+ 'AbstractController::ActionNotFound' => :not_found,
'ActiveRecord::RecordNotFound' => :not_found,
'ActiveRecord::StaleObjectError' => :conflict,
'ActiveRecord::RecordInvalid' => :unprocessable_entity,
@@ -28,8 +26,8 @@ module ActionDispatch
@@rescue_templates.update({
'ActionView::MissingTemplate' => 'missing_template',
'ActionController::RoutingError' => 'routing_error',
- ActionController::UnknownAction.name => 'unknown_action',
- 'ActionView::TemplateError' => 'template_error'
+ 'AbstractController::ActionNotFound' => 'unknown_action',
+ 'ActionView::Template::Error' => 'template_error'
})
FAILSAFE_RESPONSE = [500, {'Content-Type' => 'text/html'},
@@ -104,7 +102,7 @@ module ActionDispatch
end
def status_code(exception)
- interpret_status(@@rescue_responses[exception.class.name]).to_i
+ Rack::Utils.status_code(@@rescue_responses[exception.class.name])
end
def render(status, body)
@@ -119,7 +117,7 @@ module ActionDispatch
return unless logger
ActiveSupport::Deprecation.silence do
- if ActionView::TemplateError === exception
+ if ActionView::Template::Error === exception
logger.fatal(exception.to_s)
else
logger.fatal(
diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
index f8f6b424ca..07b4919934 100644
--- a/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
+++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/_trace.erb
@@ -7,7 +7,7 @@
names = traces.collect {|name, trace| name}
%>
-<p><code>RAILS_ROOT: <%= defined?(RAILS_ROOT) ? RAILS_ROOT : "unset" %></code></p>
+<p><code>Rails.root: <%= defined?(Rails) && Rails.respond_to?(:root) ? Rails.root : "unset" %></code></p>
<div id="traces">
<% names.each do |name| %>
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 68ed1e3340..e99f979197 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -264,118 +264,11 @@ module ActionDispatch
autoload :RouteSet, 'action_dispatch/routing/route_set'
SEPARATORS = %w( / . ? )
-
HTTP_METHODS = [:get, :head, :post, :put, :delete, :options]
- ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set
-
- # The root paths which may contain controller files
- mattr_accessor :controller_paths
- self.controller_paths = []
-
# A helper module to hold URL related helpers.
module Helpers
include ActionController::PolymorphicRoutes
end
-
- class << self
- # Expects an array of controller names as the first argument.
- # Executes the passed block with only the named controllers named available.
- # This method is used in internal Rails testing.
- def with_controllers(names)
- prior_controllers = @possible_controllers
- use_controllers! names
- yield
- ensure
- use_controllers! prior_controllers
- end
-
- # Returns an array of paths, cleaned of double-slashes and relative path references.
- # * "\\\" and "//" become "\\" or "/".
- # * "/foo/bar/../config" becomes "/foo/config".
- # The returned array is sorted by length, descending.
- def normalize_paths(paths)
- # do the hokey-pokey of path normalization...
- paths = paths.collect do |path|
- path = path.
- gsub("//", "/"). # replace double / chars with a single
- gsub("\\\\", "\\"). # replace double \ chars with a single
- gsub(%r{(.)[\\/]$}, '\1') # drop final / or \ if path ends with it
-
- # eliminate .. paths where possible
- re = %r{[^/\\]+[/\\]\.\.[/\\]}
- path.gsub!(re, "") while path.match(re)
- path
- end
-
- # start with longest path, first
- paths = paths.uniq.sort_by { |path| - path.length }
- end
-
- # Returns the array of controller names currently available to ActionController::Routing.
- def possible_controllers
- unless @possible_controllers
- @possible_controllers = []
-
- paths = controller_paths.select { |path| File.directory?(path) && path != "." }
-
- seen_paths = Hash.new {|h, k| h[k] = true; false}
- normalize_paths(paths).each do |load_path|
- Dir["#{load_path}/**/*_controller.rb"].collect do |path|
- next if seen_paths[path.gsub(%r{^\.[/\\]}, "")]
-
- controller_name = path[(load_path.length + 1)..-1]
-
- controller_name.gsub!(/_controller\.rb\Z/, '')
- @possible_controllers << controller_name
- end
- end
-
- # remove duplicates
- @possible_controllers.uniq!
- end
- @possible_controllers
- end
-
- # Replaces the internal list of controllers available to ActionController::Routing with the passed argument.
- # ActionController::Routing.use_controllers!([ "posts", "comments", "admin/comments" ])
- def use_controllers!(controller_names)
- @possible_controllers = controller_names
- end
-
- # Returns a controller path for a new +controller+ based on a +previous+ controller path.
- # Handles 4 scenarios:
- #
- # * stay in the previous controller:
- # controller_relative_to( nil, "groups/discussion" ) # => "groups/discussion"
- #
- # * stay in the previous namespace:
- # controller_relative_to( "posts", "groups/discussion" ) # => "groups/posts"
- #
- # * forced move to the root namespace:
- # controller_relative_to( "/posts", "groups/discussion" ) # => "posts"
- #
- # * previous namespace is root:
- # controller_relative_to( "posts", "anything_with_no_slashes" ) # =>"posts"
- #
- def controller_relative_to(controller, previous)
- if controller.nil? then previous
- elsif controller[0] == ?/ then controller[1..-1]
- elsif %r{^(.*)/} =~ previous then "#{$1}/#{controller}"
- else controller
- end
- end
- end
-
- ActiveSupport::Inflector.module_eval do
- # Ensures that routes are reloaded when Rails inflections are updated.
- def inflections_with_route_reloading(&block)
- returning(inflections_without_route_reloading(&block)) {
- ActionDispatch::Routing::Routes.reload! if block_given?
- }
- end
-
- alias_method_chain :inflections, :route_reloading
- end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
index 0564ba9797..8ce6b2f6d5 100644
--- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
@@ -113,8 +113,7 @@ module ActionDispatch
end
end
- possible_names = Routing.possible_controllers.collect { |n| Regexp.escape(n) }
- requirements[:controller] ||= Regexp.union(*possible_names)
+ requirements[:controller] ||= @set.controller_constraints
if defaults[:controller]
defaults[:action] ||= 'index'
@@ -176,7 +175,7 @@ module ActionDispatch
optional = false
elsif segment =~ /^:(\w+)$/
if defaults.has_key?($1.to_sym)
- defaults.delete($1.to_sym)
+ defaults.delete($1.to_sym) if defaults[$1.to_sym].nil?
else
optional = false
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 6e112c9b54..e655d6a708 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,153 +1,247 @@
module ActionDispatch
module Routing
class Mapper
- module Resources
- def resource(*resources, &block)
- options = resources.last.is_a?(Hash) ? resources.pop : {}
-
- if resources.length > 1
- raise ArgumentError if block_given?
- resources.each { |r| resource(r, options) }
- return self
+ class Constraints
+ def self.new(app, constraints = [])
+ if constraints.any?
+ super(app, constraints)
+ else
+ app
end
+ end
- resource = resources.pop
+ def initialize(app, constraints = [])
+ @app, @constraints = app, constraints
+ end
- if @scope[:scope_level] == :resources
- member do
- resource(resource, options, &block)
+ def call(env)
+ req = Rack::Request.new(env)
+
+ @constraints.each { |constraint|
+ if constraint.respond_to?(:matches?) && !constraint.matches?(req)
+ return [ 404, {'X-Cascade' => 'pass'}, [] ]
+ elsif constraint.respond_to?(:call) && !constraint.call(req)
+ return [ 404, {'X-Cascade' => 'pass'}, [] ]
end
- return self
- end
+ }
+
+ @app.call(env)
+ end
+ end
- singular = resource.to_s
- plural = singular.pluralize
+ class Mapping
+ def initialize(set, scope, args)
+ @set, @scope = set, scope
+ @path, @options = extract_path_and_options(args)
+ end
- controller(plural) do
- namespace(resource) do
- with_scope_level(:resource) do
- yield if block_given?
+ def to_route
+ [ app, conditions, requirements, defaults, @options[:as] ]
+ end
- get "", :to => :show, :as => "#{singular}"
- post "", :to => :create
- put "", :to => :update
- delete "", :to => :destroy
- get "new", :to => :new, :as => "new_#{singular}"
- get "edit", :to => :edit, :as => "edit_#{singular}"
- end
+ private
+ def extract_path_and_options(args)
+ options = args.extract_options!
+
+ if args.empty?
+ path, to = options.find { |name, value| name.is_a?(String) }
+ options.merge!(:to => to).delete(path) if path
+ else
+ path = args.first
end
+
+ [ normalize_path(path), options ]
end
- self
- end
+ def normalize_path(path)
+ path = nil if path == ""
+ path = "#{@scope[:path]}#{path}" if @scope[:path]
+ path = Rack::Mount::Utils.normalize_path(path) if path
- def resources(*resources, &block)
- options = resources.last.is_a?(Hash) ? resources.pop : {}
+ raise ArgumentError, "path is required" unless path
- if resources.length > 1
- raise ArgumentError if block_given?
- resources.each { |r| resources(r, options) }
- return self
+ path
end
- resource = resources.pop
- plural = resource.to_s
- singular = plural.singularize
+ def app
+ Constraints.new(
+ to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults),
+ blocks
+ )
+ end
- if @scope[:scope_level] == :resources
- parent_resource = @scope[:scope_level_options][:name]
- with_scope_level(:member) do
- scope(":#{parent_resource}_id", :name_prefix => parent_resource) do
- resources(resource, options, &block)
- end
- end
- return self
+ def conditions
+ { :path_info => @path }.merge(constraints).merge(request_method_condition)
end
- if @scope[:options] && (prefix = @scope[:options][:name_prefix])
- plural = "#{prefix}_#{plural}"
- singular = "#{prefix}_#{singular}"
+ def requirements
+ @requirements ||= returning(@options[:constraints] || {}) do |requirements|
+ requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
+ @options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
+ requirements[:controller] ||= @set.controller_constraints
+ end
end
- controller(resource) do
- namespace(resource) do
- with_scope_level(:resources, :name => singular) do
- yield if block_given?
+ def defaults
+ @defaults ||= if to.respond_to?(:call)
+ { }
+ else
+ defaults = case to
+ when String
+ controller, action = to.split('#')
+ { :controller => controller, :action => action }
+ when Symbol
+ { :action => to.to_s }.merge(default_controller ? { :controller => default_controller } : {})
+ else
+ default_controller ? { :controller => default_controller } : {}
+ end
- member do
- get "", :to => :show, :as => singular
- put "", :to => :update
- delete "", :to => :destroy
- get "edit", :to => :edit, :as => "edit_#{singular}"
- end
+ if defaults[:controller].blank? && segment_keys.exclude?("controller")
+ raise ArgumentError, "missing :controller"
+ end
- collection do
- get "", :to => :index, :as => plural
- post "", :to => :create
- get "new", :to => :new, :as => "new_#{singular}"
- end
+ if defaults[:action].blank? && segment_keys.exclude?("action")
+ raise ArgumentError, "missing :action"
end
+
+ defaults
end
end
- self
- end
- def collection
- unless @scope[:scope_level] == :resources
- raise ArgumentError, "can't use collection outside resources scope"
- end
+ def blocks
+ if @options[:constraints].present? && !@options[:constraints].is_a?(Hash)
+ block = @options[:constraints]
+ else
+ block = nil
+ end
- with_scope_level(:collection) do
- yield
+ ((@scope[:blocks] || []) + [ block ]).compact
end
- end
- def member
- unless @scope[:scope_level] == :resources
- raise ArgumentError, "can't use member outside resources scope"
+ def constraints
+ @constraints ||= requirements.reject { |k, v| segment_keys.include?(k.to_s) || k == :controller }
end
- with_scope_level(:member) do
- scope(":id") do
- yield
+ def request_method_condition
+ if via = @options[:via]
+ via = Array(via).map { |m| m.to_s.upcase }
+ { :request_method => Regexp.union(*via) }
+ else
+ { }
end
end
+
+ def segment_keys
+ @segment_keys ||= Rack::Mount::RegexpWithNamedGroups.new(
+ Rack::Mount::Strexp.compile(@path, requirements, SEPARATORS)
+ ).names
+ end
+
+ def to
+ @options[:to]
+ end
+
+ def default_controller
+ @scope[:controller].to_s if @scope[:controller]
+ end
+ end
+
+ module Base
+ def initialize(set)
+ @set = set
+ end
+
+ def root(options = {})
+ match '/', options.reverse_merge(:as => :root)
end
def match(*args)
+ @set.add_route(*Mapping.new(@set, @scope, args).to_route)
+ self
+ end
+ end
+
+ module HttpHelpers
+ def get(*args, &block)
+ map_method(:get, *args, &block)
+ end
+
+ def post(*args, &block)
+ map_method(:post, *args, &block)
+ end
+
+ def put(*args, &block)
+ map_method(:put, *args, &block)
+ end
+
+ def delete(*args, &block)
+ map_method(:delete, *args, &block)
+ end
+
+ def redirect(*args, &block)
options = args.last.is_a?(Hash) ? args.pop : {}
- args.push(options)
- case options.delete(:on)
- when :collection
- return collection { match(*args) }
- when :member
- return member { match(*args) }
- end
+ path = args.shift || block
+ path_proc = path.is_a?(Proc) ? path : proc { |params| path % params }
+ status = options[:status] || 301
- if @scope[:scope_level] == :resources
- raise ArgumentError, "can't define route directly in resources scope"
- end
+ lambda do |env|
+ req = Rack::Request.new(env)
+ params = path_proc.call(env["action_dispatch.request.path_parameters"])
+ url = req.scheme + '://' + req.host + params
- super
+ [ status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently'] ]
+ end
end
private
- def with_scope_level(kind, options = {})
- old, @scope[:scope_level] = @scope[:scope_level], kind
- old_options, @scope[:scope_level_options] = @scope[:scope_level_options], options
- yield
- ensure
- @scope[:scope_level] = old
- @scope[:scope_level_options] = old_options
+ def map_method(method, *args, &block)
+ options = args.extract_options!
+ options[:via] = method
+ args.push(options)
+ match(*args, &block)
+ self
end
end
module Scoping
+ def initialize(*args)
+ @scope = {}
+ super
+ end
+
def scope(*args)
- options = args.last.is_a?(Hash) ? args.pop : {}
+ options = args.extract_options!
+
+ case args.first
+ when String
+ options[:path] = args.first
+ when Symbol
+ options[:controller] = args.first
+ end
+
+ if path = options.delete(:path)
+ path_set = true
+ path, @scope[:path] = @scope[:path], Rack::Mount::Utils.normalize_path(@scope[:path].to_s + path.to_s)
+ else
+ path_set = false
+ end
+
+ if name_prefix = options.delete(:name_prefix)
+ name_prefix_set = true
+ name_prefix, @scope[:name_prefix] = @scope[:name_prefix], (@scope[:name_prefix] ? "#{@scope[:name_prefix]}_#{name_prefix}" : name_prefix)
+ else
+ name_prefix_set = false
+ end
+
+ if controller = options.delete(:controller)
+ controller_set = true
+ controller, @scope[:controller] = @scope[:controller], controller
+ else
+ controller_set = false
+ end
constraints = options.delete(:constraints) || {}
unless constraints.is_a?(Hash)
@@ -158,27 +252,15 @@ module ActionDispatch
options, @scope[:options] = @scope[:options], (@scope[:options] || {}).merge(options)
- path_set = controller_set = false
-
- case args.first
- when String
- path_set = true
- path = args.first
- path, @scope[:path] = @scope[:path], "#{@scope[:path]}#{Rack::Mount::Utils.normalize_path(path)}"
- when Symbol
- controller_set = true
- controller = args.first
- controller, @scope[:controller] = @scope[:controller], controller
- end
-
yield
self
ensure
- @scope[:path] = path if path_set
- @scope[:controller] = controller if controller_set
- @scope[:options] = options
- @scope[:blocks] = blocks
+ @scope[:path] = path if path_set
+ @scope[:name_prefix] = name_prefix if name_prefix_set
+ @scope[:controller] = controller if controller_set
+ @scope[:options] = options
+ @scope[:blocks] = blocks
@scope[:constraints] = constraints
end
@@ -187,151 +269,255 @@ module ActionDispatch
end
def namespace(path)
- scope(path.to_s) { yield }
+ scope("/#{path}") { yield }
end
def constraints(constraints = {})
scope(:constraints => constraints) { yield }
end
- end
- class Constraints
- def initialize(app, constraints = [])
- @app, @constraints = app, constraints
- end
+ def match(*args)
+ options = args.extract_options!
- def call(env)
- req = Rack::Request.new(env)
+ options = (@scope[:options] || {}).merge(options)
- @constraints.each { |constraint|
- if constraint.respond_to?(:matches?) && !constraint.matches?(req)
- return [417, {}, []]
- elsif constraint.respond_to?(:call) && !constraint.call(req)
- return [417, {}, []]
- end
- }
+ if @scope[:name_prefix] && !options[:as].blank?
+ options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}"
+ elsif @scope[:name_prefix] && options[:as] == ""
+ options[:as] = @scope[:name_prefix].to_s
+ end
- @app.call(env)
+ args.push(options)
+ super(*args)
end
end
- def initialize(set)
- @set = set
- @scope = {}
-
- extend Scoping
- extend Resources
- end
+ module Resources
+ class Resource #:nodoc:
+ attr_reader :plural, :singular
- def get(*args, &block)
- map_method(:get, *args, &block)
- end
+ def initialize(entities, options = {})
+ entities = entities.to_s
- def post(*args, &block)
- map_method(:post, *args, &block)
- end
+ @plural = entities.pluralize
+ @singular = entities.singularize
+ end
- def put(*args, &block)
- map_method(:put, *args, &block)
- end
+ def name
+ plural
+ end
- def delete(*args, &block)
- map_method(:delete, *args, &block)
- end
+ def controller
+ plural
+ end
- def root(options = {})
- match '/', options.merge(:as => :root)
- end
+ def member_name
+ singular
+ end
- def match(*args)
- options = args.last.is_a?(Hash) ? args.pop : {}
+ def collection_name
+ plural
+ end
- if args.length > 1
- args.each { |path| match(path, options) }
- return self
+ def id_segment
+ ":#{singular}_id"
+ end
end
- if args.first.is_a?(Symbol)
- return match(args.first.to_s, options.merge(:to => args.first.to_sym))
+ class SingletonResource < Resource #:nodoc:
+ def initialize(entity, options = {})
+ super
+ end
+
+ def name
+ singular
+ end
end
- path = args.first
+ def resource(*resources, &block)
+ options = resources.extract_options!
+
+ if resources.length > 1
+ raise ArgumentError if block_given?
+ resources.each { |r| resource(r, options) }
+ return self
+ end
+
+ resource = SingletonResource.new(resources.pop)
- options = (@scope[:options] || {}).merge(options)
- conditions, defaults = {}, {}
+ if @scope[:scope_level] == :resources
+ nested do
+ resource(resource.name, options, &block)
+ end
+ return self
+ end
- path = nil if path == ""
- path = Rack::Mount::Utils.normalize_path(path) if path
- path = "#{@scope[:path]}#{path}" if @scope[:path]
+ scope(:path => "/#{resource.name}", :controller => resource.controller) do
+ with_scope_level(:resource, resource) do
+ yield if block_given?
- raise ArgumentError, "path is required" unless path
+ get "(.:format)", :to => :show, :as => resource.member_name
+ post "(.:format)", :to => :create
+ put "(.:format)", :to => :update
+ delete "(.:format)", :to => :destroy
+ get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}"
+ get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}"
+ end
+ end
- constraints = options[:constraints] || {}
- unless constraints.is_a?(Hash)
- block, constraints = constraints, {}
+ self
end
- blocks = ((@scope[:blocks] || []) + [block]).compact
- constraints = (@scope[:constraints] || {}).merge(constraints)
- options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) }
- conditions[:path_info] = path
- requirements = constraints.dup
+ def resources(*resources, &block)
+ options = resources.extract_options!
+
+ if resources.length > 1
+ raise ArgumentError if block_given?
+ resources.each { |r| resources(r, options) }
+ return self
+ end
+
+ resource = Resource.new(resources.pop)
+
+ if @scope[:scope_level] == :resources
+ nested do
+ resources(resource.name, options, &block)
+ end
+ return self
+ end
+
+ scope(:path => "/#{resource.name}", :controller => resource.controller) do
+ with_scope_level(:resources, resource) do
+ yield if block_given?
+
+ with_scope_level(:collection) do
+ get "(.:format)", :to => :index, :as => resource.collection_name
+ post "(.:format)", :to => :create
+
+ with_exclusive_name_prefix :new do
+ get "/new(.:format)", :to => :new, :as => resource.singular
+ end
+ end
+
+ with_scope_level(:member) do
+ scope("/:id") do
+ get "(.:format)", :to => :show, :as => resource.member_name
+ put "(.:format)", :to => :update
+ delete "(.:format)", :to => :destroy
+
+ with_exclusive_name_prefix :edit do
+ get "/edit(.:format)", :to => :edit, :as => resource.singular
+ end
+ end
+ end
+ end
+ end
+
+ self
+ end
- path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS)
- segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names
- constraints.reject! { |k, v| segment_keys.include?(k.to_s) }
- conditions.merge!(constraints)
+ def collection
+ unless @scope[:scope_level] == :resources
+ raise ArgumentError, "can't use collection outside resources scope"
+ end
- if via = options[:via]
- via = Array(via).map { |m| m.to_s.upcase }
- conditions[:request_method] = Regexp.union(*via)
+ with_scope_level(:collection) do
+ scope(:name_prefix => parent_resource.collection_name, :as => "") do
+ yield
+ end
+ end
end
- defaults[:controller] = @scope[:controller].to_s if @scope[:controller]
+ def member
+ unless @scope[:scope_level] == :resources
+ raise ArgumentError, "can't use member outside resources scope"
+ end
- if options[:to].respond_to?(:call)
- app = options[:to]
- defaults.delete(:controller)
- defaults.delete(:action)
- elsif options[:to].is_a?(String)
- defaults[:controller], defaults[:action] = options[:to].split('#')
- elsif options[:to].is_a?(Symbol)
- defaults[:action] = options[:to].to_s
+ with_scope_level(:member) do
+ scope("/:id", :name_prefix => parent_resource.member_name, :as => "") do
+ yield
+ end
+ end
end
- app ||= Routing::RouteSet::Dispatcher.new(:defaults => defaults)
- if app.is_a?(Routing::RouteSet::Dispatcher)
- unless defaults.include?(:controller) || segment_keys.include?("controller")
- raise ArgumentError, "missing :controller"
+ def nested
+ unless @scope[:scope_level] == :resources
+ raise ArgumentError, "can't use nested outside resources scope"
end
- unless defaults.include?(:action) || segment_keys.include?("action")
- raise ArgumentError, "missing :action"
+
+ with_scope_level(:nested) do
+ scope("/#{parent_resource.id_segment}", :name_prefix => parent_resource.member_name) do
+ yield
+ end
end
end
- app = Constraints.new(app, blocks) if blocks.any?
- @set.add_route(app, conditions, requirements, defaults, options[:as])
+ def match(*args)
+ options = args.extract_options!
- self
- end
+ if args.length > 1
+ args.each { |path| match(path, options) }
+ return self
+ end
- def redirect(path, options = {})
- status = options[:status] || 301
- lambda { |env|
- req = Rack::Request.new(env)
- url = req.scheme + '://' + req.host + path
- [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']]
- }
- end
+ if args.first.is_a?(Symbol)
+ with_exclusive_name_prefix(args.first) do
+ return match("/#{args.first}(.:format)", options.merge(:to => args.first.to_sym))
+ end
+ end
- private
- def map_method(method, *args, &block)
- options = args.last.is_a?(Hash) ? args.pop : {}
- options[:via] = method
args.push(options)
- match(*args, &block)
- self
+
+ case options.delete(:on)
+ when :collection
+ return collection { match(*args) }
+ when :member
+ return member { match(*args) }
+ end
+
+ if @scope[:scope_level] == :resources
+ raise ArgumentError, "can't define route directly in resources scope"
+ end
+
+ super
end
+
+ protected
+ def parent_resource
+ @scope[:scope_level_resource]
+ end
+
+ private
+ def with_exclusive_name_prefix(prefix)
+ begin
+ old_name_prefix = @scope[:name_prefix]
+
+ if !old_name_prefix.blank?
+ @scope[:name_prefix] = "#{prefix}_#{@scope[:name_prefix]}"
+ else
+ @scope[:name_prefix] = prefix.to_s
+ end
+
+ yield
+ ensure
+ @scope[:name_prefix] = old_name_prefix
+ end
+ end
+
+ def with_scope_level(kind, resource = parent_resource)
+ old, @scope[:scope_level] = @scope[:scope_level], kind
+ old_resource, @scope[:scope_level_resource] = @scope[:scope_level_resource], resource
+ yield
+ ensure
+ @scope[:scope_level] = old
+ @scope[:scope_level_resource] = old_resource
+ end
+ end
+
+ include Base
+ include HttpHelpers
+ include Scoping
+ include Resources
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index c15aaceb5b..bd397432ce 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -5,7 +5,7 @@ module ActionDispatch
module Routing
class RouteSet #:nodoc:
NotFound = lambda { |env|
- raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect} with #{env.inspect}"
+ raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}"
}
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
@@ -18,31 +18,37 @@ module ActionDispatch
def call(env)
params = env[PARAMETERS_KEY]
+ prepare_params!(params)
+
+ unless controller = controller(params)
+ return [404, {'X-Cascade' => 'pass'}, []]
+ end
+
+ controller.action(params[:action]).call(env)
+ end
+
+ def prepare_params!(params)
merge_default_action!(params)
split_glob_param!(params) if @glob_param
+
params.each do |key, value|
if value.is_a?(String)
value = value.dup.force_encoding(Encoding::BINARY) if value.respond_to?(:force_encoding)
params[key] = URI.unescape(value)
end
end
+ end
- if env['action_controller.recognize']
- [200, {}, params]
- else
- controller = controller(params)
- controller.action(params[:action]).call(env)
+ def controller(params)
+ if params && params.has_key?(:controller)
+ controller = "#{params[:controller].camelize}Controller"
+ ActiveSupport::Inflector.constantize(controller)
end
+ rescue NameError
+ nil
end
private
- def controller(params)
- if params && params.has_key?(:controller)
- controller = "#{params[:controller].camelize}Controller"
- ActiveSupport::Inflector.constantize(controller)
- end
- end
-
def merge_default_action!(params)
params[:action] ||= 'index'
end
@@ -197,26 +203,40 @@ module ActionDispatch
end
end
- attr_accessor :routes, :named_routes, :configuration_files
+ attr_accessor :routes, :named_routes
+ attr_accessor :disable_clear_and_finalize
def initialize
- self.configuration_files = []
-
self.routes = []
self.named_routes = NamedRouteCollection.new
- clear!
+ @disable_clear_and_finalize = false
end
def draw(&block)
- clear!
- Mapper.new(self).instance_exec(DeprecatedMapper.new(self), &block)
+ clear! unless @disable_clear_and_finalize
+
+ mapper = Mapper.new(self)
+ if block.arity == 1
+ mapper.instance_exec(DeprecatedMapper.new(self), &block)
+ else
+ mapper.instance_exec(&block)
+ end
+
+ finalize! unless @disable_clear_and_finalize
+
+ nil
+ end
+
+ def finalize!
@set.add_route(NotFound)
install_helpers
@set.freeze
end
def clear!
+ # Clear the controller cache so we may discover new ones
+ @controller_constraints = nil
routes.clear
named_routes.clear
@set = ::Rack::Mount::RouteSet.new(:parameters_key => PARAMETERS_KEY)
@@ -231,63 +251,38 @@ module ActionDispatch
routes.empty?
end
- def add_configuration_file(path)
- self.configuration_files << path
- end
-
- # Deprecated accessor
- def configuration_file=(path)
- add_configuration_file(path)
- end
-
- # Deprecated accessor
- def configuration_file
- configuration_files
- end
-
- def load!
- Routing.use_controllers!(nil) # Clear the controller cache so we may discover new ones
- load_routes!
- end
-
- # reload! will always force a reload whereas load checks the timestamp first
- alias reload! load!
-
- def reload
- if configuration_files.any? && @routes_last_modified
- if routes_changed_at == @routes_last_modified
- return # routes didn't change, don't reload
- else
- @routes_last_modified = routes_changed_at
- end
- end
+ CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/
- load!
- end
-
- def load_routes!
- if configuration_files.any?
- configuration_files.each { |config| load(config) }
- @routes_last_modified = routes_changed_at
- else
- draw do |map|
- map.connect ":controller/:action/:id"
- end
+ def controller_constraints
+ @controller_constraints ||= begin
+ source = controller_namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" }
+ source << CONTROLLER_REGEXP.source
+ Regexp.compile(source.sort.reverse.join('|'))
end
end
- def routes_changed_at
- routes_changed_at = nil
+ def controller_namespaces
+ namespaces = Set.new
- configuration_files.each do |config|
- config_changed_at = File.stat(config).mtime
+ # Find any nested controllers already in memory
+ ActionController::Base.subclasses.each do |klass|
+ controller_name = klass.underscore
+ namespaces << controller_name.split('/')[0...-1].join('/')
+ end
- if routes_changed_at.nil? || config_changed_at > routes_changed_at
- routes_changed_at = config_changed_at
+ # TODO: Move this into Railties
+ if defined?(Rails.application)
+ # Find namespaces in controllers/ directory
+ Rails.application.config.controller_paths.each do |load_path|
+ load_path = File.expand_path(load_path)
+ Dir["#{load_path}/**/*_controller.rb"].collect do |path|
+ namespaces << File.dirname(path).sub(/#{load_path}\/?/, '')
+ end
end
end
- routes_changed_at
+ namespaces.delete('')
+ namespaces
end
def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil)
@@ -372,7 +367,8 @@ module ActionDispatch
end
recall[:action] = options.delete(:action) if options[:action] == 'index'
- parameterize = lambda { |name, value|
+ opts = {}
+ opts[:parameterize] = lambda { |name, value|
if name == :controller
value
elsif value.is_a?(Array)
@@ -382,14 +378,23 @@ module ActionDispatch
end
}
- path = @set.url(named_route, options, recall, :parameterize => parameterize)
+ unless result = @set.generate(:path_info, named_route, options, recall, opts)
+ raise ActionController::RoutingError, "No route matches #{options.inspect}"
+ end
+
+ path, params = result
+ params.each do |k, v|
+ if v
+ params[k] = v
+ else
+ params.delete(k)
+ end
+ end
+
if path && method == :generate_extras
- uri = URI(path)
- extras = uri.query ?
- Rack::Utils.parse_nested_query(uri.query).keys.map { |k| k.to_sym } :
- []
- [uri.path, extras]
+ [path, params.keys]
elsif path
+ path << "?#{params.to_query}" if params.any?
path
else
raise ActionController::RoutingError, "No route matches #{options.inspect}"
@@ -400,37 +405,11 @@ module ActionDispatch
def call(env)
@set.call(env)
- rescue ActionController::RoutingError => e
- raise e if env['action_controller.rescue_error'] == false
-
- method, path = env['REQUEST_METHOD'].downcase.to_sym, env['PATH_INFO']
-
- # Route was not recognized. Try to find out why (maybe wrong verb).
- allows = HTTP_METHODS.select { |verb|
- begin
- recognize_path(path, {:method => verb}, false)
- rescue ActionController::RoutingError
- nil
- end
- }
-
- if !HTTP_METHODS.include?(method)
- raise ActionController::NotImplemented.new(*allows)
- elsif !allows.empty?
- raise ActionController::MethodNotAllowed.new(*allows)
- else
- raise e
- end
- end
-
- def recognize(request)
- params = recognize_path(request.path, extract_request_environment(request))
- request.path_parameters = params.with_indifferent_access
- "#{params[:controller].to_s.camelize}Controller".constantize
end
- def recognize_path(path, environment = {}, rescue_error = true)
+ def recognize_path(path, environment = {})
method = (environment[:method] || "GET").to_s.upcase
+ path = Rack::Mount::Utils.normalize_path(path)
begin
env = Rack::MockRequest.env_for(path, {:method => method})
@@ -438,16 +417,16 @@ module ActionDispatch
raise ActionController::RoutingError, e.message
end
- env['action_controller.recognize'] = true
- env['action_controller.rescue_error'] = rescue_error
- status, headers, body = call(env)
- body
- end
+ req = Rack::Request.new(env)
+ @set.recognize(req) do |route, params|
+ dispatcher = route.app
+ if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params)
+ dispatcher.prepare_params!(params)
+ return params
+ end
+ end
- # Subclasses and plugins may override this method to extract further attributes
- # from the request, for use by route conditions and such.
- def extract_request_environment(request)
- { :method => request.method }
+ raise ActionController::RoutingError, "No route matches #{path.inspect}"
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/dom.rb b/actionpack/lib/action_dispatch/testing/assertions/dom.rb
index 9a917f704a..9c215de743 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/dom.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/dom.rb
@@ -1,3 +1,5 @@
+require 'action_controller/vendor/html-scanner'
+
module ActionDispatch
module Assertions
module DomAssertions
@@ -15,7 +17,7 @@ module ActionDispatch
assert_block(full_message) { expected_dom == actual_dom }
end
-
+
# The negated form of +assert_dom_equivalent+.
#
# ==== Examples
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index 501a7c4dfb..5686bbdbde 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -28,7 +28,7 @@ module ActionDispatch
assert_block("") { true } # to count the assertion
elsif type.is_a?(Fixnum) && @response.response_code == type
assert_block("") { true } # to count the assertion
- elsif type.is_a?(Symbol) && @response.response_code == ActionDispatch::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
+ elsif type.is_a?(Symbol) && @response.response_code == Rack::Utils::SYMBOL_TO_STATUS_CODE[type]
assert_block("") { true } # to count the assertion
else
assert_block(build_message(message, "Expected response to be a <?>, but was <?>", type, @response.response_code)) { false }
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index e6d6b5a3ef..fc477afb17 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -46,7 +46,6 @@ module ActionDispatch
request_method = nil
end
- ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
request = recognized_request_for(path, request_method)
expected_options = expected_options.clone
@@ -80,7 +79,6 @@ module ActionDispatch
def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
expected_path = "/#{expected_path}" unless expected_path[0] == ?/
# Load routes.rb if it hasn't been loaded.
- ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults)
found_extras = options.reject {|k, v| ! extra_keys.include? k}
@@ -126,6 +124,46 @@ module ActionDispatch
assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message)
end
+ # A helper to make it easier to test different route configurations.
+ # This method temporarily replaces ActionController::Routing::Routes
+ # with a new RouteSet instance.
+ #
+ # The new instance is yielded to the passed block. Typically the block
+ # will create some routes using <tt>map.draw { map.connect ... }</tt>:
+ #
+ # with_routing do |set|
+ # set.draw do |map|
+ # map.connect ':controller/:action/:id'
+ # assert_equal(
+ # ['/content/10/show', {}],
+ # map.generate(:controller => 'content', :id => 10, :action => 'show')
+ # end
+ # end
+ # end
+ #
+ def with_routing
+ real_routes = ActionController::Routing::Routes
+ ActionController::Routing.module_eval { remove_const :Routes }
+
+ temporary_routes = ActionController::Routing::RouteSet.new
+ ActionController::Routing.module_eval { const_set :Routes, temporary_routes }
+
+ yield temporary_routes
+ ensure
+ if ActionController::Routing.const_defined? :Routes
+ ActionController::Routing.module_eval { remove_const :Routes }
+ end
+ ActionController::Routing.const_set(:Routes, real_routes) if real_routes
+ end
+
+ def method_missing(selector, *args, &block)
+ if @controller && ActionController::Routing::Routes.named_routes.helpers.include?(selector)
+ @controller.send(selector, *args, &block)
+ else
+ super
+ end
+ end
+
private
# Recognizes the route for a given path.
def recognized_request_for(path, request_method = nil)
@@ -134,9 +172,11 @@ module ActionDispatch
# Assume given controller
request = ActionController::TestRequest.new
request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
- request.path = path
+ request.path = path
+
+ params = ActionController::Routing::Routes.recognize_path(path, { :method => request.method })
+ request.path_parameters = params.with_indifferent_access
- ActionController::Routing::Routes.recognize(request)
request
end
end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/selector.rb b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
index d22adfa749..c2dc591ff7 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/selector.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/selector.rb
@@ -1,3 +1,5 @@
+require 'action_controller/vendor/html-scanner'
+
#--
# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
# Under MIT and/or CC By license.
@@ -16,7 +18,7 @@ module ActionDispatch
#
# Use +css_select+ to select elements without making an assertions, either
# from the response HTML or elements selected by the enclosing assertion.
- #
+ #
# In addition to HTML responses, you can make the following assertions:
# * +assert_select_rjs+ - Assertions on HTML content of RJS update and insertion operations.
# * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
@@ -53,8 +55,8 @@ module ActionDispatch
# end
#
# # Selects all list items in unordered lists
- # items = css_select("ul>li")
- #
+ # items = css_select("ul>li")
+ #
# # Selects all form tags and then all inputs inside the form
# forms = css_select("form")
# forms.each do |form|
@@ -212,7 +214,7 @@ module ActionDispatch
# Otherwise just operate on the response document.
root = response_from_page_or_rjs
end
-
+
# First or second argument is the selector: string and we pass
# all remaining arguments. Array and we pass the argument. Also
# accepts selector itself.
@@ -225,7 +227,7 @@ module ActionDispatch
selector = arg
else raise ArgumentError, "Expecting a selector as the first argument"
end
-
+
# Next argument is used for equality tests.
equals = {}
case arg = args.shift
@@ -315,10 +317,10 @@ module ActionDispatch
# Returns all matches elements.
matches
end
-
+
def count_description(min, max) #:nodoc:
pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
-
+
if min && max && (max != min)
"between #{min} and #{max} elements"
elsif min && !(min == 1 && max == 1)
@@ -327,7 +329,7 @@ module ActionDispatch
"at most #{max} #{pluralize['element', max]}"
end
end
-
+
# :call-seq:
# assert_select_rjs(id?) { |elements| ... }
# assert_select_rjs(statement, id?) { |elements| ... }
@@ -344,7 +346,7 @@ module ActionDispatch
# that update or insert an element with that identifier.
#
# Use the first argument to narrow down assertions to only statements
- # of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>,
+ # of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>,
# <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tta>,
# <tt>:insert_html</tt> and <tt>:redirect</tt>.
#
@@ -494,7 +496,7 @@ module ActionDispatch
# end
# end
# end
- #
+ #
#
# # Selects all paragraph tags from within the description of an RSS feed
# assert_select_feed :rss, 2.0 do
diff --git a/actionpack/lib/action_dispatch/testing/assertions/tag.rb b/actionpack/lib/action_dispatch/testing/assertions/tag.rb
index ef6867576e..5c735e61b2 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/tag.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/tag.rb
@@ -1,3 +1,5 @@
+require 'action_controller/vendor/html-scanner'
+
module ActionDispatch
module Assertions
# Pair of assertions to testing elements in the HTML output of the response.
@@ -76,10 +78,10 @@ module ActionDispatch
# # Assert that there is a "span" containing between 2 and 4 "em" tags
# # as immediate children
# assert_tag :tag => "span",
- # :children => { :count => 2..4, :only => { :tag => "em" } }
+ # :children => { :count => 2..4, :only => { :tag => "em" } }
#
# # Get funky: assert that there is a "div", with an "ul" ancestor
- # # and an "li" parent (with "class" = "enum"), and containing a
+ # # and an "li" parent (with "class" = "enum"), and containing a
# # "span" descendant that contains text matching /hello world/
# assert_tag :tag => "div",
# :ancestor => { :tag => "ul" },
@@ -98,7 +100,7 @@ module ActionDispatch
tag = find_tag(opts)
assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
end
-
+
# Identical to +assert_tag+, but asserts that a matching tag does _not_
# exist. (See +assert_tag+ for a full discussion of the syntax.)
#
@@ -118,6 +120,19 @@ module ActionDispatch
tag = find_tag(opts)
assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
end
+
+ def find_tag(conditions)
+ html_document.find(conditions)
+ end
+
+ def find_all_tag(conditions)
+ html_document.find_all(conditions)
+ end
+
+ def html_document
+ xml = @response.content_type =~ /xml$/
+ @html_document ||= HTML::Document.new(@response.body, false, xml)
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 40d6f97b2a..2a5f5dcd5c 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -2,9 +2,7 @@ require 'stringio'
require 'uri'
require 'active_support/test_case'
require 'active_support/core_ext/object/metaclass'
-
-# TODO: Remove circular dependency on ActionController
-require 'action_controller/testing/process'
+require 'rack/test'
module ActionDispatch
module Integration #:nodoc:
@@ -128,9 +126,7 @@ module ActionDispatch
DEFAULT_HOST = "www.example.com"
include Test::Unit::Assertions
- include ActionDispatch::Assertions
- include ActionController::TestProcess
- include RequestHelpers
+ include TestProcess, RequestHelpers, Assertions
%w( status status_message headers body redirect? ).each do |method|
delegate method, :to => :response, :allow_nil => true
@@ -415,7 +411,7 @@ module ActionDispatch
# At its simplest, you simply extend IntegrationTest and write your tests
# using the get/post methods:
#
- # require "#{File.dirname(__FILE__)}/test_helper"
+ # require "test_helper"
#
# class ExampleTest < ActionController::IntegrationTest
# fixtures :people
@@ -439,7 +435,7 @@ module ActionDispatch
# powerful testing DSL that is specific for your application. You can even
# reference any named routes you happen to have defined!
#
- # require "#{File.dirname(__FILE__)}/test_helper"
+ # require "test_helper"
#
# class AdvancedTest < ActionController::IntegrationTest
# fixtures :people, :rooms
diff --git a/actionpack/lib/action_dispatch/testing/performance_test.rb b/actionpack/lib/action_dispatch/testing/performance_test.rb
index b1ed9d31f4..1b9a6c18b7 100644
--- a/actionpack/lib/action_dispatch/testing/performance_test.rb
+++ b/actionpack/lib/action_dispatch/testing/performance_test.rb
@@ -1,15 +1,17 @@
require 'active_support/testing/performance'
require 'active_support/testing/default'
-module ActionDispatch
- # An integration test that runs a code profiler on your test methods.
- # Profiling output for combinations of each test method, measurement, and
- # output format are written to your tmp/performance directory.
- #
- # By default, process_time is measured and both flat and graph_html output
- # formats are written, so you'll have two output files per test method.
- class PerformanceTest < ActionDispatch::IntegrationTest
- include ActiveSupport::Testing::Performance
- include ActiveSupport::Testing::Default
+if defined?(ActiveSupport::Testing::Performance)
+ module ActionDispatch
+ # An integration test that runs a code profiler on your test methods.
+ # Profiling output for combinations of each test method, measurement, and
+ # output format are written to your tmp/performance directory.
+ #
+ # By default, process_time is measured and both flat and graph_html output
+ # formats are written, so you'll have two output files per test method.
+ class PerformanceTest < ActionDispatch::IntegrationTest
+ include ActiveSupport::Testing::Performance
+ include ActiveSupport::Testing::Default
+ end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
new file mode 100644
index 0000000000..eae703e1b6
--- /dev/null
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -0,0 +1,42 @@
+module ActionDispatch
+ module TestProcess
+ def assigns(key = nil)
+ assigns = {}
+ @controller.instance_variable_names.each do |ivar|
+ next if ActionController::Base.protected_instance_variables.include?(ivar)
+ assigns[ivar[1..-1]] = @controller.instance_variable_get(ivar)
+ end
+
+ key.nil? ? assigns : assigns[key.to_s]
+ end
+
+ def session
+ @request.session
+ end
+
+ def flash
+ @request.flash
+ end
+
+ def cookies
+ @request.cookies.merge(@response.cookies)
+ end
+
+ def redirect_to_url
+ @response.redirect_url
+ end
+
+ # Shortcut for <tt>ARack::Test::UploadedFile.new(ActionController::TestCase.fixture_path + path, type)</tt>:
+ #
+ # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png')
+ #
+ # To upload binary files on Windows, pass <tt>:binary</tt> as the last parameter.
+ # This will not affect other platforms:
+ #
+ # post :change_avatar, :avatar => fixture_file_upload('/files/spongebob.png', 'image/png', :binary)
+ def fixture_file_upload(path, mime_type = nil, binary = false)
+ fixture_path = ActionController::TestCase.send(:fixture_path) if ActionController::TestCase.respond_to?(:fixture_path)
+ Rack::Test::UploadedFile.new("#{fixture_path}#{path}", mime_type, binary)
+ end
+ end
+end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index e95e84aeb5..8ce6e82524 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -21,38 +21,40 @@
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#++
-require File.join(File.dirname(__FILE__), "action_pack")
+activesupport_path = File.expand_path('../../../activesupport/lib', __FILE__)
+$:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.include?(activesupport_path)
+require 'active_support/ruby/shim'
+require 'active_support/core_ext/class/attribute_accessors'
+
+require 'action_pack'
module ActionView
- def self.load_all!
- [Context, Base, InlineTemplate, TemplateError]
- end
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :Context
+ autoload :Template
+ autoload :Helpers
+ autoload :SafeBuffer
+
+ autoload_under "render" do
+ autoload :Partials
+ autoload :Rendering
+ end
- autoload :Base, 'action_view/base'
- autoload :Context, 'action_view/context'
- autoload :Helpers, 'action_view/helpers'
- autoload :MissingTemplate, 'action_view/base'
- autoload :Partials, 'action_view/render/partials'
- autoload :Resolver, 'action_view/template/resolver'
- autoload :PathResolver, 'action_view/template/resolver'
- autoload :PathSet, 'action_view/paths'
- autoload :Rendering, 'action_view/render/rendering'
- autoload :Template, 'action_view/template/template'
- autoload :TemplateError, 'action_view/template/error'
- autoload :TemplateHandler, 'action_view/template/handler'
- autoload :TemplateHandlers, 'action_view/template/handlers'
- autoload :TextTemplate, 'action_view/template/text'
- autoload :Helpers, 'action_view/helpers'
- autoload :FileSystemResolverWithFallback, 'action_view/template/resolver'
- autoload :SafeBuffer, 'action_view/safe_buffer'
+ autoload :MissingTemplate, 'action_view/base'
+ autoload :Resolver, 'action_view/template/resolver'
+ autoload :PathResolver, 'action_view/template/resolver'
+ autoload :PathSet, 'action_view/paths'
+ autoload :FileSystemResolverWithFallback, 'action_view/template/resolver'
+
+ autoload :TemplateError, 'action_view/template/error'
+ autoload :TemplateHandler, 'action_view/template'
+ autoload :TemplateHandlers, 'action_view/template'
+ end
end
require 'action_view/erb/util'
-
+require 'action_view/base'
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
-
-activesupport_path = "#{File.dirname(__FILE__)}/../../activesupport/lib"
-$:.unshift activesupport_path if File.directory?(activesupport_path)
-require 'active_support'
-require 'active_support/core_ext/class/attribute_accessors'
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index c33695770f..d69e5109fa 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -196,7 +196,7 @@ module ActionView #:nodoc:
end
class << self
- delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB'
+ delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb
index 69d0d0fb67..f03ffe5ef4 100644
--- a/actionpack/lib/action_view/helpers/sanitize_helper.rb
+++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb
@@ -1,3 +1,4 @@
+require 'action_controller/vendor/html-scanner'
require 'action_view/helpers/tag_helper'
module ActionView
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index 564f12c955..35c431d78d 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -12,7 +12,7 @@ module ActionView
# prepend the key with a period, nothing is converted.
def translate(key, options = {})
options[:raise] = true
- I18n.translate(scope_key_by_partial(key), options)
+ I18n.translate(scope_key_by_partial(key), options).html_safe!
rescue I18n::MissingTranslationData => e
keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
content_tag('span', keys.join(', '), :class => 'translation_missing')
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 23bde61f9c..0059b79e5f 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -4,7 +4,7 @@ module ActionView #:nodoc:
# TODO: Clean this up
if obj.is_a?(String)
if cache.nil?
- cache = !defined?(Rails) || Rails.application.config.cache_classes
+ cache = !defined?(Rails.application) || Rails.application.config.cache_classes
end
FileSystemResolverWithFallback.new(obj, :cache => cache)
else
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index 2eb88ae3e5..aeaf1ee4ff 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -181,20 +181,20 @@ module ActionView
def initialize(view_context, options, block)
@view = view_context
@partial_names = PARTIAL_NAMES[@view.controller.class]
-
+
key = Thread.current[:format_locale_key]
@templates = TEMPLATES[key] if key
-
+
setup(options, block)
end
-
+
def setup(options, block)
partial = options[:partial]
-
+
@options = options
@locals = options[:locals] || {}
@block = block
-
+
if String === partial
@object = options[:object]
@path = partial
@@ -240,7 +240,7 @@ module ActionView
segments << template.render(@view, locals)
end
-
+
@template = template
segments
end
@@ -294,7 +294,7 @@ module ActionView
path && @templates[path] ||= _find_template(path)
end
end
-
+
def _find_template(path)
if controller = @view.controller
prefix = controller.controller_path unless path.include?(?/)
@@ -319,7 +319,7 @@ module ActionView
_evaluate_assigns_and_ivars
details = options[:_details]
-
+
# Is this needed
self.formats = details[:formats] if details
renderer = PartialRenderer.new(self, options, nil)
@@ -329,12 +329,12 @@ module ActionView
end
def _render_partial(options, &block) #:nodoc:
- if @renderer
+ if defined? @renderer
@renderer.setup(options, block)
else
@renderer = PartialRenderer.new(self, options, block)
end
-
+
@renderer.render
end
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 7006a5b968..d4d16b4d98 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -78,12 +78,12 @@ module ActionView
end
def _render_inline(inline, layout, options)
- handler = Template.handler_class_for_extension(options[:type] || "erb")
- template = Template.new(options[:inline],
- "inline #{options[:inline].inspect}", handler, {})
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
+ template = Template.new(options[:inline], "inline template", handler, {})
- locals = options[:locals]
+ locals = options[:locals]
content = template.render(self, locals)
+
_render_text(content, layout, locals)
end
@@ -91,6 +91,7 @@ module ActionView
content = layout.render(self, locals) do |*name|
_layout_for(*name) { content }
end if layout
+
content
end
@@ -113,21 +114,16 @@ module ActionView
msg
end
- locals = options[:locals] || {}
-
- content = if partial
- _render_partial_object(template, options)
- else
- template.render(self, locals)
- end
-
+ locals = options[:locals] || {}
+ content = partial ? _render_partial_object(template, options) : template.render(self, locals)
@_content_for[:layout] = content
if layout
@_layout = layout.identifier
logger.info("Rendering template within #{layout.inspect}") if logger
- content = layout.render(self, locals) {|*name| _layout_for(*name) }
+ content = layout.render(self, locals) { |*name| _layout_for(*name) }
end
+
content
end
end
diff --git a/actionpack/lib/action_view/safe_buffer.rb b/actionpack/lib/action_view/safe_buffer.rb
index 09f44ab26f..6be05b9e1e 100644
--- a/actionpack/lib/action_view/safe_buffer.rb
+++ b/actionpack/lib/action_view/safe_buffer.rb
@@ -1,4 +1,3 @@
-
module ActionView #:nodoc:
class SafeBuffer < String
def <<(value)
diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template.rb
index d1970ca3c7..d46c989d11 100644
--- a/actionpack/lib/action_view/template/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -6,7 +6,16 @@ require "action_view/template/resolver"
module ActionView
class Template
- extend TemplateHandlers
+ extend ActiveSupport::Autoload
+
+ eager_autoload do
+ autoload :Error
+ autoload :Handler
+ autoload :Handlers
+ autoload :Text
+ end
+
+ extend Template::Handlers
attr_reader :source, :identifier, :handler, :mime_type, :formats, :details
def initialize(source, identifier, handler, details)
@@ -32,11 +41,11 @@ module ActionView
view.send(method_name, locals, &block)
end
rescue Exception => e
- if e.is_a?(TemplateError)
+ if e.is_a?(Template::Error)
e.sub_template_of(self)
raise e
else
- raise TemplateError.new(self, view.assigns, e)
+ raise Template::Error.new(self, view.assigns, e)
end
end
@@ -103,23 +112,8 @@ module ActionView
logger.debug "Backtrace: #{e.backtrace.join("\n")}"
end
- raise ActionView::TemplateError.new(self, {}, e)
- end
- end
-
- class LocalsKey
- @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } }
-
- def self.get(*locals)
- @hash_keys[*locals] ||= new(klass, format, locale)
+ raise ActionView::Template::Error.new(self, {}, e)
end
-
- attr_accessor :hash
- def initialize(klass, format, locale)
- @hash = locals.hash
- end
-
- alias_method :eql?, :equal?
end
def build_method_name(locals)
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
index aa21606f76..648f708d3d 100644
--- a/actionpack/lib/action_view/template/error.rb
+++ b/actionpack/lib/action_view/template/error.rb
@@ -1,101 +1,105 @@
require "active_support/core_ext/enumerable"
module ActionView
- # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a
- # bunch of intimate details and uses it to report a very precise exception message.
- class TemplateError < ActionViewError #:nodoc:
- SOURCE_CODE_RADIUS = 3
+ class Template
+ # The Template::Error exception is raised when the compilation of the template fails. This exception then gathers a
+ # bunch of intimate details and uses it to report a very precise exception message.
+ class Error < ActionViewError #:nodoc:
+ SOURCE_CODE_RADIUS = 3
- attr_reader :original_exception
+ attr_reader :original_exception
- def initialize(template, assigns, original_exception)
- @template, @assigns, @original_exception = template, assigns.dup, original_exception
- @backtrace = compute_backtrace
- end
+ def initialize(template, assigns, original_exception)
+ @template, @assigns, @original_exception = template, assigns.dup, original_exception
+ @backtrace = compute_backtrace
+ end
- def file_name
- @template.identifier
- end
+ def file_name
+ @template.identifier
+ end
- def message
- ActiveSupport::Deprecation.silence { original_exception.message }
- end
+ def message
+ ActiveSupport::Deprecation.silence { original_exception.message }
+ end
- def clean_backtrace
- if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner)
- Rails.backtrace_cleaner.clean(original_exception.backtrace)
- else
- original_exception.backtrace
+ def clean_backtrace
+ if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner)
+ Rails.backtrace_cleaner.clean(original_exception.backtrace)
+ else
+ original_exception.backtrace
+ end
end
- end
- def sub_template_message
- if @sub_templates
- "Trace of template inclusion: " +
- @sub_templates.collect { |template| template.inspect }.join(", ")
- else
- ""
+ def sub_template_message
+ if @sub_templates
+ "Trace of template inclusion: " +
+ @sub_templates.collect { |template| template.inspect }.join(", ")
+ else
+ ""
+ end
end
- end
- def source_extract(indentation = 0)
- return unless num = line_number
- num = num.to_i
+ def source_extract(indentation = 0)
+ return unless num = line_number
+ num = num.to_i
- source_code = @template.source.split("\n")
+ source_code = @template.source.split("\n")
- start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
- end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
+ start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
+ end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
- indent = ' ' * indentation
- line_counter = start_on_line
- return unless source_code = source_code[start_on_line..end_on_line]
+ indent = ' ' * indentation
+ line_counter = start_on_line
+ return unless source_code = source_code[start_on_line..end_on_line]
- source_code.sum do |line|
- line_counter += 1
- "#{indent}#{line_counter}: #{line}\n"
+ source_code.sum do |line|
+ line_counter += 1
+ "#{indent}#{line_counter}: #{line}\n"
+ end
end
- end
-
- def sub_template_of(template_path)
- @sub_templates ||= []
- @sub_templates << template_path
- end
-
- def line_number
- @line_number ||=
- if file_name
- regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
- $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
- end
- end
+ def sub_template_of(template_path)
+ @sub_templates ||= []
+ @sub_templates << template_path
+ end
- def to_s
- "\n#{self.class} (#{message}) #{source_location}:\n" +
- "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
- end
+ def line_number
+ @line_number ||=
+ if file_name
+ regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
- # don't do anything nontrivial here. Any raised exception from here becomes fatal
- # (and can't be rescued).
- def backtrace
- @backtrace
- end
+ $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
+ end
+ end
- private
- def compute_backtrace
- [
- "#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
- clean_backtrace.join("\n ")
- ]
+ def to_s
+ "\n#{self.class} (#{message}) #{source_location}:\n" +
+ "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
end
- def source_location
- if line_number
- "on line ##{line_number} of "
- else
- 'in '
- end + file_name
+ # don't do anything nontrivial here. Any raised exception from here becomes fatal
+ # (and can't be rescued).
+ def backtrace
+ @backtrace
end
+
+ private
+ def compute_backtrace
+ [
+ "#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
+ clean_backtrace.join("\n ")
+ ]
+ end
+
+ def source_location
+ if line_number
+ "on line ##{line_number} of "
+ else
+ 'in '
+ end + file_name
+ end
+ end
end
-end \ No newline at end of file
+
+ TemplateError = Template::Error
+end
diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb
index 4bf58b9fa8..5a46a27893 100644
--- a/actionpack/lib/action_view/template/handler.rb
+++ b/actionpack/lib/action_view/template/handler.rb
@@ -3,34 +3,39 @@ require "action_dispatch/http/mime_type"
# Legacy TemplateHandler stub
module ActionView
- module TemplateHandlers #:nodoc:
- module Compilable
- def self.included(base)
- base.extend(ClassMethods)
- end
+ class Template
+ module Handlers #:nodoc:
+ module Compilable
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
- module ClassMethods
- def call(template)
- new.compile(template)
+ module ClassMethods
+ def call(template)
+ new.compile(template)
+ end
end
- end
- def compile(template)
- raise "Need to implement #{self.class.name}#compile(template)"
- end
+ def compile(template)
+ raise "Need to implement #{self.class.name}#compile(template)"
+ end
+ end
end
- end
- class TemplateHandler
- extlib_inheritable_accessor :default_format
- self.default_format = Mime::HTML
+ class Template::Handler
+ extlib_inheritable_accessor :default_format
+ self.default_format = Mime::HTML
- def self.call(template)
- raise "Need to implement #{self.class.name}#call(template)"
- end
+ def self.call(template)
+ raise "Need to implement #{self.class.name}#call(template)"
+ end
- def render(template, local_assigns)
- raise "Need to implement #{self.class.name}#render(template, local_assigns)"
+ def render(template, local_assigns)
+ raise "Need to implement #{self.class.name}#render(template, local_assigns)"
+ end
end
end
+
+ TemplateHandlers = Template::Handlers
+ TemplateHandler = Template::Handler
end
diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb
index faf54b9fe5..35488c0391 100644
--- a/actionpack/lib/action_view/template/handlers.rb
+++ b/actionpack/lib/action_view/template/handlers.rb
@@ -1,52 +1,54 @@
module ActionView #:nodoc:
- module TemplateHandlers #:nodoc:
- autoload :ERB, 'action_view/template/handlers/erb'
- autoload :RJS, 'action_view/template/handlers/rjs'
- autoload :Builder, 'action_view/template/handlers/builder'
-
- def self.extended(base)
- base.register_default_template_handler :erb, TemplateHandlers::ERB
- base.register_template_handler :rjs, TemplateHandlers::RJS
- base.register_template_handler :builder, TemplateHandlers::Builder
-
- # TODO: Depreciate old template extensions
- base.register_template_handler :rhtml, TemplateHandlers::ERB
- base.register_template_handler :rxml, TemplateHandlers::Builder
- end
-
- @@template_handlers = {}
- @@default_template_handlers = nil
+ class Template
+ module Handlers #:nodoc:
+ autoload :ERB, 'action_view/template/handlers/erb'
+ autoload :RJS, 'action_view/template/handlers/rjs'
+ autoload :Builder, 'action_view/template/handlers/builder'
+
+ def self.extended(base)
+ base.register_default_template_handler :erb, ERB
+ base.register_template_handler :rjs, RJS
+ base.register_template_handler :builder, Builder
+
+ # TODO: Depreciate old template extensions
+ base.register_template_handler :rhtml, ERB
+ base.register_template_handler :rxml, Builder
+ end
+
+ @@template_handlers = {}
+ @@default_template_handlers = nil
- def self.extensions
- @@template_handlers.keys
- end
-
- # Register a class that knows how to handle template files with the given
- # extension. This can be used to implement new template types.
- # The constructor for the class must take the ActiveView::Base instance
- # as a parameter, and the class must implement a +render+ method that
- # takes the contents of the template to render as well as the Hash of
- # local assigns available to the template. The +render+ method ought to
- # return the rendered template as a string.
- def register_template_handler(extension, klass)
- @@template_handlers[extension.to_sym] = klass
- end
-
- def template_handler_extensions
- @@template_handlers.keys.map {|key| key.to_s }.sort
- end
-
- def registered_template_handler(extension)
- extension && @@template_handlers[extension.to_sym]
- end
-
- def register_default_template_handler(extension, klass)
- register_template_handler(extension, klass)
- @@default_template_handlers = klass
- end
-
- def handler_class_for_extension(extension)
- (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers
+ def self.extensions
+ @@template_handlers.keys
+ end
+
+ # Register a class that knows how to handle template files with the given
+ # extension. This can be used to implement new template types.
+ # The constructor for the class must take the ActiveView::Base instance
+ # as a parameter, and the class must implement a +render+ method that
+ # takes the contents of the template to render as well as the Hash of
+ # local assigns available to the template. The +render+ method ought to
+ # return the rendered template as a string.
+ def register_template_handler(extension, klass)
+ @@template_handlers[extension.to_sym] = klass
+ end
+
+ def template_handler_extensions
+ @@template_handlers.keys.map {|key| key.to_s }.sort
+ end
+
+ def registered_template_handler(extension)
+ extension && @@template_handlers[extension.to_sym]
+ end
+
+ def register_default_template_handler(extension, klass)
+ register_template_handler(extension, klass)
+ @@default_template_handlers = klass
+ end
+
+ def handler_class_for_extension(extension)
+ (extension && registered_template_handler(extension.to_sym)) || @@default_template_handlers
+ end
end
end
end
diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb
index 5f381f7bf0..a93cfca8aa 100644
--- a/actionpack/lib/action_view/template/handlers/builder.rb
+++ b/actionpack/lib/action_view/template/handlers/builder.rb
@@ -1,6 +1,6 @@
module ActionView
- module TemplateHandlers
- class Builder < TemplateHandler
+ module Template::Handlers
+ class Builder < Template::Handler
include Compilable
self.default_format = Mime::XML
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 88aeb4b053..93a4315108 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -3,14 +3,15 @@ require 'active_support/core_ext/string/output_safety'
require 'erubis'
module ActionView
- module TemplateHandlers
+ module Template::Handlers
class Erubis < ::Erubis::Eruby
def add_preamble(src)
src << "@output_buffer = ActionView::SafeBuffer.new;"
end
def add_text(src, text)
- src << "@output_buffer << ('" << escape_text(text) << "'.html_safe!);"
+ return if text.empty?
+ src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
end
def add_expr_literal(src, code)
@@ -26,7 +27,7 @@ module ActionView
end
end
- class ERB < TemplateHandler
+ class ERB < Template::Handler
include Compilable
##
diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb
index b1d15dc209..63e7dc0902 100644
--- a/actionpack/lib/action_view/template/handlers/rjs.rb
+++ b/actionpack/lib/action_view/template/handlers/rjs.rb
@@ -1,6 +1,6 @@
module ActionView
- module TemplateHandlers
- class RJS < TemplateHandler
+ module Template::Handlers
+ class RJS < Template::Handler
include Compilable
self.default_format = Mime::JS
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 7336114e1b..a2f4ab2ef5 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -1,6 +1,6 @@
require "pathname"
require "active_support/core_ext/class"
-require "action_view/template/template"
+require "action_view/template"
module ActionView
# Abstract superclass
@@ -20,7 +20,7 @@ module ActionView
register_detail(:locale) { [I18n.locale] }
register_detail(:formats) { Mime::SET.symbols }
register_detail(:handlers, :allow_nil => false) do
- TemplateHandlers.extensions
+ Template::Handlers.extensions
end
def initialize(options = {})
@@ -65,7 +65,7 @@ module ActionView
# as well as incorrectly putting part of the path in the template
# name instead of the prefix.
def normalize_name(name, prefix)
- handlers = TemplateHandlers.extensions.join('|')
+ handlers = Template::Handlers.extensions.join('|')
name = name.to_s.gsub(/\.(?:#{handlers})$/, '')
parts = name.split('/')
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index f6e011a5ab..67e086d8bd 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -1,38 +1,40 @@
module ActionView #:nodoc:
- class TextTemplate < String #:nodoc:
- HTML = Mime[:html]
-
- def initialize(string, content_type = HTML)
- super(string.to_s)
- @content_type = Mime[content_type] || content_type
- end
-
- def details
- {:formats => [@content_type.to_sym]}
- end
-
- def identifier
- self
- end
-
- def inspect
- 'text template'
- end
-
- def render(*args)
- to_s
- end
-
- def mime_type
- @content_type
- end
-
- def formats
- [mime_type]
- end
-
- def partial?
- false
+ class Template
+ class Text < String #:nodoc:
+ HTML = Mime[:html]
+
+ def initialize(string, content_type = HTML)
+ super(string.to_s)
+ @content_type = Mime[content_type] || content_type
+ end
+
+ def details
+ {:formats => [@content_type.to_sym]}
+ end
+
+ def identifier
+ self
+ end
+
+ def inspect
+ 'text template'
+ end
+
+ def render(*args)
+ to_s
+ end
+
+ def mime_type
+ @content_type
+ end
+
+ def formats
+ [mime_type]
+ end
+
+ def partial?
+ false
+ end
end
end
end
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 86bbad822d..be9a2ed50d 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -1,5 +1,5 @@
require 'active_support/test_case'
-require 'action_controller/testing/test_case'
+require 'action_controller/test_case'
module ActionView
class Base
@@ -39,8 +39,7 @@ module ActionView
end
end
- include ActionDispatch::Assertions
- include ActionController::TestProcess
+ include ActionDispatch::Assertions, ActionDispatch::TestProcess
include ActionView::Context
include ActionController::PolymorphicRoutes