aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib')
-rw-r--r--actionpack/lib/abstract_controller.rb6
-rw-r--r--actionpack/lib/abstract_controller/base.rb18
-rw-r--r--actionpack/lib/abstract_controller/collector.rb2
-rw-r--r--actionpack/lib/abstract_controller/compatibility.rb18
-rw-r--r--actionpack/lib/abstract_controller/helpers.rb8
-rw-r--r--actionpack/lib/abstract_controller/layouts.rb134
-rw-r--r--actionpack/lib/abstract_controller/localized_cache.rb49
-rw-r--r--actionpack/lib/abstract_controller/logger.rb4
-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.rb34
-rw-r--r--actionpack/lib/action_controller/caching.rb37
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb4
-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.rb2
-rw-r--r--actionpack/lib/action_controller/deprecated/base.rb131
-rw-r--r--actionpack/lib/action_controller/metal.rb43
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb98
-rw-r--r--actionpack/lib/action_controller/metal/configuration.rb28
-rw-r--r--actionpack/lib/action_controller/metal/head.rb1
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb4
-rw-r--r--actionpack/lib/action_controller/metal/hide_actions.rb10
-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/redirecting.rb1
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb68
-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/streaming.rb67
-rw-r--r--actionpack/lib/action_controller/metal/testing.rb1
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb167
-rw-r--r--actionpack/lib/action_controller/middleware.rb3
-rw-r--r--actionpack/lib/action_controller/polymorphic_routes.rb3
-rw-r--r--actionpack/lib/action_controller/railtie.rb70
-rw-r--r--actionpack/lib/action_controller/railties/log_subscriber.rb (renamed from actionpack/lib/action_controller/railties/subscriber.rb)12
-rw-r--r--actionpack/lib/action_controller/railties/url_helpers.rb14
-rw-r--r--actionpack/lib/action_controller/record_identifier.rb21
-rw-r--r--actionpack/lib/action_controller/test_case.rb31
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb76
-rw-r--r--actionpack/lib/action_dispatch.rb1
-rw-r--r--actionpack/lib/action_dispatch/http/cache.rb21
-rw-r--r--actionpack/lib/action_dispatch/http/filter_parameters.rb65
-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/response.rb49
-rw-r--r--actionpack/lib/action_dispatch/http/url.rb37
-rw-r--r--actionpack/lib/action_dispatch/middleware/callbacks.rb2
-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.rb3
-rw-r--r--actionpack/lib/action_dispatch/routing.rb1
-rw-r--r--actionpack/lib/action_dispatch/routing/deprecated_mapper.rb40
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb54
-rw-r--r--actionpack/lib/action_dispatch/routing/route.rb4
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb358
-rw-r--r--actionpack/lib/action_dispatch/routing/url_for.rb139
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/response.rb23
-rw-r--r--actionpack/lib/action_dispatch/testing/assertions/routing.rb27
-rw-r--r--actionpack/lib/action_dispatch/testing/integration.rb57
-rw-r--r--actionpack/lib/action_dispatch/testing/test_process.rb2
-rw-r--r--actionpack/lib/action_pack/version.rb7
-rw-r--r--actionpack/lib/action_view.rb15
-rw-r--r--actionpack/lib/action_view/base.rb94
-rw-r--r--actionpack/lib/action_view/helpers.rb1
-rw-r--r--actionpack/lib/action_view/helpers/active_model_helper.rb20
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb17
-rw-r--r--actionpack/lib/action_view/helpers/atom_feed_helper.rb4
-rw-r--r--actionpack/lib/action_view/helpers/capture_helper.rb14
-rw-r--r--actionpack/lib/action_view/helpers/date_helper.rb11
-rw-r--r--actionpack/lib/action_view/helpers/deprecated_block_helpers.rb52
-rw-r--r--actionpack/lib/action_view/helpers/form_helper.rb48
-rw-r--r--actionpack/lib/action_view/helpers/form_options_helper.rb7
-rw-r--r--actionpack/lib/action_view/helpers/form_tag_helper.rb26
-rw-r--r--actionpack/lib/action_view/helpers/javascript_helper.rb10
-rw-r--r--actionpack/lib/action_view/helpers/number_helper.rb86
-rw-r--r--actionpack/lib/action_view/helpers/prototype_helper.rb28
-rw-r--r--actionpack/lib/action_view/helpers/tag_helper.rb28
-rw-r--r--actionpack/lib/action_view/helpers/translation_helper.rb9
-rw-r--r--actionpack/lib/action_view/helpers/url_helper.rb23
-rw-r--r--actionpack/lib/action_view/lookup_context.rb154
-rw-r--r--actionpack/lib/action_view/paths.rb85
-rw-r--r--actionpack/lib/action_view/railtie.rb8
-rw-r--r--actionpack/lib/action_view/railties/log_subscriber.rb (renamed from actionpack/lib/action_view/railties/subscriber.rb)2
-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.rb25
-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.rb9
98 files changed, 1975 insertions, 1893 deletions
diff --git a/actionpack/lib/abstract_controller.rb b/actionpack/lib/abstract_controller.rb
index 1e15ab090c..2da4dc052c 100644
--- a/actionpack/lib/abstract_controller.rb
+++ b/actionpack/lib/abstract_controller.rb
@@ -3,8 +3,11 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) && !$:.inc
require 'active_support/ruby/shim'
require 'active_support/dependencies/autoload'
+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
@@ -12,11 +15,10 @@ module AbstractController
autoload :Base
autoload :Callbacks
autoload :Collector
- autoload :Compatibility
autoload :Helpers
autoload :Layouts
- autoload :LocalizedCache
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 3119ee498b..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
@@ -84,15 +93,14 @@ module AbstractController
# ==== Returns
# String
def controller_path
- @controller_path ||= name && name.sub(/Controller$/, '').underscore
+ @controller_path ||= name.sub(/Controller$/, '').underscore unless anonymous?
end
end
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/collector.rb b/actionpack/lib/abstract_controller/collector.rb
index d429333661..81fb514770 100644
--- a/actionpack/lib/abstract_controller/collector.rb
+++ b/actionpack/lib/abstract_controller/collector.rb
@@ -1,3 +1,5 @@
+require "action_dispatch/http/mime_type"
+
module AbstractController
module Collector
def self.generate_method_for_mime(mime)
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/helpers.rb b/actionpack/lib/abstract_controller/helpers.rb
index 578b884a4d..f875213afb 100644
--- a/actionpack/lib/abstract_controller/helpers.rb
+++ b/actionpack/lib/abstract_controller/helpers.rb
@@ -1,6 +1,4 @@
require 'active_support/dependencies'
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/module/delegation'
module AbstractController
module Helpers
@@ -27,7 +25,7 @@ module AbstractController
def inherited(klass)
helpers = _helpers
klass._helpers = Module.new { include helpers }
- klass.class_eval { default_helper_module! unless name.blank? }
+ klass.class_eval { default_helper_module! unless anonymous? }
super
end
@@ -99,7 +97,7 @@ module AbstractController
def helper(*args, &block)
self._helper_serial = AbstractController::Helpers.next_serial + 1
- _modules_for_helpers(args).each do |mod|
+ modules_for_helpers(args).each do |mod|
add_template_helper(mod)
end
@@ -134,7 +132,7 @@ module AbstractController
# ==== Returns
# Array[Module]:: A normalized list of modules for the list of
# helpers provided.
- def _modules_for_helpers(args)
+ def modules_for_helpers(args)
args.flatten.map! do |arg|
case arg
when String, Symbol
diff --git a/actionpack/lib/abstract_controller/layouts.rb b/actionpack/lib/abstract_controller/layouts.rb
index 0d214396aa..2f9616124a 100644
--- a/actionpack/lib/abstract_controller/layouts.rb
+++ b/actionpack/lib/abstract_controller/layouts.rb
@@ -1,6 +1,3 @@
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/module/delegation'
-
module AbstractController
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
# repeated setups. The inclusion pattern has pages that look like this:
@@ -173,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
@@ -262,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 " \
@@ -276,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
@@ -300,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.
@@ -345,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,
@@ -357,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.
#
@@ -392,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/localized_cache.rb b/actionpack/lib/abstract_controller/localized_cache.rb
deleted file mode 100644
index 5e3efa002c..0000000000
--- a/actionpack/lib/abstract_controller/localized_cache.rb
+++ /dev/null
@@ -1,49 +0,0 @@
-module AbstractController
- class HashKey
- @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|sh,sk| sh[sk] = {} } }
-
- def self.get(klass, formats, locale)
- @hash_keys[klass][formats][locale] ||= new(klass, formats, locale)
- end
-
- attr_accessor :hash
- def initialize(klass, formats, locale)
- @formats, @locale = formats, locale
- @hash = [formats, locale].hash
- end
-
- alias_method :eql?, :equal?
-
- def inspect
- "#<HashKey -- formats: #{@formats.inspect} locale: #{@locale.inspect}>"
- end
- end
-
- module LocalizedCache
- 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(*args)
- Thread.current[:format_locale_key] = HashKey.get(self.class, formats, I18n.locale)
- super
- end
-
- private
-
- def with_template_cache(name)
- self.class.template_cache[Thread.current[:format_locale_key]][name] ||= super
- end
-
- end
-end
diff --git a/actionpack/lib/abstract_controller/logger.rb b/actionpack/lib/abstract_controller/logger.rb
index a23a13e1d6..9318f5e369 100644
--- a/actionpack/lib/abstract_controller/logger.rb
+++ b/actionpack/lib/abstract_controller/logger.rb
@@ -1,5 +1,5 @@
-require 'active_support/core_ext/logger'
-require 'active_support/benchmarkable'
+require "active_support/core_ext/logger"
+require "active_support/benchmarkable"
module AbstractController
module Logger
diff --git a/actionpack/lib/abstract_controller/rendering.rb b/actionpack/lib/abstract_controller/rendering.rb
index 619a49571b..42f4939108 100644
--- a/actionpack/lib/abstract_controller/rendering.rb
+++ b/actionpack/lib/abstract_controller/rendering.rb
@@ -1,7 +1,4 @@
require "abstract_controller/base"
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/module/delegation'
-require 'active_support/core_ext/array/wrap'
module AbstractController
class DoubleRenderError < Error
@@ -12,32 +9,47 @@ 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 to change the default behavior.
+ # Override this method in a module to change the default behavior.
def view_context
@_view_context ||= ActionView::Base.for_controller(self)
end
@@ -45,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.
@@ -117,122 +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) do
- find_template(name, details, options)
+ if (options.keys & [:partial, :file, :template]).empty?
+ options[:prefix] ||= _prefix
end
- end
-
- def _normalize_details(options)
- details = { :formats => formats }
- 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)
- 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 10244f8216..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 template_exists?(action_name.to_s, {:formats => formats}, :_prefix => controller_path)
- "default_render"
- end
- end
- end
- end
-
include ImplicitRender
include ActionController::Rescue
@@ -82,12 +59,9 @@ module ActionController
filter
end
- protected
+ ActionController.run_base_hooks(self)
- # Overwrite url rewriter to use request.
- def _url_rewriter
- return ActionController::UrlRewriter unless request
- @_url_rewriter ||= ActionController::UrlRewriter.new(request, params)
- end
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/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index 00a7f034d3..bb5ff95a67 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -41,7 +41,9 @@ module ActionController #:nodoc:
else
pos = buffer.length
block.call
- write_fragment(name, buffer[pos..-1], options)
+ content = buffer[pos..-1]
+ content = content.as_str if content.respond_to?(:as_str)
+ write_fragment(name, content, options)
end
else
block.call
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.rb b/actionpack/lib/action_controller/deprecated.rb
index a4eef07841..9f2de57033 100644
--- a/actionpack/lib/action_controller/deprecated.rb
+++ b/actionpack/lib/action_controller/deprecated.rb
@@ -1,5 +1,3 @@
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
ActionController::Routing = ActionDispatch::Routing
-ActionController::Routing::Routes = ActionDispatch::Routing::RouteSet.new
-ActionController::UrlWriter = ActionController::UrlFor
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 2b35e111ec..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 = {}
@@ -49,6 +50,14 @@ module ActionController
headers["Content-Type"] = type.to_s
end
+ def content_type
+ headers["Content-Type"]
+ end
+
+ def location
+ headers["Location"]
+ end
+
def location=(url)
headers["Location"] = url
end
@@ -58,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
@@ -70,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)
@@ -118,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 a1cfa32d4d..ab8d87b2c4 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -2,26 +2,23 @@ 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"
- delegate :resources_path_names, :to => "ActionController::Routing::Routes"
- delegate :resources_path_names=, :to => "ActionController::Routing::Routes"
end
# cattr_reader :protected_instance_variables
@@ -32,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
@@ -66,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
@@ -126,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/head.rb b/actionpack/lib/action_controller/metal/head.rb
index 37be8b3999..a5c9910d68 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -1,6 +1,7 @@
module ActionController
module Head
extend ActiveSupport::Concern
+
include ActionController::UrlFor
# Return a response that has no content (merely headers). The options
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 1b5a4c3080..8efe01e37b 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -86,7 +86,7 @@ module ActionController
end
private
- # Overwrite _modules_for_helpers to accept :all as argument, which loads
+ # Overwrite modules_for_helpers to accept :all as argument, which loads
# all helpers in helpers_dir.
#
# ==== Parameters
@@ -95,7 +95,7 @@ module ActionController
# ==== Returns
# Array[Module]:: A normalized list of modules for the list of
# helpers provided.
- def _modules_for_helpers(args)
+ def modules_for_helpers(args)
args += all_application_helpers if args.delete(:all)
super(args)
end
diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb
index e893acffdf..3358d80c35 100644
--- a/actionpack/lib/action_controller/metal/hide_actions.rb
+++ b/actionpack/lib/action_controller/metal/hide_actions.rb
@@ -15,10 +15,8 @@ module ActionController
# Overrides AbstractController::Base#action_method? to return false if the
# action name is in the list of hidden actions.
- def action_method?(action_name)
- self.class.visible_action?(action_name) do
- !self.class.hidden_actions.include?(action_name) && super
- end
+ def method_for_action(action_name)
+ self.class.visible_action?(action_name) && super
end
module ClassMethods
@@ -31,13 +29,13 @@ module ActionController
end
def inherited(klass)
- klass.instance_variable_set("@visible_actions", {})
+ klass.class_eval { @visible_actions = {} }
super
end
def visible_action?(action_name)
return @visible_actions[action_name] if @visible_actions.key?(action_name)
- @visible_actions[action_name] = yield
+ @visible_actions[action_name] = !hidden_actions.include?(action_name)
end
# Overrides AbstractController::Base#action_methods to remove any methods
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/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index faf0589fd2..25e4e18493 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -11,6 +11,7 @@ module ActionController
extend ActiveSupport::Concern
include AbstractController::Logger
+ include ActionController::RackDelegation
include ActionController::UrlFor
# Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 639b508746..49d3d6b466 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -19,7 +19,7 @@ module ActionController
<<-RUBY_EVAL
if options.key?(:#{name})
_process_options(options)
- return _render_option_#{name}(options[:#{name}], options)
+ return _render_option_#{name}(options.delete(:#{name}), options)
end
RUBY_EVAL
end
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 0aae9f8579..f892bd9b91 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -2,68 +2,54 @@ module ActionController
module Rendering
extend ActiveSupport::Concern
- included do
- include AbstractController::Rendering
- include AbstractController::LocalizedCache
- end
+ include ActionController::RackDelegation
+ include AbstractController::Rendering
- 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)
- if response_body
- raise ::AbstractController::DoubleRenderError
- end
-
- args << {} unless args.last.is_a?(Hash)
- super(*args)
- self.content_type ||= args.last[:_template].mime_type.to_s
- response_body
- end
-
- def render_to_body(options)
- _process_options(options)
+ raise ::AbstractController::DoubleRenderError if response_body
super
+ response_body
end
private
- def _render_partial(options)
- options[:partial] = action_name if options[:partial] == true
- options[:_details] = {:formats => formats}
- super
+ def _normalize_args(action=nil, options={}, &blk)
+ options = super
+ options[:update] = blk if block_given?
+ options
end
- def format_for_text
- formats.first
+ def _normalize_options(options)
+ if options.key?(:text) && options[:text].respond_to?(:to_text)
+ options[:text] = options[:text].to_text
+ end
+
+ if options[:status]
+ options[:status] = Rack::Utils.status_code(options[:status])
+ end
+
+ 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
- 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
-
- if options[:status]
- options[:status] = Rack::Utils.status_code(options[:status])
- end
+ super
+ end
- options[:update] = blk if block_given?
- options
+ 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..39a809657b 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 ||= :authenticity_token
# 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/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 8f03b8bb17..753af3dc58 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -9,18 +9,13 @@ module ActionController #:nodoc:
DEFAULT_SEND_FILE_OPTIONS = {
:type => 'application/octet-stream'.freeze,
:disposition => 'attachment'.freeze,
- :stream => true,
- :buffer_size => 4096,
- :x_sendfile => false
}.freeze
- X_SENDFILE_HEADER = 'X-Sendfile'.freeze
-
protected
- # Sends the file, by default streaming it 4096 bytes at a time. This way the
- # whole file doesn't need to be read into memory at once. This makes it
- # feasible to send even large files. You can optionally turn off streaming
- # and send the whole file at once.
+ # Sends the file. This uses a server-appropriate method (such as X-Sendfile)
+ # via the Rack::Sendfile middleware. The header to use is set via
+ # config.action_dispatch.x_sendfile_header, and defaults to "X-Sendfile".
+ # Your server can also configure this for you by setting the X-Sendfile-Type header.
#
# Be careful to sanitize the path parameter if it is coming from a web
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
@@ -31,24 +26,12 @@ module ActionController #:nodoc:
# Defaults to <tt>File.basename(path)</tt>.
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
- # * <tt>:length</tt> - used to manually override the length (in bytes) of the content that
- # is going to be sent to the client. Defaults to <tt>File.size(path)</tt>.
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
# Valid values are 'inline' and 'attachment' (default).
- # * <tt>:stream</tt> - whether to send the file to the user agent as it is read (+true+)
- # or to read the entire file before sending (+false+). Defaults to +true+.
- # * <tt>:buffer_size</tt> - specifies size (in bytes) of the buffer used to stream the file.
- # Defaults to 4096.
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
# the URL, which is necessary for i18n filenames on certain browsers
# (setting <tt>:filename</tt> overrides this option).
- # * <tt>:x_sendfile</tt> - uses X-Sendfile to send the file when set to +true+. This is currently
- # only available with Lighttpd/Apache2 and specific modules installed and activated. Since this
- # uses the web server to send the file, this may lower memory consumption on your server and
- # it will not block your application for further requests.
- # See http://blog.lighttpd.net/articles/2006/07/02/x-sendfile and
- # http://tn123.ath.cx/mod_xsendfile/ for details. Defaults to +false+.
#
# The default Content-Type and Content-Disposition headers are
# set to download arbitrary binary files in as many browsers as
@@ -81,29 +64,16 @@ module ActionController #:nodoc:
def send_file(path, options = {}) #:doc:
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
- options[:length] ||= File.size(path)
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
send_file_headers! options
- @performed_render = false
-
if options[:x_sendfile]
- head options[:status], X_SENDFILE_HEADER => path
- else
- if options[:stream]
- # TODO : Make render :text => proc {} work with the new base
- render :status => options[:status], :text => Proc.new { |response, output|
- len = options[:buffer_size] || 4096
- File.open(path, 'rb') do |file|
- while buf = file.read(len)
- output.write(buf)
- end
- end
- }
- else
- File.open(path, 'rb') { |file| render :status => options[:status], :text => file.read }
- end
+ ActiveSupport::Deprecation.warn(":x_sendfile is no longer needed in send_file", caller)
end
+
+ self.status = options[:status] || 200
+ self.content_type = options[:content_type] if options.key?(:content_type)
+ self.response_body = File.open(path, "rb")
end
# Sends the given binary data to the browser. This method is similar to
@@ -138,32 +108,35 @@ module ActionController #:nodoc:
# data to the browser, then use <tt>render :text => proc { ... }</tt>
# instead. See ActionController::Base#render for more information.
def send_data(data, options = {}) #:doc:
- send_file_headers! options.merge(:length => data.bytesize)
- render :status => options[:status], :text => data
+ send_file_headers! options.dup
+ render options.slice(:status, :content_type).merge(:text => data)
end
private
def send_file_headers!(options)
options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
- [:length, :type, :disposition].each do |arg|
+ [:type, :disposition].each do |arg|
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
end
- disposition = options[:disposition].dup || 'attachment'
+ if options.key?(:length)
+ ActiveSupport::Deprecation.warn("You do not need to provide the file's length", caller)
+ end
- disposition <<= %(; filename="#{options[:filename]}") if options[:filename]
+ disposition = options[:disposition]
+ disposition += %(; filename="#{options[:filename]}") if options[:filename]
content_type = options[:type]
if content_type.is_a?(Symbol)
- raise ArgumentError, "Unknown MIME type #{options[:type]}" unless Mime::EXTENSION_LOOKUP.key?(content_type.to_s)
- self.content_type = Mime::Type.lookup_by_extension(content_type.to_s)
+ extension = Mime[content_type]
+ raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
+ self.content_type = extension
else
self.content_type = content_type
end
headers.merge!(
- 'Content-Length' => options[:length].to_s,
'Content-Disposition' => disposition,
'Content-Transfer-Encoding' => 'binary'
)
diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb
index 707ad968f4..4b8c452d50 100644
--- a/actionpack/lib/action_controller/metal/testing.rb
+++ b/actionpack/lib/action_controller/metal/testing.rb
@@ -13,7 +13,6 @@ module ActionController
if cookies = @_request.env['action_dispatch.cookies']
cookies.write(@_response)
end
- @_response.body ||= self.response_body
@_response.prepare!
set_test_assigns
ret
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index 4f3ad07be5..10c7ca9021 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -1,165 +1,20 @@
-require 'active_support/core_ext/class/attribute'
-require 'active_support/core_ext/module/attribute_accessors'
-
module ActionController
- # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
- # is also possible: an URL can be generated from one of your routing definitions.
- # URL generation functionality is centralized in this module.
- #
- # See ActionController::Routing and ActionController::Resources for general
- # information about routing and routes.rb.
- #
- # <b>Tip:</b> If you need to generate URLs from your models or some other place,
- # then ActionController::UrlFor is what you're looking for. Read on for
- # an introduction.
- #
- # == URL generation from parameters
- #
- # As you may know, some functions - such as ActionController::Base#url_for
- # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
- # of parameters. For example, you've probably had the chance to write code
- # like this in one of your views:
- #
- # <%= link_to('Click here', :controller => 'users',
- # :action => 'new', :message => 'Welcome!') %>
- #
- # #=> Generates a link to: /users/new?message=Welcome%21
- #
- # link_to, and all other functions that require URL generation functionality,
- # actually use ActionController::UrlFor under the hood. And in particular,
- # they use the ActionController::UrlFor#url_for method. One can generate
- # the same path as the above example by using the following code:
- #
- # include UrlFor
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :only_path => true)
- # # => "/users/new?message=Welcome%21"
- #
- # Notice the <tt>:only_path => true</tt> part. This is because UrlFor has no
- # information about the website hostname that your Rails app is serving. So if you
- # want to include the hostname as well, then you must also pass the <tt>:host</tt>
- # argument:
- #
- # include UrlFor
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :host => 'www.example.com') # Changed this.
- # # => "http://www.example.com/users/new?message=Welcome%21"
- #
- # By default, all controllers and views have access to a special version of url_for,
- # that already knows what the current hostname is. So if you use url_for in your
- # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
- # argument.
- #
- # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
- # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
- # in full. However, mailers don't have hostname information, and what's why you'll still
- # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
- #
- #
- # == URL generation for named routes
- #
- # UrlFor also allows one to access methods that have been auto-generated from
- # named routes. For example, suppose that you have a 'users' resource in your
- # <b>routes.rb</b>:
- #
- # map.resources :users
- #
- # This generates, among other things, the method <tt>users_path</tt>. By default,
- # this method is accessible from your controllers, views and mailers. If you need
- # to access this auto-generated method from other places (such as a model), then
- # you can do that by including ActionController::UrlFor in your class:
- #
- # class User < ActiveRecord::Base
- # include ActionController::UrlFor
- #
- # def base_uri
- # user_path(self)
- # end
- # end
- #
- # User.find(1).base_uri # => "/users/1"
- #
module UrlFor
extend ActiveSupport::Concern
- included do
- ActionController::Routing::Routes.install_helpers(self)
-
- # 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
- end
+ include ActionDispatch::Routing::UrlFor
- self.default_url_options = {}
+ 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
- # Overwrite to implement a number of default options that all url_for-based methods will use. The default options should come in
- # the form of a hash, just like the one you would use for url_for directly. Example:
- #
- # def default_url_options(options)
- # { :project => @project.active? ? @project.url_name : "unknown" }
- # end
- #
- # As you can infer from the example, this is mostly useful for situations where you want to centralize dynamic decisions about the
- # urls as they stem from the business domain. Please note that any individual url_for call can always override the defaults set
- # by this method.
- def default_url_options(options = nil)
- self.class.default_url_options
- end
-
- def rewrite_options(options) #:nodoc:
- if options.delete(:use_defaults) != false && (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:
- #
- # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
- # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
- # * <tt>:host</tt> - Specifies the host the link should be targeted at.
- # If <tt>:only_path</tt> is false, this option must be
- # 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
- # +url_for+ is forwarded to the Routes module.
- #
- # Examples:
- #
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
- # 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 ||= {}
- case options
- when String
- options
- when Hash
- _url_rewriter.rewrite(rewrite_options(options))
- else
- polymorphic_url(options)
- end
- end
-
- protected
-
- def _url_rewriter
- ActionController::UrlRewriter
+ def _router
+ raise "In order to use #url_for, you must include the helpers of a particular " \
+ "router. For instance, `include Rails.application.routes.url_helpers"
end
end
-end
+end \ No newline at end of file
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/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb
index eaed00cfb7..ae363e300c 100644
--- a/actionpack/lib/action_controller/polymorphic_routes.rb
+++ b/actionpack/lib/action_controller/polymorphic_routes.rb
@@ -92,8 +92,7 @@ module ActionController
inflection = if options[:action].to_s == "new"
args.pop
:singular
- elsif (record.respond_to?(:new_record?) && record.new_record?) ||
- (record.respond_to?(:destroyed?) && record.destroyed?)
+ elsif (record.respond_to?(:persisted?) && !record.persisted?)
args.pop
:plural
elsif record.is_a?(Class)
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index 55a5c22ac0..6a3afbb157 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -1,30 +1,80 @@
-require "action_controller"
require "rails"
+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
railtie_name :action_controller
- require "action_controller/railties/subscriber"
- subscriber ActionController::Railties::Subscriber.new
+ 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_hook do
+ extend ::ActionController::Railtie::UrlHelpers.with(app.routes)
+ end
+
+ message = "ActionController::Routing::Routes is deprecated. " \
+ "Instead, use Rails.application.routes"
+
+ proxy = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(app.routes, message)
+ ActionController::Routing::Routes = proxy
end
end
-end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/railties/subscriber.rb b/actionpack/lib/action_controller/railties/log_subscriber.rb
index 4499e82292..c2299d0b05 100644
--- a/actionpack/lib/action_controller/railties/subscriber.rb
+++ b/actionpack/lib/action_controller/railties/log_subscriber.rb
@@ -1,6 +1,6 @@
module ActionController
module Railties
- class Subscriber < Rails::Subscriber
+ class LogSubscriber < Rails::LogSubscriber
INTERNAL_PARAMS = %w(controller action format _method only_path)
def start_processing(event)
@@ -22,15 +22,7 @@ module ActionController
end
def send_file(event)
- message = if event.payload[:x_sendfile]
- header = ActionController::Streaming::X_SENDFILE_HEADER
- "Sent #{header} header %s"
- elsif event.payload[:stream]
- "Streamed file %s"
- else
- "Sent file %s"
- end
-
+ message = "Sent file %s"
message << " (%.1fms)"
info(message % [event.payload[:path], event.duration])
end
diff --git a/actionpack/lib/action_controller/railties/url_helpers.rb b/actionpack/lib/action_controller/railties/url_helpers.rb
new file mode 100644
index 0000000000..ad2a8d4ef3
--- /dev/null
+++ b/actionpack/lib/action_controller/railties/url_helpers.rb
@@ -0,0 +1,14 @@
+module ActionController
+ class Railtie
+ module UrlHelpers
+ def self.with(router)
+ Module.new do
+ define_method(:inherited) do |klass|
+ super(klass)
+ klass.send(:include, router.url_helpers)
+ end
+ end
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb
index 1165c3b7c5..3f966b1b64 100644
--- a/actionpack/lib/action_controller/record_identifier.rb
+++ b/actionpack/lib/action_controller/record_identifier.rb
@@ -60,13 +60,32 @@ module ActionController
#
# dom_id(Post.find(45), :edit) # => "edit_post_45"
def dom_id(record, prefix = nil)
- if record_id = record.id
+ if record_id = record_key_for_dom_id(record)
"#{dom_class(record, prefix)}#{JOIN}#{record_id}"
else
dom_class(record, prefix || NEW)
end
end
+ # Returns a string representation of the key attribute(s) that is suitable for use in an HTML DOM id.
+ # This can be overwritten to customize the default generated string representation if desired.
+ # If you need to read back a key from a dom_id in order to query for the underlying database record,
+ # you should write a helper like 'person_record_from_dom_id' that will extract the key either based
+ # on the default implementation (which just joins all key attributes with '-') or on your own
+ # overwritten version of the method. By default, this implementation passes the key string through a
+ # method that replaces all characters that are invalid inside DOM ids, with valid ones. You need to
+ # make sure yourself that your dom ids are valid, in case you overwrite this method.
+ def record_key_for_dom_id(record)
+ return record.id unless record.respond_to?(:to_model)
+ key = record.to_model.to_key
+ key ? sanitize_dom_id(key.join('_')) : key
+ end
+
+ # Replaces characters that are invalid in HTML DOM ids with valid ones.
+ def sanitize_dom_id(candidate_id)
+ candidate_id # TODO implement conversion to valid DOM id values
+ end
+
# Returns the plural class name of a record or class. Examples:
#
# plural_class_name(post) # => "posts"
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 14557ca782..cdb5db32aa 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -17,9 +17,9 @@ module ActionController
end
end
- def assign_parameters(controller_path, action, parameters = {})
+ def assign_parameters(router, controller_path, action, parameters = {})
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
- extra_keys = ActionController::Routing::Routes.extra_keys(parameters)
+ extra_keys = router.extra_keys(parameters)
non_path_parameters = get? ? query_parameters : request_parameters
parameters.each do |key, value|
if value.is_a? Fixnum
@@ -220,7 +220,7 @@ module ActionController
def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
# Sanity check for required instance variables so we can give an
# understandable error message.
- %w(@controller @request @response).each do |iv_name|
+ %w(@router @controller @request @response).each do |iv_name|
if !(instance_variable_names.include?(iv_name) || instance_variable_names.include?(iv_name.to_sym)) || instance_variable_get(iv_name).nil?
raise "#{iv_name} is nil: make sure you set it in your test's setup method."
end
@@ -236,7 +236,7 @@ module ActionController
@request.env['REQUEST_METHOD'] = http_method
parameters ||= {}
- @request.assign_parameters(@controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
+ @request.assign_parameters(@router, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
@request.session = ActionController::TestSession.new(session) unless session.nil?
@request.session["flash"] = @request.flash.update(flash || {})
@@ -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']
- options = @controller.__send__(:rewrite_options, parameters)
- options.update(:only_path => true, :action => action)
-
- url = ActionController::UrlRewriter.new(@request, parameters)
- @request.request_uri = url.rewrite(options)
+ unless @request.env["PATH_INFO"]
+ options = @controller.__send__(:url_options).merge(parameters)
+ 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)
+
+ @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 933a1fa8f9..0000000000
--- a/actionpack/lib/action_controller/url_rewriter.rb
+++ /dev/null
@@ -1,76 +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(options = {})
- options[:host] ||= @request.host_with_port
- options[:protocol] ||= @request.protocol
-
- self.class.rewrite(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
-
- def self.rewrite(options, path_segments=nil)
- rewritten_url = ""
-
- 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 = Routing::Routes.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.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/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb
index 428e62dc6b..d2404e63c5 100644
--- a/actionpack/lib/action_dispatch/http/cache.rb
+++ b/actionpack/lib/action_dispatch/http/cache.rb
@@ -37,8 +37,21 @@ module ActionDispatch
end
module Response
- def cache_control
- @cache_control ||= {}
+ attr_reader :cache_control
+
+ def initialize(*)
+ status, header, body = super
+
+ @cache_control = {}
+ @etag = self["ETag"]
+
+ if cache_control = self["Cache-Control"]
+ cache_control.split(/,\s*/).each do |segment|
+ first, last = segment.split("=")
+ last ||= true
+ @cache_control[first.to_sym] = last
+ end
+ end
end
def last_modified
@@ -65,7 +78,7 @@ module ActionDispatch
def etag=(etag)
key = ActiveSupport::Cache.expand_cache_key(etag)
- @etag = %("#{Digest::MD5.hexdigest(key)}")
+ @etag = self["ETag"] = %("#{Digest::MD5.hexdigest(key)}")
end
private
@@ -100,6 +113,8 @@ module ActionDispatch
def set_conditional_cache_control!
control = @cache_control
+ return if self["Cache-Control"].present?
+
if control.empty?
headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
elsif @cache_control[:no_cache]
diff --git a/actionpack/lib/action_dispatch/http/filter_parameters.rb b/actionpack/lib/action_dispatch/http/filter_parameters.rb
index 1958e1668d..451b79b190 100644
--- a/actionpack/lib/action_dispatch/http/filter_parameters.rb
+++ b/actionpack/lib/action_dispatch/http/filter_parameters.rb
@@ -25,9 +25,16 @@ module ActionDispatch
module FilterParameters
extend ActiveSupport::Concern
+ mattr_reader :compiled_parameter_filter_for
+ @@compiled_parameter_filter_for = {}
+
# Return a hash of parameters with all sensitive data replaced.
def filtered_parameters
- @filtered_parameters ||= process_parameter_filter(parameters)
+ @filtered_parameters ||= if filtering_parameters?
+ process_parameter_filter(parameters)
+ else
+ parameters.dup
+ end
end
alias :fitered_params :filtered_parameters
@@ -46,10 +53,18 @@ module ActionDispatch
protected
- def compile_parameter_filter #:nodoc:
+ def filtering_parameters? #:nodoc:
+ @env["action_dispatch.parameter_filter"].present?
+ end
+
+ def process_parameter_filter(params) #:nodoc:
+ compiled_parameter_filter_for(@env["action_dispatch.parameter_filter"]).call(params)
+ end
+
+ def compile_parameter_filter(filters) #:nodoc:
strings, regexps, blocks = [], [], []
- Array(@env["action_dispatch.parameter_filter"]).each do |item|
+ filters.each do |item|
case item
when NilClass
when Proc
@@ -65,34 +80,34 @@ module ActionDispatch
[regexps, blocks]
end
- def filtering_parameters? #:nodoc:
- @env["action_dispatch.parameter_filter"].present?
- end
+ def compiled_parameter_filter_for(filters) #:nodoc:
+ @@compiled_parameter_filter_for[filters] ||= begin
+ regexps, blocks = compile_parameter_filter(filters)
- def process_parameter_filter(original_params) #:nodoc:
- return original_params.dup unless filtering_parameters?
+ lambda do |original_params|
+ filtered_params = {}
- filtered_params = {}
- regexps, blocks = compile_parameter_filter
+ original_params.each do |key, value|
+ if regexps.find { |r| key =~ r }
+ value = '[FILTERED]'
+ elsif value.is_a?(Hash)
+ value = process_parameter_filter(value)
+ elsif value.is_a?(Array)
+ value = value.map { |v| v.is_a?(Hash) ? process_parameter_filter(v) : v }
+ elsif blocks.present?
+ key = key.dup
+ value = value.dup if value.duplicable?
+ blocks.each { |b| b.call(key, value) }
+ end
- original_params.each do |key, value|
- if regexps.find { |r| key =~ r }
- value = '[FILTERED]'
- elsif value.is_a?(Hash)
- value = process_parameter_filter(value)
- elsif value.is_a?(Array)
- value = value.map { |i| process_parameter_filter(i) }
- elsif blocks.present?
- key = key.dup
- value = value.dup if value.duplicable?
- blocks.each { |b| b.call(key, value) }
- end
+ filtered_params[key] = value
+ end
- filtered_params[key] = value
+ filtered_params
+ end
end
-
- filtered_params
end
+
end
end
end \ No newline at end of file
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/response.rb b/actionpack/lib/action_dispatch/http/response.rb
index f299306ff4..9cfe5a5ea9 100644
--- a/actionpack/lib/action_dispatch/http/response.rb
+++ b/actionpack/lib/action_dispatch/http/response.rb
@@ -32,31 +32,38 @@ module ActionDispatch # :nodoc:
# end
# end
class Response < Rack::Response
- include ActionDispatch::Http::Cache::Response
-
attr_accessor :request, :blank
attr_writer :header, :sending_file
alias_method :headers=, :header=
- def initialize
- @status = 200
- @header = {}
- @cache_control = {}
+ module Setup
+ def initialize(status = 200, header = {}, body = [])
+ @writer = lambda { |x| @body << x }
+ @block = nil
+ @length = 0
- @writer = lambda { |x| @body << x }
- @block = nil
- @length = 0
+ @status, @header = status, header
+ self.body = body
- @body, @cookie = [], []
- @sending_file = false
+ @cookie = []
+ @sending_file = false
- @blank = false
- @etag = nil
+ @blank = false
+
+ if content_type = self["Content-Type"]
+ type, charset = content_type.split(/;\s*charset=/)
+ @content_type = Mime::Type.lookup(type)
+ @charset = charset || "UTF-8"
+ end
- yield self if block_given?
+ yield self if block_given?
+ end
end
+ include Setup
+ include ActionDispatch::Http::Cache::Response
+
def status=(status)
@status = Rack::Utils.status_code(status)
end
@@ -76,6 +83,18 @@ module ActionDispatch # :nodoc:
end
alias_method :status_message, :message
+ def respond_to?(method)
+ if method.to_sym == :to_path
+ @body.respond_to?(:to_path)
+ else
+ super
+ end
+ end
+
+ def to_path
+ @body.to_path
+ end
+
def body
str = ''
each { |part| str << part.to_s }
@@ -120,7 +139,7 @@ module ActionDispatch # :nodoc:
assign_default_content_type_and_charset!
handle_conditional_get!
self["Set-Cookie"] = @cookie.join("\n") unless @cookie.blank?
- self["ETag"] = @etag if @etag
+ self["ETag"] = @_etag if @_etag
super
end
diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb
index 40ceb5a9b6..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.
@@ -81,42 +81,15 @@ module ActionDispatch
parts[0..-(tld_length+2)]
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] || '')
+ def subdomain(tld_length = 1)
+ subdomains(tld_length).join('.')
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/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb
index 7cf75ffe63..d07841218a 100644
--- a/actionpack/lib/action_dispatch/middleware/callbacks.rb
+++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb
@@ -37,7 +37,7 @@ module ActionDispatch
def initialize(app, prepare_each_request = false)
@app, @prepare_each_request = app, prepare_each_request
- run_callbacks(:prepare) unless @prepare_each_request
+ run_callbacks(:prepare)
end
def call(env)
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 335daafc01..e486bd4079 100644
--- a/actionpack/lib/action_dispatch/railtie.rb
+++ b/actionpack/lib/action_dispatch/railtie.rb
@@ -5,6 +5,9 @@ module ActionDispatch
class Railtie < Rails::Railtie
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|
# TODO: This used to say unless defined?(Dispatcher). Find out why and fix.
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 335c9edb98..5bc3205c51 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -205,6 +205,7 @@ module ActionDispatch
autoload :Mapper, 'action_dispatch/routing/mapper'
autoload :Route, 'action_dispatch/routing/route'
autoload :RouteSet, 'action_dispatch/routing/route_set'
+ autoload :UrlFor, 'action_dispatch/routing/url_for'
SEPARATORS = %w( / . ? )
HTTP_METHODS = [:get, :head, :post, :put, :delete, :options]
diff --git a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
index 8ce6b2f6d5..dd650e83d9 100644
--- a/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
+++ b/actionpack/lib/action_dispatch/routing/deprecated_mapper.rb
@@ -1,5 +1,30 @@
module ActionDispatch
module Routing
+ class RouteSet
+ attr_accessor :controller_namespaces
+
+ CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/
+
+ def controller_constraints
+ @controller_constraints ||= begin
+ namespaces = controller_namespaces + in_memory_controller_namespaces
+ source = namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" }
+ source << CONTROLLER_REGEXP.source
+ Regexp.compile(source.sort.reverse.join('|'))
+ end
+ end
+
+ def in_memory_controller_namespaces
+ namespaces = Set.new
+ ActionController::Base.subclasses.each do |klass|
+ controller_name = klass.underscore
+ namespaces << controller_name.split('/')[0...-1].join('/')
+ end
+ namespaces.delete('')
+ namespaces
+ end
+ end
+
# Mapper instances are used to build routes. The object passed to the draw
# block in config/routes.rb is a Mapper instance.
#
@@ -244,14 +269,15 @@ module ActionDispatch
attr_reader :collection_methods, :member_methods, :new_methods
attr_reader :path_prefix, :name_prefix, :path_segment
attr_reader :plural, :singular
- attr_reader :options
+ attr_reader :options, :defaults
- def initialize(entities, options)
+ def initialize(entities, options, defaults)
@plural ||= entities
@singular ||= options[:singular] || plural.to_s.singularize
@path_segment = options.delete(:as) || @plural
@options = options
+ @defaults = defaults
arrange_actions
add_default_actions
@@ -280,7 +306,7 @@ module ActionDispatch
def new_path
new_action = self.options[:path_names][:new] if self.options[:path_names]
- new_action ||= ActionController::Base.resources_path_names[:new]
+ new_action ||= self.defaults[:path_names][:new]
@new_path ||= "#{path}/#{new_action}"
end
@@ -370,7 +396,7 @@ module ActionDispatch
end
class SingletonResource < Resource #:nodoc:
- def initialize(entity, options)
+ def initialize(entity, options, defaults)
@singular = @plural = entity
options[:controller] ||= @singular.to_s.pluralize
super
@@ -717,7 +743,7 @@ module ActionDispatch
private
def map_resource(entities, options = {}, &block)
- resource = Resource.new(entities, options)
+ resource = Resource.new(entities, options, :path_names => @set.resources_path_names)
with_options :controller => resource.controller do |map|
map_associations(resource, options)
@@ -734,7 +760,7 @@ module ActionDispatch
end
def map_singleton_resource(entities, options = {}, &block)
- resource = SingletonResource.new(entities, options)
+ resource = SingletonResource.new(entities, options, :path_names => @set.resources_path_names)
with_options :controller => resource.controller do |map|
map_associations(resource, options)
@@ -826,7 +852,7 @@ module ActionDispatch
actions.each do |action|
[method].flatten.each do |m|
action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
- action_path ||= ActionController::Base.resources_path_names[action] || action
+ action_path ||= @set.resources_path_names[action] || action
map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true })
end
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index 8de68b3174..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,10 +87,9 @@ 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) }
- requirements[:controller] ||= @set.controller_constraints
end
end
@@ -175,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
@@ -283,7 +290,7 @@ module ActionDispatch
end
def namespace(path)
- scope(path.to_s, :name_prefix => path.to_s, :namespace => path.to_s) { yield }
+ scope(path.to_s, :name_prefix => path.to_s, :controller_namespace => path.to_s) { yield }
end
def constraints(constraints = {})
@@ -294,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]}"
@@ -318,12 +326,12 @@ module ActionDispatch
parent ? "#{parent}_#{child}" : child
end
- def merge_namespace_scope(parent, child)
+ def merge_controller_namespace_scope(parent, child)
parent ? "#{parent}/#{child}" : child
end
def merge_controller_scope(parent, child)
- @scope[:namespace] ? "#{@scope[:namespace]}/#{child}" : child
+ @scope[:controller_namespace] ? "#{@scope[:controller_namespace]}/#{child}" : child
end
def merge_resources_path_names_scope(parent, child)
@@ -367,9 +375,9 @@ module ActionDispatch
def actions
if only = options[:only]
- only.map(&:to_sym)
+ Array(only).map(&:to_sym)
elsif except = options[:except]
- default_actions - except.map(&:to_sym)
+ default_actions - Array(except).map(&:to_sym)
else
default_actions
end
@@ -443,7 +451,7 @@ module ActionDispatch
def resource(*resources, &block)
options = resources.extract_options!
- if verify_common_behavior_for(:resource, resources, options, &block)
+ if apply_common_behavior_for(:resource, resources, options, &block)
return self
end
@@ -451,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)
@@ -468,7 +479,7 @@ module ActionDispatch
def resources(*resources, &block)
options = resources.extract_options!
- if verify_common_behavior_for(:resources, resources, options, &block)
+ if apply_common_behavior_for(:resources, resources, options, &block)
return self
end
@@ -534,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!
@@ -591,7 +617,7 @@ module ActionDispatch
path_names[name.to_sym] || name.to_s
end
- def verify_common_behavior_for(method, resources, options, &block)
+ def apply_common_behavior_for(method, resources, options, &block)
if resources.length > 1
resources.each { |r| send(method, r, options, &block) }
return true
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 dcf98b729b..722be432c7 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -1,5 +1,6 @@
require 'rack/mount'
require 'forwardable'
+require 'action_dispatch/routing/deprecated_mapper'
module ActionDispatch
module Routing
@@ -11,8 +12,8 @@ module ActionDispatch
PARAMETERS_KEY = 'action_dispatch.request.path_parameters'
class Dispatcher
- def initialize(options = {})
- defaults = options[:defaults]
+ def initialize(options={})
+ @defaults = options[:defaults]
@glob_param = options.delete(:glob)
end
@@ -20,7 +21,8 @@ module ActionDispatch
params = env[PARAMETERS_KEY]
prepare_params!(params)
- unless controller = controller(params)
+ # Just raise undefined constant errors if a controller was specified as default.
+ unless controller = controller(params, @defaults.key?(:controller))
return [404, {'X-Cascade' => 'pass'}, []]
end
@@ -39,14 +41,13 @@ module ActionDispatch
end
end
- def controller(params)
+ def controller(params, raise_error=true)
if params && params.has_key?(:controller)
controller = "#{params[:controller].camelize}Controller"
ActiveSupport::Inflector.constantize(controller)
end
rescue NameError => e
- raise unless e.message.include?(controller)
- nil
+ raise ActionController::RoutingError, e.message, e.backtrace if raise_error
end
private
@@ -59,7 +60,6 @@ module ActionDispatch
end
end
-
# A NamedRouteCollection instance is a collection of named routes, and also
# maintains an anonymous module that can be used to install helpers for the
# named routes.
@@ -168,63 +168,25 @@ 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)
- if args.empty? || Hash === args.first
- options = #{hash_access_method}(args.first || {})
- else
- options = #{hash_access_method}(args.extract_options!)
- default = default_url_options(options) if self.respond_to?(:default_url_options, true)
- options = (default ||= {}).merge(options)
-
- keys = #{route.segment_keys.inspect}
- 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[:use_defaults] = false
- options.merge!(args)
+ options = #{hash_access_method}(args.extract_options!)
+
+ if args.any?
+ options[:_positional_args] = args
+ options[:_positional_keys] = #{route.segment_keys.inspect}
end
url_for(options)
end
- protected :#{selector}
END_EVAL
helpers << selector
end
end
- attr_accessor :routes, :named_routes, :controller_namespaces
+ 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' }
@@ -235,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)
@@ -273,61 +237,155 @@ module ActionDispatch
named_routes.install(destinations, regenerate_code)
end
- def empty?
- routes.empty?
- end
+ def url_helpers
+ @url_helpers ||= begin
+ router = self
- CONTROLLER_REGEXP = /[_a-zA-Z0-9]+/
+ Module.new do
+ extend ActiveSupport::Concern
+ include UrlFor
- def controller_constraints
- @controller_constraints ||= begin
- namespaces = controller_namespaces + in_memory_controller_namespaces
- source = namespaces.map { |ns| "#{Regexp.escape(ns)}/#{CONTROLLER_REGEXP.source}" }
- source << CONTROLLER_REGEXP.source
- Regexp.compile(source.sort.reverse.join('|'))
+ # 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
- def in_memory_controller_namespaces
- namespaces = Set.new
- ActionController::Base.subclasses.each do |klass|
- controller_name = klass.underscore
- namespaces << controller_name.split('/')[0...-1].join('/')
- end
- namespaces.delete('')
- namespaces
+ def empty?
+ 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 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
+ 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
+
+ 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
@@ -338,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)
@@ -431,9 +451,9 @@ module ActionDispatch
end
req = Rack::Request.new(env)
- @set.recognize(req) do |route, params|
+ @set.recognize(req) do |route, matches, params|
dispatcher = route.app
- if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params)
+ if dispatcher.is_a?(Dispatcher) && dispatcher.controller(params, false)
dispatcher.prepare_params!(params)
return params
end
@@ -441,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
new file mode 100644
index 0000000000..ec78f53fa6
--- /dev/null
+++ b/actionpack/lib/action_dispatch/routing/url_for.rb
@@ -0,0 +1,139 @@
+module ActionDispatch
+ module Routing
+ # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
+ # is also possible: an URL can be generated from one of your routing definitions.
+ # URL generation functionality is centralized in this module.
+ #
+ # See ActionDispatch::Routing and ActionController::Resources for general
+ # information about routing and routes.rb.
+ #
+ # <b>Tip:</b> If you need to generate URLs from your models or some other place,
+ # then ActionController::UrlFor is what you're looking for. Read on for
+ # an introduction.
+ #
+ # == URL generation from parameters
+ #
+ # As you may know, some functions - such as ActionController::Base#url_for
+ # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
+ # of parameters. For example, you've probably had the chance to write code
+ # like this in one of your views:
+ #
+ # <%= link_to('Click here', :controller => 'users',
+ # :action => 'new', :message => 'Welcome!') %>
+ #
+ # #=> Generates a link to: /users/new?message=Welcome%21
+ #
+ # link_to, and all other functions that require URL generation functionality,
+ # actually use ActionController::UrlFor under the hood. And in particular,
+ # they use the ActionController::UrlFor#url_for method. One can generate
+ # the same path as the above example by using the following code:
+ #
+ # include UrlFor
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :only_path => true)
+ # # => "/users/new?message=Welcome%21"
+ #
+ # Notice the <tt>:only_path => true</tt> part. This is because UrlFor has no
+ # information about the website hostname that your Rails app is serving. So if you
+ # want to include the hostname as well, then you must also pass the <tt>:host</tt>
+ # argument:
+ #
+ # include UrlFor
+ # url_for(:controller => 'users',
+ # :action => 'new',
+ # :message => 'Welcome!',
+ # :host => 'www.example.com') # Changed this.
+ # # => "http://www.example.com/users/new?message=Welcome%21"
+ #
+ # By default, all controllers and views have access to a special version of url_for,
+ # that already knows what the current hostname is. So if you use url_for in your
+ # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
+ # argument.
+ #
+ # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for.
+ # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for'
+ # in full. However, mailers don't have hostname information, and what's why you'll still
+ # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
+ #
+ #
+ # == URL generation for named routes
+ #
+ # UrlFor also allows one to access methods that have been auto-generated from
+ # named routes. For example, suppose that you have a 'users' resource in your
+ # <b>routes.rb</b>:
+ #
+ # map.resources :users
+ #
+ # This generates, among other things, the method <tt>users_path</tt>. By default,
+ # this method is accessible from your controllers, views and mailers. If you need
+ # to access this auto-generated method from other places (such as a model), then
+ # you can do that by including ActionController::UrlFor in your class:
+ #
+ # class User < ActiveRecord::Base
+ # include ActionController::UrlFor
+ #
+ # def base_uri
+ # user_path(self)
+ # end
+ # end
+ #
+ # User.find(1).base_uri # => "/users/1"
+ #
+ module UrlFor
+ extend ActiveSupport::Concern
+
+ included do
+ # 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 = {}
+ end
+ end
+
+ def url_options
+ default_url_options
+ end
+
+ # Generate a url based on the options provided, default_url_options and the
+ # routes defined in routes.rb. The following options are supported:
+ #
+ # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
+ # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
+ # * <tt>:host</tt> - Specifies the host the link should be targeted at.
+ # If <tt>:only_path</tt> is false, this option must be
+ # 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>: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
+ # +url_for+ is forwarded to the Routes module.
+ #
+ # Examples:
+ #
+ # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
+ # 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 = nil)
+ case options
+ when String
+ options
+ when nil, Hash
+ _router.url_for(url_options.merge(options || {}))
+ else
+ polymorphic_url(options)
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_dispatch/testing/assertions/response.rb b/actionpack/lib/action_dispatch/testing/assertions/response.rb
index c2486d3730..937c9f48d2 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/response.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/response.rb
@@ -132,16 +132,21 @@ module ActionDispatch
end
def normalize_argument_to_redirection(fragment)
- after_routing = @controller.url_for(fragment)
- if after_routing =~ %r{^\w+://.*}
- after_routing
- else
- # FIXME - this should probably get removed.
- if after_routing.first != '/'
- after_routing = '/' + after_routing
+ case fragment
+ when %r{^\w[\w\d+.-]*:.*}
+ fragment
+ when String
+ if fragment =~ %r{^\w[\w\d+.-]*:.*}
+ fragment
+ else
+ @request.protocol + @request.host_with_port + fragment
end
- @request.protocol + @request.host_with_port + after_routing
- end
+ when :back
+ raise RedirectBackError unless refer = @request.headers["Referer"]
+ refer
+ else
+ @controller.url_for(fragment)
+ end.gsub(/[\r\n]/, '')
end
def validate_request!
diff --git a/actionpack/lib/action_dispatch/testing/assertions/routing.rb b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
index 0c33539b4a..1d7e8090e4 100644
--- a/actionpack/lib/action_dispatch/testing/assertions/routing.rb
+++ b/actionpack/lib/action_dispatch/testing/assertions/routing.rb
@@ -80,7 +80,7 @@ module ActionDispatch
expected_path = "/#{expected_path}" unless expected_path[0] == ?/
# Load routes.rb if it hasn't been loaded.
- generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults)
+ generated_path, extra_keys = @router.generate_extras(options, defaults)
found_extras = options.reject {|k, v| ! extra_keys.include? k}
msg = build_message(message, "found extras <?>, not <?>", found_extras, extras)
@@ -125,7 +125,7 @@ module ActionDispatch
end
# A helper to make it easier to test different route configurations.
- # This method temporarily replaces ActionController::Routing::Routes
+ # This method temporarily replaces @router
# with a new RouteSet instance.
#
# The new instance is yielded to the passed block. Typically the block
@@ -142,22 +142,19 @@ module ActionDispatch
# end
#
def with_routing
- real_routes = ActionController::Routing::Routes
- ActionController::Routing.module_eval { remove_const :Routes }
-
- temporary_routes = ActionController::Routing::RouteSet.new
- ActionController::Routing.module_eval { const_set :Routes, temporary_routes }
-
- yield temporary_routes
+ old_routes, @router = @router, ActionDispatch::Routing::RouteSet.new
+ old_controller, @controller = @controller, @controller.clone if @controller
+ _router = @router
+ @controller.singleton_class.send(:send, :include, @router.url_helpers) if @controller
+ yield @router
ensure
- if ActionController::Routing.const_defined? :Routes
- ActionController::Routing.module_eval { remove_const :Routes }
- end
- ActionController::Routing.const_set(:Routes, real_routes) if real_routes
+ @router = old_routes
+ @controller = old_controller if @controller
end
+ # ROUTES TODO: These assertions should really work in an integration context
def method_missing(selector, *args, &block)
- if @controller && ActionController::Routing::Routes.named_routes.helpers.include?(selector)
+ if @controller && @router.named_routes.helpers.include?(selector)
@controller.send(selector, *args, &block)
else
super
@@ -174,7 +171,7 @@ module ActionDispatch
request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
request.path = path
- params = ActionController::Routing::Routes.recognize_path(path, { :method => request.method })
+ params = @router.recognize_path(path, { :method => request.method })
request.path_parameters = params.with_indifferent_access
request
diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb
index 2093bb3a0e..0aff4250c1 100644
--- a/actionpack/lib/action_dispatch/testing/integration.rb
+++ b/actionpack/lib/action_dispatch/testing/integration.rb
@@ -1,6 +1,6 @@
require 'stringio'
require 'uri'
-require 'active_support/core_ext/object/metaclass'
+require 'active_support/core_ext/object/singleton_class'
require 'rack/test'
module ActionDispatch
@@ -162,12 +162,31 @@ module ActionDispatch
# A running counter of the number of requests processed.
attr_accessor :request_count
+ include ActionDispatch::Routing::UrlFor
+
# Create and initialize a new Session instance.
def initialize(app)
@app = app
+
+ # If the app is a Rails app, make url_helpers available on the session
+ # This makes app.url_for and app.foo_path available in the console
+ if app.respond_to?(:routes) && app.routes.respond_to?(:url_helpers)
+ singleton_class.class_eval { include app.routes.url_helpers }
+ end
+
reset!
end
+ def url_options
+ opts = super.reverse_merge(
+ :host => host,
+ :protocol => https? ? "https" : "http"
+ )
+
+ opts.merge!(:port => 443) if !opts.key?(:port) && https?
+ opts
+ end
+
# Resets the instance. This can be used to reset the state information
# in an existing session instance, so it can be used from a clean-slate
# condition.
@@ -187,12 +206,10 @@ module ActionDispatch
unless defined? @named_routes_configured
# install the named routes in this session instance.
- klass = metaclass
- ActionController::Routing::Routes.install_helpers(klass)
+ klass = singleton_class
# the helpers are made protected by default--we make them public for
# easier access during testing and troubleshooting.
- klass.module_eval { public *ActionController::Routing::Routes.named_routes.helpers }
@named_routes_configured = true
end
end
@@ -221,14 +238,6 @@ module ActionDispatch
@host = name
end
- # Returns the URL for the given options, according to the rules specified
- # in the application's routes.
- def url_for(options)
- controller ?
- controller.url_for(options) :
- generic_url_rewriter.rewrite(options)
- end
-
private
# Performs the actual request.
@@ -273,26 +282,14 @@ module ActionDispatch
@request_count += 1
@request = ActionDispatch::Request.new(session.last_request.env)
- @response = ActionDispatch::TestResponse.from_response(@mock_session.last_response)
+ response = @mock_session.last_response
+ @response = ActionDispatch::TestResponse.new(response.status, response.headers, response.body)
@html_document = nil
@controller = session.last_request.env['action_controller.instance']
return response.status
end
-
- # Get a temporary URL writer object
- def generic_url_rewriter
- env = {
- 'REQUEST_METHOD' => "GET",
- 'QUERY_STRING' => "",
- "REQUEST_URI" => "/",
- "HTTP_HOST" => host,
- "SERVER_PORT" => https? ? "443" : "80",
- "HTTPS" => https? ? "on" : "off"
- }
- ActionController::UrlRewriter.new(ActionDispatch::Request.new(env), {})
- end
end
module Runner
@@ -364,6 +361,14 @@ module ActionDispatch
end
end
+ extend ActiveSupport::Concern
+ include ActionDispatch::Routing::UrlFor
+
+ def url_options
+ reset! unless @integration_session
+ @integration_session.url_options
+ end
+
# Delegate unhandled messages to the current session instance.
def method_missing(sym, *args, &block)
reset! unless @integration_session
diff --git a/actionpack/lib/action_dispatch/testing/test_process.rb b/actionpack/lib/action_dispatch/testing/test_process.rb
index eae703e1b6..d4eecac2de 100644
--- a/actionpack/lib/action_dispatch/testing/test_process.rb
+++ b/actionpack/lib/action_dispatch/testing/test_process.rb
@@ -1,3 +1,5 @@
+require 'action_dispatch/middleware/flash'
+
module ActionDispatch
module TestProcess
def assigns(key = nil)
diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb
index 67f89e1627..72a1cd30ad 100644
--- a/actionpack/lib/action_pack/version.rb
+++ b/actionpack/lib/action_pack/version.rb
@@ -1,9 +1,10 @@
-module ActionPack #:nodoc:
+module ActionPack
module VERSION #:nodoc:
MAJOR = 3
MINOR = 0
- TINY = "0.beta1"
+ TINY = 0
+ BUILD = "beta1"
- STRING = [MAJOR, MINOR, TINY].join('.')
+ STRING = [MAJOR, MINOR, TINY, BUILD].join('.')
end
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 4096c296c3..ffe3060404 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -11,7 +11,7 @@ module ActionView #:nodoc:
def initialize(paths, path, details, partial)
@path = path
- display_paths = paths.compact.join(":")
+ display_paths = paths.compact.map{ |p| p.to_s.inspect }.join(", ")
template_type = if partial
"partial"
elsif path =~ /layouts/i
@@ -20,7 +20,7 @@ module ActionView #:nodoc:
'template'
end
- super("Missing #{template_type} #{path} with #{details.inspect} in view path #{display_paths}")
+ super("Missing #{template_type} #{path} with #{details.inspect} in view paths #{display_paths}")
end
end
@@ -173,81 +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, :formats
- attr_internal :captures
+ ActionView.run_base_hooks(self)
- def reset_formats(formats)
- @formats = formats
-
- 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, I18n.locale)
- end
- end
-
- class << self
- delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
- delegate :logger, :to => 'ActionController::Base', :allow_nil => true
- end
-
- @@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
-
- # :nodoc:
- def self.xss_safe?
- true
- end
+ class_attribute :helpers
+ attr_reader :helpers
- def self.cache_template_loading?
- ActionController::Base.allow_concurrency || (cache_template_loading.nil? ? !ActiveSupport::Dependencies.load? : cache_template_loading)
+ class << self
+ delegate :erb_trim_mode=, :to => 'ActionView::Template::Handlers::ERB'
+ delegate :logger, :to => 'ActionController::Base', :allow_nil => true
end
- attr_internal :request, :layout
+ attr_accessor :base_path, :assigns, :template_extension, :lookup_context
+ attr_internal :captures, :request, :layout, :controller, :template, :config
- 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 ||= {}
@@ -275,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 e106bb0897..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
@@ -80,13 +82,13 @@ module ActionView
record = convert_to_model(record)
options = options.symbolize_keys
- options[:action] ||= record.new_record? ? "create" : "update"
+ options[:action] ||= record.persisted? ? "update" : "create"
action = url_for(:action => options[:action], :id => record)
submit_value = options[:submit_value] || options[:action].gsub(/[^\w]/, '').capitalize
contents = form_tag({:action => action}, :method =>(options[:method] || 'post'), :enctype => options[:multipart] ? 'multipart/form-data': nil)
- contents.safe_concat hidden_field(record_name, :id) unless record.new_record?
+ contents.safe_concat hidden_field(record_name, :id) if record.persisted?
contents.safe_concat all_input_tags(record, record_name, options)
yield contents if block_given?
contents.safe_concat submit_tag(submit_value)
@@ -127,7 +129,7 @@ module ActionView
if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) &&
(errors = obj.errors[method])
content_tag("div",
- "#{options[:prepend_text]}#{ERB::Util.html_escape(errors.first)}#{options[:append_text]}",
+ (options[:prepend_text].html_safe << errors.first).safe_concat(options[:append_text]),
:class => options[:css_class]
)
else
@@ -226,16 +228,16 @@ module ActionView
error_messages = objects.sum do |object|
object.errors.full_messages.map do |msg|
- content_tag(:li, ERB::Util.html_escape(msg))
+ content_tag(:li, msg)
end
- end.join
+ end.join.html_safe
contents = ''
contents << content_tag(options[:header_tag] || :h2, header_message) unless header_message.blank?
contents << content_tag(:p, message) unless message.blank?
contents << content_tag(:ul, error_messages)
- content_tag(:div, contents, html)
+ content_tag(:div, contents.html_safe, html)
end
else
''
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 96976ce45f..0c488b6793 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -11,7 +11,7 @@ module ActionView
# the assets exist before linking to them:
#
# image_tag("rails.png")
- # # => <img alt="Rails src="/images/rails.png?1230601161" />
+ # # => <img alt="Rails" src="/images/rails.png?1230601161" />
# stylesheet_link_tag("application")
# # => <link href="/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
#
@@ -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
@@ -530,7 +523,7 @@ module ActionView
options.symbolize_keys!
src = options[:src] = path_to_image(source)
- options[:alt] ||= File.basename(src, '.*').split('.').first.to_s.capitalize
+ options[:alt] ||= File.basename(src, '.*').capitalize
if size = options.delete(:size)
options[:width], options[:height] = size.split("x") if size =~ %r{^\d+x\d+$}
@@ -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 9951e11a37..58c3a8752e 100644
--- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb
+++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb
@@ -8,7 +8,7 @@ module ActionView
# Full usage example:
#
# config/routes.rb:
- # ActionController::Routing::Routes.draw do |map|
+ # Basecamp::Application.routes.draw do |map|
# map.resources :posts
# map.root :controller => "posts"
# end
@@ -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 8be2f76bd6..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
@@ -815,7 +816,7 @@ module ActionView
tag_options[:selected] = "selected" if selected == i
select_options << content_tag(:option, value, tag_options)
end
- select_options.join("\n") + "\n"
+ (select_options.join("\n") + "\n").html_safe
end
# Builds select tag from date type and html select options
@@ -833,9 +834,9 @@ module ActionView
select_html = "\n"
select_html << content_tag(:option, '', :value => '') + "\n" if @options[:include_blank]
select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
- select_html << select_options_as_html.to_s
+ select_html << select_options_as_html
- (content_tag(:select, select_html, select_options) + "\n").html_safe
+ (content_tag(:select, select_html.html_safe, select_options) + "\n").html_safe
end
# Builds a prompt option tag with supplied options or from default options
@@ -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
@@ -907,7 +908,7 @@ module ActionView
when :hour
(@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator]
when :minute
- @options[:time_separator]
+ @options[:discard_minute] ? "" : @options[:time_separator]
when :second
@options[:include_seconds] ? @options[:time_separator] : ""
end
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 305d6b3128..7293145964 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,21 +313,20 @@ 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:
object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array
-
object = convert_to_model(object)
html_options =
- if object.respond_to?(:new_record?) && object.new_record?
- { :class => dom_class(object, :new), :id => dom_id(object), :method => :post }
- else
+ if object.respond_to?(:persisted?) && object.persisted?
{ :class => dom_class(object, :edit), :id => dom_id(object, :edit), :method => :put }
+ else
+ { :class => dom_class(object, :new), :id => dom_id(object), :method => :post }
end
options[:html] ||= {}
@@ -529,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
@@ -1150,7 +1156,7 @@ module ActionView
def submit_default_value
object = @object.respond_to?(:to_model) ? @object.to_model : @object
- key = object ? (object.new_record? ? :create : :update) : :submit
+ key = object ? (object.persisted? ? :update : :create) : :submit
model = if object.class.respond_to?(:model_name)
object.class.model_name.human
@@ -1176,7 +1182,7 @@ module ActionView
association = args.shift
association = association.to_model if association.respond_to?(:to_model)
- if association.respond_to?(:new_record?)
+ if association.respond_to?(:persisted?)
association = [association] if @object.send(association_name).is_a?(Array)
elsif !association.is_a?(Array)
association = @object.send(association_name)
@@ -1184,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
@@ -1195,13 +1203,13 @@ module ActionView
def fields_for_nested_model(name, object, options, block)
object = object.to_model if object.respond_to?(:to_model)
- if object.new_record?
- @template.fields_for(name, object, options, &block)
- else
+ if object.persisted?
@template.fields_for(name, object, options) do |builder|
block.call(builder)
@template.concat builder.hidden_field(:id) unless builder.emitted_hidden_id?
end
+ else
+ @template.fields_for(name, object, options, &block)
end
end
@@ -1212,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 21acfbbee8..4c523d4b20 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.
@@ -572,10 +574,9 @@ module ActionView
end
if value.blank? && options[:prompt]
prompt = options[:prompt].kind_of?(String) ? options[:prompt] : I18n.translate('helpers.select.prompt', :default => 'Please select')
- "<option value=\"\">#{prompt}</option>\n" + option_tags
- else
- option_tags
+ option_tags = "<option value=\"\">#{prompt}</option>\n" + option_tags
end
+ option_tags.html_safe
end
end
diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb
index 9d76870646..07694f5ebb 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.
#
@@ -90,9 +95,9 @@ module ActionView
html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
if blank = options.delete(:include_blank)
if blank.kind_of?(String)
- option_tags = "<option value=\"\">#{blank}</option>" + option_tags
+ option_tags = "<option value=\"\">#{blank}</option>".html_safe + option_tags
else
- option_tags = "<option value=\"\"></option>" + option_tags
+ option_tags = "<option value=\"\"></option>".html_safe + option_tags
end
end
content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
@@ -279,7 +284,7 @@ module ActionView
escape = options.key?("escape") ? options.delete("escape") : true
content = html_escape(content) if escape
- content_tag :textarea, content, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
+ content_tag :textarea, content.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
end
# Creates a check box form input tag.
@@ -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 0a901b2d2b..07eee3b399 100644
--- a/actionpack/lib/action_view/helpers/javascript_helper.rb
+++ b/actionpack/lib/action_view/helpers/javascript_helper.rb
@@ -83,17 +83,11 @@ 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:
- "\n//#{cdata_section("\n#{content}\n//")}\n"
+ "\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
end
end
end
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 51510449bd..4d6ea7dfb2 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -182,9 +182,11 @@ module ActionView
def initialize(context, &block) #:nodoc:
context._evaluate_assigns_and_ivars
@context, @lines = context, []
- include_helpers_from_context
- @context.with_output_buffer(@lines) do
- @context.instance_exec(self, &block)
+ @context.update_details(:formats => [:js, :html]) do
+ include_helpers_from_context
+ @context.with_output_buffer(@lines) do
+ @context.instance_exec(self, &block)
+ end
end
end
@@ -569,15 +571,19 @@ module ActionView
end
end
- def render(*options_for_render)
- old_formats = @context && @context.formats
+ def render(*options)
+ with_formats(:html) do
+ case option = options.first
+ when Hash
+ @context.render(*options)
+ else
+ option.to_s
+ end
+ end
+ end
- @context.reset_formats([:html]) if @context
- Hash === options_for_render.first ?
- @context.render(*options_for_render) :
- options_for_render.first.to_s
- ensure
- @context.reset_formats(old_formats) if @context
+ def with_formats(*args)
+ @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 ee10f8f3bb..8ae2e5f28f 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,25 +106,10 @@ 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
- "<#{name}#{tag_options}>#{content}</#{name}>".html_safe
+ ("<#{name}#{tag_options}>".html_safe << content.to_s).safe_concat("</#{name}>")
end
def tag_options(options, escape = true)
diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb
index c348ea7a0d..8a89ee58a0 100644
--- a/actionpack/lib/action_view/helpers/translation_helper.rb
+++ b/actionpack/lib/action_view/helpers/translation_helper.rb
@@ -12,9 +12,10 @@ module ActionView
# prepend the key with a period, nothing is converted.
def translate(key, options = {})
options[:raise] = true
- I18n.translate(scope_key_by_partial(key), options).html_safe
+ translation = I18n.translate(scope_key_by_partial(key), options)
+ translation.is_a?(Array) ? translation.map { |entry| entry.html_safe } : translation.html_safe
rescue I18n::MissingTranslationData => e
- keys = I18n.send(:normalize_translation_keys, e.locale, e.key, e.options[:scope])
+ keys = I18n.normalize_keys(e.locale, e.key, e.options[:scope])
content_tag('span', keys.join(', '), :class => 'translation_missing')
end
alias :t :translate
@@ -28,7 +29,7 @@ module ActionView
private
def scope_key_by_partial(key)
- if key.to_s.first == "."
+ if (key.respond_to?(:join) ? key.join : key.to_s).first == "."
if @_virtual_path
@_virtual_path.gsub(%r{/_?}, ".") + key.to_s
else
@@ -40,4 +41,4 @@ module ActionView
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb
index fde4bfa4ce..ae1385f3b7 100644
--- a/actionpack/lib/action_view/helpers/url_helper.rb
+++ b/actionpack/lib/action_view/helpers/url_helper.rb
@@ -5,7 +5,7 @@ require 'active_support/core_ext/hash/keys'
module ActionView
module Helpers #:nodoc:
# Provides a set of methods for making links and getting URLs that
- # depend on the routing subsystem (see ActionController::Routing).
+ # depend on the routing subsystem (see ActionDispatch::Routing).
# This allows you to use the same format for links in views
# and controllers.
module UrlHelper
@@ -63,7 +63,7 @@ module ActionView
# # => /testing/jump/#tax&ship
#
# <%= url_for(Workshop.new) %>
- # # relies on Workshop answering a new_record? call (and in this case returning true)
+ # # relies on Workshop answering a persisted? call (and in this case returning false)
# # => /workshops
#
# <%= url_for(@workshop) %>
@@ -202,8 +202,6 @@ module ActionView
#
# link_to("Destroy", "http://www.example.com", :method => :delete, :confirm => "Are you sure?")
# # => <a href='http://www.example.com' rel="nofollow" data-method="delete" data-confirm="Are you sure?">Destroy</a>
-
- #
def link_to(*args, &block)
if block_given?
options = args.first || {}
@@ -226,7 +224,7 @@ module ActionView
end
href_attr = "href=\"#{url}\"" unless href
- "<a #{href_attr}#{tag_options}>#{ERB::Util.h(name || url)}</a>".html_safe
+ ("<a #{href_attr}#{tag_options}>".html_safe << (name || url)).safe_concat("</a>")
end
end
@@ -469,14 +467,12 @@ module ActionView
extras << "subject=#{Rack::Utils.escape(subject).gsub("+", "%20")}&" unless subject.nil?
extras = "?" << extras.gsub!(/&?$/,"") unless extras.empty?
- email_address = email_address.to_s
-
- email_address_obfuscated = email_address.dup
+ email_address_obfuscated = html_escape(email_address)
email_address_obfuscated.gsub!(/@/, html_options.delete("replace_at")) if html_options.has_key?("replace_at")
email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")
if encode == "javascript"
- "document.write('#{content_tag("a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
+ "document.write('#{content_tag("a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|
string << sprintf("%%%x", c)
end
"<script type=\"#{Mime::JS}\">eval(decodeURIComponent('#{string}'))</script>"
@@ -493,9 +489,9 @@ module ActionView
char = c.chr
string << (char =~ /\w/ ? sprintf("%%%x", c) : char)
end
- content_tag "a", name || email_address_encoded, html_options.merge({ "href" => "#{string}#{extras}" })
+ content_tag "a", name || email_address_encoded.html_safe, html_options.merge({ "href" => "#{string}#{extras}" })
else
- content_tag "a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" })
+ content_tag "a", name || email_address_obfuscated.html_safe, html_options.merge({ "href" => "mailto:#{email_address}#{extras}" })
end
end
@@ -548,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..27ee8b23c9
--- /dev/null
+++ b/actionpack/lib/action_view/lookup_context.rb
@@ -0,0 +1,154 @@
+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 = {}, &block)
+ self.registered_details << name
+
+ Setters.send :define_method, :"_#{name}_defaults", &block
+ Setters.module_eval <<-METHOD, __FILE__, __LINE__ + 1
+ def #{name}=(value)
+ value = Array(value.presence || _#{name}_defaults)
+ #{"value << nil unless value.include?(nil)" unless options[:allow_nil] == false}
+
+ unless value == @details[:#{name}]
+ @details_key, @details = nil, @details.merge(:#{name} => value)
+ @details.freeze
+ end
+ end
+ METHOD
+ end
+
+ # Holds raw setters for the registered details.
+ module Setters #:nodoc:
+ 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, @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, details, details_key)
+ end
+
+ def find_all(name, prefix = nil, partial = false)
+ @view_paths.find_all(name, prefix, partial, details, details_key)
+ end
+
+ def exists?(name, prefix = nil, partial = false)
+ @view_paths.exists?(name, prefix, partial, 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=(given_details)
+ registered_details.each { |key| send(:"#{key}=", given_details[key]) }
+ end
+
+ def details_key
+ @details_key ||= DetailsKey.get(@details)
+ end
+
+ # Shortcut to read formats from details.
+ def formats
+ @details[:formats].compact
+ end
+
+ # Overload formats= to reject [:"*/*"] values.
+ def formats=(value, freeze=true)
+ value = nil if value == [:"*/*"]
+ super(value)
+ end
+
+ # Shortcut to read locale.
+ def locale
+ I18n.locale
+ end
+
+ # Overload locale= to also set the I18n.locale. If the current I18n.config object responds
+ # to i18n_config, it means that it's has a copy of the original I18n configuration and it's
+ # acting as proxy, which we need to skip.
+ def locale=(value)
+ value = value.first if value.is_a?(Array)
+ config = I18n.config.respond_to?(:i18n_config) ? I18n.config.i18n_config : I18n.config
+ config.locale = value if value
+ super(I18n.locale)
+ 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
+ @details = old_details
+ end
+ end
+ end
+ end
+
+ include Setters
+ 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 d9e2557d89..2e5d115630 100644
--- a/actionpack/lib/action_view/railtie.rb
+++ b/actionpack/lib/action_view/railtie.rb
@@ -5,12 +5,14 @@ module ActionView
class Railtie < Rails::Railtie
railtie_name :action_view
- require "action_view/railties/subscriber"
- subscriber ActionView::Railties::Subscriber.new
+ require "action_view/railties/log_subscriber"
+ log_subscriber ActionView::Railties::LogSubscriber.new
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/railties/subscriber.rb b/actionpack/lib/action_view/railties/log_subscriber.rb
index 803f19379c..9487a10706 100644
--- a/actionpack/lib/action_view/railties/subscriber.rb
+++ b/actionpack/lib/action_view/railties/log_subscriber.rb
@@ -1,6 +1,6 @@
module ActionView
module Railties
- class Subscriber < Rails::Subscriber
+ class LogSubscriber < Rails::LogSubscriber
def render_template(event)
message = "Rendered #{from_rails_root(event.payload[:identifier])}"
message << " within #{from_rails_root(event.payload[:layout])}" if event.payload[:layout]
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 62d31662db..d34ab0354c 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 abc7c09991..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], {:formats => formats})
- 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, {:formats => formats}) 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,61 +28,57 @@ module ActionView
end
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)
- 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)
+ def render_template(options, &block)
_evaluate_assigns_and_ivars
- template, layout = options.values_at(:_template, :_layout)
- _render_template(template, layout, options)
+
+ # 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
+
+ # 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
- def _render_template(template, layout = nil, 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
+
+ # 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
@@ -110,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 4573a440d1..ac5902cc0e 100644
--- a/actionpack/lib/action_view/template/handlers/erb.rb
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -3,10 +3,17 @@ require 'active_support/core_ext/string/output_safety'
require 'erubis'
module ActionView
+ class OutputBuffer < ActiveSupport::SafeBuffer
+ def <<(value)
+ super(value.to_s)
+ end
+ alias :append= :<<
+ 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,15 +22,15 @@ 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.append= ' << code
else
- src << '@output_buffer << ((' << code << ').to_s);'
+ src << '@output_buffer.append= (' << code << ');'
end
end
def add_expr_escaped(src, code)
- src << '@output_buffer << ' << escaped_expr(code) << ';'
+ src << '@output_buffer.append= ' << escaped_expr(code) << ';'
end
def add_postamble(src)
@@ -42,14 +49,14 @@ module ActionView
self.erb_trim_mode = '-'
self.default_format = Mime::HTML
-
- cattr_accessor :erubis_implementation
- self.erubis_implementation = Erubis
+
+ cattr_accessor :erb_implementation
+ self.erb_implementation = Erubis
def compile(template)
source = template.source.gsub(/\A(<%(#.*coding[:=]\s*(\S+)\s*)-?%>)\s*\n?/, '')
erb = "<% __in_erb_template=true %>#{source}"
- result = self.class.erubis_implementation.new(erb, :trim=>(self.class.erb_trim_mode == "-")).src
+ result = self.class.erb_implementation.new(erb, :trim=>(self.class.erb_trim_mode == "-")).src
result = "#{$2}\n#{result}" if $2
result
end
diff --git a/actionpack/lib/action_view/template/resolver.rb b/actionpack/lib/action_view/template/resolver.rb
index 8acfe6cad0..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, :allow_nil => false) 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 fc29acea6d..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
@@ -152,7 +159,7 @@ module ActionView
end
def method_missing(selector, *args)
- if ActionController::Routing::Routes.named_routes.helpers.include?(selector)
+ if @controller._router.named_routes.helpers.include?(selector)
@controller.__send__(selector, *args)
else
super