From 69e349f1bb31c80215775956467fd54e00a2f0fe Mon Sep 17 00:00:00 2001
From: Yehuda Katz
Date: Thu, 25 Dec 2008 17:12:33 -0800
Subject: Initial work to merge several places with similar logic
---
actionpack/lib/action_controller/mime_responds.rb | 7 ++-----
actionpack/lib/action_controller/mime_type.rb | 4 ++++
actionpack/lib/action_controller/request.rb | 23 ++++++++++++++++-------
actionpack/lib/action_view/base.rb | 2 +-
4 files changed, 23 insertions(+), 13 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb
index 29294476f7..de704a6a1c 100644
--- a/actionpack/lib/action_controller/mime_responds.rb
+++ b/actionpack/lib/action_controller/mime_responds.rb
@@ -109,16 +109,13 @@ module ActionController #:nodoc:
end
class Responder #:nodoc:
+
def initialize(controller)
@controller = controller
@request = controller.request
@response = controller.response
- if ActionController::Base.use_accept_header
- @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
- else
- @mime_type_priority = [@request.format]
- end
+ @mime_type_priority = @request.formats
@order = []
@responses = {}
diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb
index 017626ba27..fbc245fa5c 100644
--- a/actionpack/lib/action_controller/mime_type.rb
+++ b/actionpack/lib/action_controller/mime_type.rb
@@ -5,6 +5,10 @@ module Mime
EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
+ def self.[](type)
+ Type.lookup_by_extension(type)
+ end
+
# Encapsulates the notion of a mime type. Can be used at render time, for example, with:
#
# class PostsController < ActionController::Base
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index 8a02130d88..f62567af41 100755
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -152,24 +152,33 @@ module ActionController
end
end
+ ONLY_ALL = [Mime::ALL].freeze
+
# Returns the Mime type for the \format used in the request.
#
# GET /posts/5.xml | request.format => Mime::XML
# GET /posts/5.xhtml | request.format => Mime::HTML
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of ActionController::Base.use_accept_header
+
def format
@format ||=
if parameters[:format]
- Mime::Type.lookup_by_extension(parameters[:format])
- elsif ActionController::Base.use_accept_header
- accepts.first
- elsif xhr?
- Mime::Type.lookup_by_extension("js")
- else
- Mime::Type.lookup_by_extension("html")
+ Mime[parameters[:format]]
+ elsif Base.use_accept_header && !(accepts == ONLY_ALL)
+ accepts.first
+ elsif xhr? then Mime::JS
+ else Mime::HTML
end
end
+ def formats
+ @formats =
+ if Base.use_accept_header
+ Array(Mime[parameters[:format]] || accepts)
+ else
+ [format]
+ end
+ end
# Sets the \format by string extension, which can be used to force custom formats
# that are not controlled by the extension.
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 8958e61e9d..50b79f5e35 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -266,7 +266,7 @@ module ActionView #:nodoc:
if defined? @template_format
@template_format
elsif controller && controller.respond_to?(:request)
- @template_format = controller.request.template_format.to_sym
+ @template_format = controller.request.format.to_sym
else
@template_format = :html
end
--
cgit v1.2.3
From e4314d4362d288ff72cec5a5a9644a3a3cbf23af Mon Sep 17 00:00:00 2001
From: "Michael S. Klishin"
Date: Fri, 26 Dec 2008 03:51:59 +0300
Subject: Inline the only call site of Mime::Type#browser_generated?
Signed-off-by: Michael S. Klishin
---
actionpack/lib/action_controller/mime_type.rb | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb
index fbc245fa5c..7af8510b6b 100644
--- a/actionpack/lib/action_controller/mime_type.rb
+++ b/actionpack/lib/action_controller/mime_type.rb
@@ -191,17 +191,13 @@ module Mime
# Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
# ActionController::RequestForgeryProtection.
def verify_request?
- browser_generated?
+ @@browser_generated_types.include?(to_sym)
end
def html?
@@html_types.include?(to_sym) || @string =~ /html/
end
- def browser_generated?
- @@browser_generated_types.include?(to_sym)
- end
-
private
def method_missing(method, *args)
if method.to_s =~ /(\w+)\?$/
--
cgit v1.2.3
From d77deb89d54b18c662ae3de103802e4d7a9d7d08 Mon Sep 17 00:00:00 2001
From: "Michael S. Klishin"
Date: Sun, 28 Dec 2008 13:21:10 +0300
Subject: Annotated metaprogramming code across ActiveSupport
---
actionpack/lib/action_controller/mime_responds.rb | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb
index c41bd3ac4b..b2b73bbd57 100644
--- a/actionpack/lib/action_controller/mime_responds.rb
+++ b/actionpack/lib/action_controller/mime_responds.rb
@@ -144,13 +144,9 @@ module ActionController #:nodoc:
def self.generate_method_for_mime(mime)
sym = mime.is_a?(Symbol) ? mime : mime.to_sym
const = sym.to_s.upcase
- class_eval <<-RUBY
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
def #{sym}(&block) # def html(&block)
- if Mime::SET.include?(Mime::#{const}) # if Mime::Set.include?(Mime::HTML)
- custom(Mime::#{const}, &block) # custom(Mime::HTML, &block)
- else # else
- super # super
- end # end
+ custom(Mime::#{const}, &block) # custom(Mime::HTML, &block)
end # end
RUBY
end
--
cgit v1.2.3
From 15702104a502d0d15bea454297260a495b15b62b Mon Sep 17 00:00:00 2001
From: Jeremy Kemper
Date: Mon, 22 Dec 2008 14:48:32 -0800
Subject: Don't construct object deprecation proxy if unneeded
---
actionpack/lib/action_view/renderable_partial.rb | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb
index d92ff1b8d3..3ea836fa25 100644
--- a/actionpack/lib/action_view/renderable_partial.rb
+++ b/actionpack/lib/action_view/renderable_partial.rb
@@ -25,12 +25,11 @@ module ActionView
end
def render_partial(view, object = nil, local_assigns = {}, as = nil)
- object ||= local_assigns[:object] ||
- local_assigns[variable_name]
+ object ||= local_assigns[:object] || local_assigns[variable_name]
- if view.respond_to?(:controller)
+ if object.nil? && view.respond_to?(:controller)
ivar = :"@#{variable_name}"
- object ||=
+ object =
if view.controller.instance_variable_defined?(ivar)
ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
view.controller.instance_variable_get(ivar),
--
cgit v1.2.3
From ea2090176488246eb780ac654ce5c1063a791dfa Mon Sep 17 00:00:00 2001
From: Mark Reginald James
Date: Sun, 28 Dec 2008 01:15:48 +0000
Subject: Fixed incorrect parsing of query parameters with mixed-depth nesting
inside an array [#1622 state:resolved]
Signed-off-by: Frederick Cheung
---
actionpack/lib/action_controller/url_encoded_pair_parser.rb | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/url_encoded_pair_parser.rb b/actionpack/lib/action_controller/url_encoded_pair_parser.rb
index bea96c711d..9883ad0d85 100644
--- a/actionpack/lib/action_controller/url_encoded_pair_parser.rb
+++ b/actionpack/lib/action_controller/url_encoded_pair_parser.rb
@@ -70,11 +70,12 @@ module ActionController
top[-1][key] = value
else
top << {key => value}.with_indifferent_access
- push top.last
- value = top[key]
end
+ push top.last
+ return top[key]
else
top << value
+ return value
end
elsif top.is_a? Hash
key = CGI.unescape(key)
@@ -84,12 +85,10 @@ module ActionController
else
raise ArgumentError, "Don't know what to do: top is #{top.inspect}"
end
-
- return value
end
def type_conflict!(klass, value)
raise TypeError, "Conflicting types for parameter containers. Expected an instance of #{klass} but found an instance of #{value.class}. This can be caused by colliding Array and Hash parameters like qs[]=value&qs[key]=value. (The parameters received were #{value.inspect}.)"
end
end
-end
\ No newline at end of file
+end
--
cgit v1.2.3
From ab7c25d51174779547ab74bcd23e40204891e370 Mon Sep 17 00:00:00 2001
From: Joshua Peek
Date: Mon, 29 Dec 2008 19:27:19 -0600
Subject: Clean up view path cruft and split path implementations into
Template::Path and Template::EagerPath
---
actionpack/lib/action_controller/base.rb | 12 +--
actionpack/lib/action_controller/rescue.rb | 4 +-
actionpack/lib/action_view/base.rb | 2 +-
actionpack/lib/action_view/inline_template.rb | 2 +-
actionpack/lib/action_view/paths.rb | 107 +-------------------------
actionpack/lib/action_view/renderable.rb | 9 +--
actionpack/lib/action_view/template.rb | 85 +++++++++++++++++++-
7 files changed, 95 insertions(+), 126 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index da3d1f46ee..bc18dfaec8 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -869,7 +869,7 @@ module ActionController #:nodoc:
validate_render_arguments(options, extra_options, block_given?)
if options.nil?
- options = { :template => default_template.filename, :layout => true }
+ options = { :template => default_template, :layout => true }
elsif options == :update
options = extra_options.merge({ :update => true })
elsif options.is_a?(String) || options.is_a?(Symbol)
@@ -1125,7 +1125,7 @@ module ActionController #:nodoc:
end
# Sets the etag, last_modified, or both on the response and renders a
- # "304 Not Modified" response if the request is already fresh.
+ # "304 Not Modified" response if the request is already fresh.
#
# Example:
#
@@ -1133,8 +1133,8 @@ module ActionController #:nodoc:
# @article = Article.find(params[:id])
# fresh_when(:etag => @article, :last_modified => @article.created_at.utc)
# end
- #
- # This will render the show template if the request isn't sending a matching etag or
+ #
+ # This will render the show template if the request isn't sending a matching etag or
# If-Modified-Since header and just a "304 Not Modified" response if there's a match.
def fresh_when(options)
options.assert_valid_keys(:etag, :last_modified)
@@ -1239,7 +1239,7 @@ module ActionController #:nodoc:
log_processing_for_parameters
end
end
-
+
def log_processing_for_request_id
request_id = "\n\nProcessing #{self.class.name}\##{action_name} "
request_id << "to #{params[:format]} " if params[:format]
@@ -1251,7 +1251,7 @@ module ActionController #:nodoc:
def log_processing_for_parameters
parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
parameters = parameters.except!(:controller, :action, :format, :_method)
-
+
logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
end
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index 3a5e5071bb..de35b53872 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -38,8 +38,8 @@ module ActionController #:nodoc:
'ActionView::TemplateError' => 'template_error'
}
- RESCUES_TEMPLATE_PATH = ActionView::PathSet::Path.new(
- File.join(File.dirname(__FILE__), "templates"), true)
+ RESCUES_TEMPLATE_PATH = ActionView::Template::EagerPath.new(
+ File.join(File.dirname(__FILE__), "templates"))
def self.included(base) #:nodoc:
base.cattr_accessor :rescue_responses
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 50b79f5e35..1529bd3de7 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -225,7 +225,7 @@ module ActionView #:nodoc:
end
# Returns the result of a render that's dictated by the options hash. The primary options are:
- #
+ #
# * :partial - See ActionView::Partials.
# * :update - Calls update_page with the block given.
# * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
diff --git a/actionpack/lib/action_view/inline_template.rb b/actionpack/lib/action_view/inline_template.rb
index 5e00cef13f..54efa543c8 100644
--- a/actionpack/lib/action_view/inline_template.rb
+++ b/actionpack/lib/action_view/inline_template.rb
@@ -12,7 +12,7 @@ module ActionView #:nodoc:
private
# Always recompile inline templates
- def recompile?(local_assigns)
+ def recompile?
true
end
end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index b030156889..19207e7262 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -2,7 +2,7 @@ module ActionView #:nodoc:
class PathSet < Array #:nodoc:
def self.type_cast(obj)
if obj.is_a?(String)
- Path.new(obj)
+ Template::EagerPath.new(obj)
else
obj
end
@@ -32,111 +32,6 @@ module ActionView #:nodoc:
super(*objs.map { |obj| self.class.type_cast(obj) })
end
- class Path #:nodoc:
- attr_reader :path, :paths
- delegate :hash, :inspect, :to => :path
-
- def initialize(path, load = false)
- raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
- @path = path.freeze
- reload! if load
- end
-
- def to_s
- if defined?(RAILS_ROOT)
- path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '')
- else
- path.to_s
- end
- end
-
- def to_str
- path.to_str
- end
-
- def ==(path)
- to_str == path.to_str
- end
-
- def eql?(path)
- to_str == path.to_str
- end
-
- # Returns a ActionView::Template object for the given path string. The
- # input path should be relative to the view path directory,
- # +hello/index.html.erb+. This method also has a special exception to
- # match partial file names without a handler extension. So
- # +hello/index.html+ will match the first template it finds with a
- # known template extension, +hello/index.html.erb+. Template extensions
- # should not be confused with format extensions +html+, +js+, +xml+,
- # etc. A format must be supplied to match a formated file. +hello/index+
- # will never match +hello/index.html.erb+.
- #
- # This method also has two different implementations, one that is "lazy"
- # and makes file system calls every time and the other is cached,
- # "eager" which looks up the template in an in memory index. The "lazy"
- # version is designed for development where you want to automatically
- # find new templates between requests. The "eager" version is designed
- # for production mode and it is much faster but requires more time
- # upfront to build the file index.
- def [](path)
- if loaded?
- @paths[path]
- else
- Dir.glob("#{@path}/#{path}*").each do |file|
- template = create_template(file)
- if template.accessible_paths.include?(path)
- return template
- end
- end
- nil
- end
- end
-
- def loaded?
- @loaded ? true : false
- end
-
- def load
- reload! unless loaded?
- self
- end
-
- # Rebuild load path directory cache
- def reload!
- @paths = {}
-
- templates_in_path do |template|
- template.load!
- template.accessible_paths.each do |path|
- @paths[path] = template
- end
- end
-
- @paths.freeze
- @loaded = true
- end
-
- private
- def templates_in_path
- (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
- yield create_template(file) unless File.directory?(file)
- end
- end
-
- def create_template(file)
- Template.new(file.split("#{self}/").last, self)
- end
- end
-
- def load
- each { |path| path.load }
- end
-
- def reload!
- each { |path| path.reload! }
- end
-
def find_template(original_template_path, format = nil)
return original_template_path if original_template_path.respond_to?(:render)
template_path = original_template_path.sub(/^\//, '')
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
index d8e72f1179..153e14f68b 100644
--- a/actionpack/lib/action_view/renderable.rb
+++ b/actionpack/lib/action_view/renderable.rb
@@ -60,7 +60,7 @@ module ActionView
def compile(local_assigns)
render_symbol = method_name(local_assigns)
- if recompile?(render_symbol)
+ if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile?
compile!(render_symbol, local_assigns)
end
end
@@ -89,11 +89,8 @@ module ActionView
end
end
- # Method to check whether template compilation is necessary.
- # The template will be compiled if the file has not been compiled yet, or
- # if local_assigns has a new key, which isn't supported by the compiled code yet.
- def recompile?(symbol)
- !Base::CompiledTemplates.method_defined?(symbol) || !loaded?
+ def recompile?
+ false
end
end
end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
index 5b384d0e4d..88ee07d95b 100644
--- a/actionpack/lib/action_view/template.rb
+++ b/actionpack/lib/action_view/template.rb
@@ -1,5 +1,83 @@
module ActionView #:nodoc:
class Template
+ class Path
+ attr_reader :path, :paths
+ delegate :hash, :inspect, :to => :path
+
+ def initialize(path)
+ raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
+ @path = path.freeze
+ end
+
+ def to_s
+ if defined?(RAILS_ROOT)
+ path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '')
+ else
+ path.to_s
+ end
+ end
+
+ def to_str
+ path.to_str
+ end
+
+ def ==(path)
+ to_str == path.to_str
+ end
+
+ def eql?(path)
+ to_str == path.to_str
+ end
+
+ # Returns a ActionView::Template object for the given path string. The
+ # input path should be relative to the view path directory,
+ # +hello/index.html.erb+. This method also has a special exception to
+ # match partial file names without a handler extension. So
+ # +hello/index.html+ will match the first template it finds with a
+ # known template extension, +hello/index.html.erb+. Template extensions
+ # should not be confused with format extensions +html+, +js+, +xml+,
+ # etc. A format must be supplied to match a formated file. +hello/index+
+ # will never match +hello/index.html.erb+.
+ def [](path)
+ templates_in_path do |template|
+ if template.accessible_paths.include?(path)
+ return template
+ end
+ end
+ nil
+ end
+
+ private
+ def templates_in_path
+ (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
+ yield create_template(file) unless File.directory?(file)
+ end
+ end
+
+ def create_template(file)
+ Template.new(file.split("#{self}/").last, self)
+ end
+ end
+
+ class EagerPath < Path
+ def initialize(path)
+ super
+
+ @paths = {}
+ templates_in_path do |template|
+ template.load!
+ template.accessible_paths.each do |path|
+ @paths[path] = template
+ end
+ end
+ @paths.freeze
+ end
+
+ def [](path)
+ @paths[path]
+ end
+ end
+
extend TemplateHandlers
extend ActiveSupport::Memoizable
include Renderable
@@ -115,13 +193,12 @@ module ActionView #:nodoc:
File.mtime(filename) > mtime
end
- def loaded?
- @loaded
+ def recompile?
+ !@cached
end
def load!
- @loaded = true
- compile({})
+ @cached = true
freeze
end
--
cgit v1.2.3
From 6b3c702bd7321e7fcf86a82edb720b96a7e34119 Mon Sep 17 00:00:00 2001
From: Jeremy Kemper
Date: Tue, 30 Dec 2008 15:16:51 -0800
Subject: Fix formatted_* deprecation message
---
actionpack/lib/action_controller/routing/route_set.rb | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb
index 06aef6e169..044ace7de1 100644
--- a/actionpack/lib/action_controller/routing/route_set.rb
+++ b/actionpack/lib/action_controller/routing/route_set.rb
@@ -195,8 +195,8 @@ module ActionController
def formatted_#{selector}(*args) # def formatted_users_url(*args)
ActiveSupport::Deprecation.warn( # ActiveSupport::Deprecation.warn(
"formatted_#{selector}() has been deprecated. " + # "formatted_users_url() has been deprecated. " +
- "please pass format to the standard" + # "please pass format to the standard" +
- "#{selector}() method instead.", caller) # "users_url() method instead.", caller)
+ "Please pass format to the standard " + # "Please pass format to the standard " +
+ "#{selector} method instead.", caller) # "users_url method instead.", caller)
#{selector}(*args) # users_url(*args)
end # end
protected :#{selector} # protected :users_url
--
cgit v1.2.3
From 658cf556d2bf3ea099fbce434ca338bf28365b40 Mon Sep 17 00:00:00 2001
From: Jeremy Kemper
Date: Tue, 30 Dec 2008 18:06:56 -0800
Subject: Test that exceptions raised in filters are properly rescued
---
actionpack/lib/action_controller/base.rb | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index bc18dfaec8..aa604395a9 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -1350,9 +1350,12 @@ module ActionController #:nodoc:
end
Base.class_eval do
- include Flash, Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers
- include Cookies, Caching, Verification, Streaming
- include SessionManagement, HttpAuthentication::Basic::ControllerMethods
- include RecordIdentifier, RequestForgeryProtection, Translation
+ [ Flash, Filters, Layout, Benchmarking, Rescue, MimeResponds, Helpers,
+ Cookies, Caching, Verification, Streaming, SessionManagement,
+ HttpAuthentication::Basic::ControllerMethods, RecordIdentifier,
+ RequestForgeryProtection, Translation
+ ].each do |mod|
+ include mod
+ end
end
end
--
cgit v1.2.3
From a38c749d8b5fd020d7294ffb4d597d4ab3fb30db Mon Sep 17 00:00:00 2001
From: Yehuda Katz
Date: Fri, 2 Jan 2009 22:16:48 -0800
Subject: Sync 'rails/rails/master'
---
actionpack/lib/action_controller/dispatcher.rb | 1 -
actionpack/lib/action_controller/integration.rb | 5 +-
actionpack/lib/action_controller/rescue.rb | 4 +-
.../lib/action_view/helpers/asset_tag_helper.rb | 456 ++++++---------------
.../lib/action_view/helpers/benchmark_helper.rb | 29 +-
5 files changed, 160 insertions(+), 335 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index c4e7357b81..d5af45f0da 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -91,7 +91,6 @@ module ActionController
run_callbacks :prepare_dispatch
Routing::Routes.reload
- ActionView::Helpers::AssetTagHelper::AssetTag::Cache.clear
end
# Cleanup the application by clearing out loaded classes so they can
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index a8e54c2fc7..d9899112c3 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -81,8 +81,8 @@ module ActionController
end
# Create and initialize a new Session instance.
- def initialize(app)
- @application = app
+ def initialize(app = nil)
+ @application = app || ActionController::Dispatcher.new
reset!
end
@@ -591,7 +591,6 @@ EOF
# can use this method to open multiple sessions that ought to be tested
# simultaneously.
def open_session(application = nil)
- application ||= ActionController::Dispatcher.new
session = Integration::Session.new(application)
# delegate the fixture accessors back to the test instance
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index de35b53872..8824d983b4 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -60,8 +60,8 @@ module ActionController #:nodoc:
module ClassMethods
def call_with_exception(env, exception) #:nodoc:
- request = env["actioncontroller.rescue.request"]
- response = env["actioncontroller.rescue.response"]
+ request = env["actioncontroller.rescue.request"] ||= Request.new(env)
+ response = env["actioncontroller.rescue.response"] ||= Response.new
new.process(request, response, :rescue_action, exception)
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 0633d5414e..a341b453ec 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -157,7 +157,7 @@ module ActionView
# javascript_path "http://www.railsapplication.com/js/xmlhr" # => http://www.railsapplication.com/js/xmlhr.js
# javascript_path "http://www.railsapplication.com/js/xmlhr.js" # => http://www.railsapplication.com/js/xmlhr.js
def javascript_path(source)
- JavaScriptTag.new(self, @controller, source).public_path
+ compute_public_path(source, 'javascripts', 'js')
end
alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
@@ -255,17 +255,15 @@ module ActionView
joined_javascript_name = (cache == true ? "all" : cache) + ".js"
joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name)
- unless File.exists?(joined_javascript_path)
- JavaScriptSources.create(self, @controller, sources, recursive).write_asset_file_contents(joined_javascript_path)
- end
+ write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path)
javascript_src_tag(joined_javascript_name, options)
else
- JavaScriptSources.create(self, @controller, sources, recursive).expand_sources.collect { |source|
- javascript_src_tag(source, options)
- }.join("\n")
+ expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n")
end
end
+ @@javascript_expansions = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup }
+
# Register one or more javascript files to be included when symbol
# is passed to javascript_include_tag. This method is typically intended
# to be called from plugin initialization to register javascript files
@@ -278,9 +276,11 @@ module ActionView
#
#
def self.register_javascript_expansion(expansions)
- JavaScriptSources.expansions.merge!(expansions)
+ @@javascript_expansions.merge!(expansions)
end
+ @@stylesheet_expansions = {}
+
# Register one or more stylesheet files to be included when symbol
# is passed to stylesheet_link_tag. This method is typically intended
# to be called from plugin initialization to register stylesheet files
@@ -293,7 +293,7 @@ module ActionView
#
#
def self.register_stylesheet_expansion(expansions)
- StylesheetSources.expansions.merge!(expansions)
+ @@stylesheet_expansions.merge!(expansions)
end
# Register one or more additional JavaScript files to be included when
@@ -301,11 +301,11 @@ module ActionView
# typically intended to be called from plugin initialization to register additional
# .js files that the plugin installed in public/javascripts.
def self.register_javascript_include_default(*sources)
- JavaScriptSources.expansions[:defaults].concat(sources)
+ @@javascript_expansions[:defaults].concat(sources)
end
def self.reset_javascript_include_default #:nodoc:
- JavaScriptSources.expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup
+ @@javascript_expansions[:defaults] = JAVASCRIPT_DEFAULT_SOURCES.dup
end
# Computes the path to a stylesheet asset in the public stylesheets directory.
@@ -320,7 +320,7 @@ module ActionView
# stylesheet_path "http://www.railsapplication.com/css/style" # => http://www.railsapplication.com/css/style.css
# stylesheet_path "http://www.railsapplication.com/css/style.js" # => http://www.railsapplication.com/css/style.css
def stylesheet_path(source)
- StylesheetTag.new(self, @controller, source).public_path
+ compute_public_path(source, 'stylesheets', 'css')
end
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
@@ -365,7 +365,6 @@ module ActionView
# compressed by gzip (leading to faster transfers). Caching will only happen if ActionController::Base.perform_caching
# is set to true (which is the case by default for the Rails production environment, but not for the development
# environment). Examples:
-
#
# ==== Examples
# stylesheet_link_tag :all, :cache => true # when ActionController::Base.perform_caching is false =>
@@ -396,14 +395,10 @@ module ActionView
joined_stylesheet_name = (cache == true ? "all" : cache) + ".css"
joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name)
- unless File.exists?(joined_stylesheet_path)
- StylesheetSources.create(self, @controller, sources, recursive).write_asset_file_contents(joined_stylesheet_path)
- end
+ write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path)
stylesheet_tag(joined_stylesheet_name, options)
else
- StylesheetSources.create(self, @controller, sources, recursive).expand_sources.collect { |source|
- stylesheet_tag(source, options)
- }.join("\n")
+ expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n")
end
end
@@ -418,7 +413,7 @@ module ActionView
# image_path("/icons/edit.png") # => /icons/edit.png
# image_path("http://www.railsapplication.com/img/edit.png") # => http://www.railsapplication.com/img/edit.png
def image_path(source)
- ImageTag.new(self, @controller, source).public_path
+ compute_public_path(source, 'images')
end
alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
@@ -474,352 +469,163 @@ module ActionView
end
private
- def javascript_src_tag(source, options)
- content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options))
- end
-
- def stylesheet_tag(source, options)
- tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false)
- end
-
- module ImageAsset
- DIRECTORY = 'images'.freeze
-
- def directory
- DIRECTORY
+ # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
+ # Prefix with /dir/ if lacking a leading +/+. Account for relative URL
+ # roots. Rewrite the asset path for cache-busting asset ids. Include
+ # asset host, if configured, with the correct request protocol.
+ def compute_public_path(source, dir, ext = nil, include_host = true)
+ has_request = @controller.respond_to?(:request)
+
+ if ext && (File.extname(source).blank? || File.exist?(File.join(ASSETS_DIR, dir, "#{source}.#{ext}")))
+ source += ".#{ext}"
end
- def extension
- nil
- end
- end
-
- module JavaScriptAsset
- DIRECTORY = 'javascripts'.freeze
- EXTENSION = 'js'.freeze
+ unless source =~ %r{^[-a-z]+://}
+ source = "/#{dir}/#{source}" unless source[0] == ?/
- def public_directory
- JAVASCRIPTS_DIR
- end
+ source = rewrite_asset_path(source)
- def directory
- DIRECTORY
+ if has_request && include_host
+ unless source =~ %r{^#{ActionController::Base.relative_url_root}/}
+ source = "#{ActionController::Base.relative_url_root}#{source}"
+ end
+ end
end
- def extension
- EXTENSION
- end
- end
+ if include_host && source !~ %r{^[-a-z]+://}
+ host = compute_asset_host(source)
- module StylesheetAsset
- DIRECTORY = 'stylesheets'.freeze
- EXTENSION = 'css'.freeze
-
- def public_directory
- STYLESHEETS_DIR
- end
-
- def directory
- DIRECTORY
- end
+ if has_request && !host.blank? && host !~ %r{^[-a-z]+://}
+ host = "#{@controller.request.protocol}#{host}"
+ end
- def extension
- EXTENSION
+ "#{host}#{source}"
+ else
+ source
end
end
- class AssetTag
- extend ActiveSupport::Memoizable
-
- Cache = {}
- CacheGuard = Mutex.new
-
- ProtocolRegexp = %r{^[-a-z]+://}.freeze
-
- def initialize(template, controller, source, include_host = true)
- # NOTE: The template arg is temporarily needed for a legacy plugin
- # hook that is expected to call rewrite_asset_path on the
- # template. This should eventually be removed.
- @template = template
- @controller = controller
- @source = source
- @include_host = include_host
- @cache_key = if controller.respond_to?(:request)
- [self.class.name,controller.request.protocol,
- ActionController::Base.asset_host,
- ActionController::Base.relative_url_root,
- source, include_host]
+ # Pick an asset host for this source. Returns +nil+ if no host is set,
+ # the host if no wildcard is set, the host interpolated with the
+ # numbers 0-3 if it contains %d (the number is the source hash mod 4),
+ # 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.is_a?(Proc) || host.respond_to?(:call)
+ case host.is_a?(Proc) ? host.arity : host.method(:call).arity
+ when 2
+ request = @controller.respond_to?(:request) && @controller.request
+ host.call(source, request)
+ else
+ host.call(source)
+ end
else
- [self.class.name,ActionController::Base.asset_host, source, include_host]
+ (host =~ /%d/) ? host % (source.hash % 4) : host
end
end
-
- def public_path
- compute_public_path(@source)
- end
- memoize :public_path
+ end
- def asset_file_path
- File.join(ASSETS_DIR, public_path.split('?').first)
- end
- memoize :asset_file_path
+ # Use the RAILS_ASSET_ID environment variable or the source's
+ # modification time as its cache-busting asset id.
+ def rails_asset_id(source)
+ if asset_id = ENV["RAILS_ASSET_ID"]
+ asset_id
+ else
+ path = File.join(ASSETS_DIR, source)
- def contents
- File.read(asset_file_path)
+ if File.exist?(path)
+ File.mtime(path).to_i.to_s
+ else
+ ''
+ end
end
+ end
- def mtime
- File.mtime(asset_file_path)
+ # Break out the asset path rewrite in case plugins wish to put the asset id
+ # someplace other than the query string.
+ def rewrite_asset_path(source)
+ asset_id = rails_asset_id(source)
+ if asset_id.blank?
+ source
+ else
+ source + "?#{asset_id}"
end
-
- private
- def request
- request? && @controller.request
- end
-
- def request?
- @controller.respond_to?(:request)
- end
-
- # Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
- # Prefix with /dir/ if lacking a leading +/+. Account for relative URL
- # roots. Rewrite the asset path for cache-busting asset ids. Include
- # asset host, if configured, with the correct request protocol.
- def compute_public_path(source)
- if source =~ ProtocolRegexp
- source += ".#{extension}" if missing_extension?(source)
- source = prepend_asset_host(source)
- source
- else
- CacheGuard.synchronize do
- Cache[@cache_key + [source]] ||= begin
- source += ".#{extension}" if missing_extension?(source) || file_exists_with_extension?(source)
- source = "/#{directory}/#{source}" unless source[0] == ?/
- source = rewrite_asset_path(source)
- source = prepend_relative_url_root(source)
- source = prepend_asset_host(source)
- source
- end
- end
- end
- end
-
- def missing_extension?(source)
- extension && File.extname(source).blank?
- end
-
- def file_exists_with_extension?(source)
- extension && File.exist?(File.join(ASSETS_DIR, directory, "#{source}.#{extension}"))
- end
-
- def prepend_relative_url_root(source)
- relative_url_root = ActionController::Base.relative_url_root
- if request? && @include_host && source !~ %r{^#{relative_url_root}/}
- "#{relative_url_root}#{source}"
- else
- source
- end
- end
-
- def prepend_asset_host(source)
- if @include_host && source !~ ProtocolRegexp
- host = compute_asset_host(source)
- if request? && !host.blank? && host !~ ProtocolRegexp
- host = "#{request.protocol}#{host}"
- end
- "#{host}#{source}"
- else
- source
- end
- end
-
- # Pick an asset host for this source. Returns +nil+ if no host is set,
- # the host if no wildcard is set, the host interpolated with the
- # numbers 0-3 if it contains %d (the number is the source hash mod 4),
- # 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.is_a?(Proc) || host.respond_to?(:call)
- case host.is_a?(Proc) ? host.arity : host.method(:call).arity
- when 2
- host.call(source, request)
- else
- host.call(source)
- end
- else
- (host =~ /%d/) ? host % (source.hash % 4) : host
- end
- end
- end
-
- # Use the RAILS_ASSET_ID environment variable or the source's
- # modification time as its cache-busting asset id.
- def rails_asset_id(source)
- if asset_id = ENV["RAILS_ASSET_ID"]
- asset_id
- else
- path = File.join(ASSETS_DIR, source)
-
- if File.exist?(path)
- File.mtime(path).to_i.to_s
- else
- ''
- end
- end
- end
-
- # Break out the asset path rewrite in case plugins wish to put the asset id
- # someplace other than the query string.
- def rewrite_asset_path(source)
- if @template.respond_to?(:rewrite_asset_path)
- # DEPRECATE: This way to override rewrite_asset_path
- @template.send(:rewrite_asset_path, source)
- else
- asset_id = rails_asset_id(source)
- if asset_id.blank?
- source
- else
- "#{source}?#{asset_id}"
- end
- end
- end
end
- class ImageTag < AssetTag
- include ImageAsset
+ def javascript_src_tag(source, options)
+ content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options))
end
- class JavaScriptTag < AssetTag
- include JavaScriptAsset
+ def stylesheet_tag(source, options)
+ tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false)
end
- class StylesheetTag < AssetTag
- include StylesheetAsset
+ def compute_javascript_paths(*args)
+ expand_javascript_sources(*args).collect { |source| compute_public_path(source, 'javascripts', 'js', false) }
end
- class AssetCollection
- extend ActiveSupport::Memoizable
-
- Cache = {}
- CacheGuard = Mutex.new
-
- def self.create(template, controller, sources, recursive)
- CacheGuard.synchronize do
- key = [self, sources, recursive]
- Cache[key] ||= new(template, controller, sources, recursive).freeze
- end
- end
-
- def initialize(template, controller, sources, recursive)
- # NOTE: The template arg is temporarily needed for a legacy plugin
- # hook. See NOTE under AssetTag#initialize for more details
- @template = template
- @controller = controller
- @sources = sources
- @recursive = recursive
- end
+ def compute_stylesheet_paths(*args)
+ expand_stylesheet_sources(*args).collect { |source| compute_public_path(source, 'stylesheets', 'css', false) }
+ end
- def write_asset_file_contents(joined_asset_path)
- FileUtils.mkdir_p(File.dirname(joined_asset_path))
- File.open(joined_asset_path, "w+") { |cache| cache.write(joined_contents) }
- mt = latest_mtime
- File.utime(mt, mt, joined_asset_path)
+ def expand_javascript_sources(sources, recursive = false)
+ if sources.include?(:all)
+ all_javascript_files = collect_asset_files(JAVASCRIPTS_DIR, ('**' if recursive), '*.js')
+ ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq
+ else
+ expanded_sources = sources.collect do |source|
+ determine_source(source, @@javascript_expansions)
+ end.flatten
+ expanded_sources << "application" if sources.include?(:defaults) && File.exist?(File.join(JAVASCRIPTS_DIR, "application.js"))
+ expanded_sources
end
-
- private
- def determine_source(source, collection)
- case source
- when Symbol
- collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
- else
- source
- end
- end
-
- def validate_sources!
- @sources.collect { |source| determine_source(source, self.class.expansions) }.flatten
- end
-
- def all_asset_files
- path = [public_directory, ('**' if @recursive), "*.#{extension}"].compact
- Dir[File.join(*path)].collect { |file|
- file[-(file.size - public_directory.size - 1)..-1].sub(/\.\w+$/, '')
- }.sort
- end
-
- def tag_sources
- expand_sources.collect { |source| tag_class.new(@template, @controller, source, false) }
- end
-
- def joined_contents
- tag_sources.collect { |source| source.contents }.join("\n\n")
- end
-
- # Set mtime to the latest of the combined files to allow for
- # consistent ETag without a shared filesystem.
- def latest_mtime
- tag_sources.map { |source| source.mtime }.max
- end
end
- class JavaScriptSources < AssetCollection
- include JavaScriptAsset
-
- EXPANSIONS = { :defaults => JAVASCRIPT_DEFAULT_SOURCES.dup }
-
- def self.expansions
- EXPANSIONS
+ def expand_stylesheet_sources(sources, recursive)
+ if sources.first == :all
+ collect_asset_files(STYLESHEETS_DIR, ('**' if recursive), '*.css')
+ else
+ sources.collect do |source|
+ determine_source(source, @@stylesheet_expansions)
+ end.flatten
end
+ end
- APPLICATION_JS = "application".freeze
- APPLICATION_FILE = "application.js".freeze
-
- def expand_sources
- if @sources.include?(:all)
- assets = all_asset_files
- ((defaults.dup & assets) + assets).uniq!
- else
- expanded_sources = validate_sources!
- expanded_sources << APPLICATION_JS if include_application?
- expanded_sources
- end
+ def determine_source(source, collection)
+ case source
+ when Symbol
+ collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
+ else
+ source
end
- memoize :expand_sources
-
- private
- def tag_class
- JavaScriptTag
- end
-
- def defaults
- determine_source(:defaults, self.class.expansions)
- end
+ end
- def include_application?
- @sources.include?(:defaults) && File.exist?(File.join(JAVASCRIPTS_DIR, APPLICATION_FILE))
- end
+ def join_asset_file_contents(paths)
+ paths.collect { |path| File.read(asset_file_path(path)) }.join("\n\n")
end
- class StylesheetSources < AssetCollection
- include StylesheetAsset
+ def write_asset_file_contents(joined_asset_path, asset_paths)
+ FileUtils.mkdir_p(File.dirname(joined_asset_path))
+ File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }
- EXPANSIONS = {}
+ # Set mtime to the latest of the combined files to allow for
+ # consistent ETag without a shared filesystem.
+ mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max
+ File.utime(mt, mt, joined_asset_path)
+ end
- def self.expansions
- EXPANSIONS
- end
+ def asset_file_path(path)
+ File.join(ASSETS_DIR, path.split('?').first)
+ end
- def expand_sources
- @sources.first == :all ? all_asset_files : validate_sources!
- end
- memoize :expand_sources
+ def collect_asset_files(*path)
+ dir = path.first
- private
- def tag_class
- StylesheetTag
- end
+ Dir[File.join(*path.compact)].collect do |file|
+ file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
+ end.sort
end
end
end
-end
+end
\ No newline at end of file
diff --git a/actionpack/lib/action_view/helpers/benchmark_helper.rb b/actionpack/lib/action_view/helpers/benchmark_helper.rb
index 372d24a22e..61c2cecb04 100644
--- a/actionpack/lib/action_view/helpers/benchmark_helper.rb
+++ b/actionpack/lib/action_view/helpers/benchmark_helper.rb
@@ -18,12 +18,33 @@ module ActionView
# That would add something like "Process data files (345.2ms)" to the log,
# which you can then use to compare timings when optimizing your code.
#
- # You may give an optional logger level as the second argument
+ # You may give an optional logger level as the :level option.
# (:debug, :info, :warn, :error); the default value is :info.
- def benchmark(message = "Benchmarking", level = :info)
+ #
+ # <% benchmark "Low-level files", :level => :debug do %>
+ # <%= lowlevel_files_operation %>
+ # <% end %>
+ #
+ # Finally, you can pass true as the third argument to silence all log activity
+ # inside the block. This is great for boiling down a noisy block to just a single statement:
+ #
+ # <% benchmark "Process data files", :level => :info, :silence => true do %>
+ # <%= expensive_and_chatty_files_operation %>
+ # <% end %>
+ def benchmark(message = "Benchmarking", options = {})
if controller.logger
- ms = Benchmark.ms { yield }
- controller.logger.send(level, '%s (%.1fms)' % [message, ms])
+ if options.is_a?(Symbol)
+ ActiveSupport::Deprecation.warn("use benchmark('#{message}', :level => :#{options}) instead", caller)
+ options = { :level => options, :silence => false }
+ else
+ options.assert_valid_keys(:level, :silence)
+ options[:level] ||= :info
+ end
+
+ result = nil
+ ms = Benchmark.ms { result = options[:silence] ? controller.logger.silence { yield } : yield }
+ controller.logger.send(options[:level], '%s (%.1fms)' % [ message, ms ])
+ result
else
yield
end
--
cgit v1.2.3
From ca54cc5df72a8bd7660d5be8333dcee2b6949257 Mon Sep 17 00:00:00 2001
From: Yehuda Katz
Date: Sun, 4 Jan 2009 00:56:52 -0800
Subject: Sync 'rails/rails/master'
---
.../lib/action_controller/middleware_stack.rb | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/middleware_stack.rb b/actionpack/lib/action_controller/middleware_stack.rb
index 74f28565c0..2bccba2ba1 100644
--- a/actionpack/lib/action_controller/middleware_stack.rb
+++ b/actionpack/lib/action_controller/middleware_stack.rb
@@ -1,6 +1,14 @@
module ActionController
class MiddlewareStack < Array
class Middleware
+ def self.new(klass, *args, &block)
+ if klass.is_a?(self)
+ klass
+ else
+ super
+ end
+ end
+
attr_reader :args, :block
def initialize(klass, *args, &block)
@@ -65,6 +73,19 @@ module ActionController
block.call(self) if block_given?
end
+ def insert(index, *objs)
+ index = self.index(index) unless index.is_a?(Integer)
+ objs = objs.map { |obj| Middleware.new(obj) }
+ super(index, *objs)
+ end
+
+ alias_method :insert_before, :insert
+
+ def insert_after(index, *objs)
+ index = self.index(index) unless index.is_a?(Integer)
+ insert(index + 1, *objs)
+ end
+
def use(*args, &block)
middleware = Middleware.new(*args, &block)
push(middleware)
--
cgit v1.2.3
From db619c4a4115d5eeca9473f8f77f34c877e4e1dd Mon Sep 17 00:00:00 2001
From: Yehuda Katz
Date: Mon, 5 Jan 2009 01:10:55 -0800
Subject: Sync 'rails/rails/master'
---
actionpack/lib/action_controller/base.rb | 4 +--
actionpack/lib/action_controller/dispatcher.rb | 2 ++
actionpack/lib/action_controller/request.rb | 20 +++++-------
actionpack/lib/action_controller/request_parser.rb | 7 +++--
actionpack/lib/action_controller/rescue.rb | 4 +--
.../lib/action_view/helpers/asset_tag_helper.rb | 36 +++++++++++++++++++---
6 files changed, 49 insertions(+), 24 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index aa604395a9..1093a9bc04 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -384,8 +384,8 @@ module ActionController #:nodoc:
class << self
def call(env)
# HACK: For global rescue to have access to the original request and response
- request = env["actioncontroller.rescue.request"] ||= Request.new(env)
- response = env["actioncontroller.rescue.response"] ||= Response.new
+ request = env["action_controller.rescue.request"] ||= Request.new(env)
+ response = env["action_controller.rescue.response"] ||= Response.new
process(request, response)
end
diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb
index d5af45f0da..781bc48887 100644
--- a/actionpack/lib/action_controller/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatcher.rb
@@ -8,6 +8,8 @@ module ActionController
# Development mode callbacks
before_dispatch :reload_application
after_dispatch :cleanup_application
+
+ ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
end
if defined?(ActiveRecord)
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index 393923104c..f12213d3ba 100755
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -19,6 +19,7 @@ module ActionController
def initialize(env)
@env = env
+ @parser = ActionController::RequestParser.new(env)
end
%w[ AUTH_TYPE GATEWAY_INTERFACE PATH_INFO
@@ -92,16 +93,15 @@ module ActionController
# Returns the content length of the request as an integer.
def content_length
- @env['CONTENT_LENGTH'].to_i
+ @env["action_controller.request.content_length"] ||= @env['CONTENT_LENGTH'].to_i
end
- memoize :content_length
# The MIME type of the HTTP request, such as Mime::XML.
#
# For backward compatibility, the post \format is extracted from the
# X-Post-Data-Format HTTP header if present.
def content_type
- Mime::Type.lookup(parser.content_type_without_parameters)
+ Mime::Type.lookup(@parser.content_type_without_parameters)
end
memoize :content_type
@@ -398,7 +398,7 @@ EOM
# Read the request \body. This is useful for web services that need to
# work with raw requests directly.
def raw_post
- parser.raw_post
+ @parser.raw_post
end
# Returns both GET and POST \parameters in a single hash.
@@ -427,7 +427,7 @@ EOM
end
def body
- parser.body
+ @parser.body
end
def remote_addr
@@ -440,11 +440,11 @@ EOM
alias referer referrer
def query_parameters
- @query_parameters ||= parser.query_parameters
+ @parser.query_parameters
end
def request_parameters
- @request_parameters ||= parser.request_parameters
+ @parser.request_parameters
end
def body_stream #:nodoc:
@@ -460,7 +460,7 @@ EOM
end
def session=(session) #:nodoc:
- @session = session
+ @env['rack.session'] = session
end
def reset_session
@@ -483,9 +483,5 @@ EOM
def named_host?(host)
!(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
end
-
- def parser
- @parser ||= ActionController::RequestParser.new(@env)
- end
end
end
diff --git a/actionpack/lib/action_controller/request_parser.rb b/actionpack/lib/action_controller/request_parser.rb
index 82ee4c84c4..20d53f5d92 100644
--- a/actionpack/lib/action_controller/request_parser.rb
+++ b/actionpack/lib/action_controller/request_parser.rb
@@ -2,14 +2,15 @@ module ActionController
class RequestParser
def initialize(env)
@env = env
+ freeze
end
def request_parameters
- @request_parameters ||= parse_formatted_request_parameters
+ @env["action_controller.request_parser.request_parameters"] ||= parse_formatted_request_parameters
end
def query_parameters
- @query_parameters ||= self.class.parse_query_parameters(query_string)
+ @env["action_controller.request_parser.query_parameters"] ||= self.class.parse_query_parameters(query_string)
end
# Returns the query string, accounting for server idiosyncrasies.
@@ -90,7 +91,7 @@ module ActionController
end
def content_length
- @content_length ||= @env['CONTENT_LENGTH'].to_i
+ @env["action_controller.request.content_length"] ||= @env['CONTENT_LENGTH'].to_i
end
# The raw content type string. Use when you need parameters such as
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index 8824d983b4..4b7d1e32fd 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -60,8 +60,8 @@ module ActionController #:nodoc:
module ClassMethods
def call_with_exception(env, exception) #:nodoc:
- request = env["actioncontroller.rescue.request"] ||= Request.new(env)
- response = env["actioncontroller.rescue.response"] ||= Response.new
+ request = env["action_controller.rescue.request"] ||= Request.new(env)
+ response = env["action_controller.rescue.response"] ||= Response.new
new.process(request, response, :rescue_action, exception)
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index a341b453ec..58f8cca6be 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -468,6 +468,22 @@ module ActionView
tag("img", options)
end
+ def self.cache_asset_timestamps
+ @@cache_asset_timestamps
+ end
+
+ # You can enable or disable the asset tag timestamps cache.
+ # With the cache enabled, the asset tag helper methods will make fewer
+ # expense file system calls. However this prevents you from modifying
+ # any asset files while the server is running.
+ #
+ # ActionView::Helpers::AssetTagHelper.cache_asset_timestamps = false
+ def self.cache_asset_timestamps=(value)
+ @@cache_asset_timestamps = value
+ end
+
+ @@cache_asset_timestamps = true
+
private
# Add the the extension +ext+ if not present. Return full URLs otherwise untouched.
# Prefix with /dir/ if lacking a leading +/+. Account for relative URL
@@ -526,18 +542,28 @@ module ActionView
end
end
+ @@asset_timestamps_cache = {}
+ @@asset_timestamps_cache_guard = Mutex.new
+
# Use the RAILS_ASSET_ID environment variable or the source's
# modification time as its cache-busting asset id.
def rails_asset_id(source)
if asset_id = ENV["RAILS_ASSET_ID"]
asset_id
else
- path = File.join(ASSETS_DIR, source)
-
- if File.exist?(path)
- File.mtime(path).to_i.to_s
+ if @@cache_asset_timestamps && (asset_id = @@asset_timestamps_cache[source])
+ asset_id
else
- ''
+ path = File.join(ASSETS_DIR, source)
+ asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
+
+ if @@cache_asset_timestamps
+ @@asset_timestamps_cache_guard.synchronize do
+ @@asset_timestamps_cache[source] = asset_id
+ end
+ end
+
+ asset_id
end
end
end
--
cgit v1.2.3
From cf09fa74f7a9d9895b9a2d0fc475864d7e4bb56e Mon Sep 17 00:00:00 2001
From: Jeremy Kemper
Date: Wed, 7 Jan 2009 13:23:10 -0800
Subject: Include process methods in ActionController::TestCase only. No need
to alias_method_chain :process either.
---
actionpack/lib/action_controller/test_case.rb | 3 ++
actionpack/lib/action_controller/test_process.rb | 54 +++++++++---------------
actionpack/lib/action_view/test_case.rb | 1 +
3 files changed, 24 insertions(+), 34 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 7ed1a3e160..93a0be4127 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,4 +1,5 @@
require 'active_support/test_case'
+require 'action_controller/test_process'
module ActionController
# Superclass for ActionController functional tests. Functional tests allow you to
@@ -102,6 +103,8 @@ module ActionController
#
# assert_redirected_to page_url(:title => 'foo')
class TestCase < ActiveSupport::TestCase
+ include TestProcess
+
module Assertions
%w(response selector tag dom routing model).each do |kind|
include ActionController::Assertions.const_get("#{kind.camelize}Assertions")
diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb
index 285a8b09e4..8180d4ee93 100644
--- a/actionpack/lib/action_controller/test_process.rb
+++ b/actionpack/lib/action_controller/test_process.rb
@@ -1,32 +1,4 @@
-require 'action_controller/test_case'
-
module ActionController #:nodoc:
- class Base
- attr_reader :assigns
-
- # Process a test request called with a TestRequest object.
- def self.process_test(request)
- new.process_test(request)
- end
-
- def process_test(request) #:nodoc:
- process(request, TestResponse.new)
- end
-
- def process_with_test(*args)
- returning process_without_test(*args) do
- @assigns = {}
- (instance_variable_names - @@protected_instance_variables).each do |var|
- value = instance_variable_get(var)
- @assigns[var[1..-1]] = value
- response.template.assigns[var[1..-1]] = value if response
- end
- end
- end
-
- alias_method_chain :process, :test
- end
-
class TestRequest < Request #:nodoc:
attr_accessor :cookies, :session_options
attr_accessor :query_parameters, :path, :session
@@ -433,7 +405,9 @@ module ActionController #:nodoc:
@request.session = ActionController::TestSession.new(session) unless session.nil?
@request.session["flash"] = ActionController::Flash::FlashHash.new.update(flash) if flash
build_request_uri(action, parameters)
- @controller.process(@request, @response)
+
+ Base.class_eval { include ProcessWithTest } unless Base < ProcessWithTest
+ @controller.process_with_test(@request, @response)
end
def xml_http_request(request_method, action, parameters = nil, session = nil, flash = nil)
@@ -545,12 +519,24 @@ module ActionController #:nodoc:
ActionController::Routing.const_set(:Routes, real_routes) if real_routes
end
end
-end
-module Test
- module Unit
- class TestCase #:nodoc:
- include ActionController::TestProcess
+ module ProcessWithTest #:nodoc:
+ def self.included(base)
+ base.class_eval { attr_reader :assigns }
+ end
+
+ def process_with_test(*args)
+ process(*args).tap { set_test_assigns }
end
+
+ private
+ def set_test_assigns
+ @assigns = {}
+ (instance_variable_names - self.class.protected_instance_variables).each do |var|
+ name, value = var[1..-1], instance_variable_get(var)
+ @assigns[name] = value
+ response.template.assigns[name] = value if response
+ end
+ end
end
end
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index 65839256aa..ec337bb05b 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -23,6 +23,7 @@ module ActionView
class TestCase < ActiveSupport::TestCase
include ActionController::TestCase::Assertions
+ include ActionController::TestProcess
class_inheritable_accessor :helper_class
@@helper_class = nil
--
cgit v1.2.3
From d99b4ec57f07005211bad7c0b6605b03f811c865 Mon Sep 17 00:00:00 2001
From: Jeremy Kemper
Date: Wed, 7 Jan 2009 14:39:47 -0800
Subject: Take care not to mix in public methods
---
actionpack/lib/action_controller/test_case.rb | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index 93a0be4127..0b0d0c799b 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -127,17 +127,18 @@ module ActionController
#
# The exception is stored in the exception accessor for further inspection.
module RaiseActionExceptions
- attr_accessor :exception
+ protected
+ attr_accessor :exception
- def rescue_action_without_handler(e)
- self.exception = e
-
- if request.remote_addr == "0.0.0.0"
- raise(e)
- else
- super(e)
+ def rescue_action_without_handler(e)
+ self.exception = e
+
+ if request.remote_addr == "0.0.0.0"
+ raise(e)
+ else
+ super(e)
+ end
end
- end
end
setup :setup_controller_request_and_response
--
cgit v1.2.3
From 72c983466651e9e4deaff15486de0da3918ff4be Mon Sep 17 00:00:00 2001
From: Jeremy Kemper
Date: Wed, 7 Jan 2009 15:52:19 -0800
Subject: Set assigns for integration tests also
---
actionpack/lib/action_controller/integration.rb | 7 +++++--
1 file changed, 5 insertions(+), 2 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb
index d9899112c3..ded72a71fb 100644
--- a/actionpack/lib/action_controller/integration.rb
+++ b/actionpack/lib/action_controller/integration.rb
@@ -367,8 +367,10 @@ module ActionController
env[key] = value
end
- unless ActionController::Base.respond_to?(:clear_last_instantiation!)
- ActionController::Base.module_eval { include ControllerCapture }
+ [ControllerCapture, ActionController::ProcessWithTest].each do |mod|
+ unless ActionController::Base < mod
+ ActionController::Base.class_eval { include mod }
+ end
end
ActionController::Base.clear_last_instantiation!
@@ -396,6 +398,7 @@ module ActionController
if @controller = ActionController::Base.last_instantiation
@request = @controller.request
@response = @controller.response
+ @controller.send(:set_test_assigns)
else
# Decorate responses from Rack Middleware and Rails Metal
# as an Response for the purposes of integration testing
--
cgit v1.2.3
From 431e696b899dbc48afdfb842af73e8f75bf6b08c Mon Sep 17 00:00:00 2001
From: Jeremy Kemper
Date: Wed, 7 Jan 2009 15:55:28 -0800
Subject: Remove Content-Length header from :no_content responses
---
actionpack/lib/action_controller/response.rb | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller/response.rb b/actionpack/lib/action_controller/response.rb
index 64319fe102..27860a6207 100644
--- a/actionpack/lib/action_controller/response.rb
+++ b/actionpack/lib/action_controller/response.rb
@@ -231,10 +231,13 @@ module ActionController # :nodoc:
# Don't set the Content-Length for block-based bodies as that would mean
# reading it all into memory. Not nice for, say, a 2GB streaming file.
def set_content_length!
- unless body.respond_to?(:call) || (status && status.to_s[0..2] == '304')
- self.headers["Content-Length"] ||= body.size
+ if status && status.to_s[0..2] == '204'
+ headers.delete('Content-Length')
+ elsif length = headers['Content-Length']
+ headers['Content-Length'] = length.to_s
+ elsif !body.respond_to?(:call) && (!status || status.to_s[0..2] != '304')
+ headers["Content-Length"] = body.size.to_s
end
- headers["Content-Length"] = headers["Content-Length"].to_s if headers["Content-Length"]
end
def convert_language!
--
cgit v1.2.3
From eb9af20b7cc0e374277cf330bdd404f9daab28ec Mon Sep 17 00:00:00 2001
From: Yehuda Katz
Date: Thu, 22 Jan 2009 16:18:10 -0600
Subject: Begin unifying the interface between ActionController and ActionView
---
actionpack/lib/action_controller.rb | 2 +
actionpack/lib/action_controller/base.rb | 258 ++++++++++--------
actionpack/lib/action_controller/layout.rb | 144 ++++------
actionpack/lib/action_controller/mime_responds.rb | 9 +-
actionpack/lib/action_controller/mime_type.rb | 14 +-
actionpack/lib/action_controller/request.rb | 35 ++-
actionpack/lib/action_controller/rescue.rb | 10 +-
.../templates/rescues/diagnostics.erb | 5 +-
actionpack/lib/action_view.rb | 19 +-
actionpack/lib/action_view/base.rb | 91 +------
.../lib/action_view/helpers/prototype_helper.rb | 6 +-
actionpack/lib/action_view/inline_template.rb | 19 --
actionpack/lib/action_view/partials.rb | 235 ----------------
actionpack/lib/action_view/paths.rb | 16 +-
actionpack/lib/action_view/render/partials.rb | 300 +++++++++++++++++++++
actionpack/lib/action_view/render/rendering.rb | 119 ++++++++
actionpack/lib/action_view/renderable.rb | 96 -------
actionpack/lib/action_view/renderable_partial.rb | 47 ----
actionpack/lib/action_view/template.rb | 235 ----------------
actionpack/lib/action_view/template/error.rb | 99 +++++++
actionpack/lib/action_view/template/handler.rb | 34 +++
actionpack/lib/action_view/template/handlers.rb | 48 ++++
.../lib/action_view/template/handlers/builder.rb | 17 ++
.../lib/action_view/template/handlers/erb.rb | 22 ++
.../lib/action_view/template/handlers/rjs.rb | 13 +
actionpack/lib/action_view/template/inline.rb | 19 ++
actionpack/lib/action_view/template/partial.rb | 18 ++
actionpack/lib/action_view/template/renderable.rb | 74 +++++
actionpack/lib/action_view/template/template.rb | 241 +++++++++++++++++
actionpack/lib/action_view/template_error.rb | 99 -------
actionpack/lib/action_view/template_handler.rb | 34 ---
actionpack/lib/action_view/template_handlers.rb | 48 ----
.../lib/action_view/template_handlers/builder.rb | 17 --
.../lib/action_view/template_handlers/erb.rb | 22 --
.../lib/action_view/template_handlers/rjs.rb | 13 -
actionpack/lib/action_view/test_case.rb | 19 +-
36 files changed, 1295 insertions(+), 1202 deletions(-)
delete mode 100644 actionpack/lib/action_view/inline_template.rb
delete mode 100644 actionpack/lib/action_view/partials.rb
create mode 100644 actionpack/lib/action_view/render/partials.rb
create mode 100644 actionpack/lib/action_view/render/rendering.rb
delete mode 100644 actionpack/lib/action_view/renderable.rb
delete mode 100644 actionpack/lib/action_view/renderable_partial.rb
delete mode 100644 actionpack/lib/action_view/template.rb
create mode 100644 actionpack/lib/action_view/template/error.rb
create mode 100644 actionpack/lib/action_view/template/handler.rb
create mode 100644 actionpack/lib/action_view/template/handlers.rb
create mode 100644 actionpack/lib/action_view/template/handlers/builder.rb
create mode 100644 actionpack/lib/action_view/template/handlers/erb.rb
create mode 100644 actionpack/lib/action_view/template/handlers/rjs.rb
create mode 100644 actionpack/lib/action_view/template/inline.rb
create mode 100644 actionpack/lib/action_view/template/partial.rb
create mode 100644 actionpack/lib/action_view/template/renderable.rb
create mode 100644 actionpack/lib/action_view/template/template.rb
delete mode 100644 actionpack/lib/action_view/template_error.rb
delete mode 100644 actionpack/lib/action_view/template_handler.rb
delete mode 100644 actionpack/lib/action_view/template_handlers.rb
delete mode 100644 actionpack/lib/action_view/template_handlers/builder.rb
delete mode 100644 actionpack/lib/action_view/template_handlers/erb.rb
delete mode 100644 actionpack/lib/action_view/template_handlers/rjs.rb
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 3e77970367..6000ee15c6 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -35,6 +35,8 @@ gem 'rack', '>= 0.9.0'
require 'rack'
require 'action_controller/rack_ext'
+require File.join(File.dirname(__FILE__), "action_pack")
+
module ActionController
# TODO: Review explicit to see if they will automatically be handled by
# the initilizer if they are really needed.
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 50b965ce4c..78c8bf0647 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -514,8 +514,8 @@ module ActionController #:nodoc:
def process(request, response, method = :perform_action, *arguments) #:nodoc:
response.request = request
- initialize_template_class(response)
assign_shortcuts(request, response)
+ initialize_template_class(response)
initialize_current_url
assign_names
@@ -863,90 +863,82 @@ module ActionController #:nodoc:
def render(options = nil, extra_options = {}, &block) #:doc:
raise DoubleRenderError, "Can only render or redirect once per action" if performed?
- validate_render_arguments(options, extra_options, block_given?)
-
- if options.nil?
- options = { :template => default_template, :layout => true }
- elsif options == :update
- options = extra_options.merge({ :update => true })
- elsif options.is_a?(String) || options.is_a?(Symbol)
- case options.to_s.index('/')
- when 0
- extra_options[:file] = options
- when nil
- extra_options[:action] = options
- else
- extra_options[:template] = options
- end
+ options = { :layout => true } if options.nil?
+ original, options = options, extra_options unless options.is_a?(Hash)
+
+ layout_name = options.delete(:layout)
- options = extra_options
- end
+ _process_options(options)
+
+ if block_given?
+ @template.send(:_evaluate_assigns_and_ivars)
- layout = pick_layout(options)
- response.layout = layout.path_without_format_and_extension if layout
- logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger && layout
-
- if content_type = options[:content_type]
- response.content_type = content_type.to_s
+ generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
+ response.content_type = Mime::JS
+ return render_for_text(generator.to_s)
end
-
- if location = options[:location]
- response.headers["Location"] = url_for(location)
+
+ if original
+ return render_for_name(original, layout_name, options) unless block_given?
+ end
+
+ if options.key?(:text)
+ return render_for_text(@template._render_text(options[:text],
+ _pick_layout(layout_name), options))
end
- if options.has_key?(:text)
- text = layout ? @template.render(options.merge(:text => options[:text], :layout => layout)) : options[:text]
- render_for_text(text, options[:status])
+ file, template = options.values_at(:file, :template)
+ if file || template
+ file = template.sub(/^\//, '') if template
+ return render_for_file(file, [layout_name, !!template], options)
+ end
+
+ if action_option = options[:action]
+ return render_for_action(action_option, [layout_name, true], options)
+ end
+
+ if inline = options[:inline]
+ render_for_text(@template._render_inline(inline, _pick_layout(layout_name), options))
+
+ elsif xml = options[:xml]
+ response.content_type ||= Mime::XML
+ render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml)
+
+ elsif js = options[:js]
+ response.content_type ||= Mime::JS
+ render_for_text(js)
+
+ elsif json = options[:json]
+ json = json.to_json unless json.is_a?(String)
+ json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
+ response.content_type ||= Mime::JSON
+ render_for_text(json)
+
+ elsif partial = options[:partial]
+ if partial == true
+ parts = [action_name_base, formats, controller_name, true]
+ elsif partial.is_a?(String)
+ parts = partial_parts(partial, options)
+ else
+ return render_for_text(@template._render_partial(options))
+ end
+
+ render_for_parts(parts, layout_name, options)
+
+ elsif options[:nothing]
+ render_for_text(nil)
else
- if file = options[:file]
- render_for_file(file, options[:status], layout, options[:locals] || {})
-
- elsif template = options[:template]
- render_for_file(template, options[:status], layout, options[:locals] || {})
-
- elsif inline = options[:inline]
- render_for_text(@template.render(options.merge(:layout => layout)), options[:status])
-
- elsif action_name = options[:action]
- render_for_file(default_template(action_name.to_s), options[:status], layout)
-
- elsif xml = options[:xml]
- response.content_type ||= Mime::XML
- render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
-
- elsif js = options[:js]
- response.content_type ||= Mime::JS
- render_for_text(js, options[:status])
-
- elsif json = options[:json]
- json = json.to_json unless json.is_a?(String)
- json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
- response.content_type ||= Mime::JSON
- render_for_text(json, options[:status])
-
- elsif options[:partial]
- options[:partial] = default_template_name if options[:partial] == true
- if layout
- render_for_text(@template.render(:text => @template.render(options), :layout => layout), options[:status])
- else
- render_for_text(@template.render(options), options[:status])
- end
-
- elsif options[:update]
- @template.send(:_evaluate_assigns_and_ivars)
-
- generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
- response.content_type = Mime::JS
- render_for_text(generator.to_s, options[:status])
+ render_for_parts([action_name, formats, controller_path], layout_name, options)
+ end
+ end
- elsif options[:nothing]
- render_for_text(nil, options[:status])
+ def formats
+ @_request.formats.map {|f| f.symbol }.compact
+ end
- else
- render_for_file(default_template, options[:status], layout)
- end
- end
+ def action_name_base(name = action_name)
+ (name.is_a?(String) ? name.sub(/^#{controller_path}\//, '') : name).to_s
end
# Renders according to the same rules as render, but returns the result in a string instead
@@ -1174,16 +1166,70 @@ module ActionController #:nodoc:
end
private
- def render_for_file(template_path, status = nil, layout = nil, locals = {}) #:nodoc:
- path = template_path.respond_to?(:path_without_format_and_extension) ? template_path.path_without_format_and_extension : template_path
- logger.info("Rendering #{path}" + (status ? " (#{status})" : '')) if logger
- render_for_text @template.render(:file => template_path, :locals => locals, :layout => layout), status
+ def _process_options(options)
+ if content_type = options[:content_type]
+ response.content_type = content_type.to_s
+ end
+
+ if location = options[:location]
+ response.headers["Location"] = url_for(location)
+ end
+
+ response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE)
end
- def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
- @performed_render = true
+ def render_for_name(name, layout, options)
+ case name.to_s.index('/')
+ when 0
+ render_for_file(name, layout, options)
+ when nil
+ render_for_action(name, layout, options)
+ else
+ render_for_file(name.sub(/^\//, ''), [layout, true], options)
+ end
+ end
+
+ def render_for_parts(parts, layout, options = {})
+ tmp = view_paths.find_by_parts(*parts)
+ layout = _pick_layout(*layout) unless tmp.exempt_from_layout?
+
+ render_for_text(
+ @template._render_template_with_layout(tmp, layout, options, parts[3]))
+ end
+
+ def partial_parts(name, options)
+ segments = name.split("/")
+ parts = segments.pop.split(".")
+
+ case parts.size
+ when 1
+ parts
+ when 2, 3
+ extension = parts.delete_at(1).to_sym
+ if formats.include?(extension)
+ self.formats.replace [extension]
+ end
+ parts.pop if parts.size == 2
+ end
- response.status = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
+ path = parts.join(".")
+ prefix = segments[0..-1].join("/")
+ prefix = prefix.blank? ? controller_path : prefix
+ parts = [path, formats, prefix]
+ parts.push options[:object] || true
+ end
+
+ def render_for_file(file, layout, options)
+ render_for_parts([file, [request.format.to_sym]], layout, options)
+ end
+
+ def render_for_action(name, layout, options)
+ parts = [action_name_base(name), formats, controller_name]
+ render_for_parts(parts, layout, options)
+ end
+
+ def render_for_text(text = nil, append_response = false) #:nodoc:
+ @performed_render = true
if append_response
response.body ||= ''
@@ -1197,18 +1243,8 @@ module ActionController #:nodoc:
end
end
- def validate_render_arguments(options, extra_options, has_block)
- if options && (has_block && options != :update) && !options.is_a?(String) && !options.is_a?(Hash) && !options.is_a?(Symbol)
- raise RenderError, "You called render with invalid options : #{options.inspect}"
- end
-
- if !extra_options.is_a?(Hash)
- raise RenderError, "You called render with invalid options : #{options.inspect}, #{extra_options.inspect}"
- end
- end
-
def initialize_template_class(response)
- response.template = ActionView::Base.new(self.class.view_paths, {}, self)
+ @template = response.template = ActionView::Base.new(self.class.view_paths, {}, self, formats)
response.template.helpers.send :include, self.class.master_helper_module
response.redirected_to = nil
@performed_render = @performed_redirect = false
@@ -1221,7 +1257,6 @@ module ActionController #:nodoc:
@_response.session = request.session
@_session = @_response.session
- @template = @_response.template
@_headers = @_response.headers
end
@@ -1257,23 +1292,21 @@ module ActionController #:nodoc:
end
def perform_action
- if action_methods.include?(action_name)
- send(action_name)
- default_render unless performed?
- elsif respond_to? :method_missing
- method_missing action_name
- default_render unless performed?
- else
- begin
- default_render
- rescue ActionView::MissingTemplate => e
- # Was the implicit template missing, or was it another template?
- if e.path == default_template_name
- raise UnknownAction, "No action responded to #{action_name}. Actions: #{action_methods.sort.to_sentence}", caller
- else
- raise e
- end
- end
+ if called = action_methods.include?(action_name)
+ ret = send(action_name)
+ elsif called = respond_to?(:method_missing)
+ ret = method_missing(action_name)
+ end
+
+ return (performed? ? ret : default_render) if called
+
+ begin
+ default_render
+ rescue ActionView::MissingTemplate => e
+ raise e unless e.path == action_name
+ # If the path is the same as the action_name, the action is completely missing
+ raise UnknownAction, "No action responded to #{action_name}. Actions: " +
+ "#{action_methods.sort.to_sentence}", caller
end
end
@@ -1337,6 +1370,7 @@ module ActionController #:nodoc:
path.split('/', 2).last
end
+
def template_path_includes_controller?(path)
self.controller_path.split('/')[-1] == path.split('/')[0]
end
diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb
index 159c5c7326..926ae26f92 100644
--- a/actionpack/lib/action_controller/layout.rb
+++ b/actionpack/lib/action_controller/layout.rb
@@ -2,11 +2,7 @@ module ActionController #:nodoc:
module Layout #:nodoc:
def self.included(base)
base.extend(ClassMethods)
- base.class_eval do
- class << self
- alias_method_chain :inherited, :layout
- end
- end
+ base.class_inheritable_accessor :layout_name, :layout_conditions
end
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
@@ -159,122 +155,90 @@ module ActionController #:nodoc:
#
# This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
module ClassMethods
+ extend ActiveSupport::Memoizable
+
# If a layout is specified, all rendered actions will have their result rendered
# when the layout yields. This layout can itself depend on instance variables assigned during action
# performance and have access to them as any normal template would.
def layout(template_name, conditions = {}, auto = false)
add_layout_conditions(conditions)
- write_inheritable_attribute(:layout, template_name)
- write_inheritable_attribute(:auto_layout, auto)
+ self.layout_name = template_name
end
- def layout_conditions #:nodoc:
- @layout_conditions ||= read_inheritable_attribute(:layout_conditions)
+ def memoized_default_layout(formats) #:nodoc:
+ self.layout_name || begin
+ layout = default_layout_name
+ layout.is_a?(String) ? find_layout(layout, formats) : layout
+ rescue ActionView::MissingTemplate
+ end
end
- def default_layout(format) #:nodoc:
- layout = read_inheritable_attribute(:layout)
- return layout unless read_inheritable_attribute(:auto_layout)
- find_layout(layout, format)
+ def default_layout(*args)
+ (@_memoized_default_layout ||= ::ActiveSupport::ConcurrentHash.new)[args] ||= memoized_default_layout(*args)
+ end
+
+ def memoized_find_layout(layout, formats) #:nodoc:
+ return layout if layout.nil? || layout.respond_to?(:render)
+ prefix = layout.to_s =~ /layouts\// ? nil : "layouts"
+ view_paths.find_by_parts(layout.to_s, formats, prefix)
+ end
+
+ def find_layout(*args)
+ (@_memoized_find_layout ||= ::ActiveSupport::ConcurrentHash.new)[args] ||= memoized_find_layout(*args)
end
def layout_list #:nodoc:
Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
end
+ memoize :layout_list
- def find_layout(layout, *formats) #:nodoc:
- return layout if layout.respond_to?(:render)
- view_paths.find_template(layout.to_s =~ /layouts\// ? layout : "layouts/#{layout}", *formats)
- rescue ActionView::MissingTemplate
- nil
+ def default_layout_name
+ layout_match = name.underscore.sub(/_controller$/, '')
+ if layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
+ superclass.default_layout_name if superclass.respond_to?(:default_layout_name)
+ else
+ layout_match
+ end
end
+ memoize :default_layout_name
private
- def inherited_with_layout(child)
- inherited_without_layout(child)
- unless child.name.blank?
- layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
- child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
- end
- end
-
def add_layout_conditions(conditions)
- write_inheritable_hash(:layout_conditions, normalize_conditions(conditions))
- end
-
- def normalize_conditions(conditions)
- conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
+ # :except => :foo == :except => [:foo] == :except => "foo" == :except => ["foo"]
+ conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
+ write_inheritable_hash(:layout_conditions, conditions)
end
end
-
- # Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
- # is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
- # object). If the layout was defined without a directory, layouts is assumed. So layout "weblog/standard" will return
- # weblog/standard, but layout "standard" will return layouts/standard.
- def active_layout(passed_layout = nil)
- layout = passed_layout || self.class.default_layout(default_template_format)
-
- active_layout = case layout
- when Symbol then __send__(layout)
- when Proc then layout.call(self)
- else layout
+
+ def active_layout(name)
+ name = self.class.default_layout(formats) if name == true
+
+ layout_name = case name
+ when Symbol then __send__(name)
+ when Proc then name.call(self)
+ else name
end
- if active_layout
- if layout = self.class.find_layout(active_layout, @template.template_format)
- layout
- else
- raise ActionView::MissingTemplate.new(self.class.view_paths, active_layout)
- end
- end
+ self.class.find_layout(layout_name, formats)
end
- private
- def candidate_for_layout?(options)
- template = options[:template] || default_template(options[:action])
- if options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing, :update).compact.empty?
- begin
- !self.view_paths.find_template(template, default_template_format).exempt_from_layout?
- rescue ActionView::MissingTemplate
- true
- end
- end
- rescue ActionView::MissingTemplate
- false
- end
-
- def pick_layout(options)
- if options.has_key?(:layout)
- case layout = options.delete(:layout)
- when FalseClass
- nil
- when NilClass, TrueClass
- active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name)
- else
- active_layout(layout)
- end
- else
- active_layout if action_has_layout? && candidate_for_layout?(options)
- end
- end
+ def _pick_layout(layout_name, implicit = false)
+ return unless layout_name || implicit
+ layout_name = true if layout_name.nil?
+ active_layout(layout_name) if action_has_layout? && layout_name
+ end
+ private
def action_has_layout?
if conditions = self.class.layout_conditions
- case
- when only = conditions[:only]
- only.include?(action_name)
- when except = conditions[:except]
- !except.include?(action_name)
- else
- true
+ if only = conditions[:only]
+ return only.include?(action_name)
+ elsif except = conditions[:except]
+ return !except.include?(action_name)
end
- else
- true
end
+ true
end
- def default_template_format
- response.template.template_format
- end
end
end
diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb
index b755363873..bac225ab2a 100644
--- a/actionpack/lib/action_controller/mime_responds.rb
+++ b/actionpack/lib/action_controller/mime_responds.rb
@@ -109,16 +109,13 @@ module ActionController #:nodoc:
end
class Responder #:nodoc:
+
def initialize(controller)
@controller = controller
@request = controller.request
@response = controller.response
- if ActionController::Base.use_accept_header
- @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
- else
- @mime_type_priority = [@request.format]
- end
+ @mime_type_priority = @request.formats
@order = []
@responses = {}
@@ -130,7 +127,7 @@ module ActionController #:nodoc:
@order << mime_type
@responses[mime_type] ||= Proc.new do
- @response.template.template_format = mime_type.to_sym
+ @response.template.formats = [mime_type.to_sym]
@response.content_type = mime_type.to_s
block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
end
diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb
index 017626ba27..3d26870f4f 100644
--- a/actionpack/lib/action_controller/mime_type.rb
+++ b/actionpack/lib/action_controller/mime_type.rb
@@ -5,6 +5,10 @@ module Mime
EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
+ def self.[](type)
+ Type.lookup_by_extension(type.to_s)
+ end
+
# Encapsulates the notion of a mime type. Can be used at render time, for example, with:
#
# class PostsController < ActionController::Base
@@ -27,7 +31,7 @@ module Mime
# only needs to protect against these types.
@@browser_generated_types = Set.new [:html, :url_encoded_form, :multipart_form, :text]
cattr_reader :browser_generated_types
-
+ attr_reader :symbol
@@unverifiable_types = Set.new [:text, :json, :csv, :xml, :rss, :atom, :yaml]
def self.unverifiable_types
@@ -172,6 +176,8 @@ module Mime
def ==(mime_type)
return false if mime_type.blank?
(@synonyms + [ self ]).any? do |synonym|
+ require "ruby-debug"
+ debugger if mime_type.is_a?(Array)
synonym.to_s == mime_type.to_s || synonym.to_sym == mime_type.to_sym
end
end
@@ -187,17 +193,13 @@ module Mime
# Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See
# ActionController::RequestForgeryProtection.
def verify_request?
- browser_generated?
+ @@browser_generated_types.include?(to_sym)
end
def html?
@@html_types.include?(to_sym) || @string =~ /html/
end
- def browser_generated?
- @@browser_generated_types.include?(to_sym)
- end
-
private
def method_missing(method, *args)
if method.to_s =~ /(\w+)\?$/
diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb
index 09dcd684e8..f8c77241b9 100755
--- a/actionpack/lib/action_controller/request.rb
+++ b/actionpack/lib/action_controller/request.rb
@@ -101,10 +101,16 @@ module ActionController
def accepts
header = @env['HTTP_ACCEPT'].to_s.strip
+ fallback = xhr? ? Mime::JS : Mime::HTML
+
if header.empty?
- [content_type, Mime::ALL].compact
+ [content_type, fallback, Mime::ALL].compact
else
- Mime::Type.parse(header)
+ ret = Mime::Type.parse(header)
+ if ret.last == Mime::ALL
+ ret.insert(-2, fallback)
+ end
+ ret
end
end
memoize :accepts
@@ -144,24 +150,33 @@ module ActionController
end
end
+ ONLY_ALL = [Mime::ALL].freeze
+
# Returns the Mime type for the \format used in the request.
#
# GET /posts/5.xml | request.format => Mime::XML
# GET /posts/5.xhtml | request.format => Mime::HTML
# GET /posts/5 | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of ActionController::Base.use_accept_header
- def format
+
+ def format(view_path = [])
@format ||=
if parameters[:format]
- Mime::Type.lookup_by_extension(parameters[:format])
- elsif ActionController::Base.use_accept_header
- accepts.first
- elsif xhr?
- Mime::Type.lookup_by_extension("js")
- else
- Mime::Type.lookup_by_extension("html")
+ Mime[parameters[:format]]
+ elsif Base.use_accept_header && !(accepts == ONLY_ALL)
+ accepts.first
+ elsif xhr? then Mime::JS
+ else Mime::HTML
end
end
+ def formats
+ @formats =
+ if Base.use_accept_header
+ ret = Array(Mime[parameters[:format]] || accepts)
+ else
+ [format]
+ end
+ end
# Sets the \format by string extension, which can be used to force custom formats
# that are not controlled by the extension.
diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb
index 4b7d1e32fd..3beeb2da83 100644
--- a/actionpack/lib/action_controller/rescue.rb
+++ b/actionpack/lib/action_controller/rescue.rb
@@ -125,11 +125,13 @@ module ActionController #:nodoc:
@template.instance_variable_set("@exception", exception)
@template.instance_variable_set("@rescues_path", RESCUES_TEMPLATE_PATH)
@template.instance_variable_set("@contents",
- @template.render(:file => template_path_for_local_rescue(exception)))
+ @template._render_template(template_path_for_local_rescue(exception)))
response.content_type = Mime::HTML
- render_for_file(rescues_path("layout"),
- response_code_for_rescue(exception))
+ response.status = interpret_status(response_code_for_rescue(exception))
+
+ content = @template._render_template(rescues_path("layout"))
+ render_for_text(content)
end
def rescue_action_without_handler(exception)
@@ -157,7 +159,7 @@ module ActionController #:nodoc:
end
def rescues_path(template_name)
- RESCUES_TEMPLATE_PATH["rescues/#{template_name}.erb"]
+ RESCUES_TEMPLATE_PATH.find_template("rescues/#{template_name}.erb")
end
def template_path_for_local_rescue(exception)
diff --git a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb b/actionpack/lib/action_controller/templates/rescues/diagnostics.erb
index 669da1b26e..95be64511d 100644
--- a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb
+++ b/actionpack/lib/action_controller/templates/rescues/diagnostics.erb
@@ -6,6 +6,5 @@
<%=h @exception.clean_message %>
-<%= render :file => @rescues_path["rescues/_trace.erb"] %>
-
-<%= render :file => @rescues_path["rescues/_request_and_response.erb"] %>
+<%= @template._render_template(@rescues_path.find_template("rescues/_trace.erb")) %>
+<%= @template._render_template(@rescues_path.find_template("rescues/_request_and_response.erb")) %>
\ No newline at end of file
diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb
index 0b710bd8d9..aa06c19a48 100644
--- a/actionpack/lib/action_view.rb
+++ b/actionpack/lib/action_view.rb
@@ -31,6 +31,8 @@ rescue LoadError
end
end
+require File.join(File.dirname(__FILE__), "action_pack")
+
module ActionView
def self.load_all!
[Base, InlineTemplate, TemplateError]
@@ -38,15 +40,16 @@ module ActionView
autoload :Base, 'action_view/base'
autoload :Helpers, 'action_view/helpers'
- autoload :InlineTemplate, 'action_view/inline_template'
- autoload :Partials, 'action_view/partials'
+ autoload :InlineTemplate, 'action_view/template/inline'
+ autoload :Partials, 'action_view/render/partials'
autoload :PathSet, 'action_view/paths'
- autoload :Renderable, 'action_view/renderable'
- autoload :RenderablePartial, 'action_view/renderable_partial'
- autoload :Template, 'action_view/template'
- autoload :TemplateError, 'action_view/template_error'
- autoload :TemplateHandler, 'action_view/template_handler'
- autoload :TemplateHandlers, 'action_view/template_handlers'
+ autoload :Rendering, 'action_view/render/rendering'
+ autoload :Renderable, 'action_view/template/renderable'
+ autoload :RenderablePartial, 'action_view/template/partial'
+ autoload :Template, 'action_view/template/template'
+ autoload :TemplateError, 'action_view/template/error'
+ autoload :TemplateHandler, 'action_view/template/handler'
+ autoload :TemplateHandlers, 'action_view/template/handlers'
autoload :Helpers, 'action_view/helpers'
end
diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb
index 70a0ba91a7..80b8ea6a4c 100644
--- a/actionpack/lib/action_view/base.rb
+++ b/actionpack/lib/action_view/base.rb
@@ -160,14 +160,13 @@ module ActionView #:nodoc:
#
# See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.
class Base
- include Helpers, Partials, ::ERB::Util
+ include Helpers, Rendering, Partials, ::ERB::Util
+
extend ActiveSupport::Memoizable
- attr_accessor :base_path, :assigns, :template_extension
+ attr_accessor :base_path, :assigns, :template_extension, :formats
attr_accessor :controller
- attr_writer :template_format
-
attr_accessor :output_buffer
class << self
@@ -184,9 +183,13 @@ module ActionView #:nodoc:
attr_internal :request
+ delegate :controller_path, :to => :controller, :allow_nil => true
+
delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers,
:flash, :logger, :action_name, :controller_name, :to => :controller
+ delegate :find_by_parts, :to => :view_paths
+
module CompiledTemplates #:nodoc:
# holds compiled template code
end
@@ -209,7 +212,8 @@ module ActionView #:nodoc:
end
end
- def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
+ def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc:
+ @formats = formats || [:html]
@assigns = assigns_for_first_render
@assigns_added = nil
@_render_stack = []
@@ -223,55 +227,7 @@ module ActionView #:nodoc:
def view_paths=(paths)
@view_paths = self.class.process_view_paths(paths)
end
-
- # Returns the result of a render that's dictated by the options hash. The primary options are:
- #
- # * :partial - See ActionView::Partials.
- # * :update - Calls update_page with the block given.
- # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
- # * :inline - Renders an inline template similar to how it's done in the controller.
- # * :text - Renders the text passed in out.
- #
- # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
- # as the locals hash.
- def render(options = {}, local_assigns = {}, &block) #:nodoc:
- local_assigns ||= {}
-
- case options
- when Hash
- options = options.reverse_merge(:locals => {})
- if options[:layout]
- _render_with_layout(options, local_assigns, &block)
- elsif options[:file]
- tempalte = self.view_paths.find_template(options[:file], template_format)
- tempalte.render_template(self, options[:locals])
- elsif options[:partial]
- render_partial(options)
- elsif options[:inline]
- InlineTemplate.new(options[:inline], options[:type]).render(self, options[:locals])
- elsif options[:text]
- options[:text]
- end
- when :update
- update_page(&block)
- else
- render_partial(:partial => options, :locals => local_assigns)
- end
- end
-
- # The format to be used when choosing between multiple templates with
- # the same name but differing formats. See +Request#template_format+
- # for more details.
- def template_format
- if defined? @template_format
- @template_format
- elsif controller && controller.respond_to?(:request)
- @template_format = controller.request.template_format.to_sym
- else
- @template_format = :html
- end
- end
-
+
# Access the current template being rendered.
# Returns a ActionView::Template object.
def template
@@ -301,32 +257,5 @@ module ActionView #:nodoc:
controller.response.content_type ||= content_type
end
end
-
- def _render_with_layout(options, local_assigns, &block) #:nodoc:
- partial_layout = options.delete(:layout)
-
- if block_given?
- begin
- @_proc_for_layout = block
- concat(render(options.merge(:partial => partial_layout)))
- ensure
- @_proc_for_layout = nil
- end
- else
- begin
- original_content_for_layout = @content_for_layout if defined?(@content_for_layout)
- @content_for_layout = render(options)
-
- if (options[:inline] || options[:file] || options[:text])
- @cached_content_for_layout = @content_for_layout
- render(:file => partial_layout, :locals => local_assigns)
- else
- render(options.merge(:partial => partial_layout))
- end
- ensure
- @content_for_layout = original_content_for_layout
- end
- end
- end
end
end
diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb
index 18a209dcea..99676a9c27 100644
--- a/actionpack/lib/action_view/helpers/prototype_helper.rb
+++ b/actionpack/lib/action_view/helpers/prototype_helper.rb
@@ -987,13 +987,13 @@ module ActionView
end
def render(*options_for_render)
- old_format = @context && @context.template_format
- @context.template_format = :html if @context
+ old_formats = @context && @context.formats
+ @context.formats = [:html] if @context
Hash === options_for_render.first ?
@context.render(*options_for_render) :
options_for_render.first.to_s
ensure
- @context.template_format = old_format if @context
+ @context.formats = old_formats if @context
end
def javascript_object_for(object)
diff --git a/actionpack/lib/action_view/inline_template.rb b/actionpack/lib/action_view/inline_template.rb
deleted file mode 100644
index 54efa543c8..0000000000
--- a/actionpack/lib/action_view/inline_template.rb
+++ /dev/null
@@ -1,19 +0,0 @@
-module ActionView #:nodoc:
- class InlineTemplate #:nodoc:
- include Renderable
-
- attr_reader :source, :extension, :method_segment
-
- def initialize(source, type = nil)
- @source = source
- @extension = type
- @method_segment = "inline_#{@source.hash.abs}"
- end
-
- private
- # Always recompile inline templates
- def recompile?
- true
- end
- end
-end
diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb
deleted file mode 100644
index 59e82b98a4..0000000000
--- a/actionpack/lib/action_view/partials.rb
+++ /dev/null
@@ -1,235 +0,0 @@
-module ActionView
- # There's also a convenience method for rendering sub templates within the current controller that depends on a
- # single object (we call this kind of sub templates for partials). It relies on the fact that partials should
- # follow the naming convention of being prefixed with an underscore -- as to separate them from regular
- # templates that could be rendered on their own.
- #
- # In a template for Advertiser#account:
- #
- # <%= render :partial => "account" %>
- #
- # This would render "advertiser/_account.erb" and pass the instance variable @account in as a local variable
- # +account+ to the template for display.
- #
- # In another template for Advertiser#buy, we could have:
- #
- # <%= render :partial => "account", :locals => { :account => @buyer } %>
- #
- # <% for ad in @advertisements %>
- # <%= render :partial => "ad", :locals => { :ad => ad } %>
- # <% end %>
- #
- # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then
- # render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
- #
- # == Rendering a collection of partials
- #
- # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
- # render a sub template for each of the elements. This pattern has been implemented as a single method that
- # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined
- # example in "Using partials" can be rewritten with a single line:
- #
- # <%= render :partial => "ad", :collection => @advertisements %>
- #
- # This will render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display. An
- # iteration counter will automatically be made available to the template with a name of the form
- # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
- #
- # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
- # just keep domain objects, like Active Records, in there.
- #
- # == Rendering shared partials
- #
- # Two controllers can share a set of partials and render them like this:
- #
- # <%= render :partial => "advertisement/ad", :locals => { :ad => @advertisement } %>
- #
- # This will render the partial "advertisement/_ad.erb" regardless of which controller this is being called from.
- #
- # == Rendering objects with the RecordIdentifier
- #
- # Instead of explicitly naming the location of a partial, you can also let the RecordIdentifier do the work if
- # you're following its conventions for RecordIdentifier#partial_path. Examples:
- #
- # # @account is an Account instance, so it uses the RecordIdentifier to replace
- # # <%= render :partial => "accounts/account", :locals => { :account => @buyer } %>
- # <%= render :partial => @account %>
- #
- # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
- # # <%= render :partial => "posts/post", :collection => @posts %>
- # <%= render :partial => @posts %>
- #
- # == Rendering the default case
- #
- # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand
- # defaults of render to render partials. Examples:
- #
- # # Instead of <%= render :partial => "account" %>
- # <%= render "account" %>
- #
- # # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %>
- # <%= render "account", :account => @buyer %>
- #
- # # @account is an Account instance, so it uses the RecordIdentifier to replace
- # # <%= render :partial => "accounts/account", :locals => { :account => @account } %>
- # <%= render(@account) %>
- #
- # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
- # # <%= render :partial => "posts/post", :collection => @posts %>
- # <%= render(@posts) %>
- #
- # == Rendering partials with layouts
- #
- # Partials can have their own layouts applied to them. These layouts are different than the ones that are
- # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
- # of users:
- #
- # <%# app/views/users/index.html.erb &>
- # Here's the administrator:
- # <%= render :partial => "user", :layout => "administrator", :locals => { :user => administrator } %>
- #
- # Here's the editor:
- # <%= render :partial => "user", :layout => "editor", :locals => { :user => editor } %>
- #
- # <%# app/views/users/_user.html.erb &>
- # Name: <%= user.name %>
- #
- # <%# app/views/users/_administrator.html.erb &>
- #
- #
- # As you can see, the :locals hash is shared between both the partial and its layout.
- #
- # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
- # an array to layout and treat it as an enumerable.
- #
- # <%# app/views/users/_user.html.erb &>
- #
- #
- # <%# app/views/users/index.html.erb &>
- # <% render :layout => @users do |user| %>
- # Title: <%= user.title %>
- # <% end %>
- #
- # This will render the layout for each user and yield to the block, passing the user, each time.
- #
- # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
- #
- # <%# app/views/users/_user.html.erb &>
- #
- #
- # <%# app/views/users/index.html.erb &>
- # <% render :layout => @users do |user, section| %>
- # <%- case section when :header -%>
- # Title: <%= user.title %>
- # <%- when :footer -%>
- # Deadline: <%= user.deadline %>
- # <%- end -%>
- # <% end %>
- module Partials
- extend ActiveSupport::Memoizable
-
- private
- def render_partial(options = {}) #:nodoc:
- local_assigns = options[:locals] || {}
-
- case partial_path = options[:partial]
- when String, Symbol, NilClass
- if options.has_key?(:collection)
- render_partial_collection(options)
- else
- _pick_partial_template(partial_path).render_partial(self, options[:object], local_assigns)
- end
- when ActionView::Helpers::FormBuilder
- builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '')
- local_assigns.merge!(builder_partial_path.to_sym => partial_path)
- render_partial(:partial => builder_partial_path, :object => options[:object], :locals => local_assigns)
- when Array, ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope
- render_partial_collection(options.except(:partial).merge(:collection => partial_path))
- else
- object = partial_path
- render_partial(
- :partial => ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path),
- :object => object,
- :locals => local_assigns
- )
- end
- end
-
- def render_partial_collection(options = {}) #:nodoc:
- return nil if options[:collection].blank?
-
- partial = options[:partial]
- spacer = options[:spacer_template] ? render(:partial => options[:spacer_template]) : ''
- local_assigns = options[:locals] ? options[:locals].clone : {}
- as = options[:as]
-
- index = 0
- options[:collection].map do |object|
- _partial_path ||= partial ||
- ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path)
- template = _pick_partial_template(_partial_path)
- local_assigns[template.counter_name] = index
- result = template.render_partial(self, object, local_assigns.dup, as)
- index += 1
- result
- end.join(spacer)
- end
-
- def _pick_partial_template(partial_path) #:nodoc:
- if partial_path.include?('/')
- path = File.join(File.dirname(partial_path), "_#{File.basename(partial_path)}")
- elsif controller
- path = "#{controller.class.controller_path}/_#{partial_path}"
- else
- path = "_#{partial_path}"
- end
-
- self.view_paths.find_template(path, self.template_format)
- end
- memoize :_pick_partial_template
- end
-end
diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb
index 19207e7262..4c3a226ac6 100644
--- a/actionpack/lib/action_view/paths.rb
+++ b/actionpack/lib/action_view/paths.rb
@@ -32,14 +32,24 @@ module ActionView #:nodoc:
super(*objs.map { |obj| self.class.type_cast(obj) })
end
+ def find_by_parts(path, extension = nil, prefix = nil, partial = false)
+ template_path = path.sub(/^\//, '')
+
+ each do |load_path|
+ if template = load_path.find_by_parts(template_path, extension, prefix, partial)
+ return template
+ end
+ end
+
+ Template.new(path, self)
+ end
+
def find_template(original_template_path, format = nil)
return original_template_path if original_template_path.respond_to?(:render)
template_path = original_template_path.sub(/^\//, '')
each do |load_path|
- if format && (template = load_path["#{template_path}.#{format}"])
- return template
- elsif template = load_path[template_path]
+ if template = load_path.find_by_parts(template_path, format)
return template
end
end
diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb
new file mode 100644
index 0000000000..aa4c4af213
--- /dev/null
+++ b/actionpack/lib/action_view/render/partials.rb
@@ -0,0 +1,300 @@
+module ActionView
+ # There's also a convenience method for rendering sub templates within the current controller that depends on a
+ # single object (we call this kind of sub templates for partials). It relies on the fact that partials should
+ # follow the naming convention of being prefixed with an underscore -- as to separate them from regular
+ # templates that could be rendered on their own.
+ #
+ # In a template for Advertiser#account:
+ #
+ # <%= render :partial => "account" %>
+ #
+ # This would render "advertiser/_account.erb" and pass the instance variable @account in as a local variable
+ # +account+ to the template for display.
+ #
+ # In another template for Advertiser#buy, we could have:
+ #
+ # <%= render :partial => "account", :locals => { :account => @buyer } %>
+ #
+ # <% for ad in @advertisements %>
+ # <%= render :partial => "ad", :locals => { :ad => ad } %>
+ # <% end %>
+ #
+ # This would first render "advertiser/_account.erb" with @buyer passed in as the local variable +account+, then
+ # render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display.
+ #
+ # == Rendering a collection of partials
+ #
+ # The example of partial use describes a familiar pattern where a template needs to iterate over an array and
+ # render a sub template for each of the elements. This pattern has been implemented as a single method that
+ # accepts an array and renders a partial by the same name as the elements contained within. So the three-lined
+ # example in "Using partials" can be rewritten with a single line:
+ #
+ # <%= render :partial => "ad", :collection => @advertisements %>
+ #
+ # This will render "advertiser/_ad.erb" and pass the local variable +ad+ to the template for display. An
+ # iteration counter will automatically be made available to the template with a name of the form
+ # +partial_name_counter+. In the case of the example above, the template would be fed +ad_counter+.
+ #
+ # NOTE: Due to backwards compatibility concerns, the collection can't be one of hashes. Normally you'd also
+ # just keep domain objects, like Active Records, in there.
+ #
+ # == Rendering shared partials
+ #
+ # Two controllers can share a set of partials and render them like this:
+ #
+ # <%= render :partial => "advertisement/ad", :locals => { :ad => @advertisement } %>
+ #
+ # This will render the partial "advertisement/_ad.erb" regardless of which controller this is being called from.
+ #
+ # == Rendering objects with the RecordIdentifier
+ #
+ # Instead of explicitly naming the location of a partial, you can also let the RecordIdentifier do the work if
+ # you're following its conventions for RecordIdentifier#partial_path. Examples:
+ #
+ # # @account is an Account instance, so it uses the RecordIdentifier to replace
+ # # <%= render :partial => "accounts/account", :locals => { :account => @buyer } %>
+ # <%= render :partial => @account %>
+ #
+ # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
+ # # <%= render :partial => "posts/post", :collection => @posts %>
+ # <%= render :partial => @posts %>
+ #
+ # == Rendering the default case
+ #
+ # If you're not going to be using any of the options like collections or layouts, you can also use the short-hand
+ # defaults of render to render partials. Examples:
+ #
+ # # Instead of <%= render :partial => "account" %>
+ # <%= render "account" %>
+ #
+ # # Instead of <%= render :partial => "account", :locals => { :account => @buyer } %>
+ # <%= render "account", :account => @buyer %>
+ #
+ # # @account is an Account instance, so it uses the RecordIdentifier to replace
+ # # <%= render :partial => "accounts/account", :locals => { :account => @account } %>
+ # <%= render(@account) %>
+ #
+ # # @posts is an array of Post instances, so it uses the RecordIdentifier to replace
+ # # <%= render :partial => "posts/post", :collection => @posts %>
+ # <%= render(@posts) %>
+ #
+ # == Rendering partials with layouts
+ #
+ # Partials can have their own layouts applied to them. These layouts are different than the ones that are
+ # specified globally for the entire action, but they work in a similar fashion. Imagine a list with two types
+ # of users:
+ #
+ # <%# app/views/users/index.html.erb &>
+ # Here's the administrator:
+ # <%= render :partial => "user", :layout => "administrator", :locals => { :user => administrator } %>
+ #
+ # Here's the editor:
+ # <%= render :partial => "user", :layout => "editor", :locals => { :user => editor } %>
+ #
+ # <%# app/views/users/_user.html.erb &>
+ # Name: <%= user.name %>
+ #
+ # <%# app/views/users/_administrator.html.erb &>
+ #
+ #
+ # As you can see, the :locals hash is shared between both the partial and its layout.
+ #
+ # If you pass arguments to "yield" then this will be passed to the block. One way to use this is to pass
+ # an array to layout and treat it as an enumerable.
+ #
+ # <%# app/views/users/_user.html.erb &>
+ #
+ #
+ # <%# app/views/users/index.html.erb &>
+ # <% render :layout => @users do |user| %>
+ # Title: <%= user.title %>
+ # <% end %>
+ #
+ # This will render the layout for each user and yield to the block, passing the user, each time.
+ #
+ # You can also yield multiple times in one layout and use block arguments to differentiate the sections.
+ #
+ # <%# app/views/users/_user.html.erb &>
+ #
+ #
+ # <%# app/views/users/index.html.erb &>
+ # <% render :layout => @users do |user, section| %>
+ # <%- case section when :header -%>
+ # Title: <%= user.title %>
+ # <%- when :footer -%>
+ # Deadline: <%= user.deadline %>
+ # <%- end -%>
+ # <% end %>
+ module Partials
+ extend ActiveSupport::Memoizable
+
+ def _render_partial(options = {}) #:nodoc:
+ options[:locals] ||= {}
+
+ case path = partial = options[:partial]
+ when *_array_like_objects
+ return _render_partial_collection(partial, options)
+ else
+ if partial.is_a?(ActionView::Helpers::FormBuilder)
+ path = partial.class.to_s.demodulize.underscore.sub(/_builder$/, '')
+ options[:locals].merge!(path.to_sym => partial)
+ elsif !partial.is_a?(String)
+ options[:object] = object = partial
+ path = ActionController::RecordIdentifier.partial_path(object, controller_path)
+ end
+ _, _, prefix, object = parts = partial_parts(path, options)
+ template = find_by_parts(*parts)
+ _render_partial_object(template, options, (object unless object == true))
+ end
+ end
+
+ private
+ def partial_parts(name, options)
+ segments = name.split("/")
+ parts = segments.pop.split(".")
+
+ case parts.size
+ when 1
+ parts
+ when 2, 3
+ extension = parts.delete_at(1).to_sym
+ if formats.include?(extension)
+ self.formats.replace [extension]
+ end
+ parts.pop if parts.size == 2
+ end
+
+ path = parts.join(".")
+ prefix = segments[0..-1].join("/")
+ prefix = prefix.blank? ? controller_path : prefix
+ parts = [path, formats, prefix]
+ parts.push options[:object] || true
+ end
+
+ def _render_partial_with_block(layout, block, options)
+ @_proc_for_layout = block
+ concat(_render_partial(options.merge(:partial => layout)))
+ ensure
+ @_proc_for_layout = nil
+ end
+
+ def _render_partial_with_layout(layout, options)
+ if layout
+ prefix = controller && !layout.include?("/") ? controller.controller_path : nil
+ layout = find_by_parts(layout, formats, prefix, true)
+ end
+ content = _render_partial(options)
+ return _render_content_with_layout(content, layout, options[:locals])
+ end
+
+ def _deprecated_ivar_assign(template)
+ if respond_to?(:controller)
+ ivar = :"@#{template.variable_name}"
+ object =
+ if controller.instance_variable_defined?(ivar)
+ ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
+ controller.instance_variable_get(ivar),
+ "#{ivar} will no longer be implicitly assigned to #{template.variable_name}")
+ end
+ end
+ end
+
+ def _array_like_objects
+ array_like = [Array]
+ if defined?(ActiveRecord)
+ array_like.push(ActiveRecord::Associations::AssociationCollection, ActiveRecord::NamedScope::Scope)
+ end
+ array_like
+ end
+
+ def _render_partial_object(template, options, object = nil)
+ if options.key?(:collection)
+ _render_partial_collection(options.delete(:collection), options, template)
+ else
+ locals = (options[:locals] ||= {})
+ object ||= locals[:object] || locals[template.variable_name]
+
+ _set_locals(object, locals, template, options)
+ _render_template(template, locals)
+ end
+ end
+
+ def _set_locals(object, locals, template, options)
+ object ||= _deprecated_ivar_assign(template)
+ locals[:object] = locals[template.variable_name] = object
+ locals[options[:as]] = object if options[:as]
+ end
+
+ def _render_partial_collection(collection, options = {}, passed_template = nil) #:nodoc:
+ return nil if collection.blank?
+
+ spacer = options[:spacer_template] ? _render_partial(:partial => options[:spacer_template]) : ''
+
+ locals = (options[:locals] ||= {})
+ index, @_partial_path = 0, nil
+ collection.map do |object|
+ template = passed_template || begin
+ _partial_path =
+ ActionController::RecordIdentifier.partial_path(object, controller_path)
+ template = _pick_partial_template(_partial_path)
+ end
+
+ _set_locals(object, locals, template, options)
+ locals[template.counter_name] = index
+
+ index += 1
+ _render_template(template, locals)
+ end.join(spacer)
+ end
+
+ def _pick_partial_template(partial_path) #:nodoc:
+ prefix = controller_path unless partial_path.include?('/')
+ find_by_parts(partial_path, formats, prefix, true)
+ end
+ memoize :_pick_partial_template
+ end
+end
diff --git a/actionpack/lib/action_view/render/rendering.rb b/actionpack/lib/action_view/render/rendering.rb
new file mode 100644
index 0000000000..a02c058725
--- /dev/null
+++ b/actionpack/lib/action_view/render/rendering.rb
@@ -0,0 +1,119 @@
+module ActionView
+ module Rendering
+ # Returns the result of a render that's dictated by the options hash. The primary options are:
+ #
+ # * :partial - See ActionView::Partials.
+ # * :update - Calls update_page with the block given.
+ # * :file - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
+ # * :inline - Renders an inline template similar to how it's done in the controller.
+ # * :text - Renders the text passed in out.
+ #
+ # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
+ # as the locals hash.
+ def render(options = {}, local_assigns = {}, &block) #:nodoc:
+ local_assigns ||= {}
+
+ @exempt_from_layout = true
+
+ case options
+ when Hash
+ options[:locals] ||= {}
+ layout = options[:layout]
+
+ return _render_partial_with_layout(layout, options) if options.key?(:partial)
+ return _render_partial_with_block(layout, block, options) if block_given?
+
+ layout = find_by_parts(layout, formats) if layout
+
+ if file = options[:file]
+ template = find_by_parts(file, formats)
+ _render_template_with_layout(template, layout, :locals => options[:locals])
+ elsif inline = options[:inline]
+ _render_inline(inline, layout, options)
+ elsif text = options[:text]
+ _render_text(text, layout, options)
+ end
+ when :update
+ update_page(&block)
+ when String, NilClass
+ _render_partial(:partial => options, :locals => local_assigns)
+ end
+ end
+
+ def _render_content_with_layout(content, layout, locals)
+ return content unless layout
+
+ locals ||= {}
+
+ if controller && layout
+ response.layout = layout.path_without_format_and_extension if controller.respond_to?(:response)
+ logger.info("Rendering template within #{layout.path_without_format_and_extension}") if logger
+ end
+
+ begin
+ original_content_for_layout = @content_for_layout if defined?(@content_for_layout)
+ @content_for_layout = content
+
+ @cached_content_for_layout = @content_for_layout
+ _render_template(layout, locals)
+ ensure
+ @content_for_layout = original_content_for_layout
+ end
+ end
+
+ def _render_template(template, local_assigns = {})
+ template.compile(local_assigns)
+
+ @_render_stack.push(template)
+
+ _evaluate_assigns_and_ivars
+ _set_controller_content_type(template.mime_type) if template.respond_to?(:mime_type)
+
+ result = send(template.method_name(local_assigns), local_assigns) do |*names|
+ if !instance_variable_defined?(:"@content_for_#{names.first}") &&
+ instance_variable_defined?(:@_proc_for_layout) && (proc = @_proc_for_layout)
+ capture(*names, &proc)
+ elsif instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
+ instance_variable_get(ivar)
+ end
+ end
+
+ @_render_stack.pop
+ result
+ rescue Exception => e
+ raise e if !template.filename || template.is_a?(InlineTemplate)
+ if TemplateError === e
+ e.sub_template_of(template)
+ raise e
+ else
+ raise TemplateError.new(template, assigns, e)
+ end
+ end
+
+ def _render_inline(inline, layout, options)
+ content = _render_template(InlineTemplate.new(options[:inline], options[:type]), options[:locals] || {})
+ layout ? _render_content_with_layout(content, layout, options[:locals]) : content
+ end
+
+ def _render_text(text, layout, options)
+ layout ? _render_content_with_layout(text, layout, options[:locals]) : text
+ end
+
+ def _render_template_with_layout(template, layout = nil, options = {}, partial = false)
+ if controller && logger
+ logger.info("Rendering #{template.path_without_extension}" +
+ (options[:status] ? " (#{options[:status]})" : ''))
+ end
+
+ content = if partial
+ object = partial unless partial == true
+ _render_partial_object(template, options, object)
+ else
+ _render_template(template, options[:locals] || {})
+ end
+
+ return content unless layout && !template.exempt_from_layout?
+ _render_content_with_layout(content, layout, options[:locals] || {})
+ end
+ end
+end
\ No newline at end of file
diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb
deleted file mode 100644
index 153e14f68b..0000000000
--- a/actionpack/lib/action_view/renderable.rb
+++ /dev/null
@@ -1,96 +0,0 @@
-module ActionView
- # NOTE: The template that this mixin is being included into is frozen
- # so you cannot set or modify any instance variables
- module Renderable #:nodoc:
- extend ActiveSupport::Memoizable
-
- def filename
- 'compiled-template'
- end
-
- def handler
- Template.handler_class_for_extension(extension)
- end
- memoize :handler
-
- def compiled_source
- handler.call(self)
- end
- memoize :compiled_source
-
- def method_name_without_locals
- ['_run', extension, method_segment].compact.join('_')
- end
- memoize :method_name_without_locals
-
- def render(view, local_assigns = {})
- compile(local_assigns)
-
- stack = view.instance_variable_get(:@_render_stack)
- stack.push(self)
-
- view.send(:_evaluate_assigns_and_ivars)
- view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
-
- result = view.send(method_name(local_assigns), local_assigns) do |*names|
- ivar = :@_proc_for_layout
- if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
- view.capture(*names, &proc)
- elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
- view.instance_variable_get(ivar)
- end
- end
-
- stack.pop
- result
- end
-
- def method_name(local_assigns)
- if local_assigns && local_assigns.any?
- method_name = method_name_without_locals.dup
- method_name << "_locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
- else
- method_name = method_name_without_locals
- end
- method_name.to_sym
- end
-
- private
- # Compile and evaluate the template's code (if necessary)
- def compile(local_assigns)
- render_symbol = method_name(local_assigns)
-
- if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile?
- compile!(render_symbol, local_assigns)
- end
- end
-
- def compile!(render_symbol, local_assigns)
- locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
-
- source = <<-end_src
- def #{render_symbol}(local_assigns)
- old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
- ensure
- self.output_buffer = old_output_buffer
- end
- end_src
-
- begin
- ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
- rescue Exception => e # errors from template code
- if logger = defined?(ActionController) && Base.logger
- logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
- logger.debug "Function body: #{source}"
- logger.debug "Backtrace: #{e.backtrace.join("\n")}"
- end
-
- raise ActionView::TemplateError.new(self, {}, e)
- end
- end
-
- def recompile?
- false
- end
- end
-end
diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb
deleted file mode 100644
index 3ea836fa25..0000000000
--- a/actionpack/lib/action_view/renderable_partial.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-module ActionView
- # NOTE: The template that this mixin is being included into is frozen
- # so you cannot set or modify any instance variables
- module RenderablePartial #:nodoc:
- extend ActiveSupport::Memoizable
-
- def variable_name
- name.sub(/\A_/, '').to_sym
- end
- memoize :variable_name
-
- def counter_name
- "#{variable_name}_counter".to_sym
- end
- memoize :counter_name
-
- def render(view, local_assigns = {})
- if defined? ActionController
- ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do
- super
- end
- else
- super
- end
- end
-
- def render_partial(view, object = nil, local_assigns = {}, as = nil)
- object ||= local_assigns[:object] || local_assigns[variable_name]
-
- if object.nil? && view.respond_to?(:controller)
- ivar = :"@#{variable_name}"
- object =
- if view.controller.instance_variable_defined?(ivar)
- ActiveSupport::Deprecation::DeprecatedObjectProxy.new(
- view.controller.instance_variable_get(ivar),
- "#{ivar} will no longer be implicitly assigned to #{variable_name}")
- end
- end
-
- # Ensure correct object is reassigned to other accessors
- local_assigns[:object] = local_assigns[variable_name] = object
- local_assigns[as] = object if as
-
- render_template(view, local_assigns)
- end
- end
-end
diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb
deleted file mode 100644
index 9d1e0d3ac5..0000000000
--- a/actionpack/lib/action_view/template.rb
+++ /dev/null
@@ -1,235 +0,0 @@
-module ActionView #:nodoc:
- class Template
- class Path
- attr_reader :path, :paths
- delegate :hash, :inspect, :to => :path
-
- def initialize(path)
- raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
- @path = path.freeze
- end
-
- def to_s
- if defined?(RAILS_ROOT)
- path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '')
- else
- path.to_s
- end
- end
-
- def to_str
- path.to_str
- end
-
- def ==(path)
- to_str == path.to_str
- end
-
- def eql?(path)
- to_str == path.to_str
- end
-
- # Returns a ActionView::Template object for the given path string. The
- # input path should be relative to the view path directory,
- # +hello/index.html.erb+. This method also has a special exception to
- # match partial file names without a handler extension. So
- # +hello/index.html+ will match the first template it finds with a
- # known template extension, +hello/index.html.erb+. Template extensions
- # should not be confused with format extensions +html+, +js+, +xml+,
- # etc. A format must be supplied to match a formated file. +hello/index+
- # will never match +hello/index.html.erb+.
- def [](path)
- templates_in_path do |template|
- if template.accessible_paths.include?(path)
- return template
- end
- end
- nil
- end
-
- private
- def templates_in_path
- (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
- yield create_template(file) unless File.directory?(file)
- end
- end
-
- def create_template(file)
- Template.new(file.split("#{self}/").last, self)
- end
- end
-
- class EagerPath < Path
- def initialize(path)
- super
-
- @paths = {}
- templates_in_path do |template|
- template.load!
- template.accessible_paths.each do |path|
- @paths[path] = template
- end
- end
- @paths.freeze
- end
-
- def [](path)
- @paths[path]
- end
- end
-
- extend TemplateHandlers
- extend ActiveSupport::Memoizable
- include Renderable
-
- # Templates that are exempt from layouts
- @@exempt_from_layout = Set.new([/\.rjs$/])
-
- # Don't render layouts for templates with the given extensions.
- def self.exempt_from_layout(*extensions)
- regexps = extensions.collect do |extension|
- extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
- end
- @@exempt_from_layout.merge(regexps)
- end
-
- attr_accessor :filename, :load_path, :base_path, :name, :format, :extension
- delegate :to_s, :to => :path
-
- def initialize(template_path, load_paths = [])
- template_path = template_path.dup
- @load_path, @filename = find_full_path(template_path, load_paths)
- @base_path, @name, @format, @extension = split(template_path)
- @base_path.to_s.gsub!(/\/$/, '') # Push to split method
-
- # Extend with partial super powers
- extend RenderablePartial if @name =~ /^_/
- end
-
- def accessible_paths
- paths = []
- paths << path
- paths << path_without_extension
- if multipart?
- formats = format.split(".")
- paths << "#{path_without_format_and_extension}.#{formats.first}"
- paths << "#{path_without_format_and_extension}.#{formats.second}"
- end
- paths
- end
-
- def format_and_extension
- (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
- end
- memoize :format_and_extension
-
- def multipart?
- format && format.include?('.')
- end
-
- def content_type
- format.gsub('.', '/')
- end
-
- def mime_type
- Mime::Type.lookup_by_extension(format) if format
- end
- memoize :mime_type
-
- def path
- [base_path, [name, format, extension].compact.join('.')].compact.join('/')
- end
- memoize :path
-
- def path_without_extension
- [base_path, [name, format].compact.join('.')].compact.join('/')
- end
- memoize :path_without_extension
-
- def path_without_format_and_extension
- [base_path, name].compact.join('/')
- end
- memoize :path_without_format_and_extension
-
- def relative_path
- path = File.expand_path(filename)
- path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT)
- path
- end
- memoize :relative_path
-
- def exempt_from_layout?
- @@exempt_from_layout.any? { |exempted| path =~ exempted }
- end
-
- def mtime
- File.mtime(filename)
- end
- memoize :mtime
-
- def source
- File.read(filename)
- end
- memoize :source
-
- def method_segment
- relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
- end
- memoize :method_segment
-
- def render_template(view, local_assigns = {})
- render(view, local_assigns)
- rescue Exception => e
- raise e unless filename
- if TemplateError === e
- e.sub_template_of(self)
- raise e
- else
- raise TemplateError.new(self, view.assigns, e)
- end
- end
-
- def stale?
- File.mtime(filename) > mtime
- end
-
- def recompile?
- !@cached
- end
-
- def load!
- @cached = true
- freeze
- end
-
- private
- def valid_extension?(extension)
- !Template.registered_template_handler(extension).nil?
- end
-
- def find_full_path(path, load_paths)
- load_paths = Array(load_paths) + [nil]
- load_paths.each do |load_path|
- file = load_path ? "#{load_path.to_str}/#{path}" : path
- return load_path, file if File.file?(file)
- end
- raise MissingTemplate.new(load_paths, path)
- end
-
- # Returns file split into an array
- # [base_path, name, format, extension]
- def split(file)
- if m = file.match(/^(.*\/)?([^\.]+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/)
- if valid_extension?(m[5]) # Multipart formats
- [m[1], m[2], "#{m[3]}.#{m[4]}", m[5]]
- elsif valid_extension?(m[4]) # Single format
- [m[1], m[2], m[3], m[4]]
- elsif valid_extension?(m[3]) # No format
- [m[1], m[2], nil, m[3]]
- else # No extension
- [m[1], m[2], m[3], nil]
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/template/error.rb b/actionpack/lib/action_view/template/error.rb
new file mode 100644
index 0000000000..37cb1c7c6c
--- /dev/null
+++ b/actionpack/lib/action_view/template/error.rb
@@ -0,0 +1,99 @@
+module ActionView
+ # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a
+ # bunch of intimate details and uses it to report a very precise exception message.
+ class TemplateError < ActionViewError #:nodoc:
+ SOURCE_CODE_RADIUS = 3
+
+ attr_reader :original_exception
+
+ def initialize(template, assigns, original_exception)
+ @template, @assigns, @original_exception = template, assigns.dup, original_exception
+ @backtrace = compute_backtrace
+ end
+
+ def file_name
+ @template.relative_path
+ end
+
+ def message
+ ActiveSupport::Deprecation.silence { original_exception.message }
+ end
+
+ def clean_backtrace
+ if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner)
+ Rails.backtrace_cleaner.clean(original_exception.backtrace)
+ else
+ original_exception.backtrace
+ end
+ end
+
+ def sub_template_message
+ if @sub_templates
+ "Trace of template inclusion: " +
+ @sub_templates.collect { |template| template.relative_path }.join(", ")
+ else
+ ""
+ end
+ end
+
+ def source_extract(indentation = 0)
+ return unless num = line_number
+ num = num.to_i
+
+ source_code = @template.source.split("\n")
+
+ start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
+ end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
+
+ indent = ' ' * indentation
+ line_counter = start_on_line
+ return unless source_code = source_code[start_on_line..end_on_line]
+
+ source_code.sum do |line|
+ line_counter += 1
+ "#{indent}#{line_counter}: #{line}\n"
+ end
+ end
+
+ def sub_template_of(template_path)
+ @sub_templates ||= []
+ @sub_templates << template_path
+ end
+
+ def line_number
+ @line_number ||=
+ if file_name
+ regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
+
+ $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
+ end
+ end
+
+ def to_s
+ "\n#{self.class} (#{message}) #{source_location}:\n" +
+ "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
+ end
+
+ # don't do anything nontrivial here. Any raised exception from here becomes fatal
+ # (and can't be rescued).
+ def backtrace
+ @backtrace
+ end
+
+ private
+ def compute_backtrace
+ [
+ "#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
+ clean_backtrace.join("\n ")
+ ]
+ end
+
+ def source_location
+ if line_number
+ "on line ##{line_number} of "
+ else
+ 'in '
+ end + file_name
+ end
+ end
+end
\ No newline at end of file
diff --git a/actionpack/lib/action_view/template/handler.rb b/actionpack/lib/action_view/template/handler.rb
new file mode 100644
index 0000000000..672da0ed2b
--- /dev/null
+++ b/actionpack/lib/action_view/template/handler.rb
@@ -0,0 +1,34 @@
+# Legacy TemplateHandler stub
+module ActionView
+ module TemplateHandlers #:nodoc:
+ module Compilable
+ def self.included(base)
+ base.extend(ClassMethods)
+ end
+
+ module ClassMethods
+ def call(template)
+ new.compile(template)
+ end
+ end
+
+ def compile(template)
+ raise "Need to implement #{self.class.name}#compile(template)"
+ end
+ end
+ end
+
+ class TemplateHandler
+ def self.call(template)
+ "#{name}.new(self).render(template, local_assigns)"
+ end
+
+ def initialize(view = nil)
+ @view = view
+ end
+
+ def render(template, local_assigns)
+ raise "Need to implement #{self.class.name}#render(template, local_assigns)"
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template/handlers.rb b/actionpack/lib/action_view/template/handlers.rb
new file mode 100644
index 0000000000..fb85f28851
--- /dev/null
+++ b/actionpack/lib/action_view/template/handlers.rb
@@ -0,0 +1,48 @@
+module ActionView #:nodoc:
+ module TemplateHandlers #:nodoc:
+ autoload :ERB, 'action_view/template/handlers/erb'
+ autoload :RJS, 'action_view/template/handlers/rjs'
+ autoload :Builder, 'action_view/template/handlers/builder'
+
+ def self.extended(base)
+ base.register_default_template_handler :erb, TemplateHandlers::ERB
+ base.register_template_handler :rjs, TemplateHandlers::RJS
+ base.register_template_handler :builder, TemplateHandlers::Builder
+
+ # TODO: Depreciate old template extensions
+ base.register_template_handler :rhtml, TemplateHandlers::ERB
+ base.register_template_handler :rxml, TemplateHandlers::Builder
+ end
+
+ @@template_handlers = {}
+ @@default_template_handlers = nil
+
+ # Register a class that knows how to handle template files with the given
+ # extension. This can be used to implement new template types.
+ # The constructor for the class must take the ActiveView::Base instance
+ # as a parameter, and the class must implement a +render+ method that
+ # takes the contents of the template to render as well as the Hash of
+ # local assigns available to the template. The +render+ method ought to
+ # return the rendered template as a string.
+ def register_template_handler(extension, klass)
+ @@template_handlers[extension.to_sym] = klass
+ end
+
+ def template_handler_extensions
+ @@template_handlers.keys.map(&:to_s).sort
+ end
+
+ def registered_template_handler(extension)
+ extension && @@template_handlers[extension.to_sym]
+ end
+
+ def register_default_template_handler(extension, klass)
+ register_template_handler(extension, klass)
+ @@default_template_handlers = klass
+ end
+
+ def handler_class_for_extension(extension)
+ registered_template_handler(extension) || @@default_template_handlers
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template/handlers/builder.rb b/actionpack/lib/action_view/template/handlers/builder.rb
new file mode 100644
index 0000000000..788dc93326
--- /dev/null
+++ b/actionpack/lib/action_view/template/handlers/builder.rb
@@ -0,0 +1,17 @@
+require 'builder'
+
+module ActionView
+ module TemplateHandlers
+ class Builder < TemplateHandler
+ include Compilable
+
+ def compile(template)
+ "_set_controller_content_type(Mime::XML);" +
+ "xml = ::Builder::XmlMarkup.new(:indent => 2);" +
+ "self.output_buffer = xml.target!;" +
+ template.source +
+ ";xml.target!;"
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template/handlers/erb.rb b/actionpack/lib/action_view/template/handlers/erb.rb
new file mode 100644
index 0000000000..e3120ba267
--- /dev/null
+++ b/actionpack/lib/action_view/template/handlers/erb.rb
@@ -0,0 +1,22 @@
+module ActionView
+ module TemplateHandlers
+ class ERB < TemplateHandler
+ include Compilable
+
+ ##
+ # :singleton-method:
+ # Specify trim mode for the ERB compiler. Defaults to '-'.
+ # See ERb documentation for suitable values.
+ cattr_accessor :erb_trim_mode
+ self.erb_trim_mode = '-'
+
+ def compile(template)
+ src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src
+
+ # Ruby 1.9 prepends an encoding to the source. However this is
+ # useless because you can only set an encoding on the first line
+ RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template/handlers/rjs.rb b/actionpack/lib/action_view/template/handlers/rjs.rb
new file mode 100644
index 0000000000..802a79b3fc
--- /dev/null
+++ b/actionpack/lib/action_view/template/handlers/rjs.rb
@@ -0,0 +1,13 @@
+module ActionView
+ module TemplateHandlers
+ class RJS < TemplateHandler
+ include Compilable
+
+ def compile(template)
+ "@formats = [:html];" +
+ "controller.response.content_type ||= Mime::JS;" +
+ "update_page do |page|;#{template.source}\nend"
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template/inline.rb b/actionpack/lib/action_view/template/inline.rb
new file mode 100644
index 0000000000..54efa543c8
--- /dev/null
+++ b/actionpack/lib/action_view/template/inline.rb
@@ -0,0 +1,19 @@
+module ActionView #:nodoc:
+ class InlineTemplate #:nodoc:
+ include Renderable
+
+ attr_reader :source, :extension, :method_segment
+
+ def initialize(source, type = nil)
+ @source = source
+ @extension = type
+ @method_segment = "inline_#{@source.hash.abs}"
+ end
+
+ private
+ # Always recompile inline templates
+ def recompile?
+ true
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template/partial.rb b/actionpack/lib/action_view/template/partial.rb
new file mode 100644
index 0000000000..30dec1dc5b
--- /dev/null
+++ b/actionpack/lib/action_view/template/partial.rb
@@ -0,0 +1,18 @@
+module ActionView
+ # NOTE: The template that this mixin is being included into is frozen
+ # so you cannot set or modify any instance variables
+ module RenderablePartial #:nodoc:
+ extend ActiveSupport::Memoizable
+
+ def variable_name
+ name.sub(/\A_/, '').to_sym
+ end
+ memoize :variable_name
+
+ def counter_name
+ "#{variable_name}_counter".to_sym
+ end
+ memoize :counter_name
+
+ end
+end
diff --git a/actionpack/lib/action_view/template/renderable.rb b/actionpack/lib/action_view/template/renderable.rb
new file mode 100644
index 0000000000..35c832aaba
--- /dev/null
+++ b/actionpack/lib/action_view/template/renderable.rb
@@ -0,0 +1,74 @@
+module ActionView
+ # NOTE: The template that this mixin is being included into is frozen
+ # so you cannot set or modify any instance variables
+ module Renderable #:nodoc:
+ extend ActiveSupport::Memoizable
+
+ def filename
+ 'compiled-template'
+ end
+
+ def handler
+ Template.handler_class_for_extension(extension)
+ end
+ memoize :handler
+
+ def compiled_source
+ handler.call(self)
+ end
+ memoize :compiled_source
+
+ def method_name_without_locals
+ ['_run', extension, method_segment].compact.join('_')
+ end
+ memoize :method_name_without_locals
+
+ def method_name(local_assigns)
+ if local_assigns && local_assigns.any?
+ method_name = method_name_without_locals.dup
+ method_name << "_locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
+ else
+ method_name = method_name_without_locals
+ end
+ method_name.to_sym
+ end
+
+ # Compile and evaluate the template's code (if necessary)
+ def compile(local_assigns)
+ render_symbol = method_name(local_assigns)
+
+ if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile?
+ compile!(render_symbol, local_assigns)
+ end
+ end
+
+ private
+ def compile!(render_symbol, local_assigns)
+ locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join
+
+ source = <<-end_src
+ def #{render_symbol}(local_assigns)
+ old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
+ ensure
+ self.output_buffer = old_output_buffer
+ end
+ end_src
+
+ begin
+ ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
+ rescue Exception => e # errors from template code
+ if logger = defined?(ActionController) && Base.logger
+ logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
+ logger.debug "Function body: #{source}"
+ logger.debug "Backtrace: #{e.backtrace.join("\n")}"
+ end
+
+ raise ActionView::TemplateError.new(self, {}, e)
+ end
+ end
+
+ def recompile?
+ false
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb
new file mode 100644
index 0000000000..235a95a0f3
--- /dev/null
+++ b/actionpack/lib/action_view/template/template.rb
@@ -0,0 +1,241 @@
+module ActionView #:nodoc:
+ class Template
+ class Path
+ attr_reader :path, :paths
+ delegate :hash, :inspect, :to => :path
+
+ def initialize(path)
+ raise ArgumentError, "path already is a Path class" if path.is_a?(Path)
+ @path = path.freeze
+ end
+
+ def to_s
+ if defined?(RAILS_ROOT)
+ path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '')
+ else
+ path.to_s
+ end
+ end
+
+ def to_str
+ path.to_str
+ end
+
+ def ==(path)
+ to_str == path.to_str
+ end
+
+ def eql?(path)
+ to_str == path.to_str
+ end
+
+ # Returns a ActionView::Template object for the given path string. The
+ # input path should be relative to the view path directory,
+ # +hello/index.html.erb+. This method also has a special exception to
+ # match partial file names without a handler extension. So
+ # +hello/index.html+ will match the first template it finds with a
+ # known template extension, +hello/index.html.erb+. Template extensions
+ # should not be confused with format extensions +html+, +js+, +xml+,
+ # etc. A format must be supplied to match a formated file. +hello/index+
+ # will never match +hello/index.html.erb+.
+ def find_template(path)
+ templates_in_path do |template|
+ if template.accessible_paths.include?(path)
+ return template
+ end
+ end
+ nil
+ end
+
+ def find_by_parts(name, extensions = nil, prefix = nil, partial = nil)
+ path = prefix ? "#{prefix}/" : ""
+
+ name = name.split("/")
+ name[-1] = "_#{name[-1]}" if partial
+
+ path << name.join("/")
+
+ template = nil
+
+ Array(extensions).each do |extension|
+ extensioned_path = extension ? "#{path}.#{extension}" : path
+ template = find_template(extensioned_path) || find_template(path)
+ break if template
+ end
+ template
+ end
+
+ private
+ def templates_in_path
+ (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file|
+ yield create_template(file) unless File.directory?(file)
+ end
+ end
+
+ def create_template(file)
+ Template.new(file.split("#{self}/").last, self)
+ end
+ end
+
+ class EagerPath < Path
+ def initialize(path)
+ super
+
+ @paths = {}
+ templates_in_path do |template|
+ template.load!
+ template.accessible_paths.each do |path|
+ @paths[path] = template
+ end
+ end
+ @paths.freeze
+ end
+
+ def find_template(path)
+ @paths[path]
+ end
+ end
+
+ extend TemplateHandlers
+ extend ActiveSupport::Memoizable
+ include Renderable
+
+ # Templates that are exempt from layouts
+ @@exempt_from_layout = Set.new([/\.rjs$/])
+
+ # Don't render layouts for templates with the given extensions.
+ def self.exempt_from_layout(*extensions)
+ regexps = extensions.collect do |extension|
+ extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
+ end
+ @@exempt_from_layout.merge(regexps)
+ end
+
+ attr_accessor :filename, :load_path, :base_path, :name, :format, :extension
+ delegate :to_s, :to => :path
+
+ def initialize(template_path, load_paths = [])
+ template_path = template_path.dup
+ @load_path, @filename = find_full_path(template_path, load_paths)
+ @base_path, @name, @format, @extension = split(template_path)
+ @base_path.to_s.gsub!(/\/$/, '') # Push to split method
+
+ # Extend with partial super powers
+ extend RenderablePartial if @name =~ /^_/
+ end
+
+ def accessible_paths
+ paths = []
+ paths << path
+ paths << path_without_extension
+ if multipart?
+ formats = format.split(".")
+ paths << "#{path_without_format_and_extension}.#{formats.first}"
+ paths << "#{path_without_format_and_extension}.#{formats.second}"
+ end
+ paths
+ end
+
+ def format_and_extension
+ (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions
+ end
+ memoize :format_and_extension
+
+ def multipart?
+ format && format.include?('.')
+ end
+
+ def content_type
+ format.gsub('.', '/')
+ end
+
+ def mime_type
+ Mime::Type.lookup_by_extension(format) if format
+ end
+ memoize :mime_type
+
+ def path
+ [base_path, [name, format, extension].compact.join('.')].compact.join('/')
+ end
+ memoize :path
+
+ def path_without_extension
+ [base_path, [name, format].compact.join('.')].compact.join('/')
+ end
+ memoize :path_without_extension
+
+ def path_without_format_and_extension
+ [base_path, name].compact.join('/')
+ end
+ memoize :path_without_format_and_extension
+
+ def relative_path
+ path = File.expand_path(filename)
+ path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT)
+ path
+ end
+ memoize :relative_path
+
+ def exempt_from_layout?
+ @@exempt_from_layout.any? { |exempted| path =~ exempted }
+ end
+
+ def mtime
+ File.mtime(filename)
+ end
+ memoize :mtime
+
+ def source
+ File.read(filename)
+ end
+ memoize :source
+
+ def method_segment
+ relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
+ end
+ memoize :method_segment
+
+ def stale?
+ File.mtime(filename) > mtime
+ end
+
+ def recompile?
+ !@cached
+ end
+
+ def load!
+ @cached = true
+ freeze
+ end
+
+ private
+ def valid_extension?(extension)
+ !Template.registered_template_handler(extension).nil?
+ end
+
+ def find_full_path(path, load_paths)
+ load_paths = Array(load_paths) + [nil]
+ load_paths.each do |load_path|
+ file = load_path ? "#{load_path.to_str}/#{path}" : path
+ return load_path, file if File.file?(file)
+ end
+ raise MissingTemplate.new(load_paths, path)
+ end
+
+ # Returns file split into an array
+ # [base_path, name, format, extension]
+ def split(file)
+ if m = file.match(/^(.*\/)?([^\.]+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/)
+ if valid_extension?(m[5]) # Multipart formats
+ [m[1], m[2], "#{m[3]}.#{m[4]}", m[5]]
+ elsif valid_extension?(m[4]) # Single format
+ [m[1], m[2], m[3], m[4]]
+ elsif valid_extension?(m[3]) # No format
+ [m[1], m[2], nil, m[3]]
+ else # No extension
+ [m[1], m[2], m[3], nil]
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_view/template_error.rb b/actionpack/lib/action_view/template_error.rb
deleted file mode 100644
index 37cb1c7c6c..0000000000
--- a/actionpack/lib/action_view/template_error.rb
+++ /dev/null
@@ -1,99 +0,0 @@
-module ActionView
- # The TemplateError exception is raised when the compilation of the template fails. This exception then gathers a
- # bunch of intimate details and uses it to report a very precise exception message.
- class TemplateError < ActionViewError #:nodoc:
- SOURCE_CODE_RADIUS = 3
-
- attr_reader :original_exception
-
- def initialize(template, assigns, original_exception)
- @template, @assigns, @original_exception = template, assigns.dup, original_exception
- @backtrace = compute_backtrace
- end
-
- def file_name
- @template.relative_path
- end
-
- def message
- ActiveSupport::Deprecation.silence { original_exception.message }
- end
-
- def clean_backtrace
- if defined?(Rails) && Rails.respond_to?(:backtrace_cleaner)
- Rails.backtrace_cleaner.clean(original_exception.backtrace)
- else
- original_exception.backtrace
- end
- end
-
- def sub_template_message
- if @sub_templates
- "Trace of template inclusion: " +
- @sub_templates.collect { |template| template.relative_path }.join(", ")
- else
- ""
- end
- end
-
- def source_extract(indentation = 0)
- return unless num = line_number
- num = num.to_i
-
- source_code = @template.source.split("\n")
-
- start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
- end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
-
- indent = ' ' * indentation
- line_counter = start_on_line
- return unless source_code = source_code[start_on_line..end_on_line]
-
- source_code.sum do |line|
- line_counter += 1
- "#{indent}#{line_counter}: #{line}\n"
- end
- end
-
- def sub_template_of(template_path)
- @sub_templates ||= []
- @sub_templates << template_path
- end
-
- def line_number
- @line_number ||=
- if file_name
- regexp = /#{Regexp.escape File.basename(file_name)}:(\d+)/
-
- $1 if message =~ regexp or clean_backtrace.find { |line| line =~ regexp }
- end
- end
-
- def to_s
- "\n#{self.class} (#{message}) #{source_location}:\n" +
- "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
- end
-
- # don't do anything nontrivial here. Any raised exception from here becomes fatal
- # (and can't be rescued).
- def backtrace
- @backtrace
- end
-
- private
- def compute_backtrace
- [
- "#{source_location.capitalize}\n\n#{source_extract(4)}\n " +
- clean_backtrace.join("\n ")
- ]
- end
-
- def source_location
- if line_number
- "on line ##{line_number} of "
- else
- 'in '
- end + file_name
- end
- end
-end
\ No newline at end of file
diff --git a/actionpack/lib/action_view/template_handler.rb b/actionpack/lib/action_view/template_handler.rb
deleted file mode 100644
index 672da0ed2b..0000000000
--- a/actionpack/lib/action_view/template_handler.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-# Legacy TemplateHandler stub
-module ActionView
- module TemplateHandlers #:nodoc:
- module Compilable
- def self.included(base)
- base.extend(ClassMethods)
- end
-
- module ClassMethods
- def call(template)
- new.compile(template)
- end
- end
-
- def compile(template)
- raise "Need to implement #{self.class.name}#compile(template)"
- end
- end
- end
-
- class TemplateHandler
- def self.call(template)
- "#{name}.new(self).render(template, local_assigns)"
- end
-
- def initialize(view = nil)
- @view = view
- end
-
- def render(template, local_assigns)
- raise "Need to implement #{self.class.name}#render(template, local_assigns)"
- end
- end
-end
diff --git a/actionpack/lib/action_view/template_handlers.rb b/actionpack/lib/action_view/template_handlers.rb
deleted file mode 100644
index 205f8628f0..0000000000
--- a/actionpack/lib/action_view/template_handlers.rb
+++ /dev/null
@@ -1,48 +0,0 @@
-module ActionView #:nodoc:
- module TemplateHandlers #:nodoc:
- autoload :ERB, 'action_view/template_handlers/erb'
- autoload :RJS, 'action_view/template_handlers/rjs'
- autoload :Builder, 'action_view/template_handlers/builder'
-
- def self.extended(base)
- base.register_default_template_handler :erb, TemplateHandlers::ERB
- base.register_template_handler :rjs, TemplateHandlers::RJS
- base.register_template_handler :builder, TemplateHandlers::Builder
-
- # TODO: Depreciate old template extensions
- base.register_template_handler :rhtml, TemplateHandlers::ERB
- base.register_template_handler :rxml, TemplateHandlers::Builder
- end
-
- @@template_handlers = {}
- @@default_template_handlers = nil
-
- # Register a class that knows how to handle template files with the given
- # extension. This can be used to implement new template types.
- # The constructor for the class must take the ActiveView::Base instance
- # as a parameter, and the class must implement a +render+ method that
- # takes the contents of the template to render as well as the Hash of
- # local assigns available to the template. The +render+ method ought to
- # return the rendered template as a string.
- def register_template_handler(extension, klass)
- @@template_handlers[extension.to_sym] = klass
- end
-
- def template_handler_extensions
- @@template_handlers.keys.map(&:to_s).sort
- end
-
- def registered_template_handler(extension)
- extension && @@template_handlers[extension.to_sym]
- end
-
- def register_default_template_handler(extension, klass)
- register_template_handler(extension, klass)
- @@default_template_handlers = klass
- end
-
- def handler_class_for_extension(extension)
- registered_template_handler(extension) || @@default_template_handlers
- end
- end
-end
diff --git a/actionpack/lib/action_view/template_handlers/builder.rb b/actionpack/lib/action_view/template_handlers/builder.rb
deleted file mode 100644
index 788dc93326..0000000000
--- a/actionpack/lib/action_view/template_handlers/builder.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'builder'
-
-module ActionView
- module TemplateHandlers
- class Builder < TemplateHandler
- include Compilable
-
- def compile(template)
- "_set_controller_content_type(Mime::XML);" +
- "xml = ::Builder::XmlMarkup.new(:indent => 2);" +
- "self.output_buffer = xml.target!;" +
- template.source +
- ";xml.target!;"
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb
deleted file mode 100644
index e3120ba267..0000000000
--- a/actionpack/lib/action_view/template_handlers/erb.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-module ActionView
- module TemplateHandlers
- class ERB < TemplateHandler
- include Compilable
-
- ##
- # :singleton-method:
- # Specify trim mode for the ERB compiler. Defaults to '-'.
- # See ERb documentation for suitable values.
- cattr_accessor :erb_trim_mode
- self.erb_trim_mode = '-'
-
- def compile(template)
- src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src
-
- # Ruby 1.9 prepends an encoding to the source. However this is
- # useless because you can only set an encoding on the first line
- RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/template_handlers/rjs.rb b/actionpack/lib/action_view/template_handlers/rjs.rb
deleted file mode 100644
index 41a1fddb47..0000000000
--- a/actionpack/lib/action_view/template_handlers/rjs.rb
+++ /dev/null
@@ -1,13 +0,0 @@
-module ActionView
- module TemplateHandlers
- class RJS < TemplateHandler
- include Compilable
-
- def compile(template)
- "@template_format = :html;" +
- "controller.response.content_type ||= Mime::JS;" +
- "update_page do |page|;#{template.source}\nend"
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb
index ec337bb05b..c8f204046b 100644
--- a/actionpack/lib/action_view/test_case.rb
+++ b/actionpack/lib/action_view/test_case.rb
@@ -7,18 +7,15 @@ module ActionView
@_rendered = { :template => nil, :partials => Hash.new(0) }
initialize_without_template_tracking(*args)
end
- end
-
- module Renderable
- alias_method :render_without_template_tracking, :render
- def render(view, local_assigns = {})
- if respond_to?(:path) && !is_a?(InlineTemplate)
- rendered = view.instance_variable_get(:@_rendered)
- rendered[:partials][self] += 1 if is_a?(RenderablePartial)
- rendered[:template] ||= self
+
+ alias_method :_render_template_without_template_tracking, :_render_template
+ def _render_template(template, local_assigns = {})
+ if template.respond_to?(:path) && !template.is_a?(InlineTemplate)
+ @_rendered[:partials][template] += 1 if template.is_a?(RenderablePartial)
+ @_rendered[:template] ||= template
end
- render_without_template_tracking(view, local_assigns)
- end
+ _render_template_without_template_tracking(template, local_assigns)
+ end
end
class TestCase < ActiveSupport::TestCase
--
cgit v1.2.3
From a0f2b1d95d3785de92ae271fd7ea23e91c0cadc6 Mon Sep 17 00:00:00 2001
From: Joshua Peek
Date: Tue, 27 Jan 2009 18:17:39 -0600
Subject: Reorganize ActionController folder structure
---
actionpack/lib/action_controller.rb | 94 +-
.../action_controller/assertions/dom_assertions.rb | 39 -
.../assertions/model_assertions.rb | 21 -
.../assertions/response_assertions.rb | 150 ---
.../assertions/routing_assertions.rb | 146 --
.../assertions/selector_assertions.rb | 632 ---------
.../action_controller/assertions/tag_assertions.rb | 127 --
actionpack/lib/action_controller/base.rb | 1392 --------------------
actionpack/lib/action_controller/base/base.rb | 904 +++++++++++++
.../action_controller/base/chained/benchmarking.rb | 107 ++
.../lib/action_controller/base/chained/filters.rb | 680 ++++++++++
.../lib/action_controller/base/chained/flash.rb | 163 +++
actionpack/lib/action_controller/base/cookies.rb | 94 ++
actionpack/lib/action_controller/base/headers.rb | 33 +
actionpack/lib/action_controller/base/helpers.rb | 225 ++++
.../action_controller/base/http_authentication.rb | 124 ++
actionpack/lib/action_controller/base/layout.rb | 244 ++++
actionpack/lib/action_controller/base/redirect.rb | 91 ++
actionpack/lib/action_controller/base/render.rb | 378 ++++++
.../base/request_forgery_protection.rb | 108 ++
actionpack/lib/action_controller/base/responder.rb | 41 +
actionpack/lib/action_controller/base/streaming.rb | 171 +++
.../lib/action_controller/base/verification.rb | 130 ++
actionpack/lib/action_controller/benchmarking.rb | 107 --
actionpack/lib/action_controller/cgi/ext.rb | 15 +
actionpack/lib/action_controller/cgi/ext/cookie.rb | 112 ++
.../action_controller/cgi/ext/query_extension.rb | 22 +
.../lib/action_controller/cgi/ext/stdinput.rb | 24 +
actionpack/lib/action_controller/cgi/process.rb | 70 +
actionpack/lib/action_controller/cgi_ext.rb | 15 -
actionpack/lib/action_controller/cgi_ext/cookie.rb | 112 --
.../action_controller/cgi_ext/query_extension.rb | 22 -
.../lib/action_controller/cgi_ext/stdinput.rb | 24 -
actionpack/lib/action_controller/cgi_process.rb | 72 -
actionpack/lib/action_controller/cookies.rb | 94 --
.../lib/action_controller/dispatch/dispatcher.rb | 116 ++
.../action_controller/dispatch/params_parser.rb | 71 +
.../action_controller/dispatch/rack/failsafe.rb | 52 +
.../lib/action_controller/dispatch/rack/lock.rb | 16 +
.../dispatch/rack/middleware_stack.rb | 109 ++
.../action_controller/dispatch/rack/middlewares.rb | 21 +
.../lib/action_controller/dispatch/request.rb | 492 +++++++
.../action_controller/dispatch/request_parser.rb | 315 +++++
.../lib/action_controller/dispatch/rescue.rb | 179 +++
.../lib/action_controller/dispatch/response.rb | 255 ++++
.../action_controller/dispatch/rewindable_input.rb | 28 +
.../lib/action_controller/dispatch/status_codes.rb | 88 ++
.../templates/rescues/_request_and_response.erb | 24 +
.../dispatch/templates/rescues/_trace.erb | 26 +
.../dispatch/templates/rescues/diagnostics.erb | 10 +
.../dispatch/templates/rescues/layout.erb | 29 +
.../templates/rescues/missing_template.erb | 2 +
.../dispatch/templates/rescues/routing_error.erb | 10 +
.../dispatch/templates/rescues/template_error.erb | 21 +
.../dispatch/templates/rescues/unknown_action.erb | 2 +
.../action_controller/dispatch/uploaded_file.rb | 44 +
.../dispatch/url_encoded_pair_parser.rb | 155 +++
actionpack/lib/action_controller/dispatcher.rb | 116 --
actionpack/lib/action_controller/failsafe.rb | 52 -
actionpack/lib/action_controller/filters.rb | 680 ----------
actionpack/lib/action_controller/flash.rb | 163 ---
actionpack/lib/action_controller/headers.rb | 33 -
actionpack/lib/action_controller/helpers.rb | 225 ----
.../lib/action_controller/http_authentication.rb | 124 --
actionpack/lib/action_controller/integration.rb | 676 ----------
actionpack/lib/action_controller/layout.rb | 244 ----
.../lib/action_controller/middleware_stack.rb | 109 --
actionpack/lib/action_controller/middlewares.rb | 21 -
.../lib/action_controller/mime/default_types.rb | 21 +
actionpack/lib/action_controller/mime/responds.rb | 190 +++
actionpack/lib/action_controller/mime/type.rb | 214 +++
actionpack/lib/action_controller/mime_responds.rb | 190 ---
actionpack/lib/action_controller/mime_type.rb | 214 ---
actionpack/lib/action_controller/mime_types.rb | 21 -
actionpack/lib/action_controller/params_parser.rb | 71 -
.../lib/action_controller/performance_test.rb | 15 -
.../lib/action_controller/polymorphic_routes.rb | 201 ---
.../lib/action_controller/record_identifier.rb | 2 +-
actionpack/lib/action_controller/request.rb | 492 -------
.../request_forgery_protection.rb | 108 --
actionpack/lib/action_controller/rescue.rb | 179 ---
actionpack/lib/action_controller/resources.rb | 674 ----------
actionpack/lib/action_controller/response.rb | 255 ----
.../lib/action_controller/rewindable_input.rb | 28 -
.../routing/generation/polymorphic_routes.rb | 201 +++
.../routing/generation/url_rewriter.rb | 219 +++
.../lib/action_controller/routing/resources.rb | 674 ++++++++++
.../lib/action_controller/session/management.rb | 54 +
.../lib/action_controller/session_management.rb | 54 -
actionpack/lib/action_controller/status_codes.rb | 88 --
actionpack/lib/action_controller/streaming.rb | 171 ---
.../templates/rescues/_request_and_response.erb | 24 -
.../action_controller/templates/rescues/_trace.erb | 26 -
.../templates/rescues/diagnostics.erb | 10 -
.../action_controller/templates/rescues/layout.erb | 29 -
.../templates/rescues/missing_template.erb | 2 -
.../templates/rescues/routing_error.erb | 10 -
.../templates/rescues/template_error.erb | 21 -
.../templates/rescues/unknown_action.erb | 2 -
actionpack/lib/action_controller/test_case.rb | 199 ---
actionpack/lib/action_controller/test_process.rb | 543 --------
.../action_controller/testing/assertions/dom.rb | 39 +
.../action_controller/testing/assertions/model.rb | 21 +
.../testing/assertions/response.rb | 150 +++
.../testing/assertions/routing.rb | 146 ++
.../testing/assertions/selector.rb | 632 +++++++++
.../action_controller/testing/assertions/tag.rb | 127 ++
.../lib/action_controller/testing/integration.rb | 676 ++++++++++
.../lib/action_controller/testing/performance.rb | 15 +
.../lib/action_controller/testing/process.rb | 543 ++++++++
.../lib/action_controller/testing/test_case.rb | 199 +++
actionpack/lib/action_controller/uploaded_file.rb | 44 -
.../action_controller/url_encoded_pair_parser.rb | 155 ---
actionpack/lib/action_controller/url_rewriter.rb | 219 ---
actionpack/lib/action_controller/verification.rb | 130 --
actionpack/lib/action_view/render/partials.rb | 28 +
116 files changed, 9998 insertions(+), 9619 deletions(-)
delete mode 100644 actionpack/lib/action_controller/assertions/dom_assertions.rb
delete mode 100644 actionpack/lib/action_controller/assertions/model_assertions.rb
delete mode 100644 actionpack/lib/action_controller/assertions/response_assertions.rb
delete mode 100644 actionpack/lib/action_controller/assertions/routing_assertions.rb
delete mode 100644 actionpack/lib/action_controller/assertions/selector_assertions.rb
delete mode 100644 actionpack/lib/action_controller/assertions/tag_assertions.rb
delete mode 100644 actionpack/lib/action_controller/base.rb
create mode 100644 actionpack/lib/action_controller/base/base.rb
create mode 100644 actionpack/lib/action_controller/base/chained/benchmarking.rb
create mode 100644 actionpack/lib/action_controller/base/chained/filters.rb
create mode 100644 actionpack/lib/action_controller/base/chained/flash.rb
create mode 100644 actionpack/lib/action_controller/base/cookies.rb
create mode 100644 actionpack/lib/action_controller/base/headers.rb
create mode 100644 actionpack/lib/action_controller/base/helpers.rb
create mode 100644 actionpack/lib/action_controller/base/http_authentication.rb
create mode 100644 actionpack/lib/action_controller/base/layout.rb
create mode 100644 actionpack/lib/action_controller/base/redirect.rb
create mode 100644 actionpack/lib/action_controller/base/render.rb
create mode 100644 actionpack/lib/action_controller/base/request_forgery_protection.rb
create mode 100644 actionpack/lib/action_controller/base/responder.rb
create mode 100644 actionpack/lib/action_controller/base/streaming.rb
create mode 100644 actionpack/lib/action_controller/base/verification.rb
delete mode 100644 actionpack/lib/action_controller/benchmarking.rb
create mode 100644 actionpack/lib/action_controller/cgi/ext.rb
create mode 100644 actionpack/lib/action_controller/cgi/ext/cookie.rb
create mode 100644 actionpack/lib/action_controller/cgi/ext/query_extension.rb
create mode 100644 actionpack/lib/action_controller/cgi/ext/stdinput.rb
create mode 100644 actionpack/lib/action_controller/cgi/process.rb
delete mode 100644 actionpack/lib/action_controller/cgi_ext.rb
delete mode 100644 actionpack/lib/action_controller/cgi_ext/cookie.rb
delete mode 100644 actionpack/lib/action_controller/cgi_ext/query_extension.rb
delete mode 100644 actionpack/lib/action_controller/cgi_ext/stdinput.rb
delete mode 100644 actionpack/lib/action_controller/cgi_process.rb
delete mode 100644 actionpack/lib/action_controller/cookies.rb
create mode 100644 actionpack/lib/action_controller/dispatch/dispatcher.rb
create mode 100644 actionpack/lib/action_controller/dispatch/params_parser.rb
create mode 100644 actionpack/lib/action_controller/dispatch/rack/failsafe.rb
create mode 100644 actionpack/lib/action_controller/dispatch/rack/lock.rb
create mode 100644 actionpack/lib/action_controller/dispatch/rack/middleware_stack.rb
create mode 100644 actionpack/lib/action_controller/dispatch/rack/middlewares.rb
create mode 100755 actionpack/lib/action_controller/dispatch/request.rb
create mode 100644 actionpack/lib/action_controller/dispatch/request_parser.rb
create mode 100644 actionpack/lib/action_controller/dispatch/rescue.rb
create mode 100644 actionpack/lib/action_controller/dispatch/response.rb
create mode 100644 actionpack/lib/action_controller/dispatch/rewindable_input.rb
create mode 100644 actionpack/lib/action_controller/dispatch/status_codes.rb
create mode 100644 actionpack/lib/action_controller/dispatch/templates/rescues/_request_and_response.erb
create mode 100644 actionpack/lib/action_controller/dispatch/templates/rescues/_trace.erb
create mode 100644 actionpack/lib/action_controller/dispatch/templates/rescues/diagnostics.erb
create mode 100644 actionpack/lib/action_controller/dispatch/templates/rescues/layout.erb
create mode 100644 actionpack/lib/action_controller/dispatch/templates/rescues/missing_template.erb
create mode 100644 actionpack/lib/action_controller/dispatch/templates/rescues/routing_error.erb
create mode 100644 actionpack/lib/action_controller/dispatch/templates/rescues/template_error.erb
create mode 100644 actionpack/lib/action_controller/dispatch/templates/rescues/unknown_action.erb
create mode 100644 actionpack/lib/action_controller/dispatch/uploaded_file.rb
create mode 100644 actionpack/lib/action_controller/dispatch/url_encoded_pair_parser.rb
delete mode 100644 actionpack/lib/action_controller/dispatcher.rb
delete mode 100644 actionpack/lib/action_controller/failsafe.rb
delete mode 100644 actionpack/lib/action_controller/filters.rb
delete mode 100644 actionpack/lib/action_controller/flash.rb
delete mode 100644 actionpack/lib/action_controller/headers.rb
delete mode 100644 actionpack/lib/action_controller/helpers.rb
delete mode 100644 actionpack/lib/action_controller/http_authentication.rb
delete mode 100644 actionpack/lib/action_controller/integration.rb
delete mode 100644 actionpack/lib/action_controller/layout.rb
delete mode 100644 actionpack/lib/action_controller/middleware_stack.rb
delete mode 100644 actionpack/lib/action_controller/middlewares.rb
create mode 100644 actionpack/lib/action_controller/mime/default_types.rb
create mode 100644 actionpack/lib/action_controller/mime/responds.rb
create mode 100644 actionpack/lib/action_controller/mime/type.rb
delete mode 100644 actionpack/lib/action_controller/mime_responds.rb
delete mode 100644 actionpack/lib/action_controller/mime_type.rb
delete mode 100644 actionpack/lib/action_controller/mime_types.rb
delete mode 100644 actionpack/lib/action_controller/params_parser.rb
delete mode 100644 actionpack/lib/action_controller/performance_test.rb
delete mode 100644 actionpack/lib/action_controller/polymorphic_routes.rb
delete mode 100755 actionpack/lib/action_controller/request.rb
delete mode 100644 actionpack/lib/action_controller/request_forgery_protection.rb
delete mode 100644 actionpack/lib/action_controller/rescue.rb
delete mode 100644 actionpack/lib/action_controller/resources.rb
delete mode 100644 actionpack/lib/action_controller/response.rb
delete mode 100644 actionpack/lib/action_controller/rewindable_input.rb
create mode 100644 actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb
create mode 100644 actionpack/lib/action_controller/routing/generation/url_rewriter.rb
create mode 100644 actionpack/lib/action_controller/routing/resources.rb
create mode 100644 actionpack/lib/action_controller/session/management.rb
delete mode 100644 actionpack/lib/action_controller/session_management.rb
delete mode 100644 actionpack/lib/action_controller/status_codes.rb
delete mode 100644 actionpack/lib/action_controller/streaming.rb
delete mode 100644 actionpack/lib/action_controller/templates/rescues/_request_and_response.erb
delete mode 100644 actionpack/lib/action_controller/templates/rescues/_trace.erb
delete mode 100644 actionpack/lib/action_controller/templates/rescues/diagnostics.erb
delete mode 100644 actionpack/lib/action_controller/templates/rescues/layout.erb
delete mode 100644 actionpack/lib/action_controller/templates/rescues/missing_template.erb
delete mode 100644 actionpack/lib/action_controller/templates/rescues/routing_error.erb
delete mode 100644 actionpack/lib/action_controller/templates/rescues/template_error.erb
delete mode 100644 actionpack/lib/action_controller/templates/rescues/unknown_action.erb
delete mode 100644 actionpack/lib/action_controller/test_case.rb
delete mode 100644 actionpack/lib/action_controller/test_process.rb
create mode 100644 actionpack/lib/action_controller/testing/assertions/dom.rb
create mode 100644 actionpack/lib/action_controller/testing/assertions/model.rb
create mode 100644 actionpack/lib/action_controller/testing/assertions/response.rb
create mode 100644 actionpack/lib/action_controller/testing/assertions/routing.rb
create mode 100644 actionpack/lib/action_controller/testing/assertions/selector.rb
create mode 100644 actionpack/lib/action_controller/testing/assertions/tag.rb
create mode 100644 actionpack/lib/action_controller/testing/integration.rb
create mode 100644 actionpack/lib/action_controller/testing/performance.rb
create mode 100644 actionpack/lib/action_controller/testing/process.rb
create mode 100644 actionpack/lib/action_controller/testing/test_case.rb
delete mode 100644 actionpack/lib/action_controller/uploaded_file.rb
delete mode 100644 actionpack/lib/action_controller/url_encoded_pair_parser.rb
delete mode 100644 actionpack/lib/action_controller/url_rewriter.rb
delete mode 100644 actionpack/lib/action_controller/verification.rb
(limited to 'actionpack/lib')
diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb
index 6000ee15c6..d973cbb382 100644
--- a/actionpack/lib/action_controller.rb
+++ b/actionpack/lib/action_controller.rb
@@ -44,57 +44,61 @@ module ActionController
[Base, CGIHandler, CgiRequest, Request, Response, Http::Headers, UrlRewriter, UrlWriter]
end
- autoload :AbstractRequest, 'action_controller/request'
- autoload :Base, 'action_controller/base'
- autoload :Benchmarking, 'action_controller/benchmarking'
+ autoload :Base, 'action_controller/base/base'
+ autoload :Benchmarking, 'action_controller/base/chained/benchmarking'
autoload :Caching, 'action_controller/caching'
- autoload :Cookies, 'action_controller/cookies'
- autoload :Dispatcher, 'action_controller/dispatcher'
- autoload :Failsafe, 'action_controller/failsafe'
- autoload :Filters, 'action_controller/filters'
- autoload :Flash, 'action_controller/flash'
- autoload :Helpers, 'action_controller/helpers'
- autoload :HttpAuthentication, 'action_controller/http_authentication'
- autoload :Integration, 'action_controller/integration'
- autoload :IntegrationTest, 'action_controller/integration'
- autoload :Layout, 'action_controller/layout'
- autoload :MiddlewareStack, 'action_controller/middleware_stack'
- autoload :MimeResponds, 'action_controller/mime_responds'
- autoload :ParamsParser, 'action_controller/params_parser'
- autoload :PolymorphicRoutes, 'action_controller/polymorphic_routes'
+ autoload :Cookies, 'action_controller/base/cookies'
+ autoload :Dispatcher, 'action_controller/dispatch/dispatcher'
+ autoload :Failsafe, 'action_controller/dispatch/rack/failsafe'
+ autoload :Filters, 'action_controller/base/chained/filters'
+ autoload :Flash, 'action_controller/base/chained/flash'
+ autoload :Helpers, 'action_controller/base/helpers'
+ autoload :HttpAuthentication, 'action_controller/base/http_authentication'
+ autoload :Integration, 'action_controller/testing/integration'
+ autoload :IntegrationTest, 'action_controller/testing/integration'
+ autoload :Layout, 'action_controller/base/layout'
+ autoload :Lock, 'action_controller/dispatch/rack/lock'
+ autoload :MiddlewareStack, 'action_controller/dispatch/rack/middleware_stack'
+ autoload :MimeResponds, 'action_controller/mime/responds'
+ autoload :ParamsParser, 'action_controller/dispatch/params_parser'
+ autoload :PolymorphicRoutes, 'action_controller/routing/generation/polymorphic_routes'
autoload :RecordIdentifier, 'action_controller/record_identifier'
- autoload :Request, 'action_controller/request'
- autoload :RequestForgeryProtection, 'action_controller/request_forgery_protection'
- autoload :Rescue, 'action_controller/rescue'
- autoload :Resources, 'action_controller/resources'
- autoload :Response, 'action_controller/response'
- autoload :RewindableInput, 'action_controller/rewindable_input'
+ autoload :Redirector, 'action_controller/base/redirect'
+ autoload :Renderer, 'action_controller/base/render'
+ autoload :Request, 'action_controller/dispatch/request'
+ autoload :RequestForgeryProtection, 'action_controller/base/request_forgery_protection'
+ autoload :RequestParser, 'action_controller/dispatch/request_parser'
+ autoload :Rescue, 'action_controller/dispatch/rescue'
+ autoload :Resources, 'action_controller/routing/resources'
+ autoload :Responder, 'action_controller/base/responder'
+ autoload :Response, 'action_controller/dispatch/response'
+ autoload :RewindableInput, 'action_controller/dispatch/rewindable_input'
autoload :Routing, 'action_controller/routing'
- autoload :SessionManagement, 'action_controller/session_management'
- autoload :StatusCodes, 'action_controller/status_codes'
- autoload :Streaming, 'action_controller/streaming'
- autoload :TestCase, 'action_controller/test_case'
- autoload :TestProcess, 'action_controller/test_process'
+ autoload :SessionManagement, 'action_controller/session/management'
+ autoload :StatusCodes, 'action_controller/dispatch/status_codes'
+ autoload :Streaming, 'action_controller/base/streaming'
+ autoload :TestCase, 'action_controller/testing/test_case'
+ autoload :TestProcess, 'action_controller/testing/process'
autoload :Translation, 'action_controller/translation'
- autoload :UploadedFile, 'action_controller/uploaded_file'
- autoload :UploadedStringIO, 'action_controller/uploaded_file'
- autoload :UploadedTempfile, 'action_controller/uploaded_file'
- autoload :UrlEncodedPairParser, 'action_controller/url_encoded_pair_parser'
- autoload :UrlRewriter, 'action_controller/url_rewriter'
- autoload :UrlWriter, 'action_controller/url_rewriter'
- autoload :Verification, 'action_controller/verification'
+ autoload :UploadedFile, 'action_controller/dispatch/uploaded_file'
+ autoload :UploadedStringIO, 'action_controller/dispatch/uploaded_file'
+ autoload :UploadedTempfile, 'action_controller/dispatch/uploaded_file'
+ autoload :UrlEncodedPairParser, 'action_controller/dispatch/url_encoded_pair_parser'
+ autoload :UrlRewriter, 'action_controller/routing/generation/url_rewriter'
+ autoload :UrlWriter, 'action_controller/routing/generation/url_rewriter'
+ autoload :Verification, 'action_controller/base/verification'
module Assertions
- autoload :DomAssertions, 'action_controller/assertions/dom_assertions'
- autoload :ModelAssertions, 'action_controller/assertions/model_assertions'
- autoload :ResponseAssertions, 'action_controller/assertions/response_assertions'
- autoload :RoutingAssertions, 'action_controller/assertions/routing_assertions'
- autoload :SelectorAssertions, 'action_controller/assertions/selector_assertions'
- autoload :TagAssertions, 'action_controller/assertions/tag_assertions'
+ autoload :DomAssertions, 'action_controller/testing/assertions/dom'
+ autoload :ModelAssertions, 'action_controller/testing/assertions/model'
+ autoload :ResponseAssertions, 'action_controller/testing/assertions/response'
+ autoload :RoutingAssertions, 'action_controller/testing/assertions/routing'
+ autoload :SelectorAssertions, 'action_controller/testing/assertions/selector'
+ autoload :TagAssertions, 'action_controller/testing/assertions/tag'
end
module Http
- autoload :Headers, 'action_controller/headers'
+ autoload :Headers, 'action_controller/base/headers'
end
module Session
@@ -102,13 +106,9 @@ module ActionController
autoload :CookieStore, 'action_controller/session/cookie_store'
autoload :MemCacheStore, 'action_controller/session/mem_cache_store'
end
-
- # DEPRECATE: Remove CGI support
- autoload :CgiRequest, 'action_controller/cgi_process'
- autoload :CGIHandler, 'action_controller/cgi_process'
end
-autoload :Mime, 'action_controller/mime_type'
+autoload :Mime, 'action_controller/mime/type'
autoload :HTML, 'action_controller/vendor/html-scanner'
diff --git a/actionpack/lib/action_controller/assertions/dom_assertions.rb b/actionpack/lib/action_controller/assertions/dom_assertions.rb
deleted file mode 100644
index 5ffe5f1883..0000000000
--- a/actionpack/lib/action_controller/assertions/dom_assertions.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-module ActionController
- module Assertions
- module DomAssertions
- # Test two HTML strings for equivalency (e.g., identical up to reordering of attributes)
- #
- # ==== Examples
- #
- # # assert that the referenced method generates the appropriate HTML string
- # assert_dom_equal 'Apples', link_to("Apples", "http://www.example.com")
- #
- def assert_dom_equal(expected, actual, message = "")
- clean_backtrace do
- expected_dom = HTML::Document.new(expected).root
- actual_dom = HTML::Document.new(actual).root
- full_message = build_message(message, "> expected to be == to\n>.", expected_dom.to_s, actual_dom.to_s)
-
- assert_block(full_message) { expected_dom == actual_dom }
- end
- end
-
- # The negated form of +assert_dom_equivalent+.
- #
- # ==== Examples
- #
- # # assert that the referenced method does not generate the specified HTML string
- # assert_dom_not_equal 'Apples', link_to("Oranges", "http://www.example.com")
- #
- def assert_dom_not_equal(expected, actual, message = "")
- clean_backtrace do
- expected_dom = HTML::Document.new(expected).root
- actual_dom = HTML::Document.new(actual).root
- full_message = build_message(message, "> expected to be != to\n>.", expected_dom.to_s, actual_dom.to_s)
-
- assert_block(full_message) { expected_dom != actual_dom }
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/assertions/model_assertions.rb b/actionpack/lib/action_controller/assertions/model_assertions.rb
deleted file mode 100644
index 3a7b39b106..0000000000
--- a/actionpack/lib/action_controller/assertions/model_assertions.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-module ActionController
- module Assertions
- module ModelAssertions
- # Ensures that the passed record is valid by Active Record standards and
- # returns any error messages if it is not.
- #
- # ==== Examples
- #
- # # assert that a newly created record is valid
- # model = Model.new
- # assert_valid(model)
- #
- def assert_valid(record)
- ::ActiveSupport::Deprecation.warn("assert_valid is deprecated. Use assert record.valid? instead", caller)
- clean_backtrace do
- assert record.valid?, record.errors.full_messages.join("\n")
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb
deleted file mode 100644
index 5976090273..0000000000
--- a/actionpack/lib/action_controller/assertions/response_assertions.rb
+++ /dev/null
@@ -1,150 +0,0 @@
-module ActionController
- module Assertions
- # A small suite of assertions that test responses from Rails applications.
- module ResponseAssertions
- # Asserts that the response is one of the following types:
- #
- # * :success - Status code was 200
- # * :redirect - Status code was in the 300-399 range
- # * :missing - Status code was 404
- # * :error - Status code was in the 500-599 range
- #
- # You can also pass an explicit status number like assert_response(501)
- # or its symbolic equivalent assert_response(:not_implemented).
- # See ActionController::StatusCodes for a full list.
- #
- # ==== Examples
- #
- # # assert that the response was a redirection
- # assert_response :redirect
- #
- # # assert that the response code was status code 401 (unauthorized)
- # assert_response 401
- #
- def assert_response(type, message = nil)
- clean_backtrace do
- if [ :success, :missing, :redirect, :error ].include?(type) && @response.send("#{type}?")
- assert_block("") { true } # to count the assertion
- elsif type.is_a?(Fixnum) && @response.response_code == type
- assert_block("") { true } # to count the assertion
- elsif type.is_a?(Symbol) && @response.response_code == ActionController::StatusCodes::SYMBOL_TO_STATUS_CODE[type]
- assert_block("") { true } # to count the assertion
- else
- if @response.error?
- exception = @response.template.instance_variable_get(:@exception)
- exception_message = exception && exception.message
- assert_block(build_message(message, "Expected response to be a >, but was >\n>", type, @response.response_code, exception_message.to_s)) { false }
- else
- assert_block(build_message(message, "Expected response to be a >, but was >", type, @response.response_code)) { false }
- end
- end
- end
- end
-
- # Assert that the redirection options passed in match those of the redirect called in the latest action.
- # This match can be partial, such that assert_redirected_to(:controller => "weblog") will also
- # match the redirection of redirect_to(:controller => "weblog", :action => "show") and so on.
- #
- # ==== Examples
- #
- # # assert that the redirection was to the "index" action on the WeblogController
- # assert_redirected_to :controller => "weblog", :action => "index"
- #
- # # assert that the redirection was to the named route login_url
- # assert_redirected_to login_url
- #
- # # assert that the redirection was to the url for @customer
- # assert_redirected_to @customer
- #
- def assert_redirected_to(options = {}, message=nil)
- clean_backtrace do
- assert_response(:redirect, message)
- return true if options == @response.redirected_to
-
- # Support partial arguments for hash redirections
- if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash)
- return true if options.all? {|(key, value)| @response.redirected_to[key] == value}
- end
-
- redirected_to_after_normalisation = normalize_argument_to_redirection(@response.redirected_to)
- options_after_normalisation = normalize_argument_to_redirection(options)
-
- if redirected_to_after_normalisation != options_after_normalisation
- flunk "Expected response to be a redirect to <#{options_after_normalisation}> but was a redirect to <#{redirected_to_after_normalisation}>"
- end
- end
- end
-
- # Asserts that the request was rendered with the appropriate template file or partials
- #
- # ==== Examples
- #
- # # assert that the "new" view template was rendered
- # assert_template "new"
- #
- # # assert that the "_customer" partial was rendered twice
- # assert_template :partial => '_customer', :count => 2
- #
- # # assert that no partials were rendered
- # assert_template :partial => false
- #
- def assert_template(options = {}, message = nil)
- clean_backtrace do
- case options
- when NilClass, String
- rendered = @response.rendered[:template].to_s
- msg = build_message(message,
- "expecting > but rendering with >",
- options, rendered)
- assert_block(msg) do
- if options.nil?
- @response.rendered[:template].blank?
- else
- rendered.to_s.match(options)
- end
- end
- when Hash
- if expected_partial = options[:partial]
- partials = @response.rendered[:partials]
- if expected_count = options[:count]
- found = partials.detect { |p, _| p.to_s.match(expected_partial) }
- actual_count = found.nil? ? 0 : found.second
- msg = build_message(message,
- "expecting ? to be rendered ? time(s) but rendered ? time(s)",
- expected_partial, expected_count, actual_count)
- assert(actual_count == expected_count.to_i, msg)
- else
- msg = build_message(message,
- "expecting partial > but action rendered >",
- options[:partial], partials.keys)
- assert(partials.keys.any? { |p| p.to_s.match(expected_partial) }, msg)
- end
- else
- assert @response.rendered[:partials].empty?,
- "Expected no partials to be rendered"
- end
- end
- end
- end
-
- private
- # Proxy to to_param if the object will respond to it.
- def parameterize(value)
- value.respond_to?(:to_param) ? value.to_param : value
- 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
- end
- @request.protocol + @request.host_with_port + after_routing
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/assertions/routing_assertions.rb b/actionpack/lib/action_controller/assertions/routing_assertions.rb
deleted file mode 100644
index 5101751cea..0000000000
--- a/actionpack/lib/action_controller/assertions/routing_assertions.rb
+++ /dev/null
@@ -1,146 +0,0 @@
-module ActionController
- module Assertions
- # Suite of assertions to test routes generated by Rails and the handling of requests made to them.
- module RoutingAssertions
- # Asserts that the routing of the given +path+ was handled correctly and that the parsed options (given in the +expected_options+ hash)
- # match +path+. Basically, it asserts that Rails recognizes the route given by +expected_options+.
- #
- # Pass a hash in the second argument (+path+) to specify the request method. This is useful for routes
- # requiring a specific HTTP method. The hash should contain a :path with the incoming request path
- # and a :method containing the required HTTP verb.
- #
- # # assert that POSTing to /items will call the create action on ItemsController
- # assert_recognizes {:controller => 'items', :action => 'create'}, {:path => 'items', :method => :post}
- #
- # You can also pass in +extras+ with a hash containing URL parameters that would normally be in the query string. This can be used
- # to assert that values in the query string string will end up in the params hash correctly. To test query strings you must use the
- # extras argument, appending the query string on the path directly will not work. For example:
- #
- # # assert that a path of '/items/list/1?view=print' returns the correct options
- # assert_recognizes {:controller => 'items', :action => 'list', :id => '1', :view => 'print'}, 'items/list/1', { :view => "print" }
- #
- # The +message+ parameter allows you to pass in an error message that is displayed upon failure.
- #
- # ==== Examples
- # # Check the default route (i.e., the index action)
- # assert_recognizes {:controller => 'items', :action => 'index'}, 'items'
- #
- # # Test a specific action
- # assert_recognizes {:controller => 'items', :action => 'list'}, 'items/list'
- #
- # # Test an action with a parameter
- # assert_recognizes {:controller => 'items', :action => 'destroy', :id => '1'}, 'items/destroy/1'
- #
- # # Test a custom route
- # assert_recognizes {:controller => 'items', :action => 'show', :id => '1'}, 'view/item1'
- #
- # # Check a Simply RESTful generated route
- # assert_recognizes list_items_url, 'items/list'
- def assert_recognizes(expected_options, path, extras={}, message=nil)
- if path.is_a? Hash
- request_method = path[:method]
- path = path[:path]
- else
- request_method = nil
- end
-
- clean_backtrace do
- ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
- request = recognized_request_for(path, request_method)
-
- expected_options = expected_options.clone
- extras.each_key { |key| expected_options.delete key } unless extras.nil?
-
- expected_options.stringify_keys!
- routing_diff = expected_options.diff(request.path_parameters)
- msg = build_message(message, "The recognized options > did not match >, difference: >",
- request.path_parameters, expected_options, expected_options.diff(request.path_parameters))
- assert_block(msg) { request.path_parameters == expected_options }
- end
- end
-
- # Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+.
- # The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in
- # a query string. The +message+ parameter allows you to specify a custom error message for assertion failures.
- #
- # The +defaults+ parameter is unused.
- #
- # ==== Examples
- # # Asserts that the default action is generated for a route with no action
- # assert_generates "/items", :controller => "items", :action => "index"
- #
- # # Tests that the list action is properly routed
- # assert_generates "/items/list", :controller => "items", :action => "list"
- #
- # # Tests the generation of a route with a parameter
- # assert_generates "/items/list/1", { :controller => "items", :action => "list", :id => "1" }
- #
- # # Asserts that the generated route gives us our custom route
- # assert_generates "changesets/12", { :controller => 'scm', :action => 'show_diff', :revision => "12" }
- def assert_generates(expected_path, options, defaults={}, extras = {}, message=nil)
- clean_backtrace do
- expected_path = "/#{expected_path}" unless expected_path[0] == ?/
- # Load routes.rb if it hasn't been loaded.
- ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty?
-
- generated_path, extra_keys = ActionController::Routing::Routes.generate_extras(options, defaults)
- found_extras = options.reject {|k, v| ! extra_keys.include? k}
-
- msg = build_message(message, "found extras >, not >", found_extras, extras)
- assert_block(msg) { found_extras == extras }
-
- msg = build_message(message, "The generated path > did not match >", generated_path,
- expected_path)
- assert_block(msg) { expected_path == generated_path }
- end
- end
-
- # Asserts that path and options match both ways; in other words, it verifies that path generates
- # options and then that options generates path. This essentially combines +assert_recognizes+
- # and +assert_generates+ into one step.
- #
- # The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The
- # +message+ parameter allows you to specify a custom error message to display upon failure.
- #
- # ==== Examples
- # # Assert a basic route: a controller with the default action (index)
- # assert_routing '/home', :controller => 'home', :action => 'index'
- #
- # # Test a route generated with a specific controller, action, and parameter (id)
- # assert_routing '/entries/show/23', :controller => 'entries', :action => 'show', id => 23
- #
- # # Assert a basic route (controller + default action), with an error message if it fails
- # assert_routing '/store', { :controller => 'store', :action => 'index' }, {}, {}, 'Route for store index not generated properly'
- #
- # # Tests a route, providing a defaults hash
- # assert_routing 'controller/action/9', {:id => "9", :item => "square"}, {:controller => "controller", :action => "action"}, {}, {:item => "square"}
- #
- # # Tests a route with a HTTP method
- # assert_routing { :method => 'put', :path => '/product/321' }, { :controller => "product", :action => "update", :id => "321" }
- def assert_routing(path, options, defaults={}, extras={}, message=nil)
- assert_recognizes(options, path, extras, message)
-
- controller, default_controller = options[:controller], defaults[:controller]
- if controller && controller.include?(?/) && default_controller && default_controller.include?(?/)
- options[:controller] = "/#{controller}"
- end
-
- assert_generates(path.is_a?(Hash) ? path[:path] : path, options, defaults, extras, message)
- end
-
- private
- # Recognizes the route for a given path.
- def recognized_request_for(path, request_method = nil)
- path = "/#{path}" unless path.first == '/'
-
- # Assume given controller
- request = ActionController::TestRequest.new
- request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method
- request.path = path
-
- ActionController::Routing::Routes.recognize(request)
- request
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb
deleted file mode 100644
index 0d56ea5ef7..0000000000
--- a/actionpack/lib/action_controller/assertions/selector_assertions.rb
+++ /dev/null
@@ -1,632 +0,0 @@
-#--
-# Copyright (c) 2006 Assaf Arkin (http://labnotes.org)
-# Under MIT and/or CC By license.
-#++
-
-module ActionController
- module Assertions
- unless const_defined?(:NO_STRIP)
- NO_STRIP = %w{pre script style textarea}
- end
-
- # Adds the +assert_select+ method for use in Rails functional
- # test cases, which can be used to make assertions on the response HTML of a controller
- # action. You can also call +assert_select+ within another +assert_select+ to
- # make assertions on elements selected by the enclosing assertion.
- #
- # Use +css_select+ to select elements without making an assertions, either
- # from the response HTML or elements selected by the enclosing assertion.
- #
- # In addition to HTML responses, you can make the following assertions:
- # * +assert_select_rjs+ - Assertions on HTML content of RJS update and insertion operations.
- # * +assert_select_encoded+ - Assertions on HTML encoded inside XML, for example for dealing with feed item descriptions.
- # * +assert_select_email+ - Assertions on the HTML body of an e-mail.
- #
- # Also see HTML::Selector to learn how to use selectors.
- module SelectorAssertions
- # :call-seq:
- # css_select(selector) => array
- # css_select(element, selector) => array
- #
- # Select and return all matching elements.
- #
- # If called with a single argument, uses that argument as a selector
- # to match all elements of the current page. Returns an empty array
- # if no match is found.
- #
- # If called with two arguments, uses the first argument as the base
- # element and the second argument as the selector. Attempts to match the
- # base element and any of its children. Returns an empty array if no
- # match is found.
- #
- # The selector may be a CSS selector expression (String), an expression
- # with substitution values (Array) or an HTML::Selector object.
- #
- # ==== Examples
- # # Selects all div tags
- # divs = css_select("div")
- #
- # # Selects all paragraph tags and does something interesting
- # pars = css_select("p")
- # pars.each do |par|
- # # Do something fun with paragraphs here...
- # end
- #
- # # Selects all list items in unordered lists
- # items = css_select("ul>li")
- #
- # # Selects all form tags and then all inputs inside the form
- # forms = css_select("form")
- # forms.each do |form|
- # inputs = css_select(form, "input")
- # ...
- # end
- #
- def css_select(*args)
- # See assert_select to understand what's going on here.
- arg = args.shift
-
- if arg.is_a?(HTML::Node)
- root = arg
- arg = args.shift
- elsif arg == nil
- raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
- elsif @selected
- matches = []
-
- @selected.each do |selected|
- subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
- subset.each do |match|
- matches << match unless matches.any? { |m| m.equal?(match) }
- end
- end
-
- return matches
- else
- root = response_from_page_or_rjs
- end
-
- case arg
- when String
- selector = HTML::Selector.new(arg, args)
- when Array
- selector = HTML::Selector.new(*arg)
- when HTML::Selector
- selector = arg
- else raise ArgumentError, "Expecting a selector as the first argument"
- end
-
- selector.select(root)
- end
-
- # :call-seq:
- # assert_select(selector, equality?, message?)
- # assert_select(element, selector, equality?, message?)
- #
- # An assertion that selects elements and makes one or more equality tests.
- #
- # If the first argument is an element, selects all matching elements
- # starting from (and including) that element and all its children in
- # depth-first order.
- #
- # If no element if specified, calling +assert_select+ selects from the
- # response HTML unless +assert_select+ is called from within an +assert_select+ block.
- #
- # When called with a block +assert_select+ passes an array of selected elements
- # to the block. Calling +assert_select+ from the block, with no element specified,
- # runs the assertion on the complete set of elements selected by the enclosing assertion.
- # Alternatively the array may be iterated through so that +assert_select+ can be called
- # separately for each element.
- #
- #
- # ==== Example
- # If the response contains two ordered lists, each with four list elements then:
- # assert_select "ol" do |elements|
- # elements.each do |element|
- # assert_select element, "li", 4
- # end
- # end
- #
- # will pass, as will:
- # assert_select "ol" do
- # assert_select "li", 8
- # end
- #
- # The selector may be a CSS selector expression (String), an expression
- # with substitution values, or an HTML::Selector object.
- #
- # === Equality Tests
- #
- # The equality test may be one of the following:
- # * true - Assertion is true if at least one element selected.
- # * false - Assertion is true if no element selected.
- # * String/Regexp - Assertion is true if the text value of at least
- # one element matches the string or regular expression.
- # * Integer - Assertion is true if exactly that number of
- # elements are selected.
- # * Range - Assertion is true if the number of selected
- # elements fit the range.
- # If no equality test specified, the assertion is true if at least one
- # element selected.
- #
- # To perform more than one equality tests, use a hash with the following keys:
- # * :text - Narrow the selection to elements that have this text
- # value (string or regexp).
- # * :html - Narrow the selection to elements that have this HTML
- # content (string or regexp).
- # * :count - Assertion is true if the number of selected elements
- # is equal to this value.
- # * :minimum - Assertion is true if the number of selected
- # elements is at least this value.
- # * :maximum - Assertion is true if the number of selected
- # elements is at most this value.
- #
- # If the method is called with a block, once all equality tests are
- # evaluated the block is called with an array of all matched elements.
- #
- # ==== Examples
- #
- # # At least one form element
- # assert_select "form"
- #
- # # Form element includes four input fields
- # assert_select "form input", 4
- #
- # # Page title is "Welcome"
- # assert_select "title", "Welcome"
- #
- # # Page title is "Welcome" and there is only one title element
- # assert_select "title", {:count=>1, :text=>"Welcome"},
- # "Wrong title or more than one title element"
- #
- # # Page contains no forms
- # assert_select "form", false, "This page must contain no forms"
- #
- # # Test the content and style
- # assert_select "body div.header ul.menu"
- #
- # # Use substitution values
- # assert_select "ol>li#?", /item-\d+/
- #
- # # All input fields in the form have a name
- # assert_select "form input" do
- # assert_select "[name=?]", /.+/ # Not empty
- # end
- def assert_select(*args, &block)
- # Start with optional element followed by mandatory selector.
- arg = args.shift
-
- if arg.is_a?(HTML::Node)
- # First argument is a node (tag or text, but also HTML root),
- # so we know what we're selecting from.
- root = arg
- arg = args.shift
- elsif arg == nil
- # This usually happens when passing a node/element that
- # happens to be nil.
- raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
- elsif @selected
- root = HTML::Node.new(nil)
- root.children.concat @selected
- else
- # Otherwise just operate on the response document.
- root = response_from_page_or_rjs
- end
-
- # First or second argument is the selector: string and we pass
- # all remaining arguments. Array and we pass the argument. Also
- # accepts selector itself.
- case arg
- when String
- selector = HTML::Selector.new(arg, args)
- when Array
- selector = HTML::Selector.new(*arg)
- when HTML::Selector
- selector = arg
- else raise ArgumentError, "Expecting a selector as the first argument"
- end
-
- # Next argument is used for equality tests.
- equals = {}
- case arg = args.shift
- when Hash
- equals = arg
- when String, Regexp
- equals[:text] = arg
- when Integer
- equals[:count] = arg
- when Range
- equals[:minimum] = arg.begin
- equals[:maximum] = arg.end
- when FalseClass
- equals[:count] = 0
- when NilClass, TrueClass
- equals[:minimum] = 1
- else raise ArgumentError, "I don't understand what you're trying to match"
- end
-
- # By default we're looking for at least one match.
- if equals[:count]
- equals[:minimum] = equals[:maximum] = equals[:count]
- else
- equals[:minimum] = 1 unless equals[:minimum]
- end
-
- # Last argument is the message we use if the assertion fails.
- message = args.shift
- #- message = "No match made with selector #{selector.inspect}" unless message
- if args.shift
- raise ArgumentError, "Not expecting that last argument, you either have too many arguments, or they're the wrong type"
- end
-
- matches = selector.select(root)
- # If text/html, narrow down to those elements that match it.
- content_mismatch = nil
- if match_with = equals[:text]
- matches.delete_if do |match|
- text = ""
- text.force_encoding(match_with.encoding) if text.respond_to?(:force_encoding)
- stack = match.children.reverse
- while node = stack.pop
- if node.tag?
- stack.concat node.children.reverse
- else
- content = node.content
- content.force_encoding(match_with.encoding) if content.respond_to?(:force_encoding)
- text << content
- end
- end
- text.strip! unless NO_STRIP.include?(match.name)
- unless match_with.is_a?(Regexp) ? (text =~ match_with) : (text == match_with.to_s)
- content_mismatch ||= build_message(message, "> expected but was\n>.", match_with, text)
- true
- end
- end
- elsif match_with = equals[:html]
- matches.delete_if do |match|
- html = match.children.map(&:to_s).join
- html.strip! unless NO_STRIP.include?(match.name)
- unless match_with.is_a?(Regexp) ? (html =~ match_with) : (html == match_with.to_s)
- content_mismatch ||= build_message(message, "> expected but was\n>.", match_with, html)
- true
- end
- end
- end
- # Expecting foo found bar element only if found zero, not if
- # found one but expecting two.
- message ||= content_mismatch if matches.empty?
- # Test minimum/maximum occurrence.
- min, max = equals[:minimum], equals[:maximum]
- message = message || %(Expected #{count_description(min, max)} matching "#{selector.to_s}", found #{matches.size}.)
- assert matches.size >= min, message if min
- assert matches.size <= max, message if max
-
- # If a block is given call that block. Set @selected to allow
- # nested assert_select, which can be nested several levels deep.
- if block_given? && !matches.empty?
- begin
- in_scope, @selected = @selected, matches
- yield matches
- ensure
- @selected = in_scope
- end
- end
-
- # Returns all matches elements.
- matches
- end
-
- def count_description(min, max) #:nodoc:
- pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
-
- if min && max && (max != min)
- "between #{min} and #{max} elements"
- elsif min && !(min == 1 && max == 1)
- "at least #{min} #{pluralize['element', min]}"
- elsif max
- "at most #{max} #{pluralize['element', max]}"
- end
- end
-
- # :call-seq:
- # assert_select_rjs(id?) { |elements| ... }
- # assert_select_rjs(statement, id?) { |elements| ... }
- # assert_select_rjs(:insert, position, id?) { |elements| ... }
- #
- # Selects content from the RJS response.
- #
- # === Narrowing down
- #
- # With no arguments, asserts that one or more elements are updated or
- # inserted by RJS statements.
- #
- # Use the +id+ argument to narrow down the assertion to only statements
- # that update or insert an element with that identifier.
- #
- # Use the first argument to narrow down assertions to only statements
- # of that type. Possible values are :replace, :replace_html,
- # :show, :hide, :toggle, :remove and
- # :insert_html.
- #
- # Use the argument :insert followed by an insertion position to narrow
- # down the assertion to only statements that insert elements in that
- # position. Possible values are :top, :bottom, :before
- # and :after.
- #
- # Using the :remove statement, you will be able to pass a block, but it will
- # be ignored as there is no HTML passed for this statement.
- #
- # === Using blocks
- #
- # Without a block, +assert_select_rjs+ merely asserts that the response
- # contains one or more RJS statements that replace or update content.
- #
- # With a block, +assert_select_rjs+ also selects all elements used in
- # these statements and passes them to the block. Nested assertions are
- # supported.
- #
- # Calling +assert_select_rjs+ with no arguments and using nested asserts
- # asserts that the HTML content is returned by one or more RJS statements.
- # Using +assert_select+ directly makes the same assertion on the content,
- # but without distinguishing whether the content is returned in an HTML
- # or JavaScript.
- #
- # ==== Examples
- #
- # # Replacing the element foo.
- # # page.replace 'foo', ...
- # assert_select_rjs :replace, "foo"
- #
- # # Replacing with the chained RJS proxy.
- # # page[:foo].replace ...
- # assert_select_rjs :chained_replace, 'foo'
- #
- # # Inserting into the element bar, top position.
- # assert_select_rjs :insert, :top, "bar"
- #
- # # Remove the element bar
- # assert_select_rjs :remove, "bar"
- #
- # # Changing the element foo, with an image.
- # assert_select_rjs "foo" do
- # assert_select "img[src=/images/logo.gif""
- # end
- #
- # # RJS inserts or updates a list with four items.
- # assert_select_rjs do
- # assert_select "ol>li", 4
- # end
- #
- # # The same, but shorter.
- # assert_select "ol>li", 4
- def assert_select_rjs(*args, &block)
- rjs_type = args.first.is_a?(Symbol) ? args.shift : nil
- id = args.first.is_a?(String) ? args.shift : nil
-
- # If the first argument is a symbol, it's the type of RJS statement we're looking
- # for (update, replace, insertion, etc). Otherwise, we're looking for just about
- # any RJS statement.
- if rjs_type
- if rjs_type == :insert
- position = args.shift
- id = args.shift
- insertion = "insert_#{position}".to_sym
- raise ArgumentError, "Unknown RJS insertion type #{position}" unless RJS_STATEMENTS[insertion]
- statement = "(#{RJS_STATEMENTS[insertion]})"
- else
- raise ArgumentError, "Unknown RJS statement type #{rjs_type}" unless RJS_STATEMENTS[rjs_type]
- statement = "(#{RJS_STATEMENTS[rjs_type]})"
- end
- else
- statement = "#{RJS_STATEMENTS[:any]}"
- end
-
- # Next argument we're looking for is the element identifier. If missing, we pick
- # any element, otherwise we replace it in the statement.
- pattern = Regexp.new(
- id ? statement.gsub(RJS_ANY_ID, "\"#{id}\"") : statement
- )
-
- # Duplicate the body since the next step involves destroying it.
- matches = nil
- case rjs_type
- when :remove, :show, :hide, :toggle
- matches = @response.body.match(pattern)
- else
- @response.body.gsub(pattern) do |match|
- html = unescape_rjs(match)
- matches ||= []
- matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
- ""
- end
- end
-
- if matches
- assert_block("") { true } # to count the assertion
- if block_given? && !([:remove, :show, :hide, :toggle].include? rjs_type)
- begin
- in_scope, @selected = @selected, matches
- yield matches
- ensure
- @selected = in_scope
- end
- end
- matches
- else
- # RJS statement not found.
- case rjs_type
- when :remove, :show, :hide, :toggle
- flunk_message = "No RJS statement that #{rjs_type.to_s}s '#{id}' was rendered."
- else
- flunk_message = "No RJS statement that replaces or inserts HTML content."
- end
- flunk args.shift || flunk_message
- end
- end
-
- # :call-seq:
- # assert_select_encoded(element?) { |elements| ... }
- #
- # Extracts the content of an element, treats it as encoded HTML and runs
- # nested assertion on it.
- #
- # You typically call this method within another assertion to operate on
- # all currently selected elements. You can also pass an element or array
- # of elements.
- #
- # The content of each element is un-encoded, and wrapped in the root
- # element +encoded+. It then calls the block with all un-encoded elements.
- #
- # ==== Examples
- # # Selects all bold tags from within the title of an ATOM feed's entries (perhaps to nab a section name prefix)
- # assert_select_feed :atom, 1.0 do
- # # Select each entry item and then the title item
- # assert_select "entry>title" do
- # # Run assertions on the encoded title elements
- # assert_select_encoded do
- # assert_select "b"
- # end
- # end
- # end
- #
- #
- # # Selects all paragraph tags from within the description of an RSS feed
- # assert_select_feed :rss, 2.0 do
- # # Select description element of each feed item.
- # assert_select "channel>item>description" do
- # # Run assertions on the encoded elements.
- # assert_select_encoded do
- # assert_select "p"
- # end
- # end
- # end
- def assert_select_encoded(element = nil, &block)
- case element
- when Array
- elements = element
- when HTML::Node
- elements = [element]
- when nil
- unless elements = @selected
- raise ArgumentError, "First argument is optional, but must be called from a nested assert_select"
- end
- else
- raise ArgumentError, "Argument is optional, and may be node or array of nodes"
- end
-
- fix_content = lambda do |node|
- # Gets around a bug in the Rails 1.1 HTML parser.
- node.content.gsub(/)?/m) { CGI.escapeHTML($1) }
- end
-
- selected = elements.map do |element|
- text = element.children.select{ |c| not c.tag? }.map{ |c| fix_content[c] }.join
- root = HTML::Document.new(CGI.unescapeHTML("#{text}")).root
- css_select(root, "encoded:root", &block)[0]
- end
-
- begin
- old_selected, @selected = @selected, selected
- assert_select ":root", &block
- ensure
- @selected = old_selected
- end
- end
-
- # :call-seq:
- # assert_select_email { }
- #
- # Extracts the body of an email and runs nested assertions on it.
- #
- # You must enable deliveries for this assertion to work, use:
- # ActionMailer::Base.perform_deliveries = true
- #
- # ==== Examples
- #
- # assert_select_email do
- # assert_select "h1", "Email alert"
- # end
- #
- # assert_select_email do
- # items = assert_select "ol>li"
- # items.each do
- # # Work with items here...
- # end
- # end
- #
- def assert_select_email(&block)
- deliveries = ActionMailer::Base.deliveries
- assert !deliveries.empty?, "No e-mail in delivery list"
-
- for delivery in deliveries
- for part in delivery.parts
- if part["Content-Type"].to_s =~ /^text\/html\W/
- root = HTML::Document.new(part.body).root
- assert_select root, ":root", &block
- end
- end
- end
- end
-
- protected
- unless const_defined?(:RJS_STATEMENTS)
- RJS_PATTERN_HTML = "\"((\\\\\"|[^\"])*)\""
- RJS_ANY_ID = "\"([^\"])*\""
- RJS_STATEMENTS = {
- :chained_replace => "\\$\\(#{RJS_ANY_ID}\\)\\.replace\\(#{RJS_PATTERN_HTML}\\)",
- :chained_replace_html => "\\$\\(#{RJS_ANY_ID}\\)\\.update\\(#{RJS_PATTERN_HTML}\\)",
- :replace_html => "Element\\.update\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)",
- :replace => "Element\\.replace\\(#{RJS_ANY_ID}, #{RJS_PATTERN_HTML}\\)"
- }
- [:remove, :show, :hide, :toggle].each do |action|
- RJS_STATEMENTS[action] = "Element\\.#{action}\\(#{RJS_ANY_ID}\\)"
- end
- RJS_INSERTIONS = ["top", "bottom", "before", "after"]
- RJS_INSERTIONS.each do |insertion|
- RJS_STATEMENTS["insert_#{insertion}".to_sym] = "Element.insert\\(#{RJS_ANY_ID}, \\{ #{insertion}: #{RJS_PATTERN_HTML} \\}\\)"
- end
- RJS_STATEMENTS[:insert_html] = "Element.insert\\(#{RJS_ANY_ID}, \\{ (#{RJS_INSERTIONS.join('|')}): #{RJS_PATTERN_HTML} \\}\\)"
- RJS_STATEMENTS[:any] = Regexp.new("(#{RJS_STATEMENTS.values.join('|')})")
- RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/
- end
-
- # +assert_select+ and +css_select+ call this to obtain the content in the HTML
- # page, or from all the RJS statements, depending on the type of response.
- def response_from_page_or_rjs()
- content_type = @response.content_type
-
- if content_type && Mime::JS =~ content_type
- body = @response.body.dup
- root = HTML::Node.new(nil)
-
- while true
- next if body.sub!(RJS_STATEMENTS[:any]) do |match|
- html = unescape_rjs(match)
- matches = HTML::Document.new(html).root.children.select { |n| n.tag? }
- root.children.concat matches
- ""
- end
- break
- end
-
- root
- else
- html_document.root
- end
- end
-
- # Unescapes a RJS string.
- def unescape_rjs(rjs_string)
- # RJS encodes double quotes and line breaks.
- unescaped= rjs_string.gsub('\"', '"')
- unescaped.gsub!(/\\\//, '/')
- unescaped.gsub!('\n', "\n")
- unescaped.gsub!('\076', '>')
- unescaped.gsub!('\074', '<')
- # RJS encodes non-ascii characters.
- unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
- unescaped
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/assertions/tag_assertions.rb b/actionpack/lib/action_controller/assertions/tag_assertions.rb
deleted file mode 100644
index 80249e0e83..0000000000
--- a/actionpack/lib/action_controller/assertions/tag_assertions.rb
+++ /dev/null
@@ -1,127 +0,0 @@
-module ActionController
- module Assertions
- # Pair of assertions to testing elements in the HTML output of the response.
- module TagAssertions
- # Asserts that there is a tag/node/element in the body of the response
- # that meets all of the given conditions. The +conditions+ parameter must
- # be a hash of any of the following keys (all are optional):
- #
- # * :tag: the node type must match the corresponding value
- # * :attributes: a hash. The node's attributes must match the
- # corresponding values in the hash.
- # * :parent: a hash. The node's parent must match the
- # corresponding hash.
- # * :child: a hash. At least one of the node's immediate children
- # must meet the criteria described by the hash.
- # * :ancestor: a hash. At least one of the node's ancestors must
- # meet the criteria described by the hash.
- # * :descendant: a hash. At least one of the node's descendants
- # must meet the criteria described by the hash.
- # * :sibling: a hash. At least one of the node's siblings must
- # meet the criteria described by the hash.
- # * :after: a hash. The node must be after any sibling meeting
- # the criteria described by the hash, and at least one sibling must match.
- # * :before: a hash. The node must be before any sibling meeting
- # the criteria described by the hash, and at least one sibling must match.
- # * :children: a hash, for counting children of a node. Accepts
- # the keys:
- # * :count: either a number or a range which must equal (or
- # include) the number of children that match.
- # * :less_than: the number of matching children must be less
- # than this number.
- # * :greater_than: the number of matching children must be
- # greater than this number.
- # * :only: another hash consisting of the keys to use
- # to match on the children, and only matching children will be
- # counted.
- # * :content: the textual content of the node must match the
- # given value. This will not match HTML tags in the body of a
- # tag--only text.
- #
- # Conditions are matched using the following algorithm:
- #
- # * if the condition is a string, it must be a substring of the value.
- # * if the condition is a regexp, it must match the value.
- # * if the condition is a number, the value must match number.to_s.
- # * if the condition is +true+, the value must not be +nil+.
- # * if the condition is +false+ or +nil+, the value must be +nil+.
- #
- # === Examples
- #
- # # Assert that there is a "span" tag
- # assert_tag :tag => "span"
- #
- # # Assert that there is a "span" tag with id="x"
- # assert_tag :tag => "span", :attributes => { :id => "x" }
- #
- # # Assert that there is a "span" tag using the short-hand
- # assert_tag :span
- #
- # # Assert that there is a "span" tag with id="x" using the short-hand
- # assert_tag :span, :attributes => { :id => "x" }
- #
- # # Assert that there is a "span" inside of a "div"
- # assert_tag :tag => "span", :parent => { :tag => "div" }
- #
- # # Assert that there is a "span" somewhere inside a table
- # assert_tag :tag => "span", :ancestor => { :tag => "table" }
- #
- # # Assert that there is a "span" with at least one "em" child
- # assert_tag :tag => "span", :child => { :tag => "em" }
- #
- # # Assert that there is a "span" containing a (possibly nested)
- # # "strong" tag.
- # assert_tag :tag => "span", :descendant => { :tag => "strong" }
- #
- # # Assert that there is a "span" containing between 2 and 4 "em" tags
- # # as immediate children
- # assert_tag :tag => "span",
- # :children => { :count => 2..4, :only => { :tag => "em" } }
- #
- # # Get funky: assert that there is a "div", with an "ul" ancestor
- # # and an "li" parent (with "class" = "enum"), and containing a
- # # "span" descendant that contains text matching /hello world/
- # assert_tag :tag => "div",
- # :ancestor => { :tag => "ul" },
- # :parent => { :tag => "li",
- # :attributes => { :class => "enum" } },
- # :descendant => { :tag => "span",
- # :child => /hello world/ }
- #
- # Please note: +assert_tag+ and +assert_no_tag+ only work
- # with well-formed XHTML. They recognize a few tags as implicitly self-closing
- # (like br and hr and such) but will not work correctly with tags
- # that allow optional closing tags (p, li, td). You must explicitly
- # close all of your tags to use these assertions.
- def assert_tag(*opts)
- clean_backtrace do
- opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
- tag = find_tag(opts)
- assert tag, "expected tag, but no tag found matching #{opts.inspect} in:\n#{@response.body.inspect}"
- end
- end
-
- # Identical to +assert_tag+, but asserts that a matching tag does _not_
- # exist. (See +assert_tag+ for a full discussion of the syntax.)
- #
- # === Examples
- # # Assert that there is not a "div" containing a "p"
- # assert_no_tag :tag => "div", :descendant => { :tag => "p" }
- #
- # # Assert that an unordered list is empty
- # assert_no_tag :tag => "ul", :descendant => { :tag => "li" }
- #
- # # Assert that there is not a "p" tag with between 1 to 3 "img" tags
- # # as immediate children
- # assert_no_tag :tag => "p",
- # :children => { :count => 1..3, :only => { :tag => "img" } }
- def assert_no_tag(*opts)
- clean_backtrace do
- opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
- tag = find_tag(opts)
- assert !tag, "expected no tag, but found tag matching #{opts.inspect} in:\n#{@response.body.inspect}"
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
deleted file mode 100644
index 78c8bf0647..0000000000
--- a/actionpack/lib/action_controller/base.rb
+++ /dev/null
@@ -1,1392 +0,0 @@
-require 'set'
-
-module ActionController #:nodoc:
- class ActionControllerError < StandardError #:nodoc:
- end
-
- class SessionRestoreError < ActionControllerError #:nodoc:
- end
-
- class RenderError < ActionControllerError #:nodoc:
- end
-
- class RoutingError < ActionControllerError #:nodoc:
- attr_reader :failures
- def initialize(message, failures=[])
- super(message)
- @failures = failures
- end
- end
-
- class MethodNotAllowed < ActionControllerError #:nodoc:
- attr_reader :allowed_methods
-
- def initialize(*allowed_methods)
- super("Only #{allowed_methods.to_sentence} requests are allowed.")
- @allowed_methods = allowed_methods
- end
-
- def allowed_methods_header
- allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
- end
-
- def handle_response!(response)
- response.headers['Allow'] ||= allowed_methods_header
- end
- end
-
- class NotImplemented < MethodNotAllowed #:nodoc:
- end
-
- class UnknownController < ActionControllerError #:nodoc:
- end
-
- class UnknownAction < ActionControllerError #:nodoc:
- end
-
- class MissingFile < ActionControllerError #:nodoc:
- end
-
- class RenderError < ActionControllerError #:nodoc:
- end
-
- class SessionOverflowError < ActionControllerError #:nodoc:
- DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
-
- def initialize(message = nil)
- super(message || DEFAULT_MESSAGE)
- end
- end
-
- class DoubleRenderError < ActionControllerError #:nodoc:
- DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\"."
-
- def initialize(message = nil)
- super(message || DEFAULT_MESSAGE)
- end
- end
-
- class RedirectBackError < ActionControllerError #:nodoc:
- DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
-
- def initialize(message = nil)
- super(message || DEFAULT_MESSAGE)
- end
- end
-
- class UnknownHttpMethod < ActionControllerError #:nodoc:
- end
-
- # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
- # on request and then either render a template or redirect to another action. An action is defined as a public method
- # on the controller, which will automatically be made accessible to the web-server through Rails Routes.
- #
- # A sample controller could look like this:
- #
- # class GuestBookController < ActionController::Base
- # def index
- # @entries = Entry.find(:all)
- # end
- #
- # def sign
- # Entry.create(params[:entry])
- # redirect_to :action => "index"
- # end
- # end
- #
- # Actions, by default, render a template in the app/views directory corresponding to the name of the controller and action
- # after executing code in the action. For example, the +index+ action of the GuestBookController would render the
- # template app/views/guestbook/index.erb by default after populating the @entries instance variable.
- #
- # Unlike index, the sign action will not render a template. After performing its main purpose (creating a
- # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
- # "302 Moved" HTTP response that takes the user to the index action.
- #
- # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
- # Most actions are variations of these themes.
- #
- # == Requests
- #
- # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters.
- # This value should hold the name of the action to be performed. Once the action has been identified, the remaining
- # request parameters, the session (if one is available), and the full request with all the HTTP headers are made available to
- # the action through instance variables. Then the action is performed.
- #
- # The full request object is available with the request accessor and is primarily used to query for HTTP headers. These queries
- # are made by accessing the environment hash, like this:
- #
- # def server_ip
- # location = request.env["SERVER_ADDR"]
- # render :text => "This server hosted at #{location}"
- # end
- #
- # == Parameters
- #
- # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
- # which returns a hash. For example, an action that was performed through /weblog/list?category=All&limit=5 will include
- # { "category" => "All", "limit" => 5 } in params.
- #
- # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
- #
- #
- #
- #
- # A request stemming from a form holding these inputs will include { "post" => { "name" => "david", "address" => "hyacintvej" } }.
- # If the address input had been named "post[address][street]", the params would have included
- # { "post" => { "address" => { "street" => "hyacintvej" } } }. There's no limit to the depth of the nesting.
- #
- # == Sessions
- #
- # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
- # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
- # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
- # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
- #
- # You can place objects in the session by using the session method, which accesses a hash:
- #
- # session[:person] = Person.authenticate(user_name, password)
- #
- # And retrieved again through the same hash:
- #
- # Hello #{session[:person]}
- #
- # For removing objects from the session, you can either assign a single key to +nil+:
- #
- # # removes :person from session
- # session[:person] = nil
- #
- # or you can remove the entire session with +reset_session+.
- #
- # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
- # This prevents the user from tampering with the session but also allows him to see its contents.
- #
- # Do not put secret information in cookie-based sessions!
- #
- # Other options for session storage are:
- #
- # * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
- # unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
- #
- # config.action_controller.session_store = :active_record_store
- #
- # in your config/environment.rb and run rake db:sessions:create.
- #
- # * MemCacheStore - Sessions are stored as entries in your memcached cache.
- # Set the session store type in config/environment.rb:
- #
- # config.action_controller.session_store = :mem_cache_store
- #
- # This assumes that memcached has been installed and configured properly.
- # See the MemCacheStore docs for more information.
- #
- # == Responses
- #
- # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
- # object is generated automatically through the use of renders and redirects and requires no user intervention.
- #
- # == Renders
- #
- # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
- # of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured.
- # The controller passes objects to the view by assigning instance variables:
- #
- # def show
- # @post = Post.find(params[:id])
- # end
- #
- # Which are then automatically available to the view:
- #
- # Title: <%= @post.title %>
- #
- # You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use
- # the manual rendering methods:
- #
- # def search
- # @results = Search.find(params[:query])
- # case @results
- # when 0 then render :action => "no_results"
- # when 1 then render :action => "show"
- # when 2..10 then render :action => "show_many"
- # end
- # end
- #
- # Read more about writing ERb and Builder templates in link:classes/ActionView/Base.html.
- #
- # == Redirects
- #
- # Redirects are used to move from one action to another. For example, after a create action, which stores a blog entry to a database,
- # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to)
- # a show action that we'll assume has already been created. The code might look like this:
- #
- # def create
- # @entry = Entry.new(params[:entry])
- # if @entry.save
- # # The entry was saved correctly, redirect to show
- # redirect_to :action => 'show', :id => @entry.id
- # else
- # # things didn't go so well, do something else
- # end
- # end
- #
- # In this case, after saving our new entry to the database, the user is redirected to the show method which is then executed.
- #
- # == Calling multiple redirects or renders
- #
- # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
- #
- # def do_something
- # redirect_to :action => "elsewhere"
- # render :action => "overthere" # raises DoubleRenderError
- # end
- #
- # If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
- #
- # def do_something
- # redirect_to(:action => "elsewhere") and return if monkeys.nil?
- # render :action => "overthere" # won't be called if monkeys is nil
- # end
- #
- class Base
- DEFAULT_RENDER_STATUS_CODE = "200 OK"
-
- include StatusCodes
-
- cattr_reader :protected_instance_variables
- # Controller specific instance variables which will not be accessible inside views.
- @@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
- @action_name @before_filter_chain_aborted @action_cache_path @_session @_headers @_params
- @_flash @_response)
-
- # 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"
- @@asset_host = ""
- cattr_accessor :asset_host
-
- # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors.
- # When the application is ready to go public, this should be set to false, and the protected method local_request?
- # should instead be implemented in the controller to determine when debugging screens should be shown.
- @@consider_all_requests_local = true
- cattr_accessor :consider_all_requests_local
-
- # Indicates whether to allow concurrent action processing. Your
- # controller actions and any other code they call must also behave well
- # when called from concurrent threads. Turned off by default.
- @@allow_concurrency = false
- cattr_accessor :allow_concurrency
-
- # Modern REST web services often need to submit complex data to the web application.
- # The @@param_parsers hash lets you register handlers which will process the HTTP body and add parameters to the
- # params hash. These handlers are invoked for POST and PUT requests.
- #
- # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instantiated
- # in the params. This allows XML requests to mask themselves as regular form submissions, so you can have one
- # action serve both regular forms and web service requests.
- #
- # Example of doing your own parser for a custom content type:
- #
- # ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data|
- # node = REXML::Document.new(post)
- # { node.root.name => node.root }
- # end
- #
- # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the
- # root node for such requests. The new default is to keep the root, such that "David" results
- # in params[:r][:name] for "David" instead of params[:name]. To get the old behavior, you can
- # re-register XmlSimple as application/xml handler ike this:
- #
- # ActionController::Base.param_parsers[Mime::XML] =
- # Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) }
- #
- # A YAML parser is also available and can be turned on with:
- #
- # ActionController::Base.param_parsers[Mime::YAML] = :yaml
- @@param_parsers = {}
- cattr_accessor :param_parsers
-
- # Controls the default charset for all renders.
- @@default_charset = "utf-8"
- cattr_accessor :default_charset
-
- # The logger is used for generating information on the action run-time (including benchmarking) if available.
- # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
- cattr_accessor :logger
-
- # Controls the resource action separator
- @@resource_action_separator = "/"
- cattr_accessor :resource_action_separator
-
- # Allow to override path names for default resources' actions
- @@resources_path_names = { :new => 'new', :edit => 'edit' }
- cattr_accessor :resources_path_names
-
- # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
- # sets it to :authenticity_token by default.
- cattr_accessor :request_forgery_protection_token
-
- # Controls the IP Spoofing check when determining the remote IP.
- @@ip_spoofing_check = true
- cattr_accessor :ip_spoofing_check
-
- # Indicates whether or not optimise the generated named
- # route helper methods
- cattr_accessor :optimise_named_routes
- self.optimise_named_routes = true
-
- # Indicates whether the response format should be determined by examining the Accept HTTP header,
- # or by using the simpler params + ajax rules.
- #
- # If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept
- # header into account. If it is set to false then the request format will be determined solely
- # by examining params[:format]. If params format is missing, the format will be either HTML or
- # Javascript depending on whether the request is an AJAX request.
- cattr_accessor :use_accept_header
- self.use_accept_header = true
-
- # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
- class_inheritable_accessor :allow_forgery_protection
- self.allow_forgery_protection = true
-
- # If you are deploying to a subdirectory, you will need to set
- # config.action_controller.relative_url_root
- # This defaults to ENV['RAILS_RELATIVE_URL_ROOT']
- cattr_accessor :relative_url_root
- self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
-
- # Holds the request object that's primarily used to get environment variables through access like
- # request.env["REQUEST_URI"].
- attr_internal :request
-
- # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like params["post_id"]
- # to get the post_id. No type casts are made, so all values are returned as strings.
- attr_internal :params
-
- # Holds the response object that's primarily used to set additional HTTP headers through access like
- # response.headers["Cache-Control"] = "no-cache". Can also be used to access the final body HTML after a template
- # has been rendered through response.body -- useful for after_filters that wants to manipulate the output,
- # such as a OutputCompressionFilter.
- attr_internal :response
-
- # Holds a hash of objects in the session. Accessed like session[:person] to get the object tied to the "person"
- # key. The session will hold any type of object as values, but the key should be a string or symbol.
- attr_internal :session
-
- # Holds a hash of header names and values. Accessed like headers["Cache-Control"] to get the value of the Cache-Control
- # directive. Values should always be specified as strings.
- attr_internal :headers
-
- # Returns the name of the action this controller is processing.
- attr_accessor :action_name
-
- class << self
- def call(env)
- # HACK: For global rescue to have access to the original request and response
- request = env["action_controller.rescue.request"] ||= Request.new(env)
- response = env["action_controller.rescue.response"] ||= Response.new
- process(request, response)
- end
-
- # Factory for the standard create, process loop where the controller is discarded after processing.
- def process(request, response) #:nodoc:
- new.process(request, response)
- end
-
- # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
- def controller_class_name
- @controller_class_name ||= name.demodulize
- end
-
- # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat".
- def controller_name
- @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore
- end
-
- # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
- def controller_path
- @controller_path ||= name.gsub(/Controller$/, '').underscore
- end
-
- # Return an array containing the names of public methods that have been marked hidden from the action processor.
- # By default, all methods defined in ActionController::Base and included modules are hidden.
- # More methods can be hidden using hide_actions.
- def hidden_actions
- read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, [])
- end
-
- # Hide each of the given methods from being callable as actions.
- def hide_action(*names)
- write_inheritable_attribute(:hidden_actions, hidden_actions | names.map { |name| name.to_s })
- end
-
- # View load paths determine the bases from which template references can be made. So a call to
- # render("test/template") will be looked up in the view load paths array and the closest match will be
- # returned.
- def view_paths
- if defined? @view_paths
- @view_paths
- else
- superclass.view_paths
- end
- end
-
- def view_paths=(value)
- @view_paths = ActionView::Base.process_view_paths(value) if value
- end
-
- # Adds a view_path to the front of the view_paths array.
- # If the current class has no view paths, copy them from
- # the superclass. This change will be visible for all future requests.
- #
- # ArticleController.prepend_view_path("views/default")
- # ArticleController.prepend_view_path(["views/default", "views/custom"])
- #
- def prepend_view_path(path)
- @view_paths = superclass.view_paths.dup if !defined?(@view_paths) || @view_paths.nil?
- @view_paths.unshift(*path)
- end
-
- # Adds a view_path to the end of the view_paths array.
- # If the current class has no view paths, copy them from
- # the superclass. This change will be visible for all future requests.
- #
- # ArticleController.append_view_path("views/default")
- # ArticleController.append_view_path(["views/default", "views/custom"])
- #
- def append_view_path(path)
- @view_paths = superclass.view_paths.dup if @view_paths.nil?
- @view_paths.push(*path)
- end
-
- # Replace sensitive parameter data from the request log.
- # Filters parameters that have any of the arguments as a substring.
- # Looks in all subhashes of the param hash for keys to filter.
- # If a block is given, each key and value of the parameter hash and all
- # subhashes is passed to it, the value or key
- # can be replaced using String#replace or similar method.
- #
- # Examples:
- # filter_parameter_logging
- # => Does nothing, just slows the logging process down
- #
- # filter_parameter_logging :password
- # => replaces the value to all keys matching /password/i with "[FILTERED]"
- #
- # filter_parameter_logging :foo, "bar"
- # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
- #
- # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
- # => reverses the value to all keys matching /secret/i
- #
- # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
- # => reverses the value to all keys matching /secret/i, and
- # replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
- def filter_parameter_logging(*filter_words, &block)
- parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
-
- define_method(:filter_parameters) do |unfiltered_parameters|
- filtered_parameters = {}
-
- unfiltered_parameters.each do |key, value|
- if key =~ parameter_filter
- filtered_parameters[key] = '[FILTERED]'
- elsif value.is_a?(Hash)
- filtered_parameters[key] = filter_parameters(value)
- elsif block_given?
- key = key.dup
- value = value.dup if value
- yield key, value
- filtered_parameters[key] = value
- else
- filtered_parameters[key] = value
- end
- end
-
- filtered_parameters
- end
- protected :filter_parameters
- end
-
- delegate :exempt_from_layout, :to => 'ActionView::Template'
- end
-
- public
- # Extracts the action_name from the request parameters and performs that action.
- def process(request, response, method = :perform_action, *arguments) #:nodoc:
- response.request = request
-
- assign_shortcuts(request, response)
- initialize_template_class(response)
- initialize_current_url
- assign_names
-
- log_processing
- send(method, *arguments)
-
- send_response
- ensure
- process_cleanup
- end
-
- def send_response
- response.prepare!
- response
- end
-
- # Returns a URL that has been rewritten according to the options hash and the defined routes.
- # (For doing a complete redirect, use +redirect_to+).
- #
- # url_for is used to:
- #
- # All keys given to +url_for+ are forwarded to the Route module, save for the following:
- # * :anchor - Specifies the anchor name to be appended to the path. For example,
- # url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments'
- # will produce "/posts/show/10#comments".
- # * :only_path - If true, returns the relative URL (omitting the protocol, host name, and port) (false by default).
- # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2005/". Note that this
- # is currently not recommended since it breaks caching.
- # * :host - Overrides the default (current) host if provided.
- # * :protocol - Overrides the default (current) protocol if provided.
- # * :port - Optionally specify the port to connect to.
- # * :user - Inline HTTP authentication (only plucked out if :password is also present).
- # * :password - Inline HTTP authentication (only plucked out if :user is also present).
- # * :skip_relative_url_root - If true, the url is not constructed using the +relative_url_root+
- # of the request so the path will include the web server relative installation directory.
- #
- # The URL is generated from the remaining keys in the hash. A URL contains two key parts: the and a query string.
- # Routes composes a query string as the key/value pairs not included in the .
- #
- # The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with
- # action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs:
- #
- # url_for :controller => 'posts', :action => 'recent' # => 'proto://host.com/posts/recent'
- # url_for :controller => 'posts', :action => 'index' # => 'proto://host.com/posts'
- # url_for :controller => 'posts', :action => 'index', :port=>'8033' # => 'proto://host.com:8033/posts'
- # url_for :controller => 'posts', :action => 'show', :id => 10 # => 'proto://host.com/posts/show/10'
- # url_for :controller => 'posts', :user => 'd', :password => '123' # => 'proto://d:123@host.com/posts'
- #
- # When generating a new URL, missing values may be filled in from the current request's parameters. For example,
- # url_for :action => 'some_action' will retain the current controller, as expected. This behavior extends to
- # other parameters, including :controller, :id, and any other parameters that are placed into a Route's
- # path.
- # Â
- # The URL helpers such as url_for have a limited form of memory: when generating a new URL, they can look for
- # missing values in the current request's parameters. Routes attempts to guess when a value should and should not be
- # taken from the defaults. There are a few simple rules on how this is performed:
- #
- # * If the controller name begins with a slash no defaults are used:
- #
- # url_for :controller => '/home'
- #
- # In particular, a leading slash ensures no namespace is assumed. Thus,
- # while url_for :controller => 'users' may resolve to
- # Admin::UsersController if the current controller lives under
- # that module, url_for :controller => '/users' ensures you link
- # to ::UsersController no matter what.
- # * If the controller changes, the action will default to index unless provided
- #
- # The final rule is applied while the URL is being generated and is best illustrated by an example. Let us consider the
- # route given by map.connect 'people/:last/:first/:action', :action => 'bio', :controller => 'people'.
- #
- # Suppose that the current URL is "people/hh/david/contacts". Let's consider a few different cases of URLs which are generated
- # from this page.
- #
- # * url_for :action => 'bio' -- During the generation of this URL, default values will be used for the first and
- # last components, and the action shall change. The generated URL will be, "people/hh/david/bio".
- # * url_for :first => 'davids-little-brother' This generates the URL 'people/hh/davids-little-brother' -- note
- # that this URL leaves out the assumed action of 'bio'.
- #
- # However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The
- # answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the
- # value that appears in the slot for :first is not equal to default value for :first we stop using
- # defaults. On its own, this rule can account for much of the typical Rails URL behavior.
- # Â
- # Although a convenience, defaults can occasionally get in your way. In some cases a default persists longer than desired.
- # The default may be cleared by adding :name => nil to url_for's options.
- # This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the
- # helper is used from. The following line will redirect to PostController's default action, regardless of the page it is
- # displayed on:
- #
- # url_for :controller => 'posts', :action => nil
- #
- # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the
- # :overwrite_params options. Say for your posts you have different views for showing and printing them.
- # Then, in the show view, you get the URL for the print view like this
- #
- # url_for :overwrite_params => { :action => 'print' }
- #
- # This takes the current URL as is and only exchanges the action. In contrast, url_for :action => 'print'
- # would have slashed-off the path components after the changed action.
- def url_for(options = {})
- options ||= {}
- case options
- when String
- options
- when Hash
- @url.rewrite(rewrite_options(options))
- else
- polymorphic_url(options)
- end
- end
-
- # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
- def controller_class_name
- self.class.controller_class_name
- end
-
- # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat".
- def controller_name
- self.class.controller_name
- end
-
- # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
- def controller_path
- self.class.controller_path
- end
-
- def session_enabled?
- ActiveSupport::Deprecation.warn("Sessions are now lazy loaded. So if you don't access them, consider them disabled.", caller)
- end
-
- self.view_paths = []
-
- # View load paths for controller.
- def view_paths
- @template.view_paths
- end
-
- def view_paths=(value)
- @template.view_paths = ActionView::Base.process_view_paths(value)
- end
-
- # Adds a view_path to the front of the view_paths array.
- # This change affects the current request only.
- #
- # self.prepend_view_path("views/default")
- # self.prepend_view_path(["views/default", "views/custom"])
- #
- def prepend_view_path(path)
- @template.view_paths.unshift(*path)
- end
-
- # Adds a view_path to the end of the view_paths array.
- # This change affects the current request only.
- #
- # self.append_view_path("views/default")
- # self.append_view_path(["views/default", "views/custom"])
- #
- def append_view_path(path)
- @template.view_paths.push(*path)
- end
-
- protected
- # Renders the content that will be returned to the browser as the response body.
- #
- # === Rendering an action
- #
- # Action rendering is the most common form and the type used automatically by Action Controller when nothing else is
- # specified. By default, actions are rendered within the current layout (if one exists).
- #
- # # Renders the template for the action "goal" within the current controller
- # render :action => "goal"
- #
- # # Renders the template for the action "short_goal" within the current controller,
- # # but without the current active layout
- # render :action => "short_goal", :layout => false
- #
- # # Renders the template for the action "long_goal" within the current controller,
- # # but with a custom layout
- # render :action => "long_goal", :layout => "spectacular"
- #
- # === Rendering partials
- #
- # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page
- # without reloading. Rendering of partials from the controller makes it possible to use the same partial template in
- # both the full-page rendering (by calling it from within the template) and when sub-page updates happen (from the
- # controller action responding to Ajax calls). By default, the current layout is not used.
- #
- # # Renders the same partial with a local variable.
- # render :partial => "person", :locals => { :name => "david" }
- #
- # # Renders the partial, making @new_person available through
- # # the local variable 'person'
- # render :partial => "person", :object => @new_person
- #
- # # Renders a collection of the same partial by making each element
- # # of @winners available through the local variable "person" as it
- # # builds the complete response.
- # render :partial => "person", :collection => @winners
- #
- # # Renders a collection of partials but with a custom local variable name
- # render :partial => "admin_person", :collection => @winners, :as => :person
- #
- # # Renders the same collection of partials, but also renders the
- # # person_divider partial between each person partial.
- # render :partial => "person", :collection => @winners, :spacer_template => "person_divider"
- #
- # # Renders a collection of partials located in a view subfolder
- # # outside of our current controller. In this example we will be
- # # rendering app/views/shared/_note.r(html|xml) Inside the partial
- # # each element of @new_notes is available as the local var "note".
- # render :partial => "shared/note", :collection => @new_notes
- #
- # # Renders the partial with a status code of 500 (internal error).
- # render :partial => "broken", :status => 500
- #
- # Note that the partial filename must also be a valid Ruby variable name,
- # so e.g. 2005 and register-user are invalid.
- #
- #
- # == Automatic etagging
- #
- # Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the
- # response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified
- # and the response body will be set to an empty string. No etag header will be inserted if it's already set.
- #
- # === Rendering a template
- #
- # Template rendering works just like action rendering except that it takes a path relative to the template root.
- # The current layout is automatically applied.
- #
- # # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
- # render :template => "weblog/show"
- #
- # # Renders the template with a local variable
- # render :template => "weblog/show", :locals => {:customer => Customer.new}
- #
- # === Rendering a file
- #
- # File rendering works just like action rendering except that it takes a filesystem path. By default, the path
- # is assumed to be absolute, and the current layout is not applied.
- #
- # # Renders the template located at the absolute filesystem path
- # render :file => "/path/to/some/template.erb"
- # render :file => "c:/path/to/some/template.erb"
- #
- # # Renders a template within the current layout, and with a 404 status code
- # render :file => "/path/to/some/template.erb", :layout => true, :status => 404
- # render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404
- #
- # === Rendering text
- #
- # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
- # rendering is not done within the active layout.
- #
- # # Renders the clear text "hello world" with status code 200
- # render :text => "hello world!"
- #
- # # Renders the clear text "Explosion!" with status code 500
- # render :text => "Explosion!", :status => 500
- #
- # # Renders the clear text "Hi there!" within the current active layout (if one exists)
- # render :text => "Hi there!", :layout => true
- #
- # # Renders the clear text "Hi there!" within the layout
- # # placed in "app/views/layouts/special.r(html|xml)"
- # render :text => "Hi there!", :layout => "special"
- #
- # The :text option can also accept a Proc object, which can be used to manually control the page generation. This should
- # generally be avoided, as it violates the separation between code and content, and because almost everything that can be
- # done with this method can also be done more cleanly using one of the other rendering methods, most notably templates.
- #
- # # Renders "Hello from code!"
- # render :text => proc { |response, output| output.write("Hello from code!") }
- #
- # === Rendering XML
- #
- # Rendering XML sets the content type to application/xml.
- #
- # # Renders 'David'
- # render :xml => {:name => "David"}.to_xml
- #
- # It's not necessary to call to_xml on the object you want to render, since render will
- # automatically do that for you:
- #
- # # Also renders 'David'
- # render :xml => {:name => "David"}
- #
- # === Rendering JSON
- #
- # Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected
- # that the response will be parsed (or eval'd) for use as a data structure.
- #
- # # Renders '{"name": "David"}'
- # render :json => {:name => "David"}.to_json
- #
- # It's not necessary to call to_json on the object you want to render, since render will
- # automatically do that for you:
- #
- # # Also renders '{"name": "David"}'
- # render :json => {:name => "David"}
- #
- # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag),
- # so the :callback option is provided for these cases.
- #
- # # Renders 'show({"name": "David"})'
- # render :json => {:name => "David"}.to_json, :callback => 'show'
- #
- # === Rendering an inline template
- #
- # Rendering of an inline template works as a cross between text and action rendering where the source for the template
- # is supplied inline, like text, but its interpreted with ERb or Builder, like action. By default, ERb is used for rendering
- # and the current layout is not used.
- #
- # # Renders "hello, hello, hello, again"
- # render :inline => "<%= 'hello, ' * 3 + 'again' %>"
- #
- # # Renders "
Good seeing you!
" using Builder
- # render :inline => "xml.p { 'Good seeing you!' }", :type => :builder
- #
- # # Renders "hello david"
- # render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" }
- #
- # === Rendering inline JavaScriptGenerator page updates
- #
- # In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details),
- # you can also pass the :update parameter to +render+, along with a block, to render page updates inline.
- #
- # render :update do |page|
- # page.replace_html 'user_list', :partial => 'user', :collection => @users
- # page.visual_effect :highlight, 'user_list'
- # end
- #
- # === Rendering vanilla JavaScript
- #
- # In addition to using RJS with render :update, you can also just render vanilla JavaScript with :js.
- #
- # # Renders "alert('hello')" and sets the mime type to text/javascript
- # render :js => "alert('hello')"
- #
- # === Rendering with status and location headers
- # All renders take the :status and :location options and turn them into headers. They can even be used together:
- #
- # render :xml => post.to_xml, :status => :created, :location => post_url(post)
- def render(options = nil, extra_options = {}, &block) #:doc:
- raise DoubleRenderError, "Can only render or redirect once per action" if performed?
-
- options = { :layout => true } if options.nil?
- original, options = options, extra_options unless options.is_a?(Hash)
-
- layout_name = options.delete(:layout)
-
- _process_options(options)
-
- if block_given?
- @template.send(:_evaluate_assigns_and_ivars)
-
- generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
- response.content_type = Mime::JS
- return render_for_text(generator.to_s)
- end
-
- if original
- return render_for_name(original, layout_name, options) unless block_given?
- end
-
- if options.key?(:text)
- return render_for_text(@template._render_text(options[:text],
- _pick_layout(layout_name), options))
- end
-
- file, template = options.values_at(:file, :template)
- if file || template
- file = template.sub(/^\//, '') if template
- return render_for_file(file, [layout_name, !!template], options)
- end
-
- if action_option = options[:action]
- return render_for_action(action_option, [layout_name, true], options)
- end
-
- if inline = options[:inline]
- render_for_text(@template._render_inline(inline, _pick_layout(layout_name), options))
-
- elsif xml = options[:xml]
- response.content_type ||= Mime::XML
- render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml)
-
- elsif js = options[:js]
- response.content_type ||= Mime::JS
- render_for_text(js)
-
- elsif json = options[:json]
- json = json.to_json unless json.is_a?(String)
- json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
- response.content_type ||= Mime::JSON
- render_for_text(json)
-
- elsif partial = options[:partial]
- if partial == true
- parts = [action_name_base, formats, controller_name, true]
- elsif partial.is_a?(String)
- parts = partial_parts(partial, options)
- else
- return render_for_text(@template._render_partial(options))
- end
-
- render_for_parts(parts, layout_name, options)
-
- elsif options[:nothing]
- render_for_text(nil)
-
- else
- render_for_parts([action_name, formats, controller_path], layout_name, options)
- end
- end
-
- def formats
- @_request.formats.map {|f| f.symbol }.compact
- end
-
- def action_name_base(name = action_name)
- (name.is_a?(String) ? name.sub(/^#{controller_path}\//, '') : name).to_s
- end
-
- # Renders according to the same rules as render, but returns the result in a string instead
- # of sending it as the response body to the browser.
- def render_to_string(options = nil, &block) #:doc:
- render(options, &block)
- ensure
- response.content_type = nil
- erase_render_results
- reset_variables_added_to_assigns
- end
-
- # Return a response that has no content (merely headers). The options
- # argument is interpreted to be a hash of header names and values.
- # This allows you to easily return a response that consists only of
- # significant headers:
- #
- # head :created, :location => person_path(@person)
- #
- # It can also be used to return exceptional conditions:
- #
- # return head(:method_not_allowed) unless request.post?
- # return head(:bad_request) unless valid_request?
- # render
- def head(*args)
- if args.length > 2
- raise ArgumentError, "too many arguments to head"
- elsif args.empty?
- raise ArgumentError, "too few arguments to head"
- end
- options = args.extract_options!
- status = interpret_status(args.shift || options.delete(:status) || :ok)
-
- options.each do |key, value|
- headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
- end
-
- render :nothing => true, :status => status
- end
-
- # Clears the rendered results, allowing for another render to be performed.
- def erase_render_results #:nodoc:
- response.body = nil
- @performed_render = false
- end
-
- # Clears the redirected results from the headers, resets the status to 200 and returns
- # the URL that was used to redirect or nil if there was no redirected URL
- # Note that +redirect_to+ will change the body of the response to indicate a redirection.
- # The response body is not reset here, see +erase_render_results+
- def erase_redirect_results #:nodoc:
- @performed_redirect = false
- response.redirected_to = nil
- response.redirected_to_method_params = nil
- response.status = DEFAULT_RENDER_STATUS_CODE
- response.headers.delete('Location')
- end
-
- # Erase both render and redirect results
- def erase_results #:nodoc:
- erase_render_results
- erase_redirect_results
- end
-
- def rewrite_options(options) #:nodoc:
- if defaults = default_url_options(options)
- defaults.merge(options)
- else
- options
- end
- 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)
- end
-
- # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
- #
- # * Hash - The URL will be generated by calling url_for with the +options+.
- # * Record - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
- # * String starting with protocol:// (like http://) - Is passed straight through as the target for redirection.
- # * String not containing a protocol - The current protocol and host is prepended to the string.
- # * :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
- # Short-hand for redirect_to(request.env["HTTP_REFERER"])
- #
- # Examples:
- # redirect_to :action => "show", :id => 5
- # redirect_to post
- # redirect_to "http://www.rubyonrails.org"
- # redirect_to "/images/screenshot.jpg"
- # redirect_to articles_url
- # redirect_to :back
- #
- # The redirection happens as a "302 Moved" header unless otherwise specified.
- #
- # Examples:
- # redirect_to post_url(@post), :status=>:found
- # redirect_to :action=>'atom', :status=>:moved_permanently
- # redirect_to post_url(@post), :status=>301
- # redirect_to :action=>'atom', :status=>302
- #
- # When using redirect_to :back, if there is no referrer,
- # RedirectBackError will be raised. You may specify some fallback
- # behavior for this case by rescuing RedirectBackError.
- def redirect_to(options = {}, response_status = {}) #:doc:
- raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
-
- if options.is_a?(Hash) && options[:status]
- status = options.delete(:status)
- elsif response_status[:status]
- status = response_status[:status]
- else
- status = 302
- end
-
- response.redirected_to = options
- logger.info("Redirected to #{options}") if logger && logger.info?
-
- case options
- # The scheme name consist of a letter followed by any combination of
- # letters, digits, and the plus ("+"), period ("."), or hyphen ("-")
- # characters; and is terminated by a colon (":").
- when %r{^\w[\w\d+.-]*:.*}
- redirect_to_full_url(options, status)
- when String
- redirect_to_full_url(request.protocol + request.host_with_port + options, status)
- when :back
- if referer = request.headers["Referer"]
- redirect_to(referer, :status=>status)
- else
- raise RedirectBackError
- end
- else
- redirect_to_full_url(url_for(options), status)
- end
- end
-
- def redirect_to_full_url(url, status)
- raise DoubleRenderError if performed?
- response.redirect(url, interpret_status(status))
- @performed_redirect = true
- end
-
- # Sets the etag and/or last_modified on the response and checks it against
- # the client request. If the request doesn't match the options provided, the
- # request is considered stale and should be generated from scratch. Otherwise,
- # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent.
- #
- # Example:
- #
- # def show
- # @article = Article.find(params[:id])
- #
- # if stale?(:etag => @article, :last_modified => @article.created_at.utc)
- # @statistics = @article.really_expensive_call
- # respond_to do |format|
- # # all the supported formats
- # end
- # end
- # end
- def stale?(options)
- fresh_when(options)
- !request.fresh?(response)
- end
-
- # Sets the etag, last_modified, or both on the response and renders a
- # "304 Not Modified" response if the request is already fresh.
- #
- # Example:
- #
- # def show
- # @article = Article.find(params[:id])
- # fresh_when(:etag => @article, :last_modified => @article.created_at.utc)
- # end
- #
- # This will render the show template if the request isn't sending a matching etag or
- # If-Modified-Since header and just a "304 Not Modified" response if there's a match.
- def fresh_when(options)
- options.assert_valid_keys(:etag, :last_modified)
-
- response.etag = options[:etag] if options[:etag]
- response.last_modified = options[:last_modified] if options[:last_modified]
-
- if request.fresh?(response)
- head :not_modified
- end
- end
-
- # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
- # intermediate caches shouldn't cache the response.
- #
- # Examples:
- # expires_in 20.minutes
- # expires_in 3.hours, :private => false
- # expires in 3.hours, 'max-stale' => 5.hours, :private => nil, :public => true
- #
- # This method will overwrite an existing Cache-Control header.
- # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
- def expires_in(seconds, options = {}) #:doc:
- cache_options = { 'max-age' => seconds, 'private' => true }.symbolize_keys.merge!(options.symbolize_keys)
- cache_options.delete_if { |k,v| v.nil? or v == false }
- cache_control = cache_options.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
- response.headers["Cache-Control"] = cache_control.join(', ')
- end
-
- # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
- # intermediate caches (like caching proxy servers).
- def expires_now #:doc:
- response.headers["Cache-Control"] = "no-cache"
- end
-
- # Resets the session by clearing out all the objects stored within and initializing a new session object.
- def reset_session #:doc:
- request.reset_session
- @_session = request.session
- end
-
- private
- def _process_options(options)
- if content_type = options[:content_type]
- response.content_type = content_type.to_s
- end
-
- if location = options[:location]
- response.headers["Location"] = url_for(location)
- end
-
- response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE)
- end
-
- def render_for_name(name, layout, options)
- case name.to_s.index('/')
- when 0
- render_for_file(name, layout, options)
- when nil
- render_for_action(name, layout, options)
- else
- render_for_file(name.sub(/^\//, ''), [layout, true], options)
- end
- end
-
- def render_for_parts(parts, layout, options = {})
- tmp = view_paths.find_by_parts(*parts)
- layout = _pick_layout(*layout) unless tmp.exempt_from_layout?
-
- render_for_text(
- @template._render_template_with_layout(tmp, layout, options, parts[3]))
- end
-
- def partial_parts(name, options)
- segments = name.split("/")
- parts = segments.pop.split(".")
-
- case parts.size
- when 1
- parts
- when 2, 3
- extension = parts.delete_at(1).to_sym
- if formats.include?(extension)
- self.formats.replace [extension]
- end
- parts.pop if parts.size == 2
- end
-
- path = parts.join(".")
- prefix = segments[0..-1].join("/")
- prefix = prefix.blank? ? controller_path : prefix
- parts = [path, formats, prefix]
- parts.push options[:object] || true
- end
-
- def render_for_file(file, layout, options)
- render_for_parts([file, [request.format.to_sym]], layout, options)
- end
-
- def render_for_action(name, layout, options)
- parts = [action_name_base(name), formats, controller_name]
- render_for_parts(parts, layout, options)
- end
-
- def render_for_text(text = nil, append_response = false) #:nodoc:
- @performed_render = true
-
- if append_response
- response.body ||= ''
- response.body << text.to_s
- else
- response.body = case text
- when Proc then text
- when nil then " " # Safari doesn't pass the headers of the return if the response is zero length
- else text.to_s
- end
- end
- end
-
- def initialize_template_class(response)
- @template = response.template = ActionView::Base.new(self.class.view_paths, {}, self, formats)
- response.template.helpers.send :include, self.class.master_helper_module
- response.redirected_to = nil
- @performed_render = @performed_redirect = false
- end
-
- def assign_shortcuts(request, response)
- @_request, @_params = request, request.parameters
-
- @_response = response
- @_response.session = request.session
-
- @_session = @_response.session
-
- @_headers = @_response.headers
- end
-
- def initialize_current_url
- @url = UrlRewriter.new(request, params.clone)
- end
-
- def log_processing
- if logger && logger.info?
- log_processing_for_request_id
- log_processing_for_parameters
- end
- end
-
- def log_processing_for_request_id
- request_id = "\n\nProcessing #{self.class.name}\##{action_name} "
- request_id << "to #{params[:format]} " if params[:format]
- request_id << "(for #{request_origin}) [#{request.method.to_s.upcase}]"
-
- logger.info(request_id)
- end
-
- def log_processing_for_parameters
- parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
- parameters = parameters.except!(:controller, :action, :format, :_method)
-
- logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
- end
-
- def default_render #:nodoc:
- render
- end
-
- def perform_action
- if called = action_methods.include?(action_name)
- ret = send(action_name)
- elsif called = respond_to?(:method_missing)
- ret = method_missing(action_name)
- end
-
- return (performed? ? ret : default_render) if called
-
- begin
- default_render
- rescue ActionView::MissingTemplate => e
- raise e unless e.path == action_name
- # If the path is the same as the action_name, the action is completely missing
- raise UnknownAction, "No action responded to #{action_name}. Actions: " +
- "#{action_methods.sort.to_sentence}", caller
- end
- end
-
- def performed?
- @performed_render || @performed_redirect
- end
-
- def assign_names
- @action_name = (params['action'] || 'index')
- end
-
- def action_methods
- self.class.action_methods
- end
-
- def self.action_methods
- @action_methods ||=
- # All public instance methods of this class, including ancestors
- public_instance_methods(true).map { |m| m.to_s }.to_set -
- # Except for public instance methods of Base and its ancestors
- Base.public_instance_methods(true).map { |m| m.to_s } +
- # Be sure to include shadowed public instance methods of this class
- public_instance_methods(false).map { |m| m.to_s } -
- # And always exclude explicitly hidden actions
- hidden_actions
- end
-
- def reset_variables_added_to_assigns
- @template.instance_variable_set("@assigns_added", nil)
- end
-
- def request_origin
- # this *needs* to be cached!
- # otherwise you'd get different results if calling it more than once
- @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}"
- end
-
- def complete_request_uri
- "#{request.protocol}#{request.host}#{request.request_uri}"
- end
-
- def close_session
- @_session.close if @_session && @_session.respond_to?(:close)
- end
-
- def default_template(action_name = self.action_name)
- self.view_paths.find_template(default_template_name(action_name), default_template_format)
- end
-
- def default_template_name(action_name = self.action_name)
- if action_name
- action_name = action_name.to_s
- if action_name.include?('/') && template_path_includes_controller?(action_name)
- action_name = strip_out_controller(action_name)
- end
- end
- "#{self.controller_path}/#{action_name}"
- end
-
- def strip_out_controller(path)
- path.split('/', 2).last
- end
-
-
- def template_path_includes_controller?(path)
- self.controller_path.split('/')[-1] == path.split('/')[0]
- end
-
- def process_cleanup
- close_session
- end
- end
-
- Base.class_eval do
- [ Filters, Layout, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
- Cookies, Caching, Verification, Streaming, SessionManagement,
- HttpAuthentication::Basic::ControllerMethods, RecordIdentifier,
- RequestForgeryProtection, Translation
- ].each do |mod|
- include mod
- end
- end
-end
diff --git a/actionpack/lib/action_controller/base/base.rb b/actionpack/lib/action_controller/base/base.rb
new file mode 100644
index 0000000000..84371643d7
--- /dev/null
+++ b/actionpack/lib/action_controller/base/base.rb
@@ -0,0 +1,904 @@
+require 'set'
+
+module ActionController #:nodoc:
+ class ActionControllerError < StandardError #:nodoc:
+ end
+
+ class SessionRestoreError < ActionControllerError #:nodoc:
+ end
+
+ class RenderError < ActionControllerError #:nodoc:
+ end
+
+ class RoutingError < ActionControllerError #:nodoc:
+ attr_reader :failures
+ def initialize(message, failures=[])
+ super(message)
+ @failures = failures
+ end
+ end
+
+ class MethodNotAllowed < ActionControllerError #:nodoc:
+ attr_reader :allowed_methods
+
+ def initialize(*allowed_methods)
+ super("Only #{allowed_methods.to_sentence} requests are allowed.")
+ @allowed_methods = allowed_methods
+ end
+
+ def allowed_methods_header
+ allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
+ end
+
+ def handle_response!(response)
+ response.headers['Allow'] ||= allowed_methods_header
+ end
+ end
+
+ class NotImplemented < MethodNotAllowed #:nodoc:
+ end
+
+ class UnknownController < ActionControllerError #:nodoc:
+ end
+
+ class UnknownAction < ActionControllerError #:nodoc:
+ end
+
+ class MissingFile < ActionControllerError #:nodoc:
+ end
+
+ class RenderError < ActionControllerError #:nodoc:
+ end
+
+ class SessionOverflowError < ActionControllerError #:nodoc:
+ DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
+
+ def initialize(message = nil)
+ super(message || DEFAULT_MESSAGE)
+ end
+ end
+
+ class UnknownHttpMethod < ActionControllerError #:nodoc:
+ end
+
+ # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
+ # on request and then either render a template or redirect to another action. An action is defined as a public method
+ # on the controller, which will automatically be made accessible to the web-server through Rails Routes.
+ #
+ # A sample controller could look like this:
+ #
+ # class GuestBookController < ActionController::Base
+ # def index
+ # @entries = Entry.find(:all)
+ # end
+ #
+ # def sign
+ # Entry.create(params[:entry])
+ # redirect_to :action => "index"
+ # end
+ # end
+ #
+ # Actions, by default, render a template in the app/views directory corresponding to the name of the controller and action
+ # after executing code in the action. For example, the +index+ action of the GuestBookController would render the
+ # template app/views/guestbook/index.erb by default after populating the @entries instance variable.
+ #
+ # Unlike index, the sign action will not render a template. After performing its main purpose (creating a
+ # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
+ # "302 Moved" HTTP response that takes the user to the index action.
+ #
+ # The index and sign represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
+ # Most actions are variations of these themes.
+ #
+ # == Requests
+ #
+ # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters.
+ # This value should hold the name of the action to be performed. Once the action has been identified, the remaining
+ # request parameters, the session (if one is available), and the full request with all the HTTP headers are made available to
+ # the action through instance variables. Then the action is performed.
+ #
+ # The full request object is available with the request accessor and is primarily used to query for HTTP headers. These queries
+ # are made by accessing the environment hash, like this:
+ #
+ # def server_ip
+ # location = request.env["SERVER_ADDR"]
+ # render :text => "This server hosted at #{location}"
+ # end
+ #
+ # == Parameters
+ #
+ # All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
+ # which returns a hash. For example, an action that was performed through /weblog/list?category=All&limit=5 will include
+ # { "category" => "All", "limit" => 5 } in params.
+ #
+ # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
+ #
+ #
+ #
+ #
+ # A request stemming from a form holding these inputs will include { "post" => { "name" => "david", "address" => "hyacintvej" } }.
+ # If the address input had been named "post[address][street]", the params would have included
+ # { "post" => { "address" => { "street" => "hyacintvej" } } }. There's no limit to the depth of the nesting.
+ #
+ # == Sessions
+ #
+ # Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
+ # such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
+ # as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
+ # they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
+ #
+ # You can place objects in the session by using the session method, which accesses a hash:
+ #
+ # session[:person] = Person.authenticate(user_name, password)
+ #
+ # And retrieved again through the same hash:
+ #
+ # Hello #{session[:person]}
+ #
+ # For removing objects from the session, you can either assign a single key to +nil+:
+ #
+ # # removes :person from session
+ # session[:person] = nil
+ #
+ # or you can remove the entire session with +reset_session+.
+ #
+ # Sessions are stored by default in a browser cookie that's cryptographically signed, but unencrypted.
+ # This prevents the user from tampering with the session but also allows him to see its contents.
+ #
+ # Do not put secret information in cookie-based sessions!
+ #
+ # Other options for session storage are:
+ #
+ # * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
+ # unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
+ #
+ # config.action_controller.session_store = :active_record_store
+ #
+ # in your config/environment.rb and run rake db:sessions:create.
+ #
+ # * MemCacheStore - Sessions are stored as entries in your memcached cache.
+ # Set the session store type in config/environment.rb:
+ #
+ # config.action_controller.session_store = :mem_cache_store
+ #
+ # This assumes that memcached has been installed and configured properly.
+ # See the MemCacheStore docs for more information.
+ #
+ # == Responses
+ #
+ # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
+ # object is generated automatically through the use of renders and redirects and requires no user intervention.
+ #
+ # == Renders
+ #
+ # Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
+ # of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured.
+ # The controller passes objects to the view by assigning instance variables:
+ #
+ # def show
+ # @post = Post.find(params[:id])
+ # end
+ #
+ # Which are then automatically available to the view:
+ #
+ # Title: <%= @post.title %>
+ #
+ # You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use
+ # the manual rendering methods:
+ #
+ # def search
+ # @results = Search.find(params[:query])
+ # case @results
+ # when 0 then render :action => "no_results"
+ # when 1 then render :action => "show"
+ # when 2..10 then render :action => "show_many"
+ # end
+ # end
+ #
+ # Read more about writing ERb and Builder templates in link:classes/ActionView/Base.html.
+ #
+ # == Redirects
+ #
+ # Redirects are used to move from one action to another. For example, after a create action, which stores a blog entry to a database,
+ # we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to)
+ # a show action that we'll assume has already been created. The code might look like this:
+ #
+ # def create
+ # @entry = Entry.new(params[:entry])
+ # if @entry.save
+ # # The entry was saved correctly, redirect to show
+ # redirect_to :action => 'show', :id => @entry.id
+ # else
+ # # things didn't go so well, do something else
+ # end
+ # end
+ #
+ # In this case, after saving our new entry to the database, the user is redirected to the show method which is then executed.
+ #
+ # == Calling multiple redirects or renders
+ #
+ # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
+ #
+ # def do_something
+ # redirect_to :action => "elsewhere"
+ # render :action => "overthere" # raises DoubleRenderError
+ # end
+ #
+ # If you need to redirect on the condition of something, then be sure to add "and return" to halt execution.
+ #
+ # def do_something
+ # redirect_to(:action => "elsewhere") and return if monkeys.nil?
+ # render :action => "overthere" # won't be called if monkeys is nil
+ # end
+ #
+ class Base
+
+ include StatusCodes
+
+ cattr_reader :protected_instance_variables
+ # Controller specific instance variables which will not be accessible inside views.
+ @@protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
+ @action_name @before_filter_chain_aborted @action_cache_path @_session @_headers @_params
+ @_flash @_response)
+
+ # 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"
+ @@asset_host = ""
+ cattr_accessor :asset_host
+
+ # All requests are considered local by default, so everyone will be exposed to detailed debugging screens on errors.
+ # When the application is ready to go public, this should be set to false, and the protected method local_request?
+ # should instead be implemented in the controller to determine when debugging screens should be shown.
+ @@consider_all_requests_local = true
+ cattr_accessor :consider_all_requests_local
+
+ # Indicates whether to allow concurrent action processing. Your
+ # controller actions and any other code they call must also behave well
+ # when called from concurrent threads. Turned off by default.
+ @@allow_concurrency = false
+ cattr_accessor :allow_concurrency
+
+ # Modern REST web services often need to submit complex data to the web application.
+ # The @@param_parsers hash lets you register handlers which will process the HTTP body and add parameters to the
+ # params hash. These handlers are invoked for POST and PUT requests.
+ #
+ # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instantiated
+ # in the params. This allows XML requests to mask themselves as regular form submissions, so you can have one
+ # action serve both regular forms and web service requests.
+ #
+ # Example of doing your own parser for a custom content type:
+ #
+ # ActionController::Base.param_parsers[Mime::Type.lookup('application/atom+xml')] = Proc.new do |data|
+ # node = REXML::Document.new(post)
+ # { node.root.name => node.root }
+ # end
+ #
+ # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the
+ # root node for such requests. The new default is to keep the root, such that "David" results
+ # in params[:r][:name] for "David" instead of params[:name]. To get the old behavior, you can
+ # re-register XmlSimple as application/xml handler ike this:
+ #
+ # ActionController::Base.param_parsers[Mime::XML] =
+ # Proc.new { |data| XmlSimple.xml_in(data, 'ForceArray' => false) }
+ #
+ # A YAML parser is also available and can be turned on with:
+ #
+ # ActionController::Base.param_parsers[Mime::YAML] = :yaml
+ @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
+ Mime::URL_ENCODED_FORM => :url_encoded_form,
+ Mime::XML => :xml_simple,
+ Mime::JSON => :json }
+ cattr_accessor :param_parsers
+
+ # Controls the default charset for all renders.
+ @@default_charset = "utf-8"
+ cattr_accessor :default_charset
+
+ # The logger is used for generating information on the action run-time (including benchmarking) if available.
+ # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
+ cattr_accessor :logger
+
+ # Controls the resource action separator
+ @@resource_action_separator = "/"
+ cattr_accessor :resource_action_separator
+
+ # Allow to override path names for default resources' actions
+ @@resources_path_names = { :new => 'new', :edit => 'edit' }
+ cattr_accessor :resources_path_names
+
+ # Sets the token parameter name for RequestForgery. Calling +protect_from_forgery+
+ # sets it to :authenticity_token by default.
+ cattr_accessor :request_forgery_protection_token
+
+ # Controls the IP Spoofing check when determining the remote IP.
+ @@ip_spoofing_check = true
+ cattr_accessor :ip_spoofing_check
+
+ # Indicates whether or not optimise the generated named
+ # route helper methods
+ cattr_accessor :optimise_named_routes
+ self.optimise_named_routes = true
+
+ # Indicates whether the response format should be determined by examining the Accept HTTP header,
+ # or by using the simpler params + ajax rules.
+ #
+ # If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept
+ # header into account. If it is set to false then the request format will be determined solely
+ # by examining params[:format]. If params format is missing, the format will be either HTML or
+ # Javascript depending on whether the request is an AJAX request.
+ cattr_accessor :use_accept_header
+ self.use_accept_header = true
+
+ # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
+ class_inheritable_accessor :allow_forgery_protection
+ self.allow_forgery_protection = true
+
+ # If you are deploying to a subdirectory, you will need to set
+ # config.action_controller.relative_url_root
+ # This defaults to ENV['RAILS_RELATIVE_URL_ROOT']
+ cattr_accessor :relative_url_root
+ self.relative_url_root = ENV['RAILS_RELATIVE_URL_ROOT']
+
+ # Holds the request object that's primarily used to get environment variables through access like
+ # request.env["REQUEST_URI"].
+ attr_internal :request
+
+ # Holds a hash of all the GET, POST, and Url parameters passed to the action. Accessed like params["post_id"]
+ # to get the post_id. No type casts are made, so all values are returned as strings.
+ attr_internal :params
+
+ # Holds the response object that's primarily used to set additional HTTP headers through access like
+ # response.headers["Cache-Control"] = "no-cache". Can also be used to access the final body HTML after a template
+ # has been rendered through response.body -- useful for after_filters that wants to manipulate the output,
+ # such as a OutputCompressionFilter.
+ attr_internal :response
+
+ # Holds a hash of objects in the session. Accessed like session[:person] to get the object tied to the "person"
+ # key. The session will hold any type of object as values, but the key should be a string or symbol.
+ attr_internal :session
+
+ # Holds a hash of header names and values. Accessed like headers["Cache-Control"] to get the value of the Cache-Control
+ # directive. Values should always be specified as strings.
+ attr_internal :headers
+
+ # Returns the name of the action this controller is processing.
+ attr_accessor :action_name
+
+ class << self
+ def call(env)
+ # HACK: For global rescue to have access to the original request and response
+ request = env["action_controller.rescue.request"] ||= Request.new(env)
+ response = env["action_controller.rescue.response"] ||= Response.new
+ process(request, response)
+ end
+
+ # Factory for the standard create, process loop where the controller is discarded after processing.
+ def process(request, response) #:nodoc:
+ new.process(request, response)
+ end
+
+ # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
+ def controller_class_name
+ @controller_class_name ||= name.demodulize
+ end
+
+ # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat".
+ def controller_name
+ @controller_name ||= controller_class_name.sub(/Controller$/, '').underscore
+ end
+
+ # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
+ def controller_path
+ @controller_path ||= name.gsub(/Controller$/, '').underscore
+ end
+
+ # Return an array containing the names of public methods that have been marked hidden from the action processor.
+ # By default, all methods defined in ActionController::Base and included modules are hidden.
+ # More methods can be hidden using hide_actions.
+ def hidden_actions
+ read_inheritable_attribute(:hidden_actions) || write_inheritable_attribute(:hidden_actions, [])
+ end
+
+ # Hide each of the given methods from being callable as actions.
+ def hide_action(*names)
+ write_inheritable_attribute(:hidden_actions, hidden_actions | names.map { |name| name.to_s })
+ end
+
+ # View load paths determine the bases from which template references can be made. So a call to
+ # render("test/template") will be looked up in the view load paths array and the closest match will be
+ # returned.
+ def view_paths
+ if defined? @view_paths
+ @view_paths
+ else
+ superclass.view_paths
+ end
+ end
+
+ def view_paths=(value)
+ @view_paths = ActionView::Base.process_view_paths(value) if value
+ end
+
+ # Adds a view_path to the front of the view_paths array.
+ # If the current class has no view paths, copy them from
+ # the superclass. This change will be visible for all future requests.
+ #
+ # ArticleController.prepend_view_path("views/default")
+ # ArticleController.prepend_view_path(["views/default", "views/custom"])
+ #
+ def prepend_view_path(path)
+ @view_paths = superclass.view_paths.dup if !defined?(@view_paths) || @view_paths.nil?
+ @view_paths.unshift(*path)
+ end
+
+ # Adds a view_path to the end of the view_paths array.
+ # If the current class has no view paths, copy them from
+ # the superclass. This change will be visible for all future requests.
+ #
+ # ArticleController.append_view_path("views/default")
+ # ArticleController.append_view_path(["views/default", "views/custom"])
+ #
+ def append_view_path(path)
+ @view_paths = superclass.view_paths.dup if @view_paths.nil?
+ @view_paths.push(*path)
+ end
+
+ # Replace sensitive parameter data from the request log.
+ # Filters parameters that have any of the arguments as a substring.
+ # Looks in all subhashes of the param hash for keys to filter.
+ # If a block is given, each key and value of the parameter hash and all
+ # subhashes is passed to it, the value or key
+ # can be replaced using String#replace or similar method.
+ #
+ # Examples:
+ # filter_parameter_logging
+ # => Does nothing, just slows the logging process down
+ #
+ # filter_parameter_logging :password
+ # => replaces the value to all keys matching /password/i with "[FILTERED]"
+ #
+ # filter_parameter_logging :foo, "bar"
+ # => replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
+ #
+ # filter_parameter_logging { |k,v| v.reverse! if k =~ /secret/i }
+ # => reverses the value to all keys matching /secret/i
+ #
+ # filter_parameter_logging(:foo, "bar") { |k,v| v.reverse! if k =~ /secret/i }
+ # => reverses the value to all keys matching /secret/i, and
+ # replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
+ def filter_parameter_logging(*filter_words, &block)
+ parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
+
+ define_method(:filter_parameters) do |unfiltered_parameters|
+ filtered_parameters = {}
+
+ unfiltered_parameters.each do |key, value|
+ if key =~ parameter_filter
+ filtered_parameters[key] = '[FILTERED]'
+ elsif value.is_a?(Hash)
+ filtered_parameters[key] = filter_parameters(value)
+ elsif block_given?
+ key = key.dup
+ value = value.dup if value
+ yield key, value
+ filtered_parameters[key] = value
+ else
+ filtered_parameters[key] = value
+ end
+ end
+
+ filtered_parameters
+ end
+ protected :filter_parameters
+ end
+
+ delegate :exempt_from_layout, :to => 'ActionView::Template'
+ end
+
+ public
+ # Extracts the action_name from the request parameters and performs that action.
+ def process(request, response, method = :perform_action, *arguments) #:nodoc:
+ response.request = request
+
+ assign_shortcuts(request, response)
+ initialize_template_class(response)
+ initialize_current_url
+ assign_names
+
+ log_processing
+ send(method, *arguments)
+
+ send_response
+ ensure
+ process_cleanup
+ end
+
+ def send_response
+ response.prepare!
+ response
+ end
+
+ # Returns a URL that has been rewritten according to the options hash and the defined routes.
+ # (For doing a complete redirect, use +redirect_to+).
+ #
+ # url_for is used to:
+ #
+ # All keys given to +url_for+ are forwarded to the Route module, save for the following:
+ # * :anchor - Specifies the anchor name to be appended to the path. For example,
+ # url_for :controller => 'posts', :action => 'show', :id => 10, :anchor => 'comments'
+ # will produce "/posts/show/10#comments".
+ # * :only_path - If true, returns the relative URL (omitting the protocol, host name, and port) (false by default).
+ # * :trailing_slash - If true, adds a trailing slash, as in "/archive/2005/". Note that this
+ # is currently not recommended since it breaks caching.
+ # * :host - Overrides the default (current) host if provided.
+ # * :protocol - Overrides the default (current) protocol if provided.
+ # * :port - Optionally specify the port to connect to.
+ # * :user - Inline HTTP authentication (only plucked out if :password is also present).
+ # * :password - Inline HTTP authentication (only plucked out if :user is also present).
+ # * :skip_relative_url_root - If true, the url is not constructed using the +relative_url_root+
+ # of the request so the path will include the web server relative installation directory.
+ #
+ # The URL is generated from the remaining keys in the hash. A URL contains two key parts: the and a query string.
+ # Routes composes a query string as the key/value pairs not included in the .
+ #
+ # The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with
+ # action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs:
+ #
+ # url_for :controller => 'posts', :action => 'recent' # => 'proto://host.com/posts/recent'
+ # url_for :controller => 'posts', :action => 'index' # => 'proto://host.com/posts'
+ # url_for :controller => 'posts', :action => 'index', :port=>'8033' # => 'proto://host.com:8033/posts'
+ # url_for :controller => 'posts', :action => 'show', :id => 10 # => 'proto://host.com/posts/show/10'
+ # url_for :controller => 'posts', :user => 'd', :password => '123' # => 'proto://d:123@host.com/posts'
+ #
+ # When generating a new URL, missing values may be filled in from the current request's parameters. For example,
+ # url_for :action => 'some_action' will retain the current controller, as expected. This behavior extends to
+ # other parameters, including :controller, :id, and any other parameters that are placed into a Route's
+ # path.
+ # Â
+ # The URL helpers such as url_for have a limited form of memory: when generating a new URL, they can look for
+ # missing values in the current request's parameters. Routes attempts to guess when a value should and should not be
+ # taken from the defaults. There are a few simple rules on how this is performed:
+ #
+ # * If the controller name begins with a slash no defaults are used:
+ #
+ # url_for :controller => '/home'
+ #
+ # In particular, a leading slash ensures no namespace is assumed. Thus,
+ # while url_for :controller => 'users' may resolve to
+ # Admin::UsersController if the current controller lives under
+ # that module, url_for :controller => '/users' ensures you link
+ # to ::UsersController no matter what.
+ # * If the controller changes, the action will default to index unless provided
+ #
+ # The final rule is applied while the URL is being generated and is best illustrated by an example. Let us consider the
+ # route given by map.connect 'people/:last/:first/:action', :action => 'bio', :controller => 'people'.
+ #
+ # Suppose that the current URL is "people/hh/david/contacts". Let's consider a few different cases of URLs which are generated
+ # from this page.
+ #
+ # * url_for :action => 'bio' -- During the generation of this URL, default values will be used for the first and
+ # last components, and the action shall change. The generated URL will be, "people/hh/david/bio".
+ # * url_for :first => 'davids-little-brother' This generates the URL 'people/hh/davids-little-brother' -- note
+ # that this URL leaves out the assumed action of 'bio'.
+ #
+ # However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The
+ # answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the
+ # value that appears in the slot for :first is not equal to default value for :first we stop using
+ # defaults. On its own, this rule can account for much of the typical Rails URL behavior.
+ # Â
+ # Although a convenience, defaults can occasionally get in your way. In some cases a default persists longer than desired.
+ # The default may be cleared by adding :name => nil to url_for's options.
+ # This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the
+ # helper is used from. The following line will redirect to PostController's default action, regardless of the page it is
+ # displayed on:
+ #
+ # url_for :controller => 'posts', :action => nil
+ #
+ # If you explicitly want to create a URL that's almost the same as the current URL, you can do so using the
+ # :overwrite_params options. Say for your posts you have different views for showing and printing them.
+ # Then, in the show view, you get the URL for the print view like this
+ #
+ # url_for :overwrite_params => { :action => 'print' }
+ #
+ # This takes the current URL as is and only exchanges the action. In contrast, url_for :action => 'print'
+ # would have slashed-off the path components after the changed action.
+ def url_for(options = {})
+ options ||= {}
+ case options
+ when String
+ options
+ when Hash
+ @url.rewrite(rewrite_options(options))
+ else
+ polymorphic_url(options)
+ end
+ end
+
+ # Converts the class name from something like "OneModule::TwoModule::NeatController" to "NeatController".
+ def controller_class_name
+ self.class.controller_class_name
+ end
+
+ # Converts the class name from something like "OneModule::TwoModule::NeatController" to "neat".
+ def controller_name
+ self.class.controller_name
+ end
+
+ # Converts the class name from something like "OneModule::TwoModule::NeatController" to "one_module/two_module/neat".
+ def controller_path
+ self.class.controller_path
+ end
+
+ def session_enabled?
+ request.session_options && request.session_options[:disabled] != false
+ end
+
+ self.view_paths = []
+
+ # View load paths for controller.
+ def view_paths
+ @template.view_paths
+ end
+
+ def view_paths=(value)
+ @template.view_paths = ActionView::Base.process_view_paths(value)
+ end
+
+ # Adds a view_path to the front of the view_paths array.
+ # This change affects the current request only.
+ #
+ # self.prepend_view_path("views/default")
+ # self.prepend_view_path(["views/default", "views/custom"])
+ #
+ def prepend_view_path(path)
+ @template.view_paths.unshift(*path)
+ end
+
+ # Adds a view_path to the end of the view_paths array.
+ # This change affects the current request only.
+ #
+ # self.append_view_path("views/default")
+ # self.append_view_path(["views/default", "views/custom"])
+ #
+ def append_view_path(path)
+ @template.view_paths.push(*path)
+ end
+
+ def rewrite_options(options) #:nodoc:
+ if defaults = default_url_options(options)
+ defaults.merge(options)
+ else
+ options
+ end
+ 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)
+ end
+
+ # Sets the etag and/or last_modified on the response and checks it against
+ # the client request. If the request doesn't match the options provided, the
+ # request is considered stale and should be generated from scratch. Otherwise,
+ # it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent.
+ #
+ # Example:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ #
+ # if stale?(:etag => @article, :last_modified => @article.created_at.utc)
+ # @statistics = @article.really_expensive_call
+ # respond_to do |format|
+ # # all the supported formats
+ # end
+ # end
+ # end
+ def stale?(options)
+ fresh_when(options)
+ !request.fresh?(response)
+ end
+
+ # Sets the etag, last_modified, or both on the response and renders a
+ # "304 Not Modified" response if the request is already fresh.
+ #
+ # Example:
+ #
+ # def show
+ # @article = Article.find(params[:id])
+ # fresh_when(:etag => @article, :last_modified => @article.created_at.utc)
+ # end
+ #
+ # This will render the show template if the request isn't sending a matching etag or
+ # If-Modified-Since header and just a "304 Not Modified" response if there's a match.
+ def fresh_when(options)
+ options.assert_valid_keys(:etag, :last_modified)
+
+ response.etag = options[:etag] if options[:etag]
+ response.last_modified = options[:last_modified] if options[:last_modified]
+
+ if request.fresh?(response)
+ head :not_modified
+ end
+ end
+
+ # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
+ # intermediate caches shouldn't cache the response.
+ #
+ # Examples:
+ # expires_in 20.minutes
+ # expires_in 3.hours, :private => false
+ # expires in 3.hours, 'max-stale' => 5.hours, :private => nil, :public => true
+ #
+ # This method will overwrite an existing Cache-Control header.
+ # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
+ def expires_in(seconds, options = {}) #:doc:
+ cache_options = { 'max-age' => seconds, 'private' => true }.symbolize_keys.merge!(options.symbolize_keys)
+ cache_options.delete_if { |k,v| v.nil? or v == false }
+ cache_control = cache_options.map{ |k,v| v == true ? k.to_s : "#{k.to_s}=#{v.to_s}"}
+ response.headers["Cache-Control"] = cache_control.join(', ')
+ end
+
+ # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
+ # intermediate caches (like caching proxy servers).
+ def expires_now #:doc:
+ response.headers["Cache-Control"] = "no-cache"
+ end
+
+ # Resets the session by clearing out all the objects stored within and initializing a new session object.
+ def reset_session #:doc:
+ request.reset_session
+ @_session = request.session
+ end
+
+ private
+ def _process_options(options)
+ if content_type = options[:content_type]
+ response.content_type = content_type.to_s
+ end
+
+ if location = options[:location]
+ response.headers["Location"] = url_for(location)
+ end
+
+ response.status = interpret_status(options[:status] || DEFAULT_RENDER_STATUS_CODE)
+ end
+
+ def initialize_template_class(response)
+ @template = response.template = ActionView::Base.new(self.class.view_paths, {}, self, formats)
+ response.template.helpers.send :include, self.class.master_helper_module
+ response.redirected_to = nil
+ @performed_render = @performed_redirect = false
+ end
+
+ def assign_shortcuts(request, response)
+ @_request, @_params = request, request.parameters
+
+ @_response = response
+ @_response.session = request.session
+
+ @_session = @_response.session
+
+ @_headers = @_response.headers
+ end
+
+ def initialize_current_url
+ @url = UrlRewriter.new(request, params.clone)
+ end
+
+ def log_processing
+ if logger && logger.info?
+ log_processing_for_request_id
+ log_processing_for_parameters
+ end
+ end
+
+ def log_processing_for_request_id
+ request_id = "\n\nProcessing #{self.class.name}\##{action_name} "
+ request_id << "to #{params[:format]} " if params[:format]
+ request_id << "(for #{request_origin}) [#{request.method.to_s.upcase}]"
+
+ logger.info(request_id)
+ end
+
+ def log_processing_for_parameters
+ parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
+ parameters = parameters.except!(:controller, :action, :format, :_method)
+
+ logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
+ end
+
+ def default_render #:nodoc:
+ render
+ end
+
+ def perform_action
+ if called = action_methods.include?(action_name)
+ ret = send(action_name)
+ elsif called = respond_to?(:method_missing)
+ ret = method_missing(action_name)
+ end
+
+ return (performed? ? ret : default_render) if called
+
+ begin
+ default_render
+ rescue ActionView::MissingTemplate => e
+ raise e unless e.path == action_name
+ # If the path is the same as the action_name, the action is completely missing
+ raise UnknownAction, "No action responded to #{action_name}. Actions: " +
+ "#{action_methods.sort.to_sentence}", caller
+ end
+ end
+
+ def performed?
+ @performed_render || @performed_redirect
+ end
+
+ def assign_names
+ @action_name = (params['action'] || 'index')
+ end
+
+ def reset_variables_added_to_assigns
+ @template.instance_variable_set("@assigns_added", nil)
+ end
+
+ def request_origin
+ # this *needs* to be cached!
+ # otherwise you'd get different results if calling it more than once
+ @request_origin ||= "#{request.remote_ip} at #{Time.now.to_s(:db)}"
+ end
+
+ def complete_request_uri
+ "#{request.protocol}#{request.host}#{request.request_uri}"
+ end
+
+ def close_session
+ @_session.close if @_session && @_session.respond_to?(:close)
+ end
+
+ def default_template(action_name = self.action_name)
+ self.view_paths.find_template(default_template_name(action_name), default_template_format)
+ end
+
+ def default_template_name(action_name = self.action_name)
+ if action_name
+ action_name = action_name.to_s
+ if action_name.include?('/') && template_path_includes_controller?(action_name)
+ action_name = strip_out_controller(action_name)
+ end
+ end
+ "#{controller_path}/#{action_name}"
+ end
+
+ def strip_out_controller(path)
+ path.split('/', 2).last
+ end
+
+ def template_path_includes_controller?(path)
+ self.controller_path.split('/')[-1] == path.split('/')[0]
+ end
+
+ def process_cleanup
+ close_session
+ end
+ end
+
+ Base.class_eval do
+ [ Filters, Layout, Renderer, Redirector, Responder, Benchmarking, Rescue, Flash, MimeResponds, Helpers,
+ Cookies, Caching, Verification, Streaming, SessionManagement,
+ HttpAuthentication::Basic::ControllerMethods, RecordIdentifier,
+ RequestForgeryProtection, Translation
+ ].each do |mod|
+ include mod
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/base/chained/benchmarking.rb b/actionpack/lib/action_controller/base/chained/benchmarking.rb
new file mode 100644
index 0000000000..066150f58a
--- /dev/null
+++ b/actionpack/lib/action_controller/base/chained/benchmarking.rb
@@ -0,0 +1,107 @@
+require 'benchmark'
+
+module ActionController #:nodoc:
+ # The benchmarking module times the performance of actions and reports to the logger. If the Active Record
+ # package has been included, a separate timing section for database calls will be added as well.
+ module Benchmarking #:nodoc:
+ def self.included(base)
+ base.extend(ClassMethods)
+
+ base.class_eval do
+ alias_method_chain :perform_action, :benchmark
+ alias_method_chain :render, :benchmark
+ end
+ end
+
+ module ClassMethods
+ # Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it
+ # (unless use_silence is set to false).
+ #
+ # The benchmark is only recorded if the current level of the logger matches the log_level, which makes it
+ # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
+ # will only be conducted if the log level is low enough.
+ def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
+ if logger && logger.level == log_level
+ result = nil
+ ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
+ logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)")
+ result
+ else
+ yield
+ end
+ end
+
+ # Silences the logger for the duration of the block.
+ def silence
+ old_logger_level, logger.level = logger.level, Logger::ERROR if logger
+ yield
+ ensure
+ logger.level = old_logger_level if logger
+ end
+ end
+
+ protected
+ def render_with_benchmark(options = nil, extra_options = {}, &block)
+ if logger
+ if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
+ db_runtime = ActiveRecord::Base.connection.reset_runtime
+ end
+
+ render_output = nil
+ @view_runtime = Benchmark.ms { render_output = render_without_benchmark(options, extra_options, &block) }
+
+ if Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
+ @db_rt_before_render = db_runtime
+ @db_rt_after_render = ActiveRecord::Base.connection.reset_runtime
+ @view_runtime -= @db_rt_after_render
+ end
+
+ render_output
+ else
+ render_without_benchmark(options, extra_options, &block)
+ end
+ end
+
+ private
+ def perform_action_with_benchmark
+ if logger && logger.info?
+ ms = [Benchmark.ms { perform_action_without_benchmark }, 0.01].max
+ logging_view = defined?(@view_runtime)
+ logging_active_record = Object.const_defined?("ActiveRecord") && ActiveRecord::Base.connected?
+
+ log_message = 'Completed in %.0fms' % ms
+
+ if logging_view || logging_active_record
+ log_message << " ("
+ log_message << view_runtime if logging_view
+
+ if logging_active_record
+ log_message << ", " if logging_view
+ log_message << active_record_runtime + ")"
+ else
+ ")"
+ end
+ end
+
+ log_message << " | #{response.status}"
+ log_message << " [#{complete_request_uri rescue "unknown"}]"
+
+ logger.info(log_message)
+ response.headers["X-Runtime"] = "%.0f" % ms
+ else
+ perform_action_without_benchmark
+ end
+ end
+
+ def view_runtime
+ "View: %.0f" % @view_runtime
+ end
+
+ def active_record_runtime
+ db_runtime = ActiveRecord::Base.connection.reset_runtime
+ db_runtime += @db_rt_before_render if @db_rt_before_render
+ db_runtime += @db_rt_after_render if @db_rt_after_render
+ "DB: %.0f" % db_runtime
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/base/chained/filters.rb b/actionpack/lib/action_controller/base/chained/filters.rb
new file mode 100644
index 0000000000..9022b8b279
--- /dev/null
+++ b/actionpack/lib/action_controller/base/chained/filters.rb
@@ -0,0 +1,680 @@
+module ActionController #:nodoc:
+ module Filters #:nodoc:
+ def self.included(base)
+ base.class_eval do
+ extend ClassMethods
+ include ActionController::Filters::InstanceMethods
+ end
+ end
+
+ class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc:
+ def append_filter_to_chain(filters, filter_type, &block)
+ pos = find_filter_append_position(filters, filter_type)
+ update_filter_chain(filters, filter_type, pos, &block)
+ end
+
+ def prepend_filter_to_chain(filters, filter_type, &block)
+ pos = find_filter_prepend_position(filters, filter_type)
+ update_filter_chain(filters, filter_type, pos, &block)
+ end
+
+ def create_filters(filters, filter_type, &block)
+ filters, conditions = extract_options(filters, &block)
+ filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) }
+ filters
+ end
+
+ def skip_filter_in_chain(*filters, &test)
+ filters, conditions = extract_options(filters)
+ filters.each do |filter|
+ if callback = find(filter) then delete(callback) end
+ end if conditions.empty?
+ update_filter_in_chain(filters, :skip => conditions, &test)
+ end
+
+ private
+ def update_filter_chain(filters, filter_type, pos, &block)
+ new_filters = create_filters(filters, filter_type, &block)
+ insert(pos, new_filters).flatten!
+ end
+
+ def find_filter_append_position(filters, filter_type)
+ # appending an after filter puts it at the end of the call chain
+ # before and around filters go before the first after filter in the chain
+ unless filter_type == :after
+ each_with_index do |f,i|
+ return i if f.after?
+ end
+ end
+ return -1
+ end
+
+ def find_filter_prepend_position(filters, filter_type)
+ # prepending a before or around filter puts it at the front of the call chain
+ # after filters go before the first after filter in the chain
+ if filter_type == :after
+ each_with_index do |f,i|
+ return i if f.after?
+ end
+ return -1
+ end
+ return 0
+ end
+
+ def find_or_create_filter(filter, filter_type, options = {})
+ update_filter_in_chain([filter], options)
+
+ if found_filter = find(filter) { |f| f.type == filter_type }
+ found_filter
+ else
+ filter_kind = case
+ when filter.respond_to?(:before) && filter_type == :before
+ :before
+ when filter.respond_to?(:after) && filter_type == :after
+ :after
+ else
+ :filter
+ end
+
+ case filter_type
+ when :before
+ BeforeFilter.new(filter_kind, filter, options)
+ when :after
+ AfterFilter.new(filter_kind, filter, options)
+ else
+ AroundFilter.new(filter_kind, filter, options)
+ end
+ end
+ end
+
+ def update_filter_in_chain(filters, options, &test)
+ filters.map! { |f| block_given? ? find(f, &test) : find(f) }
+ filters.compact!
+
+ map! do |filter|
+ if filters.include?(filter)
+ new_filter = filter.dup
+ new_filter.update_options!(options)
+ new_filter
+ else
+ filter
+ end
+ end
+ end
+ end
+
+ class Filter < ActiveSupport::Callbacks::Callback #:nodoc:
+ def initialize(kind, method, options = {})
+ super
+ update_options! options
+ end
+
+ # override these to return true in appropriate subclass
+ def before?
+ false
+ end
+
+ def after?
+ false
+ end
+
+ def around?
+ false
+ end
+
+ # Make sets of strings from :only/:except options
+ def update_options!(other)
+ if other
+ convert_only_and_except_options_to_sets_of_strings(other)
+ if other[:skip]
+ convert_only_and_except_options_to_sets_of_strings(other[:skip])
+ end
+ end
+
+ options.update(other)
+ end
+
+ private
+ def should_not_skip?(controller)
+ if options[:skip]
+ !included_in_action?(controller, options[:skip])
+ else
+ true
+ end
+ end
+
+ def included_in_action?(controller, options)
+ if options[:only]
+ options[:only].include?(controller.action_name)
+ elsif options[:except]
+ !options[:except].include?(controller.action_name)
+ else
+ true
+ end
+ end
+
+ def should_run_callback?(controller)
+ should_not_skip?(controller) && included_in_action?(controller, options) && super
+ end
+
+ def convert_only_and_except_options_to_sets_of_strings(opts)
+ [:only, :except].each do |key|
+ if values = opts[key]
+ opts[key] = Array(values).map(&:to_s).to_set
+ end
+ end
+ end
+ end
+
+ class AroundFilter < Filter #:nodoc:
+ def type
+ :around
+ end
+
+ def around?
+ true
+ end
+
+ def call(controller, &block)
+ if should_run_callback?(controller)
+ method = filter_responds_to_before_and_after? ? around_proc : self.method
+
+ # For around_filter do |controller, action|
+ if method.is_a?(Proc) && method.arity == 2
+ evaluate_method(method, controller, block)
+ else
+ evaluate_method(method, controller, &block)
+ end
+ else
+ block.call
+ end
+ end
+
+ private
+ def filter_responds_to_before_and_after?
+ method.respond_to?(:before) && method.respond_to?(:after)
+ end
+
+ def around_proc
+ Proc.new do |controller, action|
+ method.before(controller)
+
+ if controller.__send__(:performed?)
+ controller.__send__(:halt_filter_chain, method, :rendered_or_redirected)
+ else
+ begin
+ action.call
+ ensure
+ method.after(controller)
+ end
+ end
+ end
+ end
+ end
+
+ class BeforeFilter < Filter #:nodoc:
+ def type
+ :before
+ end
+
+ def before?
+ true
+ end
+
+ def call(controller, &block)
+ super
+ if controller.__send__(:performed?)
+ controller.__send__(:halt_filter_chain, method, :rendered_or_redirected)
+ end
+ end
+ end
+
+ class AfterFilter < Filter #:nodoc:
+ def type
+ :after
+ end
+
+ def after?
+ true
+ end
+ end
+
+ # Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do
+ # authentication, caching, or auditing before the intended action is performed. Or to do localization or output
+ # compression after the action has been performed. Filters have access to the request, response, and all the instance
+ # variables set by other filters in the chain or by the action (in the case of after filters).
+ #
+ # == Filter inheritance
+ #
+ # Controller inheritance hierarchies share filters downwards, but subclasses can also add or skip filters without
+ # affecting the superclass. For example:
+ #
+ # class BankController < ActionController::Base
+ # before_filter :audit
+ #
+ # private
+ # def audit
+ # # record the action and parameters in an audit log
+ # end
+ # end
+ #
+ # class VaultController < BankController
+ # before_filter :verify_credentials
+ #
+ # private
+ # def verify_credentials
+ # # make sure the user is allowed into the vault
+ # end
+ # end
+ #
+ # Now any actions performed on the BankController will have the audit method called before. On the VaultController,
+ # first the audit method is called, then the verify_credentials method. If the audit method renders or redirects, then
+ # verify_credentials and the intended action are never called.
+ #
+ # == Filter types
+ #
+ # A filter can take one of three forms: method reference (symbol), external class, or inline method (proc). The first
+ # is the most common and works by referencing a protected or private method somewhere in the inheritance hierarchy of
+ # the controller by use of a symbol. In the bank example above, both BankController and VaultController use this form.
+ #
+ # Using an external class makes for more easily reused generic filters, such as output compression. External filter classes
+ # are implemented by having a static +filter+ method on any class and then passing this class to the filter method. Example:
+ #
+ # class OutputCompressionFilter
+ # def self.filter(controller)
+ # controller.response.body = compress(controller.response.body)
+ # end
+ # end
+ #
+ # class NewspaperController < ActionController::Base
+ # after_filter OutputCompressionFilter
+ # end
+ #
+ # The filter method is passed the controller instance and is hence granted access to all aspects of the controller and can
+ # manipulate them as it sees fit.
+ #
+ # The inline method (using a proc) can be used to quickly do something small that doesn't require a lot of explanation.
+ # Or just as a quick test. It works like this:
+ #
+ # class WeblogController < ActionController::Base
+ # before_filter { |controller| head(400) if controller.params["stop_action"] }
+ # end
+ #
+ # As you can see, the block expects to be passed the controller after it has assigned the request to the internal variables.
+ # This means that the block has access to both the request and response objects complete with convenience methods for params,
+ # session, template, and assigns. Note: The inline method doesn't strictly have to be a block; any object that responds to call
+ # and returns 1 or -1 on arity will do (such as a Proc or an Method object).
+ #
+ # Please note that around_filters function a little differently than the normal before and after filters with regard to filter
+ # types. Please see the section dedicated to around_filters below.
+ #
+ # == Filter chain ordering
+ #
+ # Using before_filter and after_filter appends the specified filters to the existing chain. That's usually
+ # just fine, but some times you care more about the order in which the filters are executed. When that's the case, you
+ # can use prepend_before_filter and prepend_after_filter. Filters added by these methods will be put at the
+ # beginning of their respective chain and executed before the rest. For example:
+ #
+ # class ShoppingController < ActionController::Base
+ # before_filter :verify_open_shop
+ #
+ # class CheckoutController < ShoppingController
+ # prepend_before_filter :ensure_items_in_cart, :ensure_items_in_stock
+ #
+ # The filter chain for the CheckoutController is now :ensure_items_in_cart, :ensure_items_in_stock,
+ # :verify_open_shop. So if either of the ensure filters renders or redirects, we'll never get around to see if the shop
+ # is open or not.
+ #
+ # You may pass multiple filter arguments of each type as well as a filter block.
+ # If a block is given, it is treated as the last argument.
+ #
+ # == Around filters
+ #
+ # Around filters wrap an action, executing code both before and after.
+ # They may be declared as method references, blocks, or objects responding
+ # to +filter+ or to both +before+ and +after+.
+ #
+ # To use a method as an +around_filter+, pass a symbol naming the Ruby method.
+ # Yield (or block.call) within the method to run the action.
+ #
+ # around_filter :catch_exceptions
+ #
+ # private
+ # def catch_exceptions
+ # yield
+ # rescue => exception
+ # logger.debug "Caught exception! #{exception}"
+ # raise
+ # end
+ #
+ # To use a block as an +around_filter+, pass a block taking as args both
+ # the controller and the action block. You can't call yield directly from
+ # an +around_filter+ block; explicitly call the action block instead:
+ #
+ # around_filter do |controller, action|
+ # logger.debug "before #{controller.action_name}"
+ # action.call
+ # logger.debug "after #{controller.action_name}"
+ # end
+ #
+ # To use a filter object with +around_filter+, pass an object responding
+ # to :filter or both :before and :after. With a
+ # filter method, yield to the block as above:
+ #
+ # around_filter BenchmarkingFilter
+ #
+ # class BenchmarkingFilter
+ # def self.filter(controller, &block)
+ # Benchmark.measure(&block)
+ # end
+ # end
+ #
+ # With +before+ and +after+ methods:
+ #
+ # around_filter Authorizer.new
+ #
+ # class Authorizer
+ # # This will run before the action. Redirecting aborts the action.
+ # def before(controller)
+ # unless user.authorized?
+ # redirect_to(login_url)
+ # end
+ # end
+ #
+ # # This will run after the action if and only if before did not render or redirect.
+ # def after(controller)
+ # end
+ # end
+ #
+ # If the filter has +before+ and +after+ methods, the +before+ method will be
+ # called before the action. If +before+ renders or redirects, the filter chain is
+ # halted and +after+ will not be run. See Filter Chain Halting below for
+ # an example.
+ #
+ # == Filter chain skipping
+ #
+ # Declaring a filter on a base class conveniently applies to its subclasses,
+ # but sometimes a subclass should skip some of its superclass' filters:
+ #
+ # class ApplicationController < ActionController::Base
+ # before_filter :authenticate
+ # around_filter :catch_exceptions
+ # end
+ #
+ # class WeblogController < ApplicationController
+ # # Will run the :authenticate and :catch_exceptions filters.
+ # end
+ #
+ # class SignupController < ApplicationController
+ # # Skip :authenticate, run :catch_exceptions.
+ # skip_before_filter :authenticate
+ # end
+ #
+ # class ProjectsController < ApplicationController
+ # # Skip :catch_exceptions, run :authenticate.
+ # skip_filter :catch_exceptions
+ # end
+ #
+ # class ClientsController < ApplicationController
+ # # Skip :catch_exceptions and :authenticate unless action is index.
+ # skip_filter :catch_exceptions, :authenticate, :except => :index
+ # end
+ #
+ # == Filter conditions
+ #
+ # Filters may be limited to specific actions by declaring the actions to
+ # include or exclude. Both options accept single actions
+ # (:only => :index) or arrays of actions
+ # (:except => [:foo, :bar]).
+ #
+ # class Journal < ActionController::Base
+ # # Require authentication for edit and delete.
+ # before_filter :authorize, :only => [:edit, :delete]
+ #
+ # # Passing options to a filter with a block.
+ # around_filter(:except => :index) do |controller, action_block|
+ # results = Profiler.run(&action_block)
+ # controller.response.sub! "