aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
authorMikel Lindsaar <raasdnil@gmail.com>2010-03-11 22:05:15 +1100
committerMikel Lindsaar <raasdnil@gmail.com>2010-03-11 22:05:15 +1100
commitf5774e3e3f70a3acfa559b9ff889e9417fb71d4b (patch)
treee738112994d40d6c3792065da80bddfa7439467b /actionpack/lib
parentcefe723e285f20d1f2a33f67da03348568f7e0b0 (diff)
parent073852dff0b48296a9a184f94e722183334f3c4c (diff)
downloadrails-f5774e3e3f70a3acfa559b9ff889e9417fb71d4b.tar.gz
rails-f5774e3e3f70a3acfa559b9ff889e9417fb71d4b.tar.bz2
rails-f5774e3e3f70a3acfa559b9ff889e9417fb71d4b.zip
Merge branch 'master' of git://github.com/rails/rails
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller.rb4
-rw-r--r--actionpack/lib/abstract_controller/base.rb16
-rw-r--r--actionpack/lib/abstract_controller/compatibility.rb18
-rw-r--r--actionpack/lib/abstract_controller/details_cache.rb48
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb131
-rw-r--r--actionpack/lib/abstract_controller/rendering.rb212
-rw-r--r--actionpack/lib/abstract_controller/view_paths.rb73
-rw-r--r--actionpack/lib/action_controller.rb3
-rw-r--r--actionpack/lib/action_controller/base.rb29
-rw-r--r--actionpack/lib/action_controller/caching.rb37
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb48
-rw-r--r--actionpack/lib/action_controller/caching/sweeping.rb4
-rw-r--r--actionpack/lib/action_controller/deprecated/base.rb131
-rw-r--r--actionpack/lib/action_controller/metal.rb35
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb96
-rw-r--r--actionpack/lib/action_controller/metal/configuration.rb28
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb50
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb21
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rack_delegation.rb5
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb47
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb45
-rw-r--r--actionpack/lib/action_controller/metal/session_management.rb36
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb3
-rw-r--r--actionpack/lib/action_controller/middleware.rb3
-rw-r--r--actionpack/lib/action_controller/railtie.rb50
-rw-r--r--actionpack/lib/action_controller/test_case.rb19
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb98
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/mime_negotiation.rb15
-rwxr-xr-xactionpack/lib/action_dispatch/http/request.rb39
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb37
-rw-r--r--actionpack/lib/action_dispatch/middleware/params_parser.rb1
-rw-r--r--actionpack/lib/action_dispatch/middleware/remote_ip.rb51
-rw-r--r--actionpack/lib/action_dispatch/middleware/session/cookie_store.rb13
-rw-r--r--actionpack/lib/action_dispatch/middleware/stack.rb8
-rw-r--r--actionpack/lib/action_dispatch/railtie.rb1
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb37
-rw-r--r--actionpack/lib/action_dispatch/routing/route.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb296
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb64
-rw-r--r--actionpack/lib/action_view.rb15
-rw-r--r--actionpack/lib/action_view/base.rb111
-rw-r--r--actionpack/lib/action_view/helpers.rb1
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb13
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb14
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb3
-rw-r--r--actionpack/lib/action_view/helpers/deprecated_block_helpers.rb52
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb31
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb2
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb20
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb8
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb86
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb26
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb5
-rw-r--r--actionpack/lib/action_view/lookup_context.rb151
-rw-r--r--actionpack/lib/action_view/paths.rb85
-rw-r--r--actionpack/lib/action_view/railtie.rb4
-rw-r--r--actionpack/lib/action_view/render/layouts.rb65
-rw-r--r--actionpack/lib/action_view/render/partials.rb48
-rw-r--r--actionpack/lib/action_view/render/rendering.rb115
-rw-r--r--actionpack/lib/action_view/template.rb32
-rw-r--r--actionpack/lib/action_view/template/handlers/erb.rb54
-rw-r--r--actionpack/lib/action_view/template/resolver.rb136
-rw-r--r--actionpack/lib/action_view/template/text.rb13
-rw-r--r--actionpack/lib/action_view/test_case.rb7
69 files changed, 1464 insertions, 1506 deletions
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index c1b035306b..2da4dc052c 100644
--- a/actionpack/lib/abstract_controller.rb
+++ b/actionpack/lib/abstract_controller.rb
@@ -7,6 +7,7 @@ require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/module/attr_internal'
require 'active_support/core_ext/module/delegation'
require 'active_support/core_ext/module/anonymous'
+require 'active_support/i18n'
module AbstractController
extend ActiveSupport::Autoload
@@ -14,11 +15,10 @@ module AbstractController
autoload :Base
autoload :Callbacks
autoload :Collector
- autoload :Compatibility
- autoload :DetailsCache
autoload :Helpers
autoload :Layouts
autoload :Logger
autoload :Rendering
autoload :Translation
+ autoload :ViewPaths
end
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb
index e14818e464..c12b584144 100644
--- a/actionpack/lib/abstract_controller/base.rb
+++ b/actionpack/lib/abstract_controller/base.rb
@@ -1,3 +1,5 @@
+require 'active_support/ordered_options'
+
module AbstractController
class Error < StandardError; end
class ActionNotFound < StandardError; end
@@ -5,7 +7,6 @@ module AbstractController
class Base
attr_internal :response_body
attr_internal :action_name
- attr_internal :formats
class << self
attr_reader :abstract
@@ -28,6 +29,14 @@ module AbstractController
@descendants ||= []
end
+ def config
+ @config ||= ActiveSupport::InheritableOptions.new(superclass < Base ? superclass.config : {})
+ end
+
+ def configure
+ yield config
+ end
+
# A list of all internal methods for a controller. This finds the first
# abstract superclass of a controller, and gets a list of all public
# instance methods on that abstract class. Public instance methods of
@@ -90,9 +99,8 @@ module AbstractController
abstract!
- # Initialize controller with nil formats.
- def initialize #:nodoc:
- @_formats = nil
+ def config
+ @config ||= ActiveSupport::InheritableOptions.new(self.class.config)
end
# Calls the action going through the entire action dispatch stack.
diff --git a/actionpack/lib/abstract_controller/compatibility.rb b/actionpack/lib/abstract_controller/compatibility.rb
deleted file mode 100644
index 7fb93a0eb5..0000000000
--- a/actionpack/lib/abstract_controller/compatibility.rb
+++ /dev/null
@@ -1,18 +0,0 @@
-module AbstractController
- module Compatibility
- extend ActiveSupport::Concern
-
- def _find_layout(name, details)
- 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
- rescue ActionView::MissingTemplate
- _find_layout(_layout({}), {})
- nil
- end
- end
-end
diff --git a/actionpack/lib/abstract_controller/details_cache.rb b/actionpack/lib/abstract_controller/details_cache.rb
deleted file mode 100644
index be1a1c0f34..0000000000
--- a/actionpack/lib/abstract_controller/details_cache.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-module AbstractController
- class HashKey
- @hash_keys = Hash.new {|h,k| h[k] = {} }
-
- def self.get(klass, details)
- @hash_keys[klass][details] ||= new(klass, details)
- end
-
- attr_reader :hash
- alias_method :eql?, :equal?
-
- def initialize(klass, details)
- @details, @hash = details, details.hash
- end
-
- def inspect
- "#<HashKey -- details: #{@details.inspect}>"
- end
- end
-
- module DetailsCache
- extend ActiveSupport::Concern
-
- module ClassMethods
- def clear_template_caches!
- ActionView::Partials::PartialRenderer::TEMPLATES.clear
- template_cache.clear
- super
- end
-
- def template_cache
- @template_cache ||= Hash.new {|h,k| h[k] = {} }
- end
- end
-
- def render_to_body(*args)
- Thread.current[:format_locale_key] = HashKey.get(self.class, details_for_render)
- super
- end
-
- private
-
- def with_template_cache(name, details)
- self.class.template_cache[HashKey.get(self.class, details)][name] ||= super
- end
-
- end
-end
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index beda4e633e..2f9616124a 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -170,27 +170,7 @@ module AbstractController
module ClassMethods
def inherited(klass)
super
- klass.class_eval do
- _write_layout_method
- @found_layouts = {}
- end
- end
-
- def clear_template_caches!
- @found_layouts.clear if defined? @found_layouts
- super
- end
-
- def cache_layout(details)
- layout = @found_layouts
- key = Thread.current[:format_locale_key]
-
- # Cache nil
- if layout.key?(key)
- return layout[key]
- else
- layout[key] = yield
- end
+ klass._write_layout_method
end
# This module is mixed in if layout conditions are provided. This means
@@ -259,10 +239,10 @@ module AbstractController
def _write_layout_method
case defined?(@_layout) ? @_layout : nil
when String
- self.class_eval %{def _layout(details) #{@_layout.inspect} end}
+ self.class_eval %{def _layout; #{@_layout.inspect} end}
when Symbol
self.class_eval <<-ruby_eval, __FILE__, __LINE__ + 1
- def _layout(details)
+ def _layout
#{@_layout}.tap do |layout|
unless layout.is_a?(String) || !layout
raise ArgumentError, "Your layout method :#{@_layout} returned \#{layout}. It " \
@@ -273,21 +253,21 @@ module AbstractController
ruby_eval
when Proc
define_method :_layout_from_proc, &@_layout
- self.class_eval %{def _layout(details) _layout_from_proc(self) end}
+ self.class_eval %{def _layout; _layout_from_proc(self) end}
when false
- self.class_eval %{def _layout(details) end}
+ self.class_eval %{def _layout; end}
when true
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
when nil
if name
+ _prefix = "layouts" unless _implied_layout_name =~ /\blayouts/
+
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
- def _layout(details)
- self.class.cache_layout(details) do
- if template_exists?("#{_implied_layout_name}", details, :_prefix => "layouts")
- "#{_implied_layout_name}"
- else
- super
- end
+ def _layout
+ if template_exists?("#{_implied_layout_name}", #{_prefix.inspect})
+ "#{_implied_layout_name}"
+ else
+ super
end
end
RUBY
@@ -297,42 +277,20 @@ module AbstractController
end
end
- def render_to_body(options = {})
- # In the case of a partial with a layout, handle the layout
- # here, and make sure the view does not try to handle it
- layout = options.delete(:layout) if options.key?(:partial)
-
- response = super
+ def _normalize_options(options)
+ super
- # This is a little bit messy. We need to explicitly handle partial
- # layouts here since the core lookup logic is in the view, but
- # we need to determine the layout based on the controller
- #
- # TODO: An easier way to handle this would probably be to override
- # render_template
- if layout
- layout = _layout_for_option(layout, options[:_template].details)
- response = layout.render(view_context, options[:locals] || {}) { response }
+ if _include_layout?(options)
+ layout = options.key?(:layout) ? options.delete(:layout) : :default
+ value = _layout_for_option(layout)
+ options[:layout] = (value =~ /\blayouts/ ? value : "layouts/#{value}") if value
end
-
- response
end
private
# This will be overwritten by _write_layout_method
- def _layout(details) end
-
- # Determine the layout for a given name and details.
- #
- # ==== Parameters
- # name<String>:: The name of the template
- # details<Hash{Symbol => Object}>:: A list of details to restrict
- # the lookup to. By default, layout lookup is limited to the
- # formats specified for the current request.
- def _layout_for_name(name, details)
- name && _find_layout(name, details)
- end
+ def _layout; end
# Determine the layout for a given name and details, taking into account
# the name type.
@@ -342,11 +300,11 @@ module AbstractController
# details<Hash{Symbol => Object}>:: A list of details to restrict
# the lookup to. By default, layout lookup is limited to the
# formats specified for the current request.
- def _layout_for_option(name, details)
+ def _layout_for_option(name)
case name
- when String then _layout_for_name(name, details)
- when true then _default_layout(details, true)
- when :default then _default_layout(details, false)
+ when String then name
+ when true then _default_layout(true)
+ when :default then _default_layout(false)
when false, nil then nil
else
raise ArgumentError,
@@ -354,29 +312,6 @@ module AbstractController
end
end
- def _determine_template(options)
- super
-
- return unless (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
- layout = options.key?(:layout) ? options[:layout] : :default
- options[:_layout] = _layout_for_option(layout, options[:_template].details)
- end
-
- # Take in the name and details and find a Template.
- #
- # ==== Parameters
- # name<String>:: The name of the template to retrieve
- # details<Hash>:: A list of details to restrict the search by. This
- # might include details like the format or locale of the template.
- #
- # ==== Returns
- # Template:: A template object matching the name and details
- def _find_layout(name, details)
- # TODO: Make prefix actually part of details in ViewPath#find_by_parts
- prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts"
- find_template(name, details, :_prefix => prefix)
- end
-
# Returns the default layout for this controller and a given set of details.
# Optionally raises an exception if the layout could not be found.
#
@@ -389,18 +324,24 @@ module AbstractController
#
# ==== Returns
# Template:: The template object for the default layout (or nil)
- def _default_layout(details, require_layout = false)
- if require_layout && _action_has_layout? && !_layout(details)
- raise ArgumentError,
- "There was no default layout for #{self.class} in #{view_paths.inspect}"
- end
-
+ def _default_layout(require_layout = false)
begin
- _layout_for_name(_layout(details), details) if _action_has_layout?
+ layout_name = _layout if _action_has_layout?
rescue NameError => e
raise NoMethodError,
"You specified #{@_layout.inspect} as the layout, but no such method was found"
end
+
+ if require_layout && _action_has_layout? && !layout_name
+ raise ArgumentError,
+ "There was no default layout for #{self.class} in #{view_paths.inspect}"
+ end
+
+ layout_name
+ end
+
+ def _include_layout?(options)
+ (options.keys & [:text, :inline, :partial]).empty? || options.key?(:layout)
end
def _action_has_layout?
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 2ea4f02871..42f4939108 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -1,5 +1,4 @@
require "abstract_controller/base"
-require "active_support/core_ext/array/wrap"
module AbstractController
class DoubleRenderError < Error
@@ -10,30 +9,45 @@ module AbstractController
end
end
+ # This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
+ # it will trigger the lookup_context and consequently expire the cache.
+ # TODO Add some deprecation warnings to remove I18n.locale from controllers
+ class I18nProxy < ::I18n::Config #:nodoc:
+ attr_reader :i18n_config, :lookup_context
+
+ def initialize(i18n_config, lookup_context)
+ @i18n_config, @lookup_context = i18n_config, lookup_context
+ end
+
+ def locale
+ @i18n_config.locale
+ end
+
+ def locale=(value)
+ @i18n_config.locale = value
+ @lookup_context.update_details(:locale => @i18n_config.locale)
+ end
+ end
+
module Rendering
extend ActiveSupport::Concern
+ include AbstractController::ViewPaths
- included do
- class_attribute :_view_paths
- delegate :_view_paths, :to => :'self.class'
- self._view_paths = ActionView::PathSet.new
+ # Overwrite process to setup I18n proxy.
+ def process(*) #:nodoc:
+ old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
+ super
+ ensure
+ I18n.config = old_config
end
# 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
- # controller
- # View#render_partial[options]
- # - responsible for setting options[:_template]
- # - Returns String with the rendered partial
- # options<Hash>:: see _render_partial in ActionView::Base
- # View#render_template[template, layout, options, partial]
- # - Returns String with the rendered template
- # template<ActionView::Template>:: The template to render
- # layout<ActionView::Template>:: The layout to render around the template
- # options<Hash>:: See _render_template_with_layout in ActionView::Base
- # partial<Boolean>:: Whether or not the template to render is a partial
+ # View.for_controller[controller]
+ # Create a new ActionView instance for a controller
+ # View#render_template[options]
+ # Returns String with the rendered template
#
# Override this method in a module to change the default behavior.
def view_context
@@ -43,57 +57,29 @@ module AbstractController
# Mostly abstracts the fact that calling render twice is a DoubleRenderError.
# Delegates render_to_body and sticks the result in self.response_body.
def render(*args, &block)
- options = _normalize_options(*args, &block)
+ options = _normalize_args(*args, &block)
+ _normalize_options(options)
self.response_body = render_to_body(options)
end
# Raw rendering of a template to a Rack-compatible body.
- #
- # ==== Options
- # _partial_object<Object>:: The object that is being rendered. If this
- # exists, we are in the special case of rendering an object as a partial.
- #
# :api: plugin
def render_to_body(options = {})
- # TODO: Refactor so we can just use the normal template logic for this
- if options.key?(:partial)
- _render_partial(options)
- else
- _determine_template(options)
- _render_template(options)
- end
+ _process_options(options)
+ _render_template(options)
end
# Raw rendering of a template to a string. Just convert the results of
# render_to_body into a String.
- #
# :api: plugin
- def render_to_string(*args)
- options = _normalize_options(*args)
+ def render_to_string(options={})
+ _normalize_options(options)
AbstractController::Rendering.body_to_s(render_to_body(options))
end
- # Renders the template from an object.
- #
- # ==== Options
- # _template<ActionView::Template>:: The template to render
- # _layout<ActionView::Template>:: The layout to wrap the template in (optional)
+ # Find and renders a template based on the options given.
def _render_template(options)
- view_context.render_template(options)
- end
-
- # Renders the given partial.
- #
- # ==== Options
- # partial<String|Object>:: The partial name or the object to be rendered
- def _render_partial(options)
- view_context.render_partial(options)
- end
-
- # The list of view paths for this controller. See ActionView::ViewPathSet for
- # more details about writing custom view paths.
- def view_paths
- _view_paths
+ view_context.render_template(options) { |template| _with_template_hook(template) }
end
# The prefix used in render "foo" shortcuts.
@@ -115,126 +101,42 @@ module AbstractController
private
- # Normalize options, by converting render "foo" to render :template => "prefix/foo"
- # and render "/foo" to render :file => "/foo".
- def _normalize_options(action=nil, options={})
+ # Normalize options by converting render "foo" to render :action => "foo" and
+ # render "foo/bar" to render :file => "foo/bar".
+ def _normalize_args(action=nil, options={})
case action
+ when NilClass
when Hash
options, action = action, nil
when String, Symbol
action = action.to_s
- case action.index("/")
- when NilClass
- options[:_prefix] = _prefix
- options[:_template_name] = action
- when 0
- options[:file] = action
- else
- options[:template] = action
- end
+ key = action.include?(?/) ? :file : :action
+ options[key] = action
+ else
+ options.merge!(:partial => action)
end
options
end
- # Take in a set of options and determine the template to render
- #
- # ==== Options
- # _template<ActionView::Template>:: If this is provided, the search is over
- # _template_name<#to_s>:: The name of the template to look up. Otherwise,
- # use the current action name.
- # _prefix<String>:: The prefix to look inside of. In a file system, this corresponds
- # to a directory.
- # _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::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 template", handler, {})
- options[:_template] = template
- elsif options.key?(:template)
- options[:_template_name] = options[:template]
- elsif options.key?(:file)
- options[:_template_name] = options[:file]
+ def _normalize_options(options)
+ if options[:partial] == true
+ options[:partial] = action_name
end
- name = (options[:_template_name] || options[:action] || action_name).to_s
- options[:_prefix] ||= _prefix if (options.keys & [:partial, :file, :template]).empty?
- details = _normalize_details(options)
-
- options[:_template] ||= with_template_cache(name, details) do
- find_template(name, details, options)
+ if (options.keys & [:partial, :file, :template]).empty?
+ options[:prefix] ||= _prefix
end
- end
-
- def details_for_render
- { :formats => formats, :locale => [I18n.locale] }
- end
-
- def _normalize_details(options)
- details = details_for_render
- details[:formats] = Array(options[:format]) if options[:format]
- details[:locale] = Array(options[:locale]) if options[:locale]
- details
- end
-
- def find_template(name, details, options)
- view_paths.find(name, details, options[:_prefix], options[:_partial])
- end
-
- def template_exists?(name, details, options)
- view_paths.exists?(name, details, options[:_prefix], options[:_partial])
- end
- def with_template_cache(name, details)
- yield
+ options[:template] ||= (options[:action] || action_name).to_s
+ options
end
- def format_for_text
- Mime[:text]
+ def _process_options(options)
end
- 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
- # (see ActionView::ViewPathSet for more information)
- def append_view_path(path)
- self.view_paths = view_paths.dup + Array.wrap(path)
- end
-
- # 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
- # (see ActionView::ViewPathSet for more information)
- def prepend_view_path(path)
- clear_template_caches!
- self.view_paths = Array.wrap(path) + view_paths.dup
- end
-
- # A list of all of the default view paths for this controller.
- def view_paths
- _view_paths
- end
-
- # Set the view paths.
- #
- # ==== Parameters
- # paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
- # 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)
- _view_paths.freeze
- end
+ def _with_template_hook(template)
+ self.formats = template.formats
end
end
end
diff --git a/actionpack/lib/abstract_controller/view_paths.rb b/actionpack/lib/abstract_controller/view_paths.rb
new file mode 100644
index 0000000000..c2a9f6336d
--- /dev/null
+++ b/actionpack/lib/abstract_controller/view_paths.rb
@@ -0,0 +1,73 @@
+module AbstractController
+ module ViewPaths
+ extend ActiveSupport::Concern
+
+ included do
+ class_attribute :_view_paths
+ self._view_paths = ActionView::PathSet.new
+ end
+
+ delegate :template_exists?, :view_paths, :formats, :formats=,
+ :locale, :locale=, :to => :lookup_context
+
+ # LookupContext is the object responsible to hold all information required to lookup
+ # templates, i.e. view paths and details. Check ActionView::LookupContext for more
+ # information.
+ def lookup_context
+ @lookup_context ||= ActionView::LookupContext.new(self.class._view_paths, details_for_lookup)
+ end
+
+ def details_for_lookup
+ { }
+ end
+
+ def append_view_path(path)
+ lookup_context.view_paths.push(*path)
+ end
+
+ def prepend_view_path(path)
+ lookup_context.view_paths.unshift(*path)
+ end
+
+ def template_exists?(*args)
+ lookup_context.exists?(*args)
+ end
+
+ module ClassMethods
+ # 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
+ # (see ActionView::ViewPathSet for more information)
+ def append_view_path(path)
+ self.view_paths = view_paths.dup + Array(path)
+ end
+
+ # 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
+ # (see ActionView::ViewPathSet for more information)
+ def prepend_view_path(path)
+ self.view_paths = Array(path) + view_paths.dup
+ end
+
+ # A list of all of the default view paths for this controller.
+ def view_paths
+ _view_paths
+ end
+
+ # Set the view paths.
+ #
+ # ==== Parameters
+ # paths<ViewPathSet, Object>:: If a ViewPathSet is provided, use that;
+ # otherwise, process the parameter into a ViewPathSet.
+ def view_paths=(paths)
+ self._view_paths = paths.is_a?(ActionView::PathSet) ? paths : ActionView::Base.process_view_paths(paths)
+ self._view_paths.freeze
+ 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 759e52b135..536154fc6b 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -13,13 +13,13 @@ module ActionController
autoload_under "metal" do
autoload :Compatibility
autoload :ConditionalGet
- autoload :Configuration
autoload :Cookies
autoload :Flash
autoload :Head
autoload :Helpers
autoload :HideActions
autoload :HttpAuthentication
+ autoload :ImplicitRender
autoload :Instrumentation
autoload :MimeResponds
autoload :RackDelegation
@@ -46,7 +46,6 @@ module ActionController
eager_autoload do
autoload :RecordIdentifier
- autoload :UrlRewriter
# TODO: Don't autoload exceptions, setup explicit
# requires for files that need them
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 13139358c7..fcd3cb9bd3 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -15,7 +15,6 @@ module ActionController
include ActionController::Renderers::All
include ActionController::ConditionalGet
include ActionController::RackDelegation
- include ActionController::Configuration
# Legacy modules
include SessionManagement
@@ -30,35 +29,13 @@ module ActionController
include ActionController::Verification
include ActionController::RequestForgeryProtection
include ActionController::Streaming
+ include ActionController::RecordIdentifier
include ActionController::HttpAuthentication::Basic::ControllerMethods
include ActionController::HttpAuthentication::Digest::ControllerMethods
# Add instrumentations hooks at the bottom, to ensure they instrument
# all the methods properly.
include ActionController::Instrumentation
-
- # TODO: Extract into its own module
- # This should be moved together with other normalizing behavior
- module ImplicitRender
- def send_action(*)
- ret = super
- default_render unless response_body
- ret
- end
-
- def default_render
- render
- end
-
- def method_for_action(action_name)
- super || begin
- if view_paths.exists?(action_name.to_s, details_for_render, controller_path)
- "default_render"
- end
- end
- end
- end
-
include ImplicitRender
include ActionController::Rescue
@@ -82,5 +59,9 @@ module ActionController
filter
end
+ ActionController.run_base_hooks(self)
+
end
end
+
+require "action_controller/deprecated/base"
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index d784138ebe..b3fa129929 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -40,28 +40,34 @@ module ActionController #:nodoc:
autoload :Sweeping, 'action_controller/caching/sweeping'
end
- included do
- @@cache_store = nil
- cattr_reader :cache_store
-
- # Defines the storage option for cached fragments
- def self.cache_store=(store_option)
- @@cache_store = ActiveSupport::Cache.lookup_store(store_option)
+ module ConfigMethods
+ def cache_store
+ config.cache_store
end
- include Pages, Actions, Fragments
- include Sweeping if defined?(ActiveRecord)
+ def cache_store=(store)
+ config.cache_store = ActiveSupport::Cache.lookup_store(store)
+ end
- @@perform_caching = true
- cattr_accessor :perform_caching
- end
+ private
- module ClassMethods
def cache_configured?
perform_caching && cache_store
end
end
+ include ConfigMethods
+ include Pages, Actions, Fragments
+ include Sweeping if defined?(ActiveRecord)
+
+ included do
+ extend ConfigMethods
+
+ @@perform_caching = true
+ cattr_accessor :perform_caching
+ end
+
+
def caching_allowed?
request.get? && response.status == 200
end
@@ -75,10 +81,5 @@ module ActionController #:nodoc:
yield
end
end
-
- private
- def cache_configured?
- self.class.cache_configured?
- end
end
end
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index 5797eeebd6..fe95f0e0d7 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -1,5 +1,6 @@
require 'fileutils'
require 'uri'
+require 'active_support/core_ext/class/attribute_accessors'
module ActionController #:nodoc:
module Caching
@@ -34,27 +35,26 @@ module ActionController #:nodoc:
# Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be
# expired.
module Pages
- def self.included(base) #:nodoc:
- base.extend(ClassMethods)
- base.class_eval do
- @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
- ##
- # :singleton-method:
- # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
- # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
- # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
- # web server to look in the new location for cached files.
- cattr_accessor :page_cache_directory
-
- @@page_cache_extension = '.html'
- ##
- # :singleton-method:
- # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
- # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
- # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
- # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
- cattr_accessor :page_cache_extension
- end
+ extend ActiveSupport::Concern
+
+ included do
+ @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
+ ##
+ # :singleton-method:
+ # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>.
+ # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing
+ # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your
+ # web server to look in the new location for cached files.
+ cattr_accessor :page_cache_directory
+
+ @@page_cache_extension = '.html'
+ ##
+ # :singleton-method:
+ # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in
+ # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>.
+ # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an
+ # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps.
+ cattr_accessor :page_cache_extension
end
module ClassMethods
@@ -121,10 +121,10 @@ module ActionController #:nodoc:
if options.is_a?(Hash)
if options[:action].is_a?(Array)
options[:action].dup.each do |action|
- self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :action => action)))
+ self.class.expire_page(url_for(options.merge(:only_path => true, :action => action)))
end
else
- self.class.expire_page(url_for(options.merge(:only_path => true, :skip_relative_url_root => true)))
+ self.class.expire_page(url_for(options.merge(:only_path => true)))
end
else
self.class.expire_page(options)
@@ -139,7 +139,7 @@ module ActionController #:nodoc:
path = case options
when Hash
- url_for(options.merge(:only_path => true, :skip_relative_url_root => true, :format => params[:format]))
+ url_for(options.merge(:only_path => true, :format => params[:format]))
when String
options
else
diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb
index 871f41bfdd..cf16417e84 100644
--- a/actionpack/lib/action_controller/caching/sweeping.rb
+++ b/actionpack/lib/action_controller/caching/sweeping.rb
@@ -30,9 +30,7 @@ module ActionController #:nodoc:
# cache_sweeper OpenBar::Sweeper, :only => [ :edit, :destroy, :share ]
# end
module Sweeping
- def self.included(base) #:nodoc:
- base.extend(ClassMethods)
- end
+ extend ActiveSupport::Concern
module ClassMethods #:nodoc:
def cache_sweeper(*sweepers)
diff --git a/actionpack/lib/action_controller/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb
new file mode 100644
index 0000000000..1d05b3fbd6
--- /dev/null
+++ b/actionpack/lib/action_controller/deprecated/base.rb
@@ -0,0 +1,131 @@
+module ActionController
+ class Base
+ class << self
+ def deprecated_config_accessor(option, message = nil)
+ deprecated_config_reader(option, message)
+ deprecated_config_writer(option, message)
+ end
+
+ def deprecated_config_reader(option, message = nil)
+ message ||= "Reading #{option} directly from ActionController::Base is deprecated. " \
+ "Please read it from config.#{option}"
+
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{option}
+ ActiveSupport::Deprecation.warn #{message.inspect}, caller
+ config.#{option}
+ end
+ RUBY
+ end
+
+ def deprecated_config_writer(option, message = nil)
+ message ||= "Setting #{option} directly on ActionController::Base is deprecated. " \
+ "Please set it on config.action_controller.#{option}"
+
+ self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
+ def #{option}=(val)
+ ActiveSupport::Deprecation.warn #{message.inspect}, caller
+ config.#{option} = val
+ end
+ RUBY
+ end
+
+ def consider_all_requests_local
+ ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local is deprecated, " <<
+ "use Rails.application.config.consider_all_requests_local instead", caller
+ Rails.application.config.consider_all_requests_local
+ end
+
+ def consider_all_requests_local=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local= is deprecated. " <<
+ "Please configure it on your application with config.consider_all_requests_local=", caller
+ Rails.application.config.consider_all_requests_local = value
+ end
+
+ def allow_concurrency
+ ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency is deprecated, " <<
+ "use Rails.application.config.allow_concurrency instead", caller
+ Rails.application.config.allow_concurrency
+ end
+
+ def allow_concurrency=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency= is deprecated. " <<
+ "Please configure it on your application with config.allow_concurrency=", caller
+ Rails.application.config.allow_concurrency = value
+ end
+
+ def ip_spoofing_check=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check= is deprecated. " <<
+ "Please configure it on your application with config.action_dispatch.ip_spoofing_check=", caller
+ Rails.application.config.action_dispatch.ip_spoofing_check = value
+ end
+
+ def ip_spoofing_check
+ ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check is deprecated. " <<
+ "Configuring ip_spoofing_check on the application configures a middleware.", caller
+ Rails.application.config.action_disaptch.ip_spoofing_check
+ end
+
+ def trusted_proxies=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies= is deprecated. " <<
+ "Please configure it on your application with config.action_dispatch.trusted_proxies=", caller
+ Rails.application.config.action_dispatch.ip_spoofing_check = value
+ end
+
+ def trusted_proxies
+ ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies is deprecated. " <<
+ "Configuring trusted_proxies on the application configures a middleware.", caller
+ Rails.application.config.action_dispatch.ip_spoofing_check = value
+ end
+
+ def session(*args)
+ ActiveSupport::Deprecation.warn(
+ "Disabling sessions for a single controller has been deprecated. " +
+ "Sessions are now lazy loaded. So if you don't access them, " +
+ "consider them off. You can still modify the session cookie " +
+ "options with request.session_options.", caller)
+ end
+
+ def session=(value)
+ ActiveSupport::Deprecation.warn "ActionController::Base.session= is deprecated. " <<
+ "Please configure it on your application with config.session_store :cookie_store, :key => '....'", caller
+ if value.delete(:disabled)
+ Rails.application.config.session_store :disabled
+ else
+ store = Rails.application.config.session_store
+ Rails.application.config.session_store store, value
+ end
+ end
+
+ # Controls the resource action separator
+ def resource_action_separator
+ @resource_action_separator ||= "/"
+ end
+
+ def resource_action_separator=(val)
+ ActiveSupport::Deprecation.warn "ActionController::Base.resource_action_separator is deprecated and only " \
+ "works with the deprecated router DSL."
+ @resource_action_separator = val
+ end
+
+ def use_accept_header
+ ActiveSupport::Deprecation.warn "ActionController::Base.use_accept_header doesn't do anything anymore. " \
+ "The accept header is always taken into account."
+ end
+
+ def use_accept_header=(val)
+ use_accept_header
+ end
+ end
+
+ deprecated_config_writer :session_store
+ deprecated_config_writer :session_options
+ deprecated_config_accessor :relative_url_root, "relative_url_root is ineffective. Please stop using it"
+ deprecated_config_accessor :assets_dir
+ deprecated_config_accessor :javascripts_dir
+ deprecated_config_accessor :stylesheets_dir
+
+ delegate :consider_all_requests_local, :consider_all_requests_local=,
+ :allow_concurrency, :allow_concurrency=, :to => :"self.class"
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 4fd37e7f31..eebd2c943a 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -34,7 +34,8 @@ module ActionController
# and response object available. You might wish to control the
# environment and response manually for performance reasons.
- attr_internal :status, :headers, :content_type, :response
+ attr_internal :status, :headers, :content_type, :response, :request
+ delegate :session, :to => "@_request"
def initialize(*)
@_headers = {}
@@ -66,8 +67,9 @@ module ActionController
end
# :api: private
- def dispatch(name, env)
- @_env = env
+ def dispatch(name, request)
+ @_request = request
+ @_env = request.env
@_env['action_controller.instance'] = self
process(name)
to_a
@@ -78,31 +80,12 @@ module ActionController
response ? response.to_a : [status, headers, response_body]
end
- class ActionEndpoint
- @@endpoints = Hash.new {|h,k| h[k] = Hash.new {|sh,sk| sh[sk] = {} } }
-
- def self.for(controller, action, stack)
- @@endpoints[controller][action][stack] ||= begin
- endpoint = new(controller, action)
- stack.build(endpoint)
- end
- end
-
- def initialize(controller, action)
- @controller, @action = controller, action
- @_formats = [Mime::HTML]
- end
-
- def call(env)
- @controller.new.dispatch(@action, env)
- end
- end
-
class_attribute :middleware_stack
self.middleware_stack = ActionDispatch::MiddlewareStack.new
def self.inherited(base)
self.middleware_stack = base.middleware_stack.dup
+ super
end
def self.use(*args)
@@ -126,8 +109,10 @@ module ActionController
#
# ==== Returns
# Proc:: A rack application
- def self.action(name)
- ActionEndpoint.for(self, name, middleware_stack)
+ def self.action(name, klass = ActionDispatch::Request)
+ middleware_stack.build do |env|
+ new.dispatch(name, klass.new(env))
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index 2b1ada1426..ab8d87b2c4 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -2,21 +2,20 @@ module ActionController
module Compatibility
extend ActiveSupport::Concern
- include AbstractController::Compatibility
-
class ::ActionController::ActionControllerError < StandardError #:nodoc:
end
+ module ClassMethods
+ end
+
# Temporary hax
included do
::ActionController::UnknownAction = ::AbstractController::ActionNotFound
::ActionController::DoubleRenderError = ::AbstractController::DoubleRenderError
- cattr_accessor :session_options
- self.session_options = {}
-
- cattr_accessor :relative_url_root
- self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
+ # ROUTES TODO: This should be handled by a middleware and route generation
+ # should be able to handle SCRIPT_NAME
+ self.config.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
class << self
delegate :default_charset=, :to => "ActionDispatch::Response"
@@ -30,31 +29,17 @@ module ActionController
@before_filter_chain_aborted @_headers @_params
@_response)
- # Controls the resource action separator
- cattr_accessor :resource_action_separator
- self.resource_action_separator = "/"
-
- cattr_accessor :use_accept_header
- self.use_accept_header = true
+ def rescue_action(env)
+ raise env["action_dispatch.rescue.exception"]
+ end
self.page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : ""
-
- # Prepends all the URL-generating helpers from AssetHelper. This makes it possible to easily move javascripts, stylesheets,
- # and images to a dedicated asset server away from the main web server. Example:
- # ActionController::Base.asset_host = "http://assets.example.com"
- cattr_accessor :asset_host
-
- cattr_accessor :ip_spoofing_check
- self.ip_spoofing_check = true
-
- cattr_accessor :trusted_proxies
end
# For old tests
def initialize_template_class(*) end
def assign_shortcuts(*) end
- # TODO: Remove this after we flip
def template
@template ||= view_context
end
@@ -64,52 +49,20 @@ module ActionController
super
end
- module ClassMethods
- def consider_all_requests_local
- ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local is deprecated, " <<
- "use Rails.application.config.consider_all_requests_local instead"
- Rails.application.config.consider_all_requests_local
- end
-
- def consider_all_requests_local=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local= is no longer effective. " <<
- "Please configure it on your application with config.consider_all_requests_local="
- Rails.application.config.consider_all_requests_local = value
- end
-
- def allow_concurrency
- ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency is deprecated, " <<
- "use Rails.application.config.allow_concurrency instead"
- Rails.application.config.allow_concurrency
- end
-
- def allow_concurrency=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency= is no longer effective. " <<
- "Please configure it on your application with config.allow_concurrency="
- Rails.application.config.allow_concurrency = value
- end
-
- def rescue_action(env)
- raise env["action_dispatch.rescue.exception"]
- end
-
- # Defines the storage option for cached fragments
- def cache_store=(store_option)
- @@cache_store = ActiveSupport::Cache.lookup_store(store_option)
- end
- end
-
- delegate :consider_all_requests_local, :consider_all_requests_local=,
- :allow_concurrency, :allow_concurrency=, :to => :"self.class"
-
- def render_to_body(options)
- if options.is_a?(Hash) && options.key?(:template)
- options[:template].sub!(/^\//, '')
+ def _normalize_options(options)
+ if options[:action] && options[:action].to_s.include?(?/)
+ ActiveSupport::Deprecation.warn "Giving a path to render :action is deprecated. " <<
+ "Please use render :template instead", caller
+ options[:template] = options.delete(:action)
end
options[:text] = nil if options.delete(:nothing) == true
options[:text] = " " if options.key?(:text) && options[:text].nil?
+ super
+ end
+ def render_to_body(options)
+ options[:template].sub!(/^\//, '') if options.key?(:template)
super || " "
end
@@ -124,18 +77,5 @@ module ActionController
def performed?
response_body
end
-
- # ==== Request only view path switching ====
- def append_view_path(path)
- view_paths.push(*path)
- end
-
- def prepend_view_path(path)
- view_paths.unshift(*path)
- end
-
- def view_paths
- view_context.view_paths
- end
end
end
diff --git a/actionpack/lib/action_controller/metal/configuration.rb b/actionpack/lib/action_controller/metal/configuration.rb
deleted file mode 100644
index 5c829853b7..0000000000
--- a/actionpack/lib/action_controller/metal/configuration.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-module ActionController
- module Configuration
- extend ActiveSupport::Concern
-
- def config
- @config ||= self.class.config
- end
-
- def config=(config)
- @config = config
- end
-
- module ClassMethods
- def default_config
- @default_config ||= {}
- end
-
- def config
- self.config ||= default_config
- end
-
- def config=(config)
- @config = ActiveSupport::OrderedHash.new
- @config.merge!(config)
- end
- end
- end
-end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index 0f35a7c040..6ec788f302 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -124,7 +124,7 @@ module ActionController
end
def authenticate(request, &login_procedure)
- unless authorization(request).blank?
+ unless request.authorization.blank?
login_procedure.call(*user_name_and_password(request))
end
end
@@ -133,15 +133,8 @@ module ActionController
decode_credentials(request).split(/:/, 2)
end
- def authorization(request)
- request.env['HTTP_AUTHORIZATION'] ||
- request.env['X-HTTP_AUTHORIZATION'] ||
- request.env['X_HTTP_AUTHORIZATION'] ||
- request.env['REDIRECT_X_HTTP_AUTHORIZATION']
- end
-
def decode_credentials(request)
- ActiveSupport::Base64.decode64(authorization(request).split(' ', 2).last || '')
+ ActiveSupport::Base64.decode64(request.authorization.split(' ', 2).last || '')
end
def encode_credentials(user_name, password)
@@ -165,7 +158,7 @@ module ActionController
# Authenticate with HTTP Digest, returns true or false
def authenticate_with_http_digest(realm = "Application", &password_procedure)
- HttpAuthentication::Digest.authenticate(request, realm, &password_procedure)
+ HttpAuthentication::Digest.authenticate(config.secret, request, realm, &password_procedure)
end
# Render output including the HTTP Digest authentication header
@@ -175,30 +168,23 @@ module ActionController
end
# Returns false on a valid response, true otherwise
- def authenticate(request, realm, &password_procedure)
- authorization(request) && validate_digest_response(request, realm, &password_procedure)
- end
-
- def authorization(request)
- request.env['HTTP_AUTHORIZATION'] ||
- request.env['X-HTTP_AUTHORIZATION'] ||
- request.env['X_HTTP_AUTHORIZATION'] ||
- request.env['REDIRECT_X_HTTP_AUTHORIZATION']
+ def authenticate(secret_key, request, realm, &password_procedure)
+ request.authorization && validate_digest_response(secret_key, request, realm, &password_procedure)
end
# Returns false unless the request credentials response value matches the expected value.
# First try the password as a ha1 digest password. If this fails, then try it as a plain
# text password.
- def validate_digest_response(request, realm, &password_procedure)
+ def validate_digest_response(secret_key, request, realm, &password_procedure)
credentials = decode_credentials_header(request)
- valid_nonce = validate_nonce(request, credentials[:nonce])
+ valid_nonce = validate_nonce(secret_key, request, credentials[:nonce])
- if valid_nonce && realm == credentials[:realm] && opaque == credentials[:opaque]
+ if valid_nonce && realm == credentials[:realm] && opaque(secret_key) == credentials[:opaque]
password = password_procedure.call(credentials[:username])
return false unless password
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
- uri = credentials[:uri][0,1] == '/' ? request.request_uri : request.url
+ uri = credentials[:uri][0,1] == '/' ? request.fullpath : request.url
[true, false].any? do |password_is_ha1|
expected = expected_response(method, uri, credentials, password, password_is_ha1)
@@ -226,7 +212,7 @@ module ActionController
end
def decode_credentials_header(request)
- decode_credentials(authorization(request))
+ decode_credentials(request.authorization)
end
def decode_credentials(header)
@@ -238,6 +224,9 @@ module ActionController
end
def authentication_header(controller, realm)
+ secret_key = controller.config.secret
+ nonce = self.nonce(secret_key)
+ opaque = opaque(secret_key)
controller.headers["WWW-Authenticate"] = %(Digest realm="#{realm}", qop="auth", algorithm=MD5, nonce="#{nonce}", opaque="#{opaque}")
end
@@ -280,7 +269,7 @@ module ActionController
# The nonce is opaque to the client. Composed of Time, and hash of Time with secret
# key from the Rails session secret generated upon creation of project. Ensures
# the time cannot be modified by client.
- def nonce(time = Time.now)
+ def nonce(secret_key, time = Time.now)
t = time.to_i
hashed = [t, secret_key]
digest = ::Digest::MD5.hexdigest(hashed.join(":"))
@@ -292,21 +281,16 @@ module ActionController
# Can be much shorter if the Stale directive is implemented. This would
# allow a user to use new nonce without prompting user again for their
# username and password.
- def validate_nonce(request, value, seconds_to_timeout=5*60)
+ def validate_nonce(secret_key, request, value, seconds_to_timeout=5*60)
t = ActiveSupport::Base64.decode64(value).split(":").first.to_i
- nonce(t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
+ nonce(secret_key, t) == value && (t - Time.now.to_i).abs <= seconds_to_timeout
end
# Opaque based on random generation - but changing each request?
- def opaque()
+ def opaque(secret_key)
::Digest::MD5.hexdigest(secret_key)
end
- # Set in /initializers/session_store.rb, and loaded even if sessions are not in use.
- def secret_key
- ActionController::Base.session_options[:secret]
- end
-
end
end
end
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
new file mode 100644
index 0000000000..282dcf66b3
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -0,0 +1,21 @@
+module ActionController
+ module ImplicitRender
+ def send_action(*)
+ ret = super
+ default_render unless response_body
+ ret
+ end
+
+ def default_render
+ render
+ end
+
+ def method_for_action(action_name)
+ super || begin
+ if template_exists?(action_name.to_s, _prefix)
+ "default_render"
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index 85035dc09c..d69de65f28 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -20,7 +20,7 @@ module ActionController
:params => request.filtered_parameters,
:formats => request.formats.map(&:to_sym),
:method => request.method,
- :path => (request.request_uri rescue "unknown")
+ :path => (request.fullpath rescue "unknown")
}
ActiveSupport::Notifications.instrument("action_controller.start_processing", raw_payload.dup)
diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb
index bb55383631..37106733cb 100644
--- a/actionpack/lib/action_controller/metal/rack_delegation.rb
+++ b/actionpack/lib/action_controller/metal/rack_delegation.rb
@@ -6,14 +6,11 @@ module ActionController
extend ActiveSupport::Concern
included do
- delegate :session, :to => "@_request"
delegate :headers, :status=, :location=, :content_type=,
:status, :location, :content_type, :to => "@_response"
- attr_internal :request
end
- def dispatch(action, env)
- @_request = ActionDispatch::Request.new(env)
+ def dispatch(action, request)
@_response = ActionDispatch::Response.new
@_response.request = request
super
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 00a09309bf..f892bd9b91 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -4,44 +4,27 @@ module ActionController
include ActionController::RackDelegation
include AbstractController::Rendering
- include AbstractController::DetailsCache
- def process_action(*)
- self.formats = request.formats.map {|x| x.to_sym }
+ def process(*)
+ self.formats = request.formats.map { |x| x.to_sym }
super
end
def render(*args)
raise ::AbstractController::DoubleRenderError if response_body
- args << {} unless args.last.is_a?(Hash)
- super(*args)
- self.content_type ||= args.last[:_template].mime_type.to_s
+ super
response_body
end
private
- def _render_partial(options)
- options[:partial] = action_name if options[:partial] == true
- options[:_details] = details_for_render
- super
- end
-
- def format_for_text
- formats.first
+ def _normalize_args(action=nil, options={}, &blk)
+ options = super
+ options[:update] = blk if block_given?
+ options
end
- def _normalize_options(action=nil, options={}, &blk)
- case action
- when NilClass
- when Hash
- options = super(action.delete(:action), action)
- when String, Symbol
- options = super
- else
- options.merge! :partial => action
- end
-
+ def _normalize_options(options)
if options.key?(:text) && options[:text].respond_to?(:to_text)
options[:text] = options[:text].to_text
end
@@ -50,17 +33,23 @@ module ActionController
options[:status] = Rack::Utils.status_code(options[:status])
end
- options[:update] = blk if block_given?
-
- _process_options(options)
- options
+ super
end
def _process_options(options)
status, content_type, location = options.values_at(:status, :content_type, :location)
+
self.status = status if status
self.content_type = content_type if content_type
self.headers["Location"] = url_for(location) if location
+
+ super
end
+
+ def _with_template_hook(template)
+ super
+ self.content_type ||= template.mime_type.to_s
+ end
+
end
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index 276c703307..6765314df2 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -12,11 +12,10 @@ module ActionController #:nodoc:
included do
# Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
# sets it to <tt>:authenticity_token</tt> by default.
- cattr_accessor :request_forgery_protection_token
+ config.request_forgery_protection_token ||= true
# Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
- class_attribute :allow_forgery_protection
- self.allow_forgery_protection = true
+ config.allow_forgery_protection ||= true
helper_method :form_authenticity_token
helper_method :protect_against_forgery?
@@ -80,9 +79,47 @@ module ActionController #:nodoc:
self.request_forgery_protection_token ||= :authenticity_token
before_filter :verify_authenticity_token, options
end
+
+ def request_forgery_protection_token
+ config.request_forgery_protection_token
+ end
+
+ def request_forgery_protection_token=(val)
+ config.request_forgery_protection_token = val
+ end
+
+ def allow_forgery_protection
+ config.allow_forgery_protection
+ end
+
+ def allow_forgery_protection=(val)
+ config.allow_forgery_protection = val
+ end
end
protected
+
+ def protect_from_forgery(options = {})
+ self.request_forgery_protection_token ||= :authenticity_token
+ before_filter :verify_authenticity_token, options
+ end
+
+ def request_forgery_protection_token
+ config.request_forgery_protection_token
+ end
+
+ def request_forgery_protection_token=(val)
+ config.request_forgery_protection_token = val
+ end
+
+ def allow_forgery_protection
+ config.allow_forgery_protection
+ end
+
+ def allow_forgery_protection=(val)
+ config.allow_forgery_protection = val
+ end
+
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
verified_request? || raise(ActionController::InvalidAuthenticityToken)
@@ -109,7 +146,7 @@ module ActionController #:nodoc:
end
def protect_against_forgery?
- self.class.allow_forgery_protection
+ config.allow_forgery_protection
end
end
end
diff --git a/actionpack/lib/action_controller/metal/session_management.rb b/actionpack/lib/action_controller/metal/session_management.rb
index d70f40ce7a..91d89ff9a4 100644
--- a/actionpack/lib/action_controller/metal/session_management.rb
+++ b/actionpack/lib/action_controller/metal/session_management.rb
@@ -2,44 +2,8 @@ module ActionController #:nodoc:
module SessionManagement #:nodoc:
extend ActiveSupport::Concern
- include ActionController::Configuration
-
module ClassMethods
- # Set the session store to be used for keeping the session data between requests.
- # By default, sessions are stored in browser cookies (<tt>:cookie_store</tt>),
- # but you can also specify one of the other included stores (<tt>:active_record_store</tt>,
- # <tt>:mem_cache_store</tt>, or your own custom class.
- def session_store=(store)
- if store == :active_record_store
- self.session_store = ActiveRecord::SessionStore
- else
- @@session_store = store.is_a?(Symbol) ?
- ActionDispatch::Session.const_get(store.to_s.camelize) :
- store
- end
- end
-
- # Returns the session store class currently used.
- def session_store
- if defined? @@session_store
- @@session_store
- else
- ActionDispatch::Session::CookieStore
- end
- end
-
- def session=(options = {})
- self.session_store = nil if options.delete(:disabled)
- session_options.merge!(options)
- end
- def session(*args)
- ActiveSupport::Deprecation.warn(
- "Disabling sessions for a single controller has been deprecated. " +
- "Sessions are now lazy loaded. So if you don't access them, " +
- "consider them off. You can still modify the session cookie " +
- "options with request.session_options.", caller)
- end
end
end
end
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 8a06f34d23..10c7ca9021 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -3,14 +3,13 @@ module ActionController
extend ActiveSupport::Concern
include ActionDispatch::Routing::UrlFor
- include ActionController::RackDelegation
def url_options
super.reverse_merge(
:host => request.host_with_port,
:protocol => request.protocol,
:_path_segments => request.symbolized_path_parameters
- )
+ ).merge(:script_name => request.script_name)
end
def _router
diff --git a/actionpack/lib/action_controller/middleware.rb b/actionpack/lib/action_controller/middleware.rb
index 17275793b7..2115b07b3e 100644
--- a/actionpack/lib/action_controller/middleware.rb
+++ b/actionpack/lib/action_controller/middleware.rb
@@ -6,7 +6,8 @@ module ActionController
end
def call(env)
- @controller.build(@app).dispatch(:index, env)
+ request = ActionDispatch::Request.new(env)
+ @controller.build(@app).dispatch(:index, request)
end
end
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 07c6b8f2b6..6a3afbb157 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -3,6 +3,7 @@ require "action_controller"
require "action_view/railtie"
require "active_support/core_ext/class/subclasses"
require "active_support/deprecation/proxy_wrappers"
+require "active_support/deprecation"
module ActionController
class Railtie < Rails::Railtie
@@ -11,28 +12,63 @@ module ActionController
require "action_controller/railties/log_subscriber"
require "action_controller/railties/url_helpers"
+ ad = config.action_dispatch
+ config.action_controller.singleton_class.send(:define_method, :session) do
+ ActiveSupport::Deprecation.warn "config.action_controller.session has been " \
+ "renamed to config.action_dispatch.session.", caller
+ ad.session
+ end
+
+ config.action_controller.singleton_class.send(:define_method, :session=) do |val|
+ ActiveSupport::Deprecation.warn "config.action_controller.session has been " \
+ "renamed to config.action_dispatch.session.", caller
+ ad.session = val
+ end
+
+ config.action_controller.singleton_class.send(:define_method, :session_store) do
+ ActiveSupport::Deprecation.warn "config.action_controller.session_store has been " \
+ "renamed to config.action_dispatch.session_store.", caller
+ ad.session_store
+ end
+
+ config.action_controller.singleton_class.send(:define_method, :session_store=) do |val|
+ ActiveSupport::Deprecation.warn "config.action_controller.session_store has been " \
+ "renamed to config.action_dispatch.session_store.", caller
+ ad.session_store = val
+ end
+
log_subscriber ActionController::Railties::LogSubscriber.new
initializer "action_controller.logger" do
- ActionController::Base.logger ||= Rails.logger
+ ActionController.base_hook { self.logger ||= Rails.logger }
end
initializer "action_controller.set_configs" do |app|
- app.config.action_controller.each do |k,v|
- ActionController::Base.send "#{k}=", v
- end
+ paths = app.config.paths
+ ac = app.config.action_controller
+
+ ac.assets_dir = paths.public.to_a.first
+ ac.javascripts_dir = paths.public.javascripts.to_a.first
+ ac.stylesheets_dir = paths.public.stylesheets.to_a.first
+ ac.secret = app.config.cookie_secret
+
+ ActionController.base_hook { self.config.replace(ac) }
end
initializer "action_controller.initialize_framework_caches" do
- ActionController::Base.cache_store ||= RAILS_CACHE
+ ActionController.base_hook { self.cache_store ||= RAILS_CACHE }
end
initializer "action_controller.set_helpers_path" do |app|
- ActionController::Base.helpers_path = app.config.paths.app.helpers.to_a
+ ActionController.base_hook do
+ self.helpers_path = app.config.paths.app.helpers.to_a
+ end
end
initializer "action_controller.url_helpers" do |app|
- ActionController::Base.extend ::ActionController::Railtie::UrlHelpers.with(app.routes)
+ ActionController.base_hook do
+ extend ::ActionController::Railtie::UrlHelpers.with(app.routes)
+ end
message = "ActionController::Routing::Routes is deprecated. " \
"Instead, use Rails.application.routes"
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 64d9bdab2a..cdb5db32aa 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -322,6 +322,8 @@ module ActionController
@controller ||= klass.new rescue nil
end
+ @request.env.delete('PATH_INFO')
+
if @controller
@controller.request = @request
@controller.params = {}
@@ -335,13 +337,20 @@ module ActionController
private
def build_request_uri(action, parameters)
- unless @request.env['REQUEST_URI']
+ unless @request.env["PATH_INFO"]
options = @controller.__send__(:url_options).merge(parameters)
- options.update(:only_path => true, :action => action)
+ options.update(
+ :only_path => true,
+ :action => action,
+ :relative_url_root => nil,
+ :_path_segments => @request.symbolized_path_parameters)
+
+ url, query_string = @router.url_for(options).split("?", 2)
- url = ActionController::UrlRewriter.new(@request, parameters)
- @request.request_uri = url.rewrite(@router, options)
+ @request.env["SCRIPT_NAME"] = @controller.config.relative_url_root
+ @request.env["PATH_INFO"] = url
+ @request.env["QUERY_STRING"] = query_string || ""
end
end
- end
+ end
end
diff --git a/actionpack/lib/action_controller/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb
deleted file mode 100644
index 807b21cd0e..0000000000
--- a/actionpack/lib/action_controller/url_rewriter.rb
+++ /dev/null
@@ -1,98 +0,0 @@
-require 'active_support/core_ext/hash/except'
-
-module ActionController
- # Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
- class UrlRewriter #:nodoc:
- RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
-
- def initialize(request, parameters)
- @request, @parameters = request, parameters
- end
-
- def rewrite(router, options = {})
- options[:host] ||= @request.host_with_port
- options[:protocol] ||= @request.protocol
-
- self.class.rewrite(router, options, @request.symbolized_path_parameters) do |options|
- process_path_options(options)
- end
- end
-
- def to_str
- "#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@parameters[:controller]}, #{@parameters[:action]}, #{@request.parameters.inspect}"
- end
-
- alias_method :to_s, :to_str
-
- # ROUTES TODO: Class method code smell
- def self.rewrite(router, options, path_segments=nil)
- handle_positional_args(options)
-
- rewritten_url = ""
-
- # ROUTES TODO: Fix the tests
- segments = options.delete(:_path_segments)
- path_segments = path_segments ? path_segments.merge(segments || {}) : segments
-
- unless options[:only_path]
- rewritten_url << (options[:protocol] || "http")
- rewritten_url << "://" unless rewritten_url.match("://")
- rewritten_url << rewrite_authentication(options)
-
- raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
-
- rewritten_url << options[:host]
- rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
- end
-
- path_options = options.except(*RESERVED_OPTIONS)
- path_options = yield(path_options) if block_given?
- path = router.generate(path_options, path_segments || {})
-
- rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
- rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
- rewritten_url << "##{Rack::Utils.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
-
- rewritten_url
- end
-
- protected
-
- def self.handle_positional_args(options)
- return unless args = options.delete(:_positional_args)
-
- keys = options.delete(:_positional_keys)
- keys -= options.keys if args.size < keys.size - 1 # take format into account
-
- args = args.zip(keys).inject({}) do |h, (v, k)|
- h[k] = v
- h
- end
-
- # Tell url_for to skip default_url_options
- options.merge!(args)
- end
-
- def self.rewrite_authentication(options)
- if options[:user] && options[:password]
- "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
- else
- ""
- end
- end
-
- # Given a Hash of options, generates a route
- def process_path_options(options)
- options = options.symbolize_keys
- options.update(options[:params].symbolize_keys) if options[:params]
-
- if (overwrite = options.delete(:overwrite_params))
- options.update(@parameters.symbolize_keys)
- options.update(overwrite.symbolize_keys)
- end
-
- options
- end
-
- end
-end
diff --git a/actionpack/lib/action_dispatch.rb b/actionpack/lib/action_dispatch.rb
index 479ea959e6..dfb8919561 100644
--- a/actionpack/lib/action_dispatch.rb
+++ b/actionpack/lib/action_dispatch.rb
@@ -48,6 +48,7 @@ module ActionDispatch
autoload :Flash
autoload :Head
autoload :ParamsParser
+ autoload :RemoteIp
autoload :Rescue
autoload :ShowExceptions
autoload :Static
diff --git a/actionpack/lib/action_dispatch/http/mime_negotiation.rb b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
index 40617e239a..fec250e928 100644
--- a/actionpack/lib/action_dispatch/http/mime_negotiation.rb
+++ b/actionpack/lib/action_dispatch/http/mime_negotiation.rb
@@ -67,21 +67,6 @@ module ActionDispatch
@env["action_dispatch.request.formats"] = [Mime::Type.lookup_by_extension(parameters[:format])]
end
- # Returns a symbolized version of the <tt>:format</tt> parameter of the request.
- # If no \format is given it returns <tt>:js</tt>for Ajax requests and <tt>:html</tt>
- # otherwise.
- def template_format
- parameter_format = parameters[:format]
-
- if parameter_format
- parameter_format
- elsif xhr?
- :js
- else
- :html
- end
- 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/request.rb b/actionpack/lib/action_dispatch/http/request.rb
index 7a17023ed2..ea9f0f99c2 100755
--- a/actionpack/lib/action_dispatch/http/request.rb
+++ b/actionpack/lib/action_dispatch/http/request.rb
@@ -30,6 +30,14 @@ module ActionDispatch
METHOD
end
+ def self.new(env)
+ if request = env["action_dispatch.request"] && request.instance_of?(self)
+ return request
+ end
+
+ super
+ end
+
def key?(key)
@env.key?(key)
end
@@ -119,36 +127,7 @@ module ActionDispatch
# delimited list in the case of multiple chained proxies; the last
# address which is not trusted is the originating IP.
def remote_ip
- remote_addr_list = @env['REMOTE_ADDR'] && @env['REMOTE_ADDR'].scan(/[^,\s]+/)
-
- unless remote_addr_list.blank?
- not_trusted_addrs = remote_addr_list.reject {|addr| addr =~ TRUSTED_PROXIES || addr =~ ActionController::Base.trusted_proxies}
- return not_trusted_addrs.first unless not_trusted_addrs.empty?
- end
- remote_ips = @env['HTTP_X_FORWARDED_FOR'] && @env['HTTP_X_FORWARDED_FOR'].split(',')
-
- if @env.include? 'HTTP_CLIENT_IP'
- if ActionController::Base.ip_spoofing_check && remote_ips && !remote_ips.include?(@env['HTTP_CLIENT_IP'])
- # We don't know which came from the proxy, and which from the user
- raise ActionController::ActionControllerError.new <<EOM
-IP spoofing attack?!
-HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}
-HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}
-EOM
- end
-
- return @env['HTTP_CLIENT_IP']
- end
-
- if remote_ips
- while remote_ips.size > 1 && (TRUSTED_PROXIES =~ remote_ips.last.strip || ActionController::Base.trusted_proxies =~ remote_ips.last.strip)
- remote_ips.pop
- end
-
- return remote_ips.last.strip
- end
-
- @env['REMOTE_ADDR']
+ (@env["action_dispatch.remote_ip"] || ip).to_s
end
# Returns the lowercase name of the HTTP server software.
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 42ad19044d..b64a83c62e 100644
--- a/actionpack/lib/action_dispatch/http/url.rb
+++ b/actionpack/lib/action_dispatch/http/url.rb
@@ -3,7 +3,7 @@ module ActionDispatch
module URL
# Returns the complete URL used for this request.
def url
- protocol + host_with_port + request_uri
+ protocol + host_with_port + fullpath
end
# Returns 'https://' if this is an SSL request and 'http://' otherwise.
@@ -85,42 +85,11 @@ module ActionDispatch
subdomains(tld_length).join('.')
end
- # Returns the query string, accounting for server idiosyncrasies.
- def query_string
- @env['QUERY_STRING'].present? ? @env['QUERY_STRING'] : (@env['REQUEST_URI'].to_s.split('?', 2)[1] || '')
- end
-
# Returns the request URI, accounting for server idiosyncrasies.
# WEBrick includes the full URL. IIS leaves REQUEST_URI blank.
def request_uri
- if uri = @env['REQUEST_URI']
- # Remove domain, which webrick puts into the request_uri.
- (%r{^\w+\://[^/]+(/.*|$)$} =~ uri) ? $1 : uri
- else
- # Construct IIS missing REQUEST_URI from SCRIPT_NAME and PATH_INFO.
- uri = @env['PATH_INFO'].to_s
-
- if script_filename = @env['SCRIPT_NAME'].to_s.match(%r{[^/]+$})
- uri = uri.sub(/#{script_filename}\//, '')
- end
-
- env_qs = @env['QUERY_STRING'].to_s
- uri += "?#{env_qs}" unless env_qs.empty?
-
- if uri.blank?
- @env.delete('REQUEST_URI')
- else
- @env['REQUEST_URI'] = uri
- end
- end
- end
-
- # Returns the interpreted \path to requested resource after all the installation
- # directory of this application was taken into account.
- def path
- path = request_uri.to_s[/\A[^\?]*/]
- path.sub!(/\A#{ActionController::Base.relative_url_root}/, '')
- path
+ ActiveSupport::Deprecation.warn "Using #request_uri is deprecated. Use fullpath instead.", caller
+ fullpath
end
private
diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb
index 522982e202..f4c4324fb0 100644
--- a/actionpack/lib/action_dispatch/middleware/params_parser.rb
+++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb
@@ -1,4 +1,3 @@
-require 'active_support/json'
require 'action_dispatch/http/request'
module ActionDispatch
diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
new file mode 100644
index 0000000000..c7d710b98e
--- /dev/null
+++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb
@@ -0,0 +1,51 @@
+module ActionDispatch
+ class RemoteIp
+ class IpSpoofAttackError < StandardError ; end
+
+ class RemoteIpGetter
+ def initialize(env, check_ip_spoofing, trusted_proxies)
+ @env = env
+ @check_ip_spoofing = check_ip_spoofing
+ @trusted_proxies = trusted_proxies
+ end
+
+ def remote_addrs
+ @remote_addrs ||= begin
+ list = @env['REMOTE_ADDR'] ? @env['REMOTE_ADDR'].split(/[,\s]+/) : []
+ list.reject { |addr| addr =~ @trusted_proxies }
+ end
+ end
+
+ def to_s
+ return remote_addrs.first if remote_addrs.any?
+
+ forwarded_ips = @env['HTTP_X_FORWARDED_FOR'] ? @env['HTTP_X_FORWARDED_FOR'].strip.split(/[,\s]+/) : []
+
+ if client_ip = @env['HTTP_CLIENT_IP']
+ if @check_ip_spoofing && !forwarded_ips.include?(client_ip)
+ # We don't know which came from the proxy, and which from the user
+ raise IpSpoofAttackError, "IP spoofing attack?!" \
+ "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \
+ "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}"
+ end
+ return client_ip
+ end
+
+ return forwarded_ips.reject { |ip| ip =~ @trusted_proxies }.last || @env["REMOTE_ADDR"]
+ end
+ end
+
+ def initialize(app, check_ip_spoofing = true, trusted_proxies = nil)
+ @app = app
+ @check_ip_spoofing = check_ip_spoofing
+ regex = '(^127\.0\.0\.1$|^(10|172\.(1[6-9]|2[0-9]|30|31)|192\.168)\.)'
+ regex << "|(#{trusted_proxies})" if trusted_proxies
+ @trusted_proxies = Regexp.new(regex, "i")
+ end
+
+ def call(env)
+ env["action_dispatch.remote_ip"] = RemoteIpGetter.new(env, @check_ip_spoofing, @trusted_proxies)
+ @app.call(env)
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
index 04a101dbb2..22da82479e 100644
--- a/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
+++ b/actionpack/lib/action_dispatch/middleware/session/cookie_store.rb
@@ -1,5 +1,4 @@
require 'active_support/core_ext/hash/keys'
-require 'rack/request'
module ActionDispatch
module Session
@@ -177,9 +176,8 @@ module ActionDispatch
if key.blank?
raise ArgumentError, 'A key is required to write a ' +
'cookie containing the session data. Use ' +
- 'config.action_controller.session = { :key => ' +
- '"_myapp_session", :secret => "some secret phrase" } in ' +
- 'config/application.rb'
+ 'config.action_controller.session_store :cookie_store, { :key => ' +
+ '"_myapp_session" } in config/application.rb'
end
end
@@ -193,10 +191,9 @@ module ActionDispatch
if secret.blank?
raise ArgumentError, "A secret is required to generate an " +
"integrity hash for cookie session data. Use " +
- "config.action_controller.session = { :key => " +
- "\"_myapp_session\", :secret => \"some secret phrase of at " +
- "least #{SECRET_MIN_LENGTH} characters\" } " +
- "in config/environment.rb"
+ "config.cookie_secret = \"some secret phrase of at " +
+ "least #{SECRET_MIN_LENGTH} characters\"" +
+ "in config/application.rb"
end
if secret.length < SECRET_MIN_LENGTH
diff --git a/actionpack/lib/action_dispatch/middleware/stack.rb b/actionpack/lib/action_dispatch/middleware/stack.rb
index 18a2922fa7..5c5362ce4a 100644
--- a/actionpack/lib/action_dispatch/middleware/stack.rb
+++ b/actionpack/lib/action_dispatch/middleware/stack.rb
@@ -58,7 +58,7 @@ module ActionDispatch
if lazy_compare?(@klass) && lazy_compare?(middleware)
normalize(@klass) == normalize(middleware)
else
- klass == ActiveSupport::Inflector.constantize(middleware.to_s)
+ klass.name == middleware.to_s
end
end
end
@@ -122,7 +122,11 @@ module ActionDispatch
find_all { |middleware| middleware.active? }
end
- def build(app)
+ def build(app = nil, &blk)
+ app ||= blk
+
+ raise "MiddlewareStack#build requires an app" unless app
+
active.reverse.inject(app) { |a, e| e.build(a) }
end
end
diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb
index 79e9464337..e486bd4079 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -6,6 +6,7 @@ module ActionDispatch
railtie_name :action_dispatch
config.action_dispatch.x_sendfile_header = "X-Sendfile"
+ config.action_dispatch.ip_spoofing_check = true
# Prepare dispatcher callbacks and run 'prepare' callbacks
initializer "action_dispatch.prepare_dispatcher" do |app|
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 52e7b0e77d..0b7b09ee7a 100644
--- a/actionpack/lib/action_dispatch/routing/mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,3 +1,5 @@
+require 'active_support/core_ext/hash/except'
+
module ActionDispatch
module Routing
class Mapper
@@ -36,7 +38,7 @@ module ActionDispatch
end
def to_route
- [ app, conditions, requirements, defaults, @options[:as] ]
+ [ app, conditions, requirements, defaults, @options[:as], @options[:anchor] ]
end
private
@@ -64,7 +66,7 @@ module ActionDispatch
# match "account/overview"
def using_match_shorthand?(args, options)
- args.present? && options.except(:via).empty? && !args.first.include?(':')
+ args.present? && options.except(:via, :anchor).empty? && !args.first.include?(':')
end
def normalize_path(path)
@@ -85,7 +87,7 @@ module ActionDispatch
end
def requirements
- @requirements ||= returning(@options[:constraints] || {}) do |requirements|
+ @requirements ||= (@options[:constraints] || {}).tap do |requirements|
requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints]
@options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) }
end
@@ -174,9 +176,15 @@ module ActionDispatch
end
def match(*args)
- @set.add_route(*Mapping.new(@set, @scope, args).to_route)
+ mapping = Mapping.new(@set, @scope, args).to_route
+ @set.add_route(*mapping)
self
end
+
+ def default_url_options=(options)
+ @set.default_url_options = options
+ end
+ alias_method :default_url_options, :default_url_options=
end
module HttpHelpers
@@ -293,6 +301,7 @@ module ActionDispatch
options = args.extract_options!
options = (@scope[:options] || {}).merge(options)
+ options[:anchor] = true unless options.key?(:anchor)
if @scope[:name_prefix] && !options[:as].blank?
options[:as] = "#{@scope[:name_prefix]}_#{options[:as]}"
@@ -450,7 +459,10 @@ module ActionDispatch
scope(:path => resource.name.to_s, :controller => resource.controller) do
with_scope_level(:resource, resource) do
- yield if block_given?
+
+ scope(:name_prefix => resource.name.to_s) do
+ yield if block_given?
+ end
get :show if resource.actions.include?(:show)
post :create if resource.actions.include?(:create)
@@ -533,6 +545,21 @@ module ActionDispatch
end
end
+ def mount(app, options = nil)
+ if options
+ path = options.delete(:at)
+ else
+ options = app
+ app, path = options.find { |k, v| k.respond_to?(:call) }
+ options.delete(app) if app
+ end
+
+ raise "A rack application must be specified" unless path
+
+ match(path, options.merge(:to => app, :anchor => false))
+ self
+ end
+
def match(*args)
options = args.extract_options!
diff --git a/actionpack/lib/action_dispatch/routing/route.rb b/actionpack/lib/action_dispatch/routing/route.rb
index e6e44d3546..6f37eadd6b 100644
--- a/actionpack/lib/action_dispatch/routing/route.rb
+++ b/actionpack/lib/action_dispatch/routing/route.rb
@@ -4,7 +4,7 @@ module ActionDispatch
attr_reader :app, :conditions, :defaults, :name
attr_reader :path, :requirements
- def initialize(app, conditions = {}, requirements = {}, defaults = {}, name = nil)
+ def initialize(app, conditions, requirements, defaults, name, anchor)
@app = app
@defaults = defaults
@name = name
@@ -17,7 +17,7 @@ module ActionDispatch
if path = conditions[:path_info]
@path = path
- conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS)
+ conditions[:path_info] = ::Rack::Mount::Strexp.compile(path, requirements, SEPARATORS, anchor)
end
@conditions = conditions.inject({}) { |h, (k, v)|
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 99436e3cb0..722be432c7 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -168,31 +168,6 @@ module ActionDispatch
selector = url_helper_name(name, kind)
hash_access_method = hash_access_name(name, kind)
- # We use module_eval to avoid leaks.
- #
- # def users_url(*args)
- # if args.empty? || Hash === args.first
- # options = hash_for_users_url(args.first || {})
- # else
- # options = hash_for_users_url(args.extract_options!)
- # default = default_url_options(options) if self.respond_to?(:default_url_options, true)
- # options = (default ||= {}).merge(options)
- #
- # keys = []
- # keys -= options.keys if args.size < keys.size - 1
- #
- # args = args.zip(keys).inject({}) do |h, (v, k)|
- # h[k] = v
- # h
- # end
- #
- # # Tell url_for to skip default_url_options
- # options[:use_defaults] = false
- # options.merge!(args)
- # end
- #
- # url_for(options)
- # end
@module.module_eval <<-END_EVAL, __FILE__, __LINE__ + 1
def #{selector}(*args)
options = #{hash_access_method}(args.extract_options!)
@@ -211,6 +186,7 @@ module ActionDispatch
attr_accessor :routes, :named_routes
attr_accessor :disable_clear_and_finalize, :resources_path_names
+ attr_accessor :default_url_options
def self.default_resources_path_names
{ :new => 'new', :edit => 'edit' }
@@ -221,8 +197,10 @@ module ActionDispatch
self.named_routes = NamedRouteCollection.new
self.resources_path_names = self.class.default_resources_path_names.dup
self.controller_namespaces = Set.new
+ self.default_url_options = {}
@disable_clear_and_finalize = false
+ clear!
end
def draw(&block)
@@ -259,31 +237,22 @@ module ActionDispatch
named_routes.install(destinations, regenerate_code)
end
- def url_for
- @url_for ||= begin
- router = self
- Module.new do
- extend ActiveSupport::Concern
- include UrlFor
-
- define_method(:_router) { router }
- end
- end
- end
-
def url_helpers
@url_helpers ||= begin
router = self
Module.new do
extend ActiveSupport::Concern
- include router.url_for
+ include UrlFor
# ROUTES TODO: install_helpers isn't great... can we make a module with the stuff that
# we can include?
+ # Yes plz - JP
included do
router.install_helpers(self)
end
+
+ define_method(:_router) { router }
end
end
end
@@ -292,36 +261,131 @@ module ActionDispatch
routes.empty?
end
- def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil)
- route = Route.new(app, conditions, requirements, defaults, name)
+ def add_route(app, conditions = {}, requirements = {}, defaults = {}, name = nil, anchor = true)
+ route = Route.new(app, conditions, requirements, defaults, name, anchor)
@set.add_route(*route)
named_routes[name] = route if name
routes << route
route
end
- def options_as_params(options)
- # If an explicit :controller was given, always make :action explicit
- # too, so that action expiry works as expected for things like
- #
- # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
- #
- # (the above is from the unit tests). In the above case, because the
- # controller was explicitly given, but no action, the action is implied to
- # be "index", not the recalled action of "show".
- #
- # great fun, eh?
-
- options_as_params = options.clone
- options_as_params[:action] ||= 'index' if options[:controller]
- options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action]
- options_as_params
- end
+ class Generator
+ attr_reader :options, :recall, :set, :script_name, :named_route
+
+ def initialize(options, recall, set, extras = false)
+ @script_name = options.delete(:script_name)
+ @named_route = options.delete(:use_route)
+ @options = options.dup
+ @recall = recall.dup
+ @set = set
+ @extras = extras
+
+ normalize_options!
+ normalize_controller_action_id!
+ use_relative_controller!
+ controller.sub!(%r{^/}, '') if controller
+ handle_nil_action!
+ end
+
+ def controller
+ @controller ||= @options[:controller]
+ end
+
+ def current_controller
+ @recall[:controller]
+ end
+
+ def use_recall_for(key)
+ if @recall[key] && (!@options.key?(key) || @options[key] == @recall[key])
+ @options[key] = @recall.delete(key)
+ end
+ end
+
+ def normalize_options!
+ # If an explicit :controller was given, always make :action explicit
+ # too, so that action expiry works as expected for things like
+ #
+ # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
+ #
+ # (the above is from the unit tests). In the above case, because the
+ # controller was explicitly given, but no action, the action is implied to
+ # be "index", not the recalled action of "show".
+
+ if options[:controller]
+ options[:action] ||= 'index'
+ options[:controller] = options[:controller].to_s
+ end
+
+ if options[:action]
+ options[:action] = options[:action].to_s
+ end
+ end
+
+ # This pulls :controller, :action, and :id out of the recall.
+ # The recall key is only used if there is no key in the options
+ # or if the key in the options is identical. If any of
+ # :controller, :action or :id is not found, don't pull any
+ # more keys from the recall.
+ def normalize_controller_action_id!
+ @recall[:action] ||= 'index' if current_controller
+
+ use_recall_for(:controller) or return
+ use_recall_for(:action) or return
+ use_recall_for(:id)
+ end
+
+ # if the current controller is "foo/bar/baz" and :controller => "baz/bat"
+ # is specified, the controller becomes "foo/baz/bat"
+ def use_relative_controller!
+ if !named_route && different_controller?
+ old_parts = current_controller.split('/')
+ size = controller.count("/") + 1
+ parts = old_parts[0...-size] << controller
+ @controller = @options[:controller] = parts.join("/")
+ end
+ end
+
+ # This handles the case of :action => nil being explicitly passed.
+ # It is identical to :action => "index"
+ def handle_nil_action!
+ if options.has_key?(:action) && options[:action].nil?
+ options[:action] = 'index'
+ end
+ recall[:action] = options.delete(:action) if options[:action] == 'index'
+ end
+
+ def generate
+ error = ActionController::RoutingError.new("No route matches #{options.inspect}")
+ path, params = @set.generate(:path_info, named_route, options, recall, opts)
+
+ raise error unless path
+
+ params.reject! {|k,v| !v }
+
+ return [path, params.keys] if @extras
- def build_expiry(options, recall)
- recall.inject({}) do |expiry, (key, recalled_value)|
- expiry[key] = (options.key?(key) && options[key].to_param != recalled_value.to_param)
- expiry
+ path << "?#{params.to_query}" if params.any?
+ "#{script_name}#{path}"
+ rescue Rack::Mount::RoutingError
+ raise error
+ end
+
+ def opts
+ parameterize = lambda do |name, value|
+ if name == :controller
+ value
+ elsif value.is_a?(Array)
+ value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
+ else
+ Rack::Mount::Utils.escape_uri(value.to_param)
+ end
+ end
+ {:parameterize => parameterize}
+ end
+
+ def different_controller?
+ return false unless current_controller
+ controller.to_param != current_controller.to_param
end
end
@@ -332,82 +396,44 @@ module ActionDispatch
end
def generate_extras(options, recall={})
- generate(options, recall, :generate_extras)
+ generate(options, recall, true)
end
- def generate(options, recall = {}, method = :generate)
- options, recall = options.dup, recall.dup
- named_route = options.delete(:use_route)
+ def generate(options, recall = {}, extras = false)
+ Generator.new(options, recall, @set, extras).generate
+ end
- options = options_as_params(options)
- expire_on = build_expiry(options, recall)
+ RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash]
- recall[:action] ||= 'index' if options[:controller] || recall[:controller]
+ def url_for(options)
+ options = default_url_options.merge(options || {})
- if recall[:controller] && (!options.has_key?(:controller) || options[:controller] == recall[:controller])
- options[:controller] = recall.delete(:controller)
+ handle_positional_args(options)
- if recall[:action] && (!options.has_key?(:action) || options[:action] == recall[:action])
- options[:action] = recall.delete(:action)
+ rewritten_url = ""
- if recall[:id] && (!options.has_key?(:id) || options[:id] == recall[:id])
- options[:id] = recall.delete(:id)
- end
- end
- end
-
- options[:controller] = options[:controller].to_s if options[:controller]
+ path_segments = options.delete(:_path_segments)
- if !named_route && expire_on[:controller] && options[:controller] && options[:controller][0] != ?/
- old_parts = recall[:controller].split('/')
- new_parts = options[:controller].split('/')
- parts = old_parts[0..-(new_parts.length + 1)] + new_parts
- options[:controller] = parts.join('/')
- end
+ unless options[:only_path]
+ rewritten_url << (options[:protocol] || "http")
+ rewritten_url << "://" unless rewritten_url.match("://")
+ rewritten_url << rewrite_authentication(options)
- options[:controller] = options[:controller][1..-1] if options[:controller] && options[:controller][0] == ?/
+ raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
- merged = options.merge(recall)
- if options.has_key?(:action) && options[:action].nil?
- options.delete(:action)
- recall[:action] = 'index'
+ rewritten_url << options[:host]
+ rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
end
- recall[:action] = options.delete(:action) if options[:action] == 'index'
-
- opts = {}
- opts[:parameterize] = lambda { |name, value|
- if name == :controller
- value
- elsif value.is_a?(Array)
- value.map { |v| Rack::Mount::Utils.escape_uri(v.to_param) }.join('/')
- else
- Rack::Mount::Utils.escape_uri(value.to_param)
- end
- }
- unless result = @set.generate(:path_info, named_route, options, recall, opts)
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
- end
+ path_options = options.except(*RESERVED_OPTIONS)
+ path_options = yield(path_options) if block_given?
+ path = generate(path_options, path_segments || {})
- path, params = result
- params.each do |k, v|
- if v
- params[k] = v
- else
- params.delete(k)
- end
- end
+ # ROUTES TODO: This can be called directly, so script_name should probably be set in the router
+ rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
+ rewritten_url << "##{Rack::Utils.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
- if path && method == :generate_extras
- [path, params.keys]
- elsif path
- path << "?#{params.to_query}" if params.any?
- path
- else
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
- end
- rescue Rack::Mount::RoutingError
- raise ActionController::RoutingError, "No route matches #{options.inspect}"
+ rewritten_url
end
def call(env)
@@ -435,6 +461,30 @@ module ActionDispatch
raise ActionController::RoutingError, "No route matches #{path.inspect}"
end
+
+ private
+ def handle_positional_args(options)
+ return unless args = options.delete(:_positional_args)
+
+ keys = options.delete(:_positional_keys)
+ keys -= options.keys if args.size < keys.size - 1 # take format into account
+
+ args = args.zip(keys).inject({}) do |h, (v, k)|
+ h[k] = v
+ h
+ end
+
+ # Tell url_for to skip default_url_options
+ options.merge!(args)
+ end
+
+ def rewrite_authentication(options)
+ if options[:user] && options[:password]
+ "#{Rack::Utils.escape(options.delete(:user))}:#{Rack::Utils.escape(options.delete(:password))}@"
+ else
+ ""
+ end
+ end
end
end
end
diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb
index 7f2c9a5c12..ec78f53fa6 100644
--- a/actionpack/lib/action_dispatch/routing/url_for.rb
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -85,37 +85,24 @@ module ActionDispatch
extend ActiveSupport::Concern
included do
- # Including in a class uses an inheritable hash. Modules get a plain hash.
- if respond_to?(:class_attribute)
- class_attribute :default_url_options
- else
- mattr_accessor :default_url_options
- remove_method :default_url_options
- end
+ # TODO: with_routing extends @controller with url_helpers, trickling down to including this module which overrides its default_url_options
+ unless method_defined?(:default_url_options)
+ # Including in a class uses an inheritable hash. Modules get a plain hash.
+ if respond_to?(:class_attribute)
+ class_attribute :default_url_options
+ else
+ mattr_accessor :default_url_options
+ remove_method :default_url_options
+ end
- self.default_url_options = {}
+ self.default_url_options = {}
+ end
end
- # def default_url_options(options = nil)
- # self.class.default_url_options
- # end
-
def url_options
- self.class.default_url_options.merge(@url_options || {})
+ default_url_options
end
- def url_options=(options)
- @url_options = options
- end
-
- # def merge_options(options) #:nodoc:
- # if respond_to?(:default_url_options) && (defaults = default_url_options(options))
- # defaults.merge(options)
- # else
- # options
- # end
- # end
-
# Generate a url based on the options provided, default_url_options and the
# routes defined in routes.rb. The following options are supported:
#
@@ -126,8 +113,6 @@ module ActionDispatch
# provided either explicitly, or via +default_url_options+.
# * <tt>:port</tt> - Optionally specify the port to connect to.
# * <tt>:anchor</tt> - An anchor name to be appended to the path.
- # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
- # +relative_url_root+ set in ActionController::Base.relative_url_root.
# * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
#
# Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
@@ -139,25 +124,16 @@ module ActionDispatch
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
# url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
# url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
- def url_for(options = {})
- options ||= {}
+ def url_for(options = nil)
case options
- when String
- options
- when Hash
- # Handle the deprecated instance level default_url_options
- if respond_to?(:default_url_options, true)
- ActiveSupport::Deprecation.warn "Overwriting #default_url_options is deprecated. Please set url options with self.url_options = { ... }"
- if defaults = default_url_options(options)
- options = defaults.merge(options)
- end
- end
-
- ActionController::UrlRewriter.rewrite(_router, url_options.merge(options))
- else
- polymorphic_url(options)
+ when String
+ options
+ when nil, Hash
+ _router.url_for(url_options.merge(options || {}))
+ else
+ polymorphic_url(options)
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index f5035fe45a..afe6386105 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -37,15 +37,18 @@ module ActionView
autoload :Helpers
autoload_under "render" do
+ autoload :Layouts
autoload :Partials
autoload :Rendering
end
- 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 :Base
+ autoload :LookupContext
+ autoload :MissingTemplate, 'action_view/base'
+ autoload :Resolver, 'action_view/template/resolver'
+ autoload :PathResolver, 'action_view/template/resolver'
+ autoload :FileSystemResolver, 'action_view/template/resolver'
+ autoload :PathSet, 'action_view/paths'
autoload :TemplateError, 'action_view/template/error'
autoload :TemplateHandler, 'action_view/template'
@@ -55,7 +58,7 @@ module ActionView
autoload :TestCase, 'action_view/test_case'
end
+require 'active_support/i18n'
require 'active_support/core_ext/string/output_safety'
-require 'action_view/base'
I18n.load_path << "#{File.dirname(__FILE__)}/action_view/locale/en.yml"
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 2d2b53a6ce..ffe3060404 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -173,102 +173,43 @@ module ActionView #:nodoc:
module Subclasses
end
- include Helpers, Rendering, Partials, ::ERB::Util
+ include Helpers, Rendering, Partials, Layouts, ::ERB::Util, Context
+ extend ActiveSupport::Memoizable
- def config
- self.config = DEFAULT_CONFIG unless @config
- @config
- end
-
- def config=(config)
- @config = ActiveSupport::OrderedOptions.new.merge(config)
- end
-
- extend ActiveSupport::Memoizable
-
- attr_accessor :base_path, :assigns, :template_extension
- attr_internal :captures
-
- def reset_formats(formats)
- old_formats, self.formats = self.formats, formats
- reset_hash_key
- yield if block_given?
- ensure
- if block_given?
- self.formats = old_formats
- reset_hash_key
- end
- end
-
- def reset_hash_key
- if defined?(AbstractController::HashKey)
- # This is expensive, but we need to reset this when the format is updated,
- # which currently only happens
- Thread.current[:format_locale_key] =
- AbstractController::HashKey.get(self.class, :formats => formats, :locale => [I18n.locale])
- end
- end
-
- def formats
- controller ? controller.formats : @formats
- end
-
- def formats=(val)
- if controller
- controller.formats = val
- else
- @formats = val
- end
- end
-
- class << self
- delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
- delegate :logger, :to => 'ActionController::Base', :allow_nil => true
- end
+ ActionView.run_base_hooks(self)
- @@debug_rjs = false
- ##
- # :singleton-method:
# Specify whether RJS responses should be wrapped in a try/catch block
# that alert()s the caught exception (and then re-raises it).
cattr_accessor :debug_rjs
+ @@debug_rjs = false
- # Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.
- # Automatically reloading templates are not thread safe and should only be used in development mode.
- @@cache_template_loading = nil
- cattr_accessor :cache_template_loading
+ class_attribute :helpers
+ attr_reader :helpers
- # :nodoc:
- def self.xss_safe?
- true
+ class << self
+ delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
+ delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
- def self.cache_template_loading?
- ActionController::Base.allow_concurrency || (cache_template_loading.nil? ? !ActiveSupport::Dependencies.load? : cache_template_loading)
- end
+ attr_accessor :base_path, :assigns, :template_extension, :lookup_context
+ attr_internal :captures, :request, :layout, :controller, :template, :config
- attr_internal :request, :layout
-
- def controller_path
- @controller_path ||= controller && controller.controller_path
- end
+ delegate :find, :exists?, :formats, :formats=, :locale, :locale=,
+ :view_paths, :view_paths=, :with_fallbacks, :update_details, :to => :lookup_context
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
:flash, :action_name, :controller_name, :to => :controller
delegate :logger, :to => :controller, :allow_nil => true
- delegate :find, :to => :view_paths
-
- include Context
+ def self.xss_safe? #:nodoc:
+ true
+ end
def self.process_view_paths(value)
ActionView::PathSet.new(Array(value))
end
- class_attribute :helpers
- attr_reader :helpers
-
def self.for_controller(controller)
@views ||= {}
@@ -296,26 +237,26 @@ module ActionView #:nodoc:
klass = self
end
- klass.new(controller.class.view_paths, {}, controller)
+ klass.new(controller.lookup_context, {}, controller)
end
- def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc:
+ def initialize(lookup_context = nil, assigns_for_first_render = {}, controller = nil, formats = nil) #:nodoc:
@config = nil
- @formats = formats
@assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) }
@helpers = self.class.helpers || Module.new
@_controller = controller
- @_content_for = Hash.new {|h,k| h[k] = ActiveSupport::SafeBuffer.new }
+ @_config = ActiveSupport::InheritableOptions.new(controller.config) if controller
+ @_content_for = Hash.new { |h,k| h[k] = ActiveSupport::SafeBuffer.new }
@_virtual_path = nil
- self.view_paths = view_paths
- end
- attr_internal :controller, :template
- attr_reader :view_paths
+ @lookup_context = lookup_context.is_a?(ActionView::LookupContext) ?
+ lookup_context : ActionView::LookupContext.new(lookup_context)
+ @lookup_context.formats = formats if formats
+ end
- def view_paths=(paths)
- @view_paths = self.class.process_view_paths(paths)
+ def controller_path
+ @controller_path ||= controller && controller.controller_path
end
def punctuate_body!(part)
diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb
index b4f649385a..e359b0bdac 100644
--- a/actionpack/lib/action_view/helpers.rb
+++ b/actionpack/lib/action_view/helpers.rb
@@ -10,6 +10,7 @@ module ActionView #:nodoc:
autoload :CsrfHelper, 'action_view/helpers/csrf_helper'
autoload :DateHelper, 'action_view/helpers/date_helper'
autoload :DebugHelper, 'action_view/helpers/debug_helper'
+ autoload :DeprecatedBlockHelpers, 'action_view/helpers/deprecated_block_helpers'
autoload :FormHelper, 'action_view/helpers/form_helper'
autoload :FormOptionsHelper, 'action_view/helpers/form_options_helper'
autoload :FormTagHelper, 'action_view/helpers/form_tag_helper'
diff --git a/actionpack/lib/action_view/helpers/active_model_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb
index ed83b8a8b2..4e12cdab54 100644
--- a/actionpack/lib/action_view/helpers/active_model_helper.rb
+++ b/actionpack/lib/action_view/helpers/active_model_helper.rb
@@ -5,9 +5,11 @@ require 'active_support/core_ext/enumerable'
require 'active_support/core_ext/kernel/reporting'
module ActionView
- class Base
- @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
- cattr_accessor :field_error_proc
+ ActionView.base_hook do
+ class ActionView::Base
+ @@field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"fieldWithErrors\">#{html_tag}</div>".html_safe }
+ cattr_accessor :field_error_proc
+ end
end
module Helpers
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 96976ce45f..de3d61ebbe 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -133,13 +133,6 @@ module ActionView
# change. You can use something like Live HTTP Headers for Firefox to verify
# that the cache is indeed working.
module AssetTagHelper
- assets_dir = defined?(Rails.public_path) ? Rails.public_path : "public"
- ActionView::DEFAULT_CONFIG = {
- :assets_dir => assets_dir,
- :javascripts_dir => "#{assets_dir}/javascripts",
- :stylesheets_dir => "#{assets_dir}/stylesheets",
- }
-
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
# Returns a link tag that browsers and news readers can use to auto-detect
@@ -648,8 +641,8 @@ module ActionView
source = rewrite_asset_path(source)
if has_request && include_host
- unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
- source = "#{ActionController::Base.relative_url_root}#{source}"
+ unless source =~ %r{^#{controller.config.relative_url_root}/}
+ source = "#{controller.config.relative_url_root}#{source}"
end
end
end
@@ -677,7 +670,7 @@ module ActionView
# or the value returned from invoking the proc if it's a proc or the value from
# invoking call if it's an object responding to call.
def compute_asset_host(source)
- if host = ActionController::Base.asset_host
+ if host = config.asset_host
if host.is_a?(Proc) || host.respond_to?(:call)
case host.is_a?(Proc) ? host.arity : host.method(:call).arity
when 2
diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
index a26a8c9b4b..58c3a8752e 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -114,7 +114,7 @@ module ActionView
feed_opts.merge!(options).reject!{|k,v| !k.to_s.match(/^xml/)}
xml.feed(feed_opts) do
- xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.request_uri.split(".")[0]}")
+ xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
xml.link(:rel => 'alternate', :type => 'text/html', :href => options[:root_url] || (request.protocol + request.host_with_port))
xml.link(:rel => 'self', :type => 'application/atom+xml', :href => options[:url] || request.url)
diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb
index 8c48300ed3..75fc2fddeb 100644
--- a/actionpack/lib/action_view/helpers/capture_helper.rb
+++ b/actionpack/lib/action_view/helpers/capture_helper.rb
@@ -30,14 +30,10 @@ module ActionView
# <b><%= @greeting %></b>
# </body></html>
#
- def capture(*args, &block)
- # Return captured buffer in erb.
- if block_called_from_erb?(block)
- with_output_buffer { block.call(*args) }
- else
- # Return block result otherwise, but protect buffer also.
- with_output_buffer { return block.call(*args) }
- end
+ def capture(*args)
+ value = nil
+ buffer = with_output_buffer { value = yield *args }
+ buffer.presence || value
end
# Calling content_for stores a block of markup in an identifier for later use.
@@ -143,7 +139,7 @@ module ActionView
# Defaults to a new empty string.
def with_output_buffer(buf = nil) #:nodoc:
unless buf
- buf = ActiveSupport::SafeBuffer.new
+ buf = ActionView::OutputBuffer.new
buf.force_encoding(output_buffer.encoding) if buf.respond_to?(:force_encoding)
end
self.output_buffer, old_buffer = buf, output_buffer
diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb
index 89ac682c18..42018ee261 100644
--- a/actionpack/lib/action_view/helpers/date_helper.rb
+++ b/actionpack/lib/action_view/helpers/date_helper.rb
@@ -1,5 +1,6 @@
require "date"
require 'action_view/helpers/tag_helper'
+require 'active_support/core_ext/hash/slice'
module ActionView
module Helpers
@@ -865,7 +866,7 @@ module ActionView
:id => input_id_from_type(type),
:name => input_name_from_type(type),
:value => value
- }) + "\n").html_safe
+ }.merge(@html_options.slice(:disabled))) + "\n").html_safe
end
# Returns the name attribute for the input tag
diff --git a/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb b/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb
new file mode 100644
index 0000000000..3d0657e873
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/deprecated_block_helpers.rb
@@ -0,0 +1,52 @@
+module ActionView
+ module Helpers
+ module DeprecatedBlockHelpers
+ extend ActiveSupport::Concern
+
+ include ActionView::Helpers::TagHelper
+ include ActionView::Helpers::TextHelper
+ include ActionView::Helpers::JavaScriptHelper
+ include ActionView::Helpers::FormHelper
+
+ def content_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def javascript_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def form_for(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def form_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def fields_for(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ def field_set_tag(*, &block)
+ block_called_from_erb?(block) ? safe_concat(super) : super
+ end
+
+ BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
+
+ if RUBY_VERSION < '1.9.0'
+ # Check whether we're called from an erb template.
+ # We'd return a string in any other case, but erb <%= ... %>
+ # can't take an <% end %> later on, so we have to use <% ... %>
+ # and implicitly concat.
+ def block_called_from_erb?(block)
+ block && eval(BLOCK_CALLED_FROM_ERB, block)
+ end
+ else
+ def block_called_from_erb?(block)
+ block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb
index ace3bcfde3..48df26efaa 100644
--- a/actionpack/lib/action_view/helpers/form_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_helper.rb
@@ -92,6 +92,10 @@ module ActionView
# link:classes/ActionView/Helpers/DateHelper.html, and
# link:classes/ActionView/Helpers/ActiveRecordHelper.html
module FormHelper
+ extend ActiveSupport::Concern
+
+ include FormTagHelper
+
# Creates a form and a scope around a specific model object that is used
# as a base for questioning about values for the fields.
#
@@ -309,9 +313,9 @@ module ActionView
options[:html][:remote] = true if options.delete(:remote)
- safe_concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
- fields_for(object_name, *(args << options), &proc)
- safe_concat('</form>')
+ output = form_tag(options.delete(:url) || {}, options.delete(:html) || {})
+ output << fields_for(object_name, *(args << options), &proc)
+ output.safe_concat('</form>')
end
def apply_form_for_options!(object_or_array, options) #:nodoc:
@@ -528,7 +532,10 @@ module ActionView
end
builder = options[:builder] || ActionView::Base.default_form_builder
- yield builder.new(object_name, object, self, options, block)
+
+ with_output_buffer do
+ yield builder.new(object_name, object, self, options, block)
+ end
end
# Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
@@ -1183,9 +1190,11 @@ module ActionView
if association.is_a?(Array)
explicit_child_index = options[:child_index]
- association.map do |child|
- fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block)
- end.join
+ output = ActiveSupport::SafeBuffer.new
+ association.each do |child|
+ output << fields_for_nested_model("#{name}[#{explicit_child_index || nested_child_index(name)}]", child, options, block)
+ end
+ output
elsif association
fields_for_nested_model(name, association, options, block)
end
@@ -1211,8 +1220,10 @@ module ActionView
end
end
- class Base
- cattr_accessor :default_form_builder
- @@default_form_builder = ::ActionView::Helpers::FormBuilder
+ ActionView.base_hook do
+ class ActionView::Base
+ cattr_accessor :default_form_builder
+ @@default_form_builder = ::ActionView::Helpers::FormBuilder
+ end
end
end
diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb
index 7f74be27cb..7039ecd233 100644
--- a/actionpack/lib/action_view/helpers/form_options_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_options_helper.rb
@@ -97,7 +97,9 @@ module ActionView
# </select>
#
module FormOptionsHelper
+ # ERB::Util can mask some helpers like textilize. Make sure to include them.
include ERB::Util
+ include TextHelper
# Create a select tag and a series of contained option tags for the provided object and method.
# The option currently held by the object will be selected, provided that the object is available.
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 7dcaee7e34..573733ffea 100644
--- a/actionpack/lib/action_view/helpers/form_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb
@@ -10,6 +10,11 @@ module ActionView
# NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
# <tt>:disabled => true</tt> will give <tt>disabled="disabled"</tt>.
module FormTagHelper
+ extend ActiveSupport::Concern
+
+ include UrlHelper
+ include TextHelper
+
# Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
# ActionController::Base#url_for. The method for the form defaults to POST.
#
@@ -441,10 +446,10 @@ module ActionView
# # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
def field_set_tag(legend = nil, options = nil, &block)
content = capture(&block)
- safe_concat(tag(:fieldset, options, true))
- safe_concat(content_tag(:legend, legend)) unless legend.blank?
- concat(content)
- safe_concat("</fieldset>")
+ output = tag(:fieldset, options, true)
+ output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
+ output.concat(content)
+ output.safe_concat("</fieldset>")
end
private
@@ -477,9 +482,10 @@ module ActionView
def form_tag_in_block(html_options, &block)
content = capture(&block)
- safe_concat(form_tag_html(html_options))
- concat(content)
- safe_concat("</form>")
+ output = ActiveSupport::SafeBuffer.new
+ output.safe_concat(form_tag_html(html_options))
+ output << content
+ output.safe_concat("</form>")
end
def token_tag
diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb
index 2c73ff88f7..8dab3094dd 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -83,13 +83,7 @@ module ActionView
content_or_options_with_block
end
- tag = content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
-
- if block_called_from_erb?(block)
- safe_concat(tag)
- else
- tag
- end
+ content_tag(:script, javascript_cdata_section(content), html_options.merge(:type => Mime::JS))
end
def javascript_cdata_section(content) #:nodoc:
diff --git a/actionpack/lib/action_view/helpers/number_helper.rb b/actionpack/lib/action_view/helpers/number_helper.rb
index 3d3502a08b..46e41bc406 100644
--- a/actionpack/lib/action_view/helpers/number_helper.rb
+++ b/actionpack/lib/action_view/helpers/number_helper.rb
@@ -28,27 +28,25 @@ module ActionView
# number_to_phone(1235551234, :country_code => 1, :extension => 1343, :delimiter => ".")
# => +1.123.555.1234 x 1343
def number_to_phone(number, options = {})
- number = number.to_s.strip unless number.nil?
+ return nil if number.nil?
+
+ number = number.to_s.strip
options = options.symbolize_keys
area_code = options[:area_code] || nil
delimiter = options[:delimiter] || "-"
extension = options[:extension].to_s.strip || nil
country_code = options[:country_code] || nil
- begin
- str = ""
- str << "+#{country_code}#{delimiter}" unless country_code.blank?
- str << if area_code
- number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
- else
- number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
- number.starts_with?('-') ? number.slice!(1..-1) : number
- end
- str << " x #{extension}" unless extension.blank?
- str
- rescue
- number
+ str = ""
+ str << "+#{country_code}#{delimiter}" unless country_code.blank?
+ str << if area_code
+ number.gsub!(/([0-9]{1,3})([0-9]{3})([0-9]{4}$)/,"(\\1) \\2#{delimiter}\\3")
+ else
+ number.gsub!(/([0-9]{0,3})([0-9]{3})([0-9]{4})$/,"\\1#{delimiter}\\2#{delimiter}\\3")
+ number.starts_with?('-') ? number.slice!(1..-1) : number
end
+ str << " x #{extension}" unless extension.blank?
+ str
end
# Formats a +number+ into a currency string (e.g., $13.65). You can customize the format
@@ -87,13 +85,14 @@ module ActionView
format = options[:format] || defaults[:format]
separator = '' if precision == 0
- begin
- format.gsub(/%n/, number_with_precision(number,
- :precision => precision,
- :delimiter => delimiter,
- :separator => separator)
- ).gsub(/%u/, unit).html_safe
- rescue
+ value = number_with_precision(number,
+ :precision => precision,
+ :delimiter => delimiter,
+ :separator => separator)
+
+ if value
+ format.gsub(/%n/, value).gsub(/%u/, unit).html_safe
+ else
number
end
end
@@ -122,14 +121,11 @@ module ActionView
separator = options[:separator] || defaults[:separator]
delimiter = options[:delimiter] || defaults[:delimiter]
- begin
- number_with_precision(number,
- :precision => precision,
- :separator => separator,
- :delimiter => delimiter) + "%"
- rescue
- number
- end
+ value = number_with_precision(number,
+ :precision => precision,
+ :separator => separator,
+ :delimiter => delimiter)
+ value ? value + "%" : number
end
# Formats a +number+ with grouped thousands using +delimiter+ (e.g., 12,324). You can
@@ -168,11 +164,11 @@ module ActionView
delimiter ||= (options[:delimiter] || defaults[:delimiter])
separator ||= (options[:separator] || defaults[:separator])
- begin
- parts = number.to_s.split('.')
+ parts = number.to_s.split('.')
+ if parts[0]
parts[0].gsub!(/(\d)(?=(\d\d\d)+(?!\d))/, "\\1#{delimiter}")
parts.join(separator)
- rescue
+ else
number
end
end
@@ -216,11 +212,17 @@ module ActionView
delimiter ||= (options[:delimiter] || defaults[:delimiter])
begin
+ value = Float(number)
+ rescue ArgumentError, TypeError
+ value = nil
+ end
+
+ if value
rounded_number = BigDecimal.new((Float(number) * (10 ** precision)).to_s).round.to_f / 10 ** precision
number_with_delimiter("%01.#{precision}f" % rounded_number,
:separator => separator,
:delimiter => delimiter)
- rescue
+ else
number
end
end
@@ -293,17 +295,13 @@ module ActionView
unit_key = STORAGE_UNITS[exponent]
unit = I18n.translate(:"number.human.storage_units.units.#{unit_key}", :locale => options[:locale], :count => number, :raise => true)
- begin
- escaped_separator = Regexp.escape(separator)
- formatted_number = number_with_precision(number,
- :precision => precision,
- :separator => separator,
- :delimiter => delimiter
- ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
- storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
- rescue
- number
- end
+ escaped_separator = Regexp.escape(separator)
+ formatted_number = number_with_precision(number,
+ :precision => precision,
+ :separator => separator,
+ :delimiter => delimiter
+ ).sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
+ storage_units_format.gsub(/%n/, formatted_number).gsub(/%u/, unit)
end
end
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 67a7586699..be49b5cc28 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -182,7 +182,7 @@ module ActionView
def initialize(context, &block) #:nodoc:
context._evaluate_assigns_and_ivars
@context, @lines = context, []
- @context.reset_formats([:js, :html]) do
+ @context.update_details(:formats => [:js, :html]) do
include_helpers_from_context
@context.with_output_buffer(@lines) do
@context.instance_exec(self, &block)
@@ -583,7 +583,7 @@ module ActionView
end
def with_formats(*args)
- @context ? @context.reset_formats(args) { yield } : yield
+ @context ? @context.update_details(:formats => args) { yield } : yield
end
def javascript_object_for(object)
diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb
index 513d72880c..d9d2588a2a 100644
--- a/actionpack/lib/action_view/helpers/tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/tag_helper.rb
@@ -7,6 +7,9 @@ module ActionView
module TagHelper
include ERB::Util
+ extend ActiveSupport::Concern
+ include CaptureHelper
+
BOOLEAN_ATTRIBUTES = %w(disabled readonly multiple checked autobuffer
autoplay controls loop selected hidden scoped async
defer reversed ismap seemless muted required
@@ -69,13 +72,7 @@ module ActionView
def content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
if block_given?
options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
- content_tag = content_tag_string(name, capture(&block), options, escape)
-
- if block_called_from_erb?(block)
- safe_concat(content_tag)
- else
- content_tag
- end
+ content_tag_string(name, capture(&block), options, escape)
else
content_tag_string(name, content_or_options_with_block, options, escape)
end
@@ -109,21 +106,6 @@ module ActionView
end
private
- BLOCK_CALLED_FROM_ERB = 'defined? __in_erb_template'
-
- if RUBY_VERSION < '1.9.0'
- # Check whether we're called from an erb template.
- # We'd return a string in any other case, but erb <%= ... %>
- # can't take an <% end %> later on, so we have to use <% ... %>
- # and implicitly concat.
- def block_called_from_erb?(block)
- block && eval(BLOCK_CALLED_FROM_ERB, block)
- end
- else
- def block_called_from_erb?(block)
- block && eval(BLOCK_CALLED_FROM_ERB, block.binding)
- end
- end
def content_tag_string(name, content, options, escape = true)
tag_options = tag_options(options, escape) if options
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index d9607c0095..148f2868e9 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -544,10 +544,11 @@ module ActionView
# submitted url doesn't have any either. This lets the function
# work with things like ?order=asc
if url_string.index("?")
- request_uri = request.request_uri
+ request_uri = request.fullpath
else
- request_uri = request.request_uri.split('?').first
+ request_uri = request.path
end
+
if url_string =~ /^\w+:\/\//
url_string == "#{request.protocol}#{request.host_with_port}#{request_uri}"
else
diff --git a/actionpack/lib/action_view/lookup_context.rb b/actionpack/lib/action_view/lookup_context.rb
new file mode 100644
index 0000000000..91885c7370
--- /dev/null
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -0,0 +1,151 @@
+require 'active_support/core_ext/object/try'
+require 'active_support/core_ext/object/blank'
+
+module ActionView
+ # LookupContext is the object responsible to hold all information required to lookup
+ # templates, i.e. view paths and details. The LookupContext is also responsible to
+ # generate a key, given to view paths, used in the resolver cache lookup. Since
+ # this key is generated just once during the request, it speeds up all cache accesses.
+ class LookupContext #:nodoc:
+ mattr_accessor :fallbacks
+ @@fallbacks = [FileSystemResolver.new(""), FileSystemResolver.new("/")]
+
+ mattr_accessor :registered_details
+ self.registered_details = {}
+
+ def self.register_detail(name, options = {})
+ registered_details[name] = lambda do |value|
+ value = Array(value.presence || yield)
+ value |= [nil] unless options[:allow_nil] == false
+ value
+ end
+ end
+
+ register_detail(:formats) { Mime::SET.symbols }
+ register_detail(:locale) { [I18n.locale] }
+
+ class DetailsKey #:nodoc:
+ attr_reader :details
+ alias :eql? :equal?
+
+ @details_keys = Hash.new
+
+ def self.get(details)
+ @details_keys[details] ||= new(details)
+ end
+
+ def initialize(details)
+ @details, @hash = details, details.hash
+ end
+ end
+
+ def initialize(view_paths, details = {})
+ @details_key = nil
+ self.view_paths = view_paths
+ self.details = details
+ end
+
+ module ViewPaths
+ attr_reader :view_paths
+
+ # Whenever setting view paths, makes a copy so we can manipulate then in
+ # instance objects as we wish.
+ def view_paths=(paths)
+ @view_paths = ActionView::Base.process_view_paths(paths)
+ end
+
+ def find(name, prefix = nil, partial = false)
+ @view_paths.find(name, prefix, partial || false, details, details_key)
+ end
+
+ def find_all(name, prefix = nil, partial = false)
+ @view_paths.find_all(name, prefix, partial || false, details, details_key)
+ end
+
+ def exists?(name, prefix = nil, partial = false)
+ @view_paths.exists?(name, prefix, partial || false, details, details_key)
+ end
+
+ # Add fallbacks to the view paths. Useful in cases you are rendering a file.
+ def with_fallbacks
+ added_resolvers = 0
+ self.class.fallbacks.each do |resolver|
+ next if view_paths.include?(resolver)
+ view_paths.push(resolver)
+ added_resolvers += 1
+ end
+ yield
+ ensure
+ added_resolvers.times { view_paths.pop }
+ end
+ end
+
+ module Details
+ attr_reader :details
+
+ def details=(details)
+ @details = normalize_details(details)
+ @details_key = nil if @details_key && @details_key.details != @details
+ end
+
+ def details_key
+ @details_key ||= DetailsKey.get(@details)
+ end
+
+ # Shortcut to read formats from details.
+ def formats
+ @details[:formats].compact
+ end
+
+ # Shortcut to set formats in details.
+ def formats=(value)
+ self.details = @details.merge(:formats => value)
+ end
+
+ # Shortcut to read locale.
+ def locale
+ I18n.locale
+ end
+
+ # Shortcut to set locale in details and I18n.
+ def locale=(value)
+ I18n.locale = value
+
+ unless I18n.config.respond_to?(:lookup_context)
+ self.details = @details.merge(:locale => value)
+ end
+ end
+
+ # Update the details keys by merging the given hash into the current
+ # details hash. If a block is given, the details are modified just during
+ # the execution of the block and reverted to the previous value after.
+ def update_details(new_details)
+ old_details = @details
+ self.details = old_details.merge(new_details)
+
+ if block_given?
+ begin
+ yield
+ ensure
+ self.details = old_details
+ end
+ end
+ end
+
+ protected
+
+ def normalize_details(details)
+ details = details.dup
+ # TODO: Refactor this concern out of the resolver
+ details.delete(:formats) if details[:formats] == [:"*/*"]
+ self.class.registered_details.each do |k, v|
+ details[k] = v.call(details[k])
+ end
+ details.freeze
+ end
+ end
+
+ include Details
+ include ViewPaths
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 6e55d69d00..35927d09d1 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -1,80 +1,39 @@
module ActionView #:nodoc:
class PathSet < Array #:nodoc:
- def self.type_cast(obj, cache = nil)
- # TODO: Clean this up
- if obj.is_a?(String)
- if cache.nil?
- cache = !defined?(Rails.application) || Rails.application.config.cache_classes
+ %w(initialize << concat insert push unshift).each do |method|
+ class_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{method}(*args)
+ super
+ typecast!
end
- FileSystemResolverWithFallback.new(obj, :cache => cache)
- else
- obj
- end
- end
-
- def initialize(*args)
- super(*args).map! { |obj| self.class.type_cast(obj) }
- end
-
- def <<(obj)
- super(self.class.type_cast(obj))
- end
-
- def concat(array)
- super(array.map! { |obj| self.class.type_cast(obj) })
- end
-
- def insert(index, obj)
- super(index, self.class.type_cast(obj))
- end
-
- def push(*objs)
- super(*objs.map { |obj| self.class.type_cast(obj) })
+ METHOD
end
- def unshift(*objs)
- super(*objs.map { |obj| self.class.type_cast(obj) })
+ def find(path, prefix = nil, partial = false, details = {}, key = nil)
+ template = find_all(path, prefix, partial, details, key).first
+ raise MissingTemplate.new(self, "#{prefix}/#{path}", details, partial) unless template
+ template
end
- def find(path, details = {}, prefix = nil, partial = false)
- template_path = path
-
- each do |load_path|
- if template = load_path.find(template_path, details, prefix, partial)
- return template
- end
+ def find_all(*args)
+ each do |resolver|
+ templates = resolver.find_all(*args)
+ return templates unless templates.empty?
end
-
- raise ActionView::MissingTemplate.new(self, "#{prefix}/#{path}", details, partial)
+ []
end
-
- def exists?(path, extension = nil, prefix = nil, partial = false)
- template_path = path.sub(/^\//, '')
- each do |load_path|
- return true if template = load_path.find(template_path, extension, prefix, partial)
- end
- false
+ def exists?(*args)
+ find_all(*args).any?
end
- def find_template(original_template_path, format = nil, html_fallback = true)
- return original_template_path if original_template_path.respond_to?(:render)
- template_path = original_template_path.sub(/^\//, '')
+ protected
- each do |load_path|
- if template = load_path.find(template_path, format)
- return template
- # Try to find html version if the format is javascript
- elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.locale}.html"]
- return template
- elsif format == :js && html_fallback && template = load_path["#{template_path}.html"]
- return template
- end
+ def typecast!
+ each_with_index do |path, i|
+ next unless path.is_a?(String)
+ self[i] = FileSystemResolver.new(path)
end
-
- return Template.new(original_template_path, original_template_path.to_s =~ /\A\// ? "" : ".") if File.file?(original_template_path)
-
- raise MissingTemplate.new(self, original_template_path, format)
end
end
end
diff --git a/actionpack/lib/action_view/railtie.rb b/actionpack/lib/action_view/railtie.rb
index 03f18ac172..2e5d115630 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -10,7 +10,9 @@ module ActionView
initializer "action_view.cache_asset_timestamps" do |app|
unless app.config.cache_classes
- ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ ActionView.base_hook do
+ ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ end
end
end
end
diff --git a/actionpack/lib/action_view/render/layouts.rb b/actionpack/lib/action_view/render/layouts.rb
new file mode 100644
index 0000000000..8688de3d18
--- /dev/null
+++ b/actionpack/lib/action_view/render/layouts.rb
@@ -0,0 +1,65 @@
+require 'active_support/core_ext/object/try'
+
+module ActionView
+ module Layouts
+
+ # You can think of a layout as a method that is called with a block. _layout_for
+ # returns the contents that are yielded to the layout. If the user calls yield
+ # :some_name, the block, by default, returns content_for(:some_name). If the user
+ # calls yield, the default block returns content_for(:layout).
+ #
+ # The user can override this default by passing a block to the layout.
+ #
+ # ==== Example
+ #
+ # # The template
+ # <% render :layout => "my_layout" do %>Content<% end %>
+ #
+ # # The layout
+ # <html><% yield %></html>
+ #
+ # In this case, instead of the default block, which would return content_for(:layout),
+ # this method returns the block that was passed in to render layout, and the response
+ # would be <html>Content</html>.
+ #
+ # Finally, the block can take block arguments, which can be passed in by yield.
+ #
+ # ==== Example
+ #
+ # # The template
+ # <% render :layout => "my_layout" do |customer| %>Hello <%= customer.name %><% end %>
+ #
+ # # The layout
+ # <html><% yield Struct.new(:name).new("David") %></html>
+ #
+ # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
+ # and the Struct specified in the layout would be passed into the block. The result
+ # would be <html>Hello David</html>.
+ def _layout_for(name = nil, &block) #:nodoc:
+ if !block || name
+ @_content_for[name || :layout]
+ else
+ capture(&block)
+ end
+ end
+
+ # This is the method which actually finds the layout using details in the lookup
+ # context object. If no layout is found, it checkes if at least a layout with
+ # the given name exists across all details before raising the error.
+ def _find_layout(layout) #:nodoc:
+ begin
+ layout =~ /^\// ?
+ with_fallbacks { find(layout) } : find(layout)
+ rescue ActionView::MissingTemplate => e
+ update_details(:formats => nil) do
+ raise unless exists?(layout)
+ end
+ end
+ end
+
+ # Contains the logic that actually renders the layout.
+ def _render_layout(layout, locals, &block) #:nodoc:
+ layout.render(self, locals){ |*name| _layout_for(*name, &block) }
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
index 8b6dce0c1c..950c9d2cd8 100644
--- a/actionpack/lib/action_view/render/partials.rb
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -174,17 +174,11 @@ module ActionView
class PartialRenderer
PARTIAL_NAMES = Hash.new {|h,k| h[k] = {} }
- TEMPLATES = Hash.new {|h,k| h[k] = {} }
-
- attr_reader :template
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
@@ -228,6 +222,7 @@ module ActionView
if !@block && (layout = @options[:layout])
content = @view._render_layout(find_template(layout), @locals){ content }
end
+
content
end
end
@@ -244,9 +239,9 @@ module ActionView
end
def collection_with_template(template = @template)
- segments, locals, as = [], @locals, @options[:as] || template.variable_name
+ segments, locals, as, template = [], @locals, @options[:as] || @template.variable_name, @template
- counter_name = template.counter_name
+ counter_name = template.counter_name
locals[counter_name] = -1
@collection.each do |object|
@@ -256,7 +251,6 @@ module ActionView
segments << template.render(@view, locals)
end
- @template = template
segments
end
@@ -277,7 +271,7 @@ module ActionView
end
def render_partial(object = @object)
- locals, view = @locals, @view
+ locals, view, template = @locals, @view, @template
object ||= locals[template.variable_name]
locals[@options[:as] || template.variable_name] = object
@@ -288,6 +282,7 @@ module ActionView
end
private
+
def collection
if @object.respond_to?(:to_ary)
@object
@@ -296,20 +291,10 @@ module ActionView
end
end
- def find_template(path = @path)
- unless @templates
- path && _find_template(path)
- else
- path && @templates[path] ||= _find_template(path)
- end
- end
-
- def _find_template(path)
- if controller = @view.controller
- prefix = controller.controller_path unless path.include?(?/)
- end
-
- @view.find(path, {:formats => @view.formats}, prefix, true)
+ def find_template(path=@path)
+ return path unless path.is_a?(String)
+ prefix = @view.controller_path unless path.include?(?/)
+ @view.find(path, prefix, true)
end
def partial_path(object = @object)
@@ -324,21 +309,8 @@ module ActionView
end
end
- def render_partial(options)
- _evaluate_assigns_and_ivars
-
- details = options[:_details]
-
- # Is this needed
- self.formats = details[:formats] if details
- renderer = PartialRenderer.new(self, options, nil)
- text = renderer.render
- options[:_template] = renderer.template
- text
- end
-
def _render_partial(options, &block) #:nodoc:
- if defined? @renderer
+ if defined?(@renderer)
@renderer.setup(options, block)
else
@renderer = PartialRenderer.new(self, options, block)
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
index 64cc0caf11..47ea70f5ad 100644
--- a/actionpack/lib/action_view/render/rendering.rb
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -15,27 +15,11 @@ module ActionView
def render(options = {}, locals = {}, &block) #:nodoc:
case options
when Hash
- layout = options[:layout]
- options[:locals] ||= {}
-
if block_given?
- return safe_concat(_render_partial(options.merge(:partial => layout), &block))
- elsif options.key?(:partial)
- return _render_partial(options)
- end
-
- template = if options[:file]
- find(options[:file], details_for_render)
- elsif options[:inline]
- handler = Template.handler_class_for_extension(options[:type] || "erb")
- Template.new(options[:inline], "inline template", handler, {})
- elsif options[:text]
- Template::Text.new(options[:text])
- end
-
- if template
- layout = find(layout, details_for_render) if layout
- _render_template(template, layout, :locals => options[:locals])
+ content = _render_partial(options.merge(:partial => options[:layout]), &block)
+ safe_concat(content)
+ else
+ _render(options)
end
when :update
update_page(&block)
@@ -44,65 +28,57 @@ module ActionView
end
end
- def details_for_render
- controller.try(:details_for_render) || {:formats => formats}
+ # This is the API to render a ViewContext's template from a controller.
+ def render_template(options, &block)
+ _evaluate_assigns_and_ivars
+
+ # TODO Layout for partials should be handled here, because inside the
+ # partial renderer it looks for the layout as a partial.
+ if options.key?(:partial) && options[:layout]
+ options[:layout] = _find_layout(options[:layout])
+ end
+
+ _render(options, &block)
end
- # You can think of a layout as a method that is called with a block. _layout_for
- # returns the contents that are yielded to the layout. If the user calls yield
- # :some_name, the block, by default, returns content_for(:some_name). If the user
- # calls yield, the default block returns content_for(:layout).
- #
- # The user can override this default by passing a block to the layout.
- #
- # ==== Example
- #
- # # The template
- # <% render :layout => "my_layout" do %>Content<% end %>
- #
- # # The layout
- # <html><% yield %></html>
- #
- # In this case, instead of the default block, which would return content_for(:layout),
- # this method returns the block that was passed in to render layout, and the response
- # would be <html>Content</html>.
- #
- # Finally, the block can take block arguments, which can be passed in by yield.
- #
- # ==== Example
- #
- # # The template
- # <% render :layout => "my_layout" do |customer| %>Hello <%= customer.name %><% end %>
- #
- # # The layout
- # <html><% yield Struct.new(:name).new("David") %></html>
- #
- # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
- # and the Struct specified in the layout would be passed into the block. The result
- # would be <html>Hello David</html>.
- def _layout_for(name = nil, &block)
- return @_content_for[name || :layout] if !block_given? || name
- capture(&block)
+ # This method holds the common render logic for both controllers and
+ # views rendering stacks.
+ def _render(options) #:nodoc:
+ if options.key?(:partial)
+ _render_partial(options)
+ else
+ template = _determine_template(options)
+ yield template if block_given?
+ _render_template(template, options[:layout], options)
+ end
end
- # This is the API to render a ViewContext's template from a controller.
- #
- # Internal Options:
- # _template:: The Template object to render
- # _layout:: The layout, if any, to wrap the Template in
- def render_template(options)
- _evaluate_assigns_and_ivars
- template, layout = options.values_at(:_template, :_layout)
- _render_template(template, layout, options)
+ # Determine the template to be rendered using the given options.
+ def _determine_template(options) #:nodoc:
+ if options.key?(:inline)
+ handler = Template.handler_class_for_extension(options[:type] || "erb")
+ Template.new(options[:inline], "inline template", handler, {})
+ elsif options.key?(:text)
+ Template::Text.new(options[:text], self.formats.try(:first))
+ elsif options.key?(:_template)
+ options[:_template]
+ elsif options.key?(:file)
+ with_fallbacks { find(options[:file], options[:prefix]) }
+ elsif options.key?(:template)
+ find(options[:template], options[:prefix])
+ end
end
- def _render_template(template, layout = nil, options = {})
+ # Renders the given template. An string representing the layout can be
+ # supplied as well.
+ def _render_template(template, layout = nil, options = {}) #:nodoc:
locals = options[:locals] || {}
+ layout = _find_layout(layout) if layout
ActiveSupport::Notifications.instrument("action_view.render_template",
:identifier => template.identifier, :layout => layout.try(:identifier)) do
- content = template.render(self, locals)
+ content = template.render(self, locals) { |*name| _layout_for(*name) }
@_content_for[:layout] = content
if layout
@@ -114,8 +90,5 @@ module ActionView
end
end
- def _render_layout(layout, locals, &block)
- layout.render(self, locals){ |*name| _layout_for(*name, &block) }
- end
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index cd6b1930a1..b4fdb49d3b 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -16,23 +16,22 @@ module ActionView
end
extend Template::Handlers
- attr_reader :source, :identifier, :handler, :mime_type, :formats, :details
+
+ attr_reader :source, :identifier, :handler, :virtual_path, :formats
def initialize(source, identifier, handler, details)
@source = source
@identifier = identifier
@handler = handler
- @details = details
+
+ @partial = details[:partial]
+ @virtual_path = details[:virtual_path]
@method_names = {}
- format = details.delete(:format) || begin
- # TODO: Clean this up
- handler.respond_to?(:default_format) ? handler.default_format.to_sym.to_s : "html"
- end
- @mime_type = Mime::Type.lookup_by_extension(format.to_s)
- @formats = [format.to_sym]
- @formats << :html if format == :js
- @details[:formats] = Array.wrap(format.to_sym)
+ format = details[:format]
+ format ||= handler.default_format.to_sym if handler.respond_to?(:default_format)
+ format ||= :html
+ @formats = [format.to_sym]
end
def render(view, locals, &block)
@@ -47,19 +46,20 @@ module ActionView
end
end
- # TODO: Figure out how to abstract this
+ def mime_type
+ @mime_type ||= Mime::Type.lookup_by_extension(@formats.first.to_s) if @formats.first
+ end
+
def variable_name
- @variable_name ||= identifier[%r'_?(\w+)(\.\w+)*$', 1].to_sym
+ @variable_name ||= @virtual_path[%r'_?(\w+)(\.\w+)*$', 1].to_sym
end
- # TODO: Figure out how to abstract this
def counter_name
@counter_name ||= "#{variable_name}_counter".to_sym
end
- # TODO: kill hax
def partial?
- @details[:partial]
+ @partial
end
def inspect
@@ -87,7 +87,7 @@ module ActionView
source = <<-end_src
def #{method_name}(local_assigns)
- _old_virtual_path, @_virtual_path = @_virtual_path, #{@details[:virtual_path].inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
+ _old_virtual_path, @_virtual_path = @_virtual_path, #{@virtual_path.inspect};_old_output_buffer = output_buffer;#{locals_code};#{code}
ensure
@_virtual_path, self.output_buffer = _old_virtual_path, _old_output_buffer
end
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
index 4b7cec50f3..937694ce8e 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -3,10 +3,54 @@ require 'active_support/core_ext/string/output_safety'
require 'erubis'
module ActionView
+ class OutputBuffer
+ def initialize
+ @buffer = ActiveSupport::SafeBuffer.new
+ end
+
+ def safe_concat(value)
+ @buffer.safe_concat(value)
+ end
+
+ def <<(value)
+ @buffer << value.to_s
+ end
+
+ def length
+ @buffer.length
+ end
+
+ def [](*args)
+ @buffer[*args]
+ end
+
+ def to_s
+ @buffer.to_s
+ end
+
+ def to_str
+ @buffer.to_str
+ end
+
+ def empty?
+ @buffer.empty?
+ end
+
+ def html_safe?
+ @buffer.html_safe?
+ end
+
+ if "".respond_to?(:force_encoding)
+ def force_encoding(encoding)
+ @buffer.force_encoding(encoding)
+ end
+ end
+ end
+
module Template::Handlers
class Erubis < ::Erubis::Eruby
def add_preamble(src)
- src << "@output_buffer = ActiveSupport::SafeBuffer.new;"
+ src << "@output_buffer = ActionView::OutputBuffer.new;"
end
def add_text(src, text)
@@ -15,10 +59,10 @@ module ActionView
end
def add_expr_literal(src, code)
- if code =~ /\s*raw\s+(.*)/
- src << "@output_buffer.safe_concat((" << $1 << ").to_s);"
+ if code =~ /(do|\{)(\s*\|[^|]*\|)?\s*\Z/
+ src << '@output_buffer << ' << code
else
- src << '@output_buffer << ((' << code << ').to_s);'
+ src << '@output_buffer << (' << code << ');'
end
end
@@ -42,7 +86,7 @@ module ActionView
self.erb_trim_mode = '-'
self.default_format = Mime::HTML
-
+
cattr_accessor :erb_implementation
self.erb_implementation = Erubis
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 20402f9d6d..a43597e728 100644
--- a/actionpack/lib/action_view/template/resolver.rb
+++ b/actionpack/lib/action_view/template/resolver.rb
@@ -4,64 +4,47 @@ require "active_support/core_ext/array/wrap"
require "action_view/template"
module ActionView
- # Abstract superclass
class Resolver
-
- class_inheritable_accessor(:registered_details)
- self.registered_details = {}
-
- def self.register_detail(name, options = {})
- registered_details[name] = lambda do |val|
- val = Array.wrap(val || yield)
- val |= [nil] unless options[:allow_nil] == false
- val
- end
- end
-
- register_detail(:locale) { [I18n.locale] }
- register_detail(:formats) { Mime::SET.symbols }
- register_detail(:handlers) do
- Template::Handlers.extensions
+ def initialize
+ @cached = Hash.new { |h1,k1| h1[k1] =
+ Hash.new { |h2,k2| h2[k2] = Hash.new { |h3, k3| h3[k3] = {} } } }
end
- def initialize(options = {})
- @cache = options[:cache]
- @cached = {}
+ def clear_cache
+ @cached.clear
end
- # Normalizes the arguments and passes it on to find_template
def find(*args)
find_all(*args).first
end
- def find_all(name, details = {}, prefix = nil, partial = nil)
- details = normalize_details(details)
+ # Normalizes the arguments and passes it on to find_template.
+ def find_all(name, prefix=nil, partial=false, details={}, key=nil)
name, prefix = normalize_name(name, prefix)
+ details = details.merge(:handlers => default_handlers)
- cached([name, details, prefix, partial]) do
- find_templates(name, details, prefix, partial)
+ cached(key, prefix, name, partial) do
+ find_templates(name, prefix, partial, details)
end
end
private
+ def caching?
+ @caching ||= !defined?(Rails.application) || Rails.application.config.cache_classes
+ end
+
+ def default_handlers
+ Template::Handlers.extensions + [nil]
+ end
+
# This is what child classes implement. No defaults are needed
# because Resolver guarantees that the arguments are present and
# normalized.
- def find_templates(name, details, prefix, partial)
+ def find_templates(name, prefix, partial, details)
raise NotImplementedError
end
- def normalize_details(details)
- details = details.dup
- # TODO: Refactor this concern out of the resolver
- details.delete(:formats) if details[:formats] == [:"*/*"]
- registered_details.each do |k, v|
- details[k] = v.call(details[k])
- end
- details
- end
-
# Support legacy foo.erb names even though we now ignore .erb
# as well as incorrectly putting part of the path in the template
# name instead of the prefix.
@@ -73,92 +56,73 @@ module ActionView
return parts.pop, [prefix, *parts].compact.join("/")
end
- def cached(key)
- return yield unless @cache
- return @cached[key] if @cached.key?(key)
- @cached[key] = yield
+ def cached(key, prefix, name, partial)
+ return yield unless key && caching?
+ scope = @cached[key][prefix][name]
+ if scope.key?(partial)
+ scope[partial]
+ else
+ scope[partial] = yield
+ end
end
end
class PathResolver < Resolver
-
EXTENSION_ORDER = [:locale, :formats, :handlers]
def to_s
@path.to_s
end
- alias to_path to_s
-
- def find_templates(name, details, prefix, partial)
- path = build_path(name, details, prefix, partial)
- query(path, EXTENSION_ORDER.map { |ext| details[ext] })
- end
+ alias :to_path :to_s
private
- def build_path(name, details, prefix, partial)
+ def find_templates(name, prefix, partial, details)
+ path = build_path(name, prefix, partial, details)
+ query(partial, path, EXTENSION_ORDER.map { |ext| details[ext] })
+ end
+
+ def build_path(name, prefix, partial, details)
path = ""
path << "#{prefix}/" unless prefix.empty?
path << (partial ? "_#{name}" : name)
path
end
- def query(path, exts)
+ def query(partial, path, exts)
query = File.join(@path, path)
+
exts.each do |ext|
query << '{' << ext.map {|e| e && ".#{e}" }.join(',') << '}'
end
Dir[query].reject { |p| File.directory?(p) }.map do |p|
- Template.new(File.read(p), File.expand_path(p), *path_to_details(p))
+ handler, format = extract_handler_and_format(p)
+ Template.new(File.read(p), File.expand_path(p), handler,
+ :partial => partial, :virtual_path => path, :format => format)
end
end
- # # TODO: fix me
- # # :api: plugin
- def path_to_details(path)
- # [:erb, :format => :html, :locale => :en, :partial => true/false]
- if m = path.match(%r'((^|.*/)(_)?[\w-]+)((?:\.[\w-]+)*)\.(\w+)$')
- partial = m[3] == '_'
- details = (m[4]||"").split('.').reject { |e| e.empty? }
- handler = Template.handler_class_for_extension(m[5])
+ def extract_handler_and_format(path)
+ pieces = File.basename(path).split(".")
+ pieces.shift
- format = Mime[details.last] && details.pop.to_sym
- locale = details.last && details.pop.to_sym
-
- virtual_path = (m[1].gsub("#{@path}/", "") << details.join("."))
-
- return handler, :format => format, :locale => locale, :partial => partial,
- :virtual_path => virtual_path
- end
+ handler = Template.handler_class_for_extension(pieces.pop)
+ format = pieces.last && Mime[pieces.last] && pieces.pop.to_sym
+ [handler, format]
end
end
class FileSystemResolver < PathResolver
- def initialize(path, options = {})
+ def initialize(path)
raise ArgumentError, "path already is a Resolver class" if path.is_a?(Resolver)
- super(options)
+ super()
@path = Pathname.new(path).expand_path
end
- end
-
- # TODO: remove hack
- class FileSystemResolverWithFallback < Resolver
- def initialize(path, options = {})
- super(options)
- @paths = [FileSystemResolver.new(path, options), FileSystemResolver.new("", options), FileSystemResolver.new("/", options)]
- end
- def find_templates(*args)
- @paths.each do |p|
- template = p.find_templates(*args)
- return template unless template.empty?
- end
- []
- end
-
- def to_s
- @paths.first.to_s
+ def eql?(resolver)
+ self.class.equal?(resolver.class) && to_path == resolver.to_path
end
+ alias :== :eql?
end
end
diff --git a/actionpack/lib/action_view/template/text.rb b/actionpack/lib/action_view/template/text.rb
index 2abb352d4e..df394b0fb0 100644
--- a/actionpack/lib/action_view/template/text.rb
+++ b/actionpack/lib/action_view/template/text.rb
@@ -1,15 +1,10 @@
module ActionView #:nodoc:
class Template
class Text < String #:nodoc:
- HTML = Mime[:html]
-
- def initialize(string, content_type = HTML)
+ def initialize(string, content_type = nil)
super(string.to_s)
- @content_type = Mime[content_type] || content_type
- end
-
- def details
- {:formats => [@content_type.to_sym]}
+ @content_type = Mime[content_type] || content_type if content_type
+ @content_type ||= Mime::TEXT
end
def identifier
@@ -29,7 +24,7 @@ module ActionView #:nodoc:
end
def formats
- [mime_type]
+ [@content_type.to_sym]
end
def partial?
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index f639700eaf..1578ac9479 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -1,4 +1,5 @@
require 'action_controller/test_case'
+require 'action_view'
module ActionView
class Base
@@ -34,6 +35,8 @@ module ActionView
@request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new
+ @request.env.delete('PATH_INFO')
+
@params = {}
end
end
@@ -60,6 +63,10 @@ module ActionView
make_test_case_available_to_view!
end
+ def config
+ @controller.config
+ end
+
def render(options = {}, local_assigns = {}, &block)
@rendered << output = _view.render(options, local_assigns, &block)
output