aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r--actionpack/lib/action_controller/base.rb172
-rw-r--r--actionpack/lib/action_controller/caching.rb4
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb94
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb60
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb29
-rw-r--r--actionpack/lib/action_controller/deprecated.rb2
-rw-r--r--actionpack/lib/action_controller/deprecated/base.rb133
-rw-r--r--actionpack/lib/action_controller/deprecated/dispatcher.rb28
-rw-r--r--actionpack/lib/action_controller/log_subscriber.rb8
-rw-r--r--actionpack/lib/action_controller/metal.rb101
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb9
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb18
-rw-r--r--actionpack/lib/action_controller/metal/head.rb8
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb49
-rw-r--r--actionpack/lib/action_controller/metal/hide_actions.rb5
-rw-r--r--actionpack/lib/action_controller/metal/http_authentication.rb69
-rw-r--r--actionpack/lib/action_controller/metal/implicit_render.rb4
-rw-r--r--actionpack/lib/action_controller/metal/instrumentation.rb2
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb22
-rw-r--r--actionpack/lib/action_controller/metal/redirecting.rb10
-rw-r--r--actionpack/lib/action_controller/metal/renderers.rb75
-rw-r--r--actionpack/lib/action_controller/metal/rendering.rb50
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb85
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb46
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb12
-rw-r--r--actionpack/lib/action_controller/metal/testing.rb9
-rw-r--r--actionpack/lib/action_controller/metal/url_for.rb26
-rw-r--r--actionpack/lib/action_controller/middleware.rb2
-rw-r--r--actionpack/lib/action_controller/railtie.rb55
-rw-r--r--actionpack/lib/action_controller/railties/paths.rb32
-rw-r--r--actionpack/lib/action_controller/test_case.rb65
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/document.rb4
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/node.rb82
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb56
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb2
-rw-r--r--actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb16
36 files changed, 778 insertions, 666 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 9dfffced75..81c0698fb8 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -1,6 +1,171 @@
require "action_controller/log_subscriber"
module ActionController
+ # 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.
+ #
+ # By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
+ # controllers in turn inherit from ApplicationController. This gives you one class to configure things such as
+ # request forgery protection and filtering of sensitive request parameters.
+ #
+ # A sample controller could look like this:
+ #
+ # class PostsController < ApplicationController
+ # def index
+ # @posts = Post.all
+ # end
+ #
+ # def create
+ # @post = Post.create params[:post]
+ # redirect_to posts_path
+ # end
+ # end
+ #
+ # Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
+ # after executing code in the action. For example, the +index+ action of the PostsController would render the
+ # template <tt>app/views/posts/index.html.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
+ #
+ # Unlike index, the create action will not render a template. After performing its main purpose (creating a
+ # new post), it initiates a redirect instead. This redirect works by returning an external
+ # "302 Moved" HTTP response that takes the user to the index action.
+ #
+ # These two methods 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
+ #
+ # For every request, the router determines the value of the +controller+ and +action+ keys. These determine which controller
+ # and action are called. 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 accessor methods. Then the action is performed.
+ #
+ # The full request object is available via the request accessor and is primarily used to query for HTTP headers:
+ #
+ # 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 <tt>/posts?category=All&limit=5</tt> will include
+ # <tt>{ "category" => "All", "limit" => 5 }</tt> in params.
+ #
+ # It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
+ #
+ # <input type="text" name="post[name]" value="david">
+ # <input type="text" name="post[address]" value="hyacintvej">
+ #
+ # A request stemming from a form holding these inputs will include <tt>{ "post" => { "name" => "david", "address" => "hyacintvej" } }</tt>.
+ # If the address input had been named "post[address][street]", the params would have included
+ # <tt>{ "post" => { "address" => { "street" => "hyacintvej" } } }</tt>. 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 <tt>session</tt> 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:
+ #
+ # * 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
+ #
+ # MyApplication::Application.config.session_store :active_record_store
+ #
+ # in your <tt>config/initializers/session_store.rb</tt> and run <tt>script/rails g session_migration</tt>.
+ #
+ # == 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 ActionView::Base.
+ #
+ # == Redirects
+ #
+ # Redirects are used to move from one action to another. For example, after a <tt>create</tt> 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 <tt>show</tt> 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 <tt>show</tt> method which is then executed.
+ #
+ # Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
+ #
+ # == 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 < Metal
abstract!
@@ -58,13 +223,6 @@ module ActionController
# Rails 2.x compatibility
include ActionController::Compatibility
- def self.inherited(klass)
- super
- klass.helper :all
- end
-
ActiveSupport.run_load_hooks(:action_controller, self)
end
end
-
-require "action_controller/deprecated/base"
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 4105f9e14f..14137f2886 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -3,7 +3,7 @@ require 'uri'
require 'set'
module ActionController #:nodoc:
- # Caching is a cheap way of speeding up slow applications by keeping the result of
+ # \Caching is a cheap way of speeding up slow applications by keeping the result of
# calculations, renderings, and database calls around for subsequent requests.
# Action Controller affords you three approaches in varying levels of granularity:
# Page, Action, Fragment.
@@ -14,7 +14,7 @@ module ActionController #:nodoc:
# Note: To turn off all caching and sweeping, set
# config.action_controller.perform_caching = false.
#
- # == Caching stores
+ # == \Caching stores
#
# All the caching stores from ActiveSupport::Cache are available to be used as backends
# for Action Controller caching. This setting only affects action and fragment caching
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index 546f043c58..2c8a6e4d4d 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -4,53 +4,58 @@ module ActionController #:nodoc:
module Caching
# Action caching is similar to page caching by the fact that the entire
# output of the response is cached, but unlike page caching, every
- # request still goes through the Action Pack. The key benefit
- # of this is that filters are run before the cache is served, which
- # allows for authentication and other restrictions on whether someone
- # is allowed to see the cache. Example:
+ # request still goes through Action Pack. The key benefit of this is
+ # that filters run before the cache is served, which allows for
+ # authentication and other restrictions on whether someone is allowed
+ # to execute such action. Example:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
+ #
# caches_page :public
- # caches_action :index, :show, :feed
+ # caches_action :index, :show
# end
#
- # In this example, the public action doesn't require authentication,
- # so it's possible to use the faster page caching method. But both
- # the show and feed action are to be shielded behind the authenticate
- # filter, so we need to implement those as action caches.
- #
- # Action caching internally uses the fragment caching and an around
- # filter to do the job. The fragment cache is named according to both
- # the current host and the path. So a page that is accessed at
- # http://david.somewhere.com/lists/show/1 will result in a fragment named
- # "david.somewhere.com/lists/show/1". This allows the cacher to
- # differentiate between "david.somewhere.com/lists/" and
- # "jamis.somewhere.com/lists/" -- which is a helpful way of assisting
+ # In this example, the +public+ action doesn't require authentication
+ # so it's possible to use the faster page caching. On the other hand
+ # +index+ and +show+ require authentication. They can still be cached,
+ # but we need action caching for them.
+ #
+ # Action caching uses fragment caching internally and an around
+ # filter to do the job. The fragment cache is named according to
+ # the host and path of the request. A page that is accessed at
+ # <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
+ # <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
+ # differentiate between <tt>david.example.com/lists/</tt> and
+ # <tt>jamis.example.com/lists/</tt> -- which is a helpful way of assisting
# the subdomain-as-account-key pattern.
#
# Different representations of the same resource, e.g.
- # <tt>http://david.somewhere.com/lists</tt> and
- # <tt>http://david.somewhere.com/lists.xml</tt>
+ # <tt>http://david.example.com/lists</tt> and
+ # <tt>http://david.example.com/lists.xml</tt>
# are treated like separate requests and so are cached separately.
# Keep in mind when expiring an action cache that
# <tt>:action => 'lists'</tt> is not the same as
# <tt>:action => 'list', :format => :xml</tt>.
#
# You can set modify the default action cache path by passing a
- # :cache_path option. This will be passed directly to
- # ActionCachePath.path_for. This is handy for actions with multiple
- # possible routes that should be cached differently. If a block is
- # given, it is called with the current controller instance.
+ # <tt>:cache_path</tt> option. This will be passed directly to
+ # <tt>ActionCachePath.path_for</tt>. This is handy for actions with
+ # multiple possible routes that should be cached differently. If a
+ # block is given, it is called with the current controller instance.
+ #
+ # And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
+ # proc that specifies when the action should be cached.
#
- # And you can also use :if (or :unless) to pass a Proc that
- # specifies when the action should be cached.
+ # Finally, if you are using memcached, you can also pass <tt>:expires_in</tt>.
#
- # Finally, if you are using memcached, you can also pass :expires_in.
+ # The following example depicts some of the points made above:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
- # caches_page :public
+ #
+ # caches_page :public
+ #
# caches_action :index, :if => proc do |c|
# !c.request.format.json? # cache if is not a JSON request
# end
@@ -58,19 +63,28 @@ module ActionController #:nodoc:
# caches_action :show, :cache_path => { :project => 1 },
# :expires_in => 1.hour
#
- # caches_action :feed, :cache_path => proc do |controller|
- # if controller.params[:user_id]
- # controller.send(:user_list_url,
- # controller.params[:user_id], controller.params[:id])
+ # caches_action :feed, :cache_path => proc do |c|
+ # if c.params[:user_id]
+ # c.send(:user_list_url,
+ # c.params[:user_id], c.params[:id])
# else
- # controller.send(:list_url, controller.params[:id])
+ # c.send(:list_url, c.params[:id])
# end
# end
# end
#
- # If you pass :layout => false, it will only cache your action
- # content. It is useful when your layout has dynamic information.
+ # If you pass <tt>:layout => false</tt>, it will only cache your action
+ # content. That's useful when your layout has dynamic information.
+ #
+ # Warning: If the format of the request is determined by the Accept HTTP
+ # header the Content-Type of the cached response could be wrong because
+ # no information about the MIME type is stored in the cache key. So, if
+ # you first ask for MIME type M in the Accept header, a cache entry is
+ # created, and then perform a second request to the same resource asking
+ # for a different MIME type, you'd get the content cached for M.
#
+ # The <tt>:format</tt> parameter is taken into account though. The safest
+ # way to cache by MIME type is to pass the format in the route.
module Actions
extend ActiveSupport::Concern
@@ -89,12 +103,14 @@ module ActionController #:nodoc:
end
def _save_fragment(name, options)
- return unless caching_allowed?
-
content = response_body
content = content.join if content.is_a?(Array)
- write_fragment(name, content, options)
+ if caching_allowed?
+ write_fragment(name, content, options)
+ else
+ content
+ end
end
protected
@@ -144,7 +160,7 @@ module ActionController #:nodoc:
attr_reader :path, :extension
# If +infer_extension+ is true, the cache path extension is looked up from the request's
- # path & format. This is desirable when reading and writing the cache, but not when
+ # path and format. This is desirable when reading and writing the cache, but not when
# expiring the cache - expire_action should expire the same files regardless of the
# request format.
def initialize(controller, options = {}, infer_extension = true)
@@ -161,7 +177,7 @@ module ActionController #:nodoc:
def normalize!(path)
path << 'index' if path[-1] == ?/
path << ".#{extension}" if extension and !path.ends_with?(extension)
- URI.unescape(path)
+ URI.parser.unescape(path)
end
end
end
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index 460273dac1..0be04b70a1 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -1,52 +1,72 @@
module ActionController #:nodoc:
module Caching
- # Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when
- # certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple
- # parties. The caching is done using the cache helper available in the Action View. A template with caching might look something like:
+ # Fragment caching is used for caching various blocks within
+ # views without caching the entire action as a whole. This is
+ # useful when certain elements of an action change frequently or
+ # depend on complicated state while other parts rarely change or
+ # can be shared amongst multiple parties. The caching is done using
+ # the <tt>cache</tt> helper available in the Action View. A
+ # template with fragment caching might look like:
#
# <b>Hello <%= @name %></b>
+ #
# <% cache do %>
# All the topics in the system:
# <%= render :partial => "topic", :collection => Topic.find(:all) %>
# <% end %>
#
- # This cache will bind to the name of the action that called it, so if this code was part of the view for the topics/list action, you would
- # be able to invalidate it using <tt>expire_fragment(:controller => "topics", :action => "list")</tt>.
+ # This cache will bind the name of the action that called it, so if
+ # this code was part of the view for the topics/list action, you
+ # would be able to invalidate it using:
+ #
+ # expire_fragment(:controller => "topics", :action => "list")
#
- # This default behavior is of limited use if you need to cache multiple fragments per action or if the action itself is cached using
- # <tt>caches_action</tt>, so we also have the option to qualify the name of the cached fragment with something like:
+ # This default behavior is limited if you need to cache multiple
+ # fragments per action or if the action itself is cached using
+ # <tt>caches_action</tt>. To remedy this, there is an option to
+ # qualify the name of the cached fragment by using the
+ # <tt>:action_suffix</tt> option:
#
# <% cache(:action => "list", :action_suffix => "all_topics") do %>
#
- # That would result in a name such as "/topics/list/all_topics", avoiding conflicts with the action cache and with any fragments that use a
- # different suffix. Note that the URL doesn't have to really exist or be callable - the url_for system is just used to generate unique
- # cache names that we can refer to when we need to expire the cache.
+ # That would result in a name such as
+ # <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
+ # action cache and with any fragments that use a different suffix.
+ # Note that the URL doesn't have to really exist or be callable
+ # - the url_for system is just used to generate unique cache names
+ # that we can refer to when we need to expire the cache.
#
# The expiration call for this example is:
#
- # expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics")
+ # expire_fragment(:controller => "topics",
+ # :action => "list",
+ # :action_suffix => "all_topics")
module Fragments
- # Given a key (as described in <tt>expire_fragment</tt>), returns a key suitable for use in reading,
- # writing, or expiring a cached fragment. If the key is a hash, the generated key is the return
- # value of url_for on that hash (without the protocol). All keys are prefixed with "views/" and uses
+ # Given a key (as described in <tt>expire_fragment</tt>), returns
+ # a key suitable for use in reading, writing, or expiring a
+ # cached fragment. If the key is a hash, the generated key is the
+ # return value of url_for on that hash (without the protocol).
+ # All keys are prefixed with <tt>views/</tt> and uses
# ActiveSupport::Cache.expand_cache_key for the expansion.
def fragment_cache_key(key)
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
end
- # Writes <tt>content</tt> to the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
+ # Writes <tt>content</tt> to the location signified by
+ # <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats).
def write_fragment(key, content, options = nil)
return content unless cache_configured?
key = fragment_cache_key(key)
instrument_fragment_cache :write_fragment, key do
- content = content.html_safe.to_str if content.respond_to?(:html_safe)
+ content = content.to_str
cache_store.write(key, content, options)
end
content
end
- # Reads a cached fragment from the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
+ # Reads a cached fragment from the location signified by <tt>key</tt>
+ # (see <tt>expire_fragment</tt> for acceptable formats).
def read_fragment(key, options = nil)
return unless cache_configured?
@@ -57,7 +77,8 @@ module ActionController #:nodoc:
end
end
- # Check if a cached fragment from the location signified by <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
+ # Check if a cached fragment from the location signified by
+ # <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
def fragment_exist?(key, options = nil)
return unless cache_configured?
key = fragment_cache_key(key)
@@ -70,8 +91,9 @@ module ActionController #:nodoc:
# Removes fragments from the cache.
#
# +key+ can take one of three forms:
+ #
# * String - This would normally take the form of a path, like
- # <tt>"pages/45/notes"</tt>.
+ # <tt>pages/45/notes</tt>.
# * Hash - Treated as an implicit call to +url_for+, like
# <tt>{:controller => "pages", :action => "notes", :id => 45}</tt>
# * Regexp - Will remove any fragment that matches, so
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index 4f7a5d3f55..8c583c7ce0 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -1,5 +1,4 @@
require 'fileutils'
-require 'uri'
require 'active_support/core_ext/class/attribute_accessors'
module ActionController #:nodoc:
@@ -71,9 +70,9 @@ module ActionController #:nodoc:
# Manually cache the +content+ in the key determined by +path+. Example:
# cache_page "I'm the cached content", "/lists/show"
- def cache_page(content, path)
+ def cache_page(content, path, extension = nil)
return unless perform_caching
- path = page_cache_path(path)
+ path = page_cache_path(path, extension)
instrument_page_cache :write_page, path do
FileUtils.makedirs(File.dirname(path))
@@ -98,14 +97,16 @@ module ActionController #:nodoc:
end
private
- def page_cache_file(path)
- name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
- name << page_cache_extension unless (name.split('/').last || name).include? '.'
+ def page_cache_file(path, extension)
+ name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
+ unless (name.split('/').last || name).include? '.'
+ name << (extension || self.page_cache_extension)
+ end
return name
end
- def page_cache_path(path)
- page_cache_directory + page_cache_file(path)
+ def page_cache_path(path, extension = nil)
+ page_cache_directory.to_s + page_cache_file(path, extension)
end
def instrument_page_cache(name, path)
@@ -135,7 +136,7 @@ module ActionController #:nodoc:
# If no options are provided, the requested url is used. Example:
# cache_page "I'm the cached content", :controller => "lists", :action => "show"
def cache_page(content = nil, options = nil)
- return unless self.class.perform_caching && caching_allowed
+ return unless self.class.perform_caching && caching_allowed?
path = case options
when Hash
@@ -146,13 +147,13 @@ module ActionController #:nodoc:
request.path
end
- self.class.cache_page(content || response.body, path)
+ if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
+ extension = ".#{type_symbol}"
+ end
+
+ self.class.cache_page(content || response.body, path, extension)
end
- private
- def caching_allowed
- request.get? && response.status.to_i == 200
- end
end
end
end
diff --git a/actionpack/lib/action_controller/deprecated.rb b/actionpack/lib/action_controller/deprecated.rb
index 9f2de57033..aa0cfc9395 100644
--- a/actionpack/lib/action_controller/deprecated.rb
+++ b/actionpack/lib/action_controller/deprecated.rb
@@ -1,3 +1,3 @@
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
-ActionController::Routing = ActionDispatch::Routing
+ActionController::Routing = ActionDispatch::Routing \ No newline at end of file
diff --git a/actionpack/lib/action_controller/deprecated/base.rb b/actionpack/lib/action_controller/deprecated/base.rb
deleted file mode 100644
index 3975afcaf0..0000000000
--- a/actionpack/lib/action_controller/deprecated/base.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-module ActionController
- class Base
- # Deprecated methods. Wrap them in a module so they can be overwritten by plugins
- # (like the verify method.)
- module DeprecatedBehavior #:nodoc:
- def relative_url_root
- ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root is ineffective. " <<
- "Please stop using it.", caller
- end
-
- def relative_url_root=
- ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root= is ineffective. " <<
- "Please stop using it.", caller
- end
-
- def consider_all_requests_local
- ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local is deprecated, " <<
- "use Rails.application.config.consider_all_requests_local instead", caller
- Rails.application.config.consider_all_requests_local
- end
-
- def consider_all_requests_local=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local= is deprecated. " <<
- "Please configure it on your application with config.consider_all_requests_local=", caller
- Rails.application.config.consider_all_requests_local = value
- end
-
- def allow_concurrency
- ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency is deprecated, " <<
- "use Rails.application.config.allow_concurrency instead", caller
- Rails.application.config.allow_concurrency
- end
-
- def allow_concurrency=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency= is deprecated. " <<
- "Please configure it on your application with config.allow_concurrency=", caller
- Rails.application.config.allow_concurrency = value
- end
-
- def ip_spoofing_check=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check= is deprecated. " <<
- "Please configure it on your application with config.action_dispatch.ip_spoofing_check=", caller
- Rails.application.config.action_dispatch.ip_spoofing_check = value
- end
-
- def ip_spoofing_check
- ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check is deprecated. " <<
- "Configuring ip_spoofing_check on the application configures a middleware.", caller
- Rails.application.config.action_dispatch.ip_spoofing_check
- end
-
- def cookie_verifier_secret=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret= is deprecated. " <<
- "Please configure it on your application with config.secret_token=", caller
- end
-
- def cookie_verifier_secret
- ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret is deprecated.", caller
- end
-
- def trusted_proxies=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies= is deprecated. " <<
- "Please configure it on your application with config.action_dispatch.trusted_proxies=", caller
- Rails.application.config.action_dispatch.ip_spoofing_check = value
- end
-
- def trusted_proxies
- ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies is deprecated. " <<
- "Configuring trusted_proxies on the application configures a middleware.", caller
- Rails.application.config.action_dispatch.ip_spoofing_check = value
- end
-
- def session(*args)
- ActiveSupport::Deprecation.warn(
- "Disabling sessions for a single controller has been deprecated. " +
- "Sessions are now lazy loaded. So if you don't access them, " +
- "consider them off. You can still modify the session cookie " +
- "options with request.session_options.", caller)
- end
-
- def session=(value)
- ActiveSupport::Deprecation.warn "ActionController::Base.session= is deprecated. " <<
- "Please configure it on your application with config.session_store :cookie_store, :key => '....'", caller
- if value.delete(:disabled)
- Rails.application.config.session_store :disabled
- else
- store = Rails.application.config.session_store
- Rails.application.config.session_store store, value
- end
- end
-
- # Controls the resource action separator
- def resource_action_separator
- @resource_action_separator ||= "/"
- end
-
- def resource_action_separator=(val)
- ActiveSupport::Deprecation.warn "ActionController::Base.resource_action_separator is deprecated and only " \
- "works with the deprecated router DSL."
- @resource_action_separator = val
- end
-
- def use_accept_header
- ActiveSupport::Deprecation.warn "ActionController::Base.use_accept_header doesn't do anything anymore. " \
- "The accept header is always taken into account."
- end
-
- def use_accept_header=(val)
- use_accept_header
- end
-
- # This method has been moved to ActionDispatch::Request.filter_parameters
- def filter_parameter_logging(*args, &block)
- ActiveSupport::Deprecation.warn("Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead", caller)
- filter = Rails.application.config.filter_parameters
- filter.concat(args)
- filter << block if block
- filter
- end
-
- # This was moved to a plugin
- def verify(*args)
- ActiveSupport::Deprecation.warn "verify was removed from Rails and is now available as a plugin. " <<
- "Please install it with `rails plugin install git://github.com/rails/verification.git`.", caller
- end
- end
-
- extend DeprecatedBehavior
-
- delegate :consider_all_requests_local, :consider_all_requests_local=,
- :allow_concurrency, :allow_concurrency=, :to => :"self.class"
- end
-end
diff --git a/actionpack/lib/action_controller/deprecated/dispatcher.rb b/actionpack/lib/action_controller/deprecated/dispatcher.rb
deleted file mode 100644
index 8c21e375dd..0000000000
--- a/actionpack/lib/action_controller/deprecated/dispatcher.rb
+++ /dev/null
@@ -1,28 +0,0 @@
-module ActionController
- class Dispatcher
- class << self
- def before_dispatch(*args, &block)
- ActiveSupport::Deprecation.warn "ActionController::Dispatcher.before_dispatch is deprecated. " <<
- "Please use ActionDispatch::Callbacks.before instead.", caller
- ActionDispatch::Callbacks.before(*args, &block)
- end
-
- def after_dispatch(*args, &block)
- ActiveSupport::Deprecation.warn "ActionController::Dispatcher.after_dispatch is deprecated. " <<
- "Please use ActionDispatch::Callbacks.after instead.", caller
- ActionDispatch::Callbacks.after(*args, &block)
- end
-
- def to_prepare(*args, &block)
- ActiveSupport::Deprecation.warn "ActionController::Dispatcher.to_prepare is deprecated. " <<
- "Please use config.to_prepare instead", caller
- ActionDispatch::Callbacks.after(*args, &block)
- end
-
- def new
- ActiveSupport::Deprecation.warn "ActionController::Dispatcher.new is deprecated, use Rails.application instead."
- Rails.application
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/log_subscriber.rb b/actionpack/lib/action_controller/log_subscriber.rb
index ece270b3ce..3fae697cc3 100644
--- a/actionpack/lib/action_controller/log_subscriber.rb
+++ b/actionpack/lib/action_controller/log_subscriber.rb
@@ -16,7 +16,11 @@ module ActionController
payload = event.payload
additions = ActionController::Base.log_process_action(payload)
- message = "Completed #{payload[:status]} #{Rack::Utils::HTTP_STATUS_CODES[payload[:status]]} in %.0fms" % event.duration
+ status = payload[:status]
+ if status.nil? && payload[:exception].present?
+ status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil
+ end
+ message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
message << " (#{additions.join(" | ")})" unless additions.blank?
info(message)
@@ -42,7 +46,7 @@ module ActionController
def #{method}(event)
key_or_path = event.payload[:key] || event.payload[:path]
human_name = #{method.to_s.humanize.inspect}
- info("\#{human_name} \#{key_or_path} (%.1fms)" % event.duration)
+ info("\#{human_name} \#{key_or_path} \#{"(%.1fms)" % event.duration}")
end
METHOD
end
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index 2281c500c5..e5db31061b 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -12,7 +12,7 @@ module ActionController
#
class MiddlewareStack < ActionDispatch::MiddlewareStack #:nodoc:
class Middleware < ActionDispatch::MiddlewareStack::Middleware #:nodoc:
- def initialize(klass, *args)
+ def initialize(klass, *args, &block)
options = args.extract_options!
@only = Array(options.delete(:only)).map(&:to_s)
@except = Array(options.delete(:except)).map(&:to_s)
@@ -36,35 +36,88 @@ module ActionController
action = action.to_s
raise "MiddlewareStack#build requires an app" unless app
- reverse.inject(app) do |a, middleware|
+ middlewares.reverse.inject(app) do |a, middleware|
middleware.valid?(action) ?
middleware.build(a) : a
end
end
end
- # ActionController::Metal provides a way to get a valid Rack application from a controller.
+ # <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
+ # valid Rack interface without the additional niceties provided by
+ # <tt>ActionController::Base</tt>.
+ #
+ # A sample metal controller might look like this:
+ #
+ # class HelloController < ActionController::Metal
+ # def index
+ # self.response_body = "Hello World!"
+ # end
+ # end
+ #
+ # And then to route requests to your metal controller, you would add
+ # something like this to <tt>config/routes.rb</tt>:
+ #
+ # match 'hello', :to => HelloController.action(:index)
+ #
+ # The +action+ method returns a valid Rack application for the \Rails
+ # router to dispatch to.
+ #
+ # == Rendering Helpers
+ #
+ # <tt>ActionController::Metal</tt> by default provides no utilities for rendering
+ # views, partials, or other responses aside from explicitly calling of
+ # <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
+ # add the render helpers you're used to having in a normal controller, you
+ # can do the following:
+ #
+ # class HelloController < ActionController::Metal
+ # include ActionController::Rendering
+ # append_view_path "#{Rails.root}/app/views"
+ #
+ # def index
+ # render "hello/index"
+ # end
+ # end
+ #
+ # == Redirection Helpers
+ #
+ # To add redirection helpers to your metal controller, do the following:
+ #
+ # class HelloController < ActionController::Metal
+ # include ActionController::Redirecting
+ # include Rails.application.routes.url_helpers
+ #
+ # def index
+ # redirect_to root_url
+ # end
+ # end
+ #
+ # == Other Helpers
+ #
+ # You can refer to the modules included in <tt>ActionController::Base</tt> to see
+ # other features you can bring into your metal controller.
#
- # In AbstractController, dispatching is triggered directly by calling #process on a new controller.
- # ActionController::Metal provides an #action method that returns a valid Rack application for a
- # given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router,
- # can dispatch directly to the action returned by FooController.action(:index).
class Metal < AbstractController::Base
abstract!
- attr_internal :env
+ attr_internal_writer :env
+
+ def env
+ @_env ||= {}
+ end
# Returns the last part of the controller's name, underscored, without the ending
- # "Controller". For instance, MyApp::MyPostsController would return "my_posts" for
- # controller_name
+ # <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
+ # Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
#
# ==== Returns
- # String
+ # * <tt>string</tt>
def self.controller_name
@controller_name ||= self.name.demodulize.sub(/Controller$/, '').underscore
end
- # Delegates to the class' #controller_name
+ # Delegates to the class' <tt>controller_name</tt>
def controller_name
self.class.controller_name
end
@@ -81,6 +134,9 @@ module ActionController
def initialize(*)
@_headers = {"Content-Type" => "text/html"}
@_status = 200
+ @_request = nil
+ @_response = nil
+ @_routes = nil
super
end
@@ -112,6 +168,11 @@ module ActionController
headers["Location"] = url
end
+ # basic url_for that can be overridden for more robust functionality
+ def url_for(string)
+ string
+ end
+
def status
@_status
end
@@ -121,12 +182,11 @@ module ActionController
end
def response_body=(val)
- body = val.respond_to?(:each) ? val : [val]
+ body = val.nil? ? nil : (val.respond_to?(:each) ? val : [val])
super body
end
- # :api: private
- def dispatch(name, request)
+ def dispatch(name, request) #:nodoc:
@_request = request
@_env = request.env
@_env['action_controller.instance'] = self
@@ -134,8 +194,7 @@ module ActionController
to_a
end
- # :api: private
- def to_a
+ def to_a #:nodoc:
response ? response.to_a : [status, headers, response_body]
end
@@ -147,8 +206,8 @@ module ActionController
super
end
- def self.use(*args)
- middleware_stack.use(*args)
+ def self.use(*args, &block)
+ middleware_stack.use(*args, &block)
end
def self.middleware
@@ -164,10 +223,10 @@ module ActionController
# for the same action.
#
# ==== Parameters
- # action<#to_s>:: An action name
+ # * <tt>action</tt> - An action name
#
# ==== Returns
- # Proc:: A rack application
+ # * <tt>proc</tt> - A rack application
def self.action(name, klass = ActionDispatch::Request)
middleware_stack.build(name.to_s) do |env|
new.dispatch(name, klass.new(env))
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index d49465fa0b..006b9fd456 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -5,9 +5,6 @@ module ActionController
class ::ActionController::ActionControllerError < StandardError #:nodoc:
end
- module ClassMethods
- end
-
# Temporary hax
included do
::ActionController::UnknownAction = ::AbstractController::ActionNotFound
@@ -39,12 +36,6 @@ module ActionController
def assign_shortcuts(*) end
def _normalize_options(options)
- if options[:action] && options[:action].to_s.include?(?/)
- ActiveSupport::Deprecation.warn "Giving a path to render :action is deprecated. " <<
- "Please use render :template instead", caller
- options[:template] = options.delete(:action)
- end
-
options[:text] = nil if options.delete(:nothing) == true
options[:text] = " " if options.key?(:text) && options[:text].nil?
super
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 61e7ece90d..a5e37172c9 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -6,7 +6,7 @@ module ActionController
include Head
# Sets the etag, last_modified, or both on the response and renders a
- # "304 Not Modified" response if the request is already fresh.
+ # <tt>304 Not Modified</tt> response if the request is already fresh.
#
# Parameters:
# * <tt>:etag</tt>
@@ -17,11 +17,11 @@ module ActionController
#
# def show
# @article = Article.find(params[:id])
- # fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true)
+ # fresh_when(:etag => @article, :last_modified => @article.created_at, :public => true)
# end
#
# This will render the show template if the request isn't sending a matching etag or
- # If-Modified-Since header and just a "304 Not Modified" response if there's a match.
+ # If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
#
def fresh_when(options)
options.assert_valid_keys(:etag, :last_modified, :public)
@@ -36,7 +36,7 @@ module ActionController
# 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.
+ # it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
#
# Parameters:
# * <tt>:etag</tt>
@@ -48,7 +48,7 @@ module ActionController
# def show
# @article = Article.find(params[:id])
#
- # if stale?(:etag => @article, :last_modified => @article.created_at.utc)
+ # if stale?(:etag => @article, :last_modified => @article.created_at)
# @statistics = @article.really_expensive_call
# respond_to do |format|
# # all the supported formats
@@ -60,13 +60,13 @@ module ActionController
!request.fresh?(response)
end
- # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
- # intermediate caches shouldn't cache the response.
+ # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a <tt>private</tt> instruction, so that
+ # intermediate caches must not cache the response.
#
# Examples:
# expires_in 20.minutes
# expires_in 3.hours, :public => true
- # expires in 3.hours, 'max-stale' => 5.hours, :public => true
+ # expires_in 3.hours, 'max-stale' => 5.hours, :public => true
#
# This method will overwrite an existing Cache-Control header.
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
@@ -77,7 +77,7 @@ module ActionController
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
end
- # Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
+ # Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should occur by the browser or
# intermediate caches (like caching proxy servers).
def expires_now #:doc:
response.cache_control.replace(:no_cache => true)
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
index a5c9910d68..8abcad55a2 100644
--- a/actionpack/lib/action_controller/metal/head.rb
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -2,8 +2,6 @@ module ActionController
module Head
extend ActiveSupport::Concern
- include ActionController::UrlFor
-
# Return a response that has no content (merely headers). The options
# argument is interpreted to be a hash of header names and values.
# This allows you to easily return a response that consists only of
@@ -22,13 +20,13 @@ module ActionController
location = options.delete(:location)
options.each do |key, value|
- headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
+ headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
end
self.status = status
self.location = url_for(location) if location
- self.content_type = Mime[formats.first]
+ self.content_type = Mime[formats.first] if formats
self.response_body = " "
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index e0bc47318a..91a88ab68a 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -2,21 +2,21 @@ require 'active_support/core_ext/array/wrap'
require 'active_support/core_ext/class/attribute'
module ActionController
- # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
- # +numbers+ and model objects, to name a few. These helpers are available to all templates
+ # The \Rails framework provides a large number of helpers for working with assets, dates, forms,
+ # numbers and model objects, to name a few. These helpers are available to all templates
# by default.
#
- # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
+ # In addition to using the standard template helpers provided, creating custom helpers to
# extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
# include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
# include <tt>MyHelper</tt>.
#
- # Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
+ # Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
# controller which inherits from it.
#
# ==== Examples
- # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if
- # the Time object is blank:
+ # The +to_s+ method from the \Time class can be wrapped in a helper method to display a custom message if
+ # a \Time object is blank:
#
# module FormattedTimeHelper
# def format_time(time, format=:long, blank_message="&nbsp;")
@@ -53,30 +53,20 @@ module ActionController
include AbstractController::Helpers
included do
- config_accessor :helpers_path
+ config_accessor :helpers_path, :include_all_helpers
self.helpers_path ||= []
+ self.include_all_helpers = true
end
module ClassMethods
- def helpers_dir
- ActiveSupport::Deprecation.warn "helpers_dir is deprecated, use helpers_path instead", caller
- self.helpers_path
- end
-
- def helpers_dir=(value)
- ActiveSupport::Deprecation.warn "helpers_dir= is deprecated, use helpers_path= instead", caller
- self.helpers_path = Array.wrap(value)
- end
-
# Declares helper accessors for controller attributes. For example, the
# following adds new +name+ and <tt>name=</tt> instance methods to a
# controller and makes them available to the view:
- # helper_attr :name
# attr_accessor :name
+ # helper_attr :name
#
# ==== Parameters
- # *attrs<Array[String, Symbol]>:: Names of attributes to be converted
- # into helpers.
+ # * <tt>attrs</tt> - Names of attributes to be converted into helpers.
def helper_attr(*attrs)
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
end
@@ -88,25 +78,28 @@ module ActionController
private
# Overwrite modules_for_helpers to accept :all as argument, which loads
- # all helpers in helpers_dir.
+ # all helpers in helpers_path.
#
# ==== Parameters
- # args<Array[String, Symbol, Module, all]>:: A list of helpers
+ # * <tt>args</tt> - A list of helpers
#
# ==== Returns
- # Array[Module]:: A normalized list of modules for the list of
- # helpers provided.
+ # * <tt>array</tt> - A normalized list of modules for the list of helpers provided.
def modules_for_helpers(args)
args += all_application_helpers if args.delete(:all)
super(args)
end
- # Extract helper names from files in app/helpers/**/*_helper.rb
+ # Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
def all_application_helpers
+ all_helpers_from_path(helpers_path)
+ end
+
+ def all_helpers_from_path(path)
helpers = []
- Array.wrap(helpers_path).each do |path|
- extract = /^#{Regexp.quote(path.to_s)}\/?(.*)_helper.rb$/
- helpers += Dir["#{path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
+ Array.wrap(path).each do |_path|
+ extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
+ helpers += Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
end
helpers.sort!
helpers.uniq!
diff --git a/actionpack/lib/action_controller/metal/hide_actions.rb b/actionpack/lib/action_controller/metal/hide_actions.rb
index 32d7a96701..b55c4643be 100644
--- a/actionpack/lib/action_controller/metal/hide_actions.rb
+++ b/actionpack/lib/action_controller/metal/hide_actions.rb
@@ -1,8 +1,7 @@
require 'active_support/core_ext/class/attribute'
module ActionController
- # ActionController::HideActions adds the ability to prevent public methods on a controller
- # to be called as actions.
+ # Adds the ability to prevent public methods on a controller to be called as actions.
module HideActions
extend ActiveSupport::Concern
@@ -23,7 +22,7 @@ module ActionController
# Sets all of the actions passed in as hidden actions.
#
# ==== Parameters
- # *args<#to_s>:: A list of actions
+ # * <tt>args</tt> - A list of actions
def hide_action(*args)
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
end
diff --git a/actionpack/lib/action_controller/metal/http_authentication.rb b/actionpack/lib/action_controller/metal/http_authentication.rb
index b0eb24a4a8..39c804d707 100644
--- a/actionpack/lib/action_controller/metal/http_authentication.rb
+++ b/actionpack/lib/action_controller/metal/http_authentication.rb
@@ -3,9 +3,9 @@ require 'active_support/core_ext/object/blank'
module ActionController
module HttpAuthentication
- # Makes it dead easy to do HTTP Basic authentication.
+ # Makes it dead easy to do HTTP \Basic and \Digest authentication.
#
- # Simple Basic example:
+ # === Simple \Basic example
#
# class PostsController < ApplicationController
# USER_NAME, PASSWORD = "dhh", "secret"
@@ -29,7 +29,9 @@ module ActionController
# end
#
#
- # Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
+ # === Advanced \Basic example
+ #
+ # Here is a more advanced \Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
# the regular HTML interface is protected by a session approach:
#
# class ApplicationController < ActionController::Base
@@ -69,7 +71,7 @@ module ActionController
# assert_equal 200, status
# end
#
- # Simple Digest example:
+ # === Simple \Digest example
#
# require 'digest/md5'
# class PostsController < ApplicationController
@@ -95,18 +97,20 @@ module ActionController
# end
# end
#
- # NOTE: The +authenticate_or_request_with_http_digest+ block must return the user's password or the ha1 digest hash so the framework can appropriately
- # hash to check the user's credentials. Returning +nil+ will cause authentication to fail.
- # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
- # the password file or database is compromised, the attacker would be able to use the ha1 hash to
- # authenticate as the user at this +realm+, but would not have the user's password to try using at
- # other sites.
+ # === Notes
#
- # On shared hosts, Apache sometimes doesn't pass authentication headers to
- # FCGI instances. If your environment matches this description and you cannot
- # authenticate, try this rule in your Apache setup:
+ # The +authenticate_or_request_with_http_digest+ block must return the user's password
+ # or the ha1 digest hash so the framework can appropriately hash to check the user's
+ # credentials. Returning +nil+ will cause authentication to fail.
#
- # RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
+ # Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
+ # the password file or database is compromised, the attacker would be able to use the ha1 hash to
+ # authenticate as the user at this +realm+, but would not have the user's password to try using at
+ # other sites.
+ #
+ # In rare instances, web servers or front proxies strip authorization headers before
+ # they reach your application. You can debug this situation by logging all environment
+ # variables, and check for HTTP_AUTHORIZATION, amongst others.
module Basic
extend self
@@ -210,7 +214,7 @@ module ActionController
def encode_credentials(http_method, credentials, password, password_is_ha1)
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
- "Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ')
+ "Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
end
def decode_credentials_header(request)
@@ -218,11 +222,10 @@ module ActionController
end
def decode_credentials(header)
- header.to_s.gsub(/^Digest\s+/,'').split(',').inject({}) do |hash, pair|
+ Hash[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
key, value = pair.split('=', 2)
- hash[key.strip.to_sym] = value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')
- hash
- end
+ [key.strip.to_sym, value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')]
+ end]
end
def authentication_header(controller, realm)
@@ -392,11 +395,11 @@ module ActionController
end
end
- # If token Authorization header is present, call the login procedure with
+ # If token Authorization header is present, call the login procedure with
# the present token and options.
#
# controller - ActionController::Base instance for the current request.
- # login_procedure - Proc to call if a token is present. The Proc should
+ # login_procedure - Proc to call if a token is present. The Proc should
# take 2 arguments:
# authenticate(controller) { |token, options| ... }
#
@@ -404,7 +407,7 @@ module ActionController
# Returns nil if no token is found.
def authenticate(controller, &login_procedure)
token, options = token_and_options(controller.request)
- if !token.blank?
+ unless token.blank?
login_procedure.call(token, options)
end
end
@@ -414,20 +417,19 @@ module ActionController
# Authorization: Token token="abc", nonce="def"
# Then the returned token is "abc", and the options is {:nonce => "def"}
#
- # request - ActionController::Request instance with the current headers.
+ # request - ActionDispatch::Request instance with the current headers.
#
# Returns an Array of [String, Hash] if a token is present.
# Returns nil if no token is found.
def token_and_options(request)
if header = request.authorization.to_s[/^Token (.*)/]
- values = $1.split(',').
- inject({}) do |memo, value|
- value.strip! # remove any spaces between commas and values
- key, value = value.split(/\=\"?/) # split key=value pairs
- value.chomp!('"') # chomp trailing " in value
- value.gsub!(/\\\"/, '"') # unescape remaining quotes
- memo.update(key => value)
- end
+ values = Hash[$1.split(',').map do |value|
+ value.strip! # remove any spaces between commas and values
+ key, value = value.split(/\=\"?/) # split key=value pairs
+ value.chomp!('"') # chomp trailing " in value
+ value.gsub!(/\\\"/, '"') # unescape remaining quotes
+ [key, value]
+ end]
[values.delete("token"), values.with_indifferent_access]
end
end
@@ -439,9 +441,8 @@ module ActionController
#
# Returns String.
def encode_credentials(token, options = {})
- values = ["token=#{token.to_s.inspect}"]
- options.each do |key, value|
- values << "#{key}=#{value.to_s.inspect}"
+ values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
+ "#{key}=#{value.to_s.inspect}"
end
"Token #{values * ", "}"
end
diff --git a/actionpack/lib/action_controller/metal/implicit_render.rb b/actionpack/lib/action_controller/metal/implicit_render.rb
index 282dcf66b3..cfa7004048 100644
--- a/actionpack/lib/action_controller/metal/implicit_render.rb
+++ b/actionpack/lib/action_controller/metal/implicit_render.rb
@@ -12,10 +12,10 @@ module ActionController
def method_for_action(action_name)
super || begin
- if template_exists?(action_name.to_s, _prefix)
+ if template_exists?(action_name.to_s, _prefixes)
"default_render"
end
end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/metal/instrumentation.rb b/actionpack/lib/action_controller/metal/instrumentation.rb
index b08d9a8434..dc3ea939e6 100644
--- a/actionpack/lib/action_controller/metal/instrumentation.rb
+++ b/actionpack/lib/action_controller/metal/instrumentation.rb
@@ -78,7 +78,7 @@ module ActionController
yield
end
- # Everytime after an action is processed, this method is invoked
+ # Every time after an action is processed, this method is invoked
# with the payload, so you can add more information.
# :api: plugin
def append_info_to_payload(payload) #:nodoc:
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index c6d4c6d936..a2e06fe0a6 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -63,13 +63,13 @@ module ActionController #:nodoc:
# might look something like this:
#
# def index
- # @people = Person.find(:all)
+ # @people = Person.all
# end
#
# Here's the same action, with web-service support baked in:
#
# def index
- # @people = Person.find(:all)
+ # @people = Person.all
#
# respond_to do |format|
# format.html
@@ -155,7 +155,7 @@ module ActionController #:nodoc:
# Respond to also allows you to specify a common block for different formats by using any:
#
# def index
- # @people = Person.find(:all)
+ # @people = Person.all
#
# respond_to do |format|
# format.html
@@ -178,7 +178,7 @@ module ActionController #:nodoc:
# respond_to :html, :xml, :json
#
# def index
- # @people = Person.find(:all)
+ # @people = Person.all
# respond_with(@person)
# end
# end
@@ -208,8 +208,8 @@ module ActionController #:nodoc:
# It also accepts a block to be given. It's used to overwrite a default
# response:
#
- # def destroy
- # @user = User.find(params[:id])
+ # def create
+ # @user = User.new(params[:user])
# flash[:notice] = "User was successfully created." if @user.save
#
# respond_with(@user) do |format|
@@ -227,7 +227,7 @@ module ActionController #:nodoc:
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
if response = retrieve_response_from_mimes(&block)
- options = resources.extract_options!
+ options = resources.size == 1 ? {} : resources.extract_options!
options.merge!(:default_response => response)
(options.delete(:responder) || self.class.responder).call(self, resources, options)
end
@@ -258,9 +258,8 @@ module ActionController #:nodoc:
# nil if :not_acceptable was sent to the client.
#
def retrieve_response_from_mimes(mimes=nil, &block)
- collector = Collector.new { default_render }
mimes ||= collect_mimes_from_class_level
- mimes.each { |mime| collector.send(mime) }
+ collector = Collector.new(mimes) { default_render }
block.call(collector) if block_given?
if format = request.negotiate_mime(collector.order)
@@ -277,8 +276,9 @@ module ActionController #:nodoc:
include AbstractController::Collector
attr_accessor :order
- def initialize(&block)
+ def initialize(mimes, &block)
@order, @responses, @default_response = [], {}, block
+ mimes.each { |mime| send(mime) }
end
def any(*args, &block)
@@ -291,7 +291,7 @@ module ActionController #:nodoc:
alias :all :any
def custom(mime_type, &block)
- mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
+ mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
@order << mime_type
@responses[mime_type] ||= block
end
diff --git a/actionpack/lib/action_controller/metal/redirecting.rb b/actionpack/lib/action_controller/metal/redirecting.rb
index b5f1d23ef0..55c650df6c 100644
--- a/actionpack/lib/action_controller/metal/redirecting.rb
+++ b/actionpack/lib/action_controller/metal/redirecting.rb
@@ -20,6 +20,7 @@ module ActionController
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
+ # * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
#
@@ -30,6 +31,7 @@ module ActionController
# redirect_to "/images/screenshot.jpg"
# redirect_to articles_url
# redirect_to :back
+ # redirect_to proc { edit_post_url(@post) }
#
# The redirection happens as a "302 Moved" header unless otherwise specified.
#
@@ -39,6 +41,9 @@ module ActionController
# redirect_to post_url(@post), :status => 301
# redirect_to :action=>'atom', :status => 302
#
+ # The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
+ # integer, or a symbol representing the downcased, underscored and symbolized description.
+ #
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
#
@@ -48,8 +53,7 @@ module ActionController
# redirect_to post_url(@post), :status => 301, :flash => { :updated_post_id => @post.id }
# redirect_to { :action=>'atom' }, :alert => "Something serious happened"
#
- # When using <tt>redirect_to :back</tt>, if there is no referrer,
- # RedirectBackError will be raised. You may specify some fallback
+ # When using <tt>redirect_to :back</tt>, if there is no referrer, RedirectBackError will be raised. You may specify some fallback
# behavior for this case by rescuing RedirectBackError.
def redirect_to(options = {}, response_status = {}) #:doc:
raise ActionControllerError.new("Cannot redirect to nil!") if options.nil?
@@ -83,6 +87,8 @@ module ActionController
when :back
raise RedirectBackError unless refer = request.headers["Referer"]
refer
+ when Proc
+ _compute_redirect_to_location options.call
else
url_for(options)
end.gsub(/[\r\n]/, '')
diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb
index 0be07cd1fc..38711c8462 100644
--- a/actionpack/lib/action_controller/metal/renderers.rb
+++ b/actionpack/lib/action_controller/metal/renderers.rb
@@ -2,6 +2,7 @@ require 'active_support/core_ext/class/attribute'
require 'active_support/core_ext/object/blank'
module ActionController
+ # See <tt>Renderers.add</tt>
def self.add_renderer(key, &block)
Renderers.add(key, &block)
end
@@ -15,30 +16,12 @@ module ActionController
end
module ClassMethods
- def _write_render_options
- renderers = _renderers.map do |name, value|
- <<-RUBY_EVAL
- if options.key?(:#{name})
- _process_options(options)
- return _render_option_#{name}(options.delete(:#{name}), options)
- end
- RUBY_EVAL
- end
-
- class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
- def _handle_render_options(options)
- #{renderers.join}
- end
- RUBY_EVAL
- end
-
def use_renderers(*args)
new = _renderers.dup
args.each do |key|
new[key] = RENDERERS[key]
end
self._renderers = new.freeze
- _write_render_options
end
alias use_renderer use_renderers
end
@@ -47,31 +30,69 @@ module ActionController
_handle_render_options(options) || super
end
+ def _handle_render_options(options)
+ _renderers.each do |name, value|
+ if options.key?(name.to_sym)
+ _process_options(options)
+ return send("_render_option_#{name}", options.delete(name.to_sym), options)
+ end
+ end
+ nil
+ end
+
+ # Hash of available renderers, mapping a renderer name to its proc.
+ # Default keys are :json, :js, :xml and :update.
RENDERERS = {}
+
+ # Adds a new renderer to call within controller actions.
+ # A renderer is invoked by passing its name as an option to
+ # <tt>AbstractController::Rendering#render</tt>. To create a renderer
+ # pass it a name and a block. The block takes two arguments, the first
+ # is the value paired with its key and the second is the remaining
+ # hash of options passed to +render+.
+ #
+ # === Example
+ # Create a csv renderer:
+ #
+ # ActionController::Renderers.add :csv do |obj, options|
+ # filename = options[:filename] || 'data'
+ # str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
+ # send_data str, :type => Mime::CSV,
+ # :disposition => "attachment; filename=#{filename}.csv"
+ # end
+ #
+ # Note that we used Mime::CSV for the csv mime type as it comes with Rails.
+ # For a custom renderer, you'll need to register a mime type with
+ # <tt>Mime::Type.register</tt>.
+ #
+ # To use the csv renderer in a controller action:
+ #
+ # def show
+ # @csvable = Csvable.find(params[:id])
+ # respond_to do |format|
+ # format.html
+ # format.csv { render :csv => @csvable, :filename => @csvable.name }
+ # }
+ # end
+ # To use renderers and their mime types in more concise ways, see
+ # <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
+ # <tt>ActionController::MimeResponds#respond_with</tt>
def self.add(key, &block)
define_method("_render_option_#{key}", &block)
RENDERERS[key] = block
- All._write_render_options
end
module All
extend ActiveSupport::Concern
include Renderers
- INCLUDED = []
included do
self._renderers = RENDERERS
- _write_render_options
- INCLUDED << self
- end
-
- def self._write_render_options
- INCLUDED.each(&:_write_render_options)
end
end
add :json do |json, options|
- json = ActiveSupport::JSON.encode(json, options) unless json.respond_to?(:to_str)
+ json = json.to_json(options) unless json.kind_of?(String)
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
self.content_type ||= Mime::JSON
self.response_body = json
diff --git a/actionpack/lib/action_controller/metal/rendering.rb b/actionpack/lib/action_controller/metal/rendering.rb
index 86bb810947..32d52c84c4 100644
--- a/actionpack/lib/action_controller/metal/rendering.rb
+++ b/actionpack/lib/action_controller/metal/rendering.rb
@@ -2,12 +2,11 @@ module ActionController
module Rendering
extend ActiveSupport::Concern
- include ActionController::RackDelegation
include AbstractController::Rendering
# Before processing, set the request formats in current controller formats.
def process_action(*) #:nodoc:
- self.formats = request.formats.map { |x| x.to_sym }
+ self.formats = request.formats.map { |x| x.ref }
super
end
@@ -21,36 +20,35 @@ module ActionController
private
- # Normalize arguments by catching blocks and setting them on :update.
- def _normalize_args(action=nil, options={}, &blk) #:nodoc:
- options = super
- options[:update] = blk if block_given?
- options
- end
-
- # Normalize both text and status options.
- def _normalize_options(options) #:nodoc:
- if options.key?(:text) && options[:text].respond_to?(:to_text)
- options[:text] = options[:text].to_text
- end
+ # Normalize arguments by catching blocks and setting them on :update.
+ def _normalize_args(action=nil, options={}, &blk) #:nodoc:
+ options = super
+ options[:update] = blk if block_given?
+ options
+ end
- if options[:status]
- options[:status] = Rack::Utils.status_code(options[:status])
- end
+ # Normalize both text and status options.
+ def _normalize_options(options) #:nodoc:
+ if options.key?(:text) && options[:text].respond_to?(:to_text)
+ options[:text] = options[:text].to_text
+ end
- super
+ if options[:status]
+ options[:status] = Rack::Utils.status_code(options[:status])
end
- # Process controller specific options, as status, content-type and location.
- def _process_options(options) #:nodoc:
- status, content_type, location = options.values_at(:status, :content_type, :location)
+ super
+ end
- self.status = status if status
- self.content_type = content_type if content_type
- self.headers["Location"] = url_for(location) if location
+ # Process controller specific options, as status, content-type and location.
+ def _process_options(options) #:nodoc:
+ status, content_type, location = options.values_at(:status, :content_type, :location)
- super
- end
+ self.status = status if status
+ self.content_type = content_type if content_type
+ self.headers["Location"] = url_for(location) if location
+ super
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index b632e7aab6..1cd93a188c 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -4,45 +4,27 @@ module ActionController #:nodoc:
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
end
- # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current
- # web application, not a forged link from another site, is done by embedding a token based on a random
- # string stored in the session (which an attacker wouldn't know) in all forms and Ajax requests generated
- # by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript
- # requests are checked, so this will not protect your XML API (presumably you'll have a different
- # authentication scheme there anyway). Also, GET requests are not protected as these should be
- # idempotent anyway.
+ # Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
+ # by including a token in the rendered html for your application. This token is
+ # stored as a random string in the session, to which an attacker does not have
+ # access. When a request reaches your application, \Rails then verifies the received
+ # token with the token in the session. Only HTML and javascript requests are checked,
+ # so this will not protect your XML API (presumably you'll have a different
+ # authentication scheme there anyway). Also, GET requests are not protected as these
+ # should be idempotent.
#
- # This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
- # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the
- # error message in production by editing public/422.html. A call to this method in ApplicationController is
- # generated by default in post-Rails 2.0 applications.
+ # CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
+ # which will check the token and raise an ActionController::InvalidAuthenticityToken
+ # if it doesn't match what was expected. A call to this method is generated for new
+ # \Rails applications by default. You can customize the error message by editing
+ # public/422.html.
#
- # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form
- # manually (without the use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to
- # include a hidden field named like that and set its value to what is returned by
- # <tt>form_authenticity_token</tt>.
- #
- # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails
- # 1.x, add this to config/environments/test.rb:
- #
- # # Disable request forgery protection in test environment
- # config.action_controller.allow_forgery_protection = false
- #
- # == Learn more about CSRF (Cross-Site Request Forgery) attacks
- #
- # Here are some resources:
- # * http://isc.sans.org/diary.html?storyid=1750
- # * http://en.wikipedia.org/wiki/Cross-site_request_forgery
- #
- # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
- # There are a few guidelines you should follow:
- #
- # * Keep your GET requests safe and idempotent. More reading material:
- # * http://www.xml.com/pub/a/2002/04/24/deviant.html
- # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
- # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look
- # for "Expires: at end of session"
+ # The token parameter is named <tt>authenticity_token</tt> by default. The name and
+ # value of this token must be added to every layout that renders forms by including
+ # <tt>csrf_meta_tags</tt> in the html +head+.
#
+ # Learn more about CSRF attacks and securing your application in the
+ # {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
module RequestForgeryProtection
extend ActiveSupport::Concern
@@ -71,39 +53,42 @@ module ActionController #:nodoc:
# class FooController < ApplicationController
# protect_from_forgery :except => :index
#
- # # you can disable csrf protection on controller-by-controller basis:
- # skip_before_filter :verify_authenticity_token
- # end
+ # You can disable csrf protection on controller-by-controller basis:
+ #
+ # skip_before_filter :verify_authenticity_token
+ #
+ # It can also be disabled for specific controller actions:
+ #
+ # skip_before_filter :verify_authenticity_token, :except => [:create]
#
# Valid Options:
#
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
def protect_from_forgery(options = {})
self.request_forgery_protection_token ||= :authenticity_token
- before_filter :verify_authenticity_token, options
+ prepend_before_filter :verify_authenticity_token, options
end
end
protected
-
- def protect_from_forgery(options = {})
- self.request_forgery_protection_token ||= :authenticity_token
- before_filter :verify_authenticity_token, options
- end
-
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
def verify_authenticity_token
- verified_request? || raise(ActionController::InvalidAuthenticityToken)
+ verified_request? || handle_unverified_request
+ end
+
+ def handle_unverified_request
+ reset_session
end
# Returns true or false if a request is verified. Checks:
#
- # * is the format restricted? By default, only HTML requests are checked.
# * is it a GET request? Gets should be safe and idempotent
# * Does the form_authenticity_token match the given token value from the params?
+ # * Does the X-CSRF-Token header match the form_authenticity_token
def verified_request?
- !protect_against_forgery? || request.forgery_whitelisted? ||
- form_authenticity_token == params[request_forgery_protection_token]
+ !protect_against_forgery? || request.get? ||
+ form_authenticity_token == params[request_forgery_protection_token] ||
+ form_authenticity_token == request.headers['X-CSRF-Token']
end
# Sets the token value for the current session.
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index cb644dfd16..4b45413cf8 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -1,7 +1,7 @@
require 'active_support/json'
module ActionController #:nodoc:
- # Responder is responsible for exposing a resource to different mime requests,
+ # Responsible for exposing a resource to different mime requests,
# usually depending on the HTTP verb. The responder is triggered when
# <code>respond_with</code> is called. The simplest case to study is a GET request:
#
@@ -24,10 +24,10 @@ module ActionController #:nodoc:
#
# === Builtin HTTP verb semantics
#
- # The default Rails responder holds semantics for each HTTP verb. Depending on the
+ # The default \Rails responder holds semantics for each HTTP verb. Depending on the
# content type, verb and the resource status, it will behave differently.
#
- # Using Rails default responder, a POST request for creating an object could
+ # Using \Rails default responder, a POST request for creating an object could
# be written as:
#
# def create
@@ -77,8 +77,6 @@ module ActionController #:nodoc:
#
# respond_with(@project, :manager, @task)
#
- # Check <code>polymorphic_url</code> documentation for more examples.
- #
class Responder
attr_reader :controller, :request, :format, :resource, :resources, :options
@@ -89,6 +87,8 @@ module ActionController #:nodoc:
def initialize(controller, resources, options={})
@controller = controller
+ @request = @controller.request
+ @format = @controller.formats.first
@resource = resources.last
@resources = resources
@options = options
@@ -99,14 +99,6 @@ module ActionController #:nodoc:
delegate :head, :render, :redirect_to, :to => :controller
delegate :get?, :post?, :put?, :delete?, :to => :request
- def request
- @request ||= @controller.request
- end
-
- def format
- @format ||= @controller.formats.first
- end
-
# Undefine :to_json and :to_yaml since it's defined on Object
undef_method(:to_json) if method_defined?(:to_json)
undef_method(:to_yaml) if method_defined?(:to_yaml)
@@ -121,7 +113,7 @@ module ActionController #:nodoc:
# Main entry point for responder responsible to dispatch to the proper format.
#
def respond
- method = :"to_#{format}"
+ method = "to_#{format}"
respond_to?(method) ? send(method) : to_format
end
@@ -146,7 +138,7 @@ module ActionController #:nodoc:
protected
- # This is the common behavior for "navigation" requests, like :html, :iphone and so forth.
+ # This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
def navigation_behavior(error)
if get?
raise error
@@ -157,7 +149,7 @@ module ActionController #:nodoc:
end
end
- # This is the common behavior for "API" requests, like :xml and :json.
+ # This is the common behavior for formats associated with APIs, such as :xml and :json.
def api_behavior(error)
raise error unless resourceful?
@@ -167,6 +159,8 @@ module ActionController #:nodoc:
display resource.errors, :status => :unprocessable_entity
elsif post?
display resource, :status => :created, :location => api_location
+ elsif has_empty_resource_definition?
+ display empty_resource, :status => :ok
else
head :ok
end
@@ -175,7 +169,7 @@ module ActionController #:nodoc:
# Checks whether the resource responds to the current format or not.
#
def resourceful?
- resource.respond_to?(:"to_#{format}")
+ resource.respond_to?("to_#{format}")
end
# Returns the resource location by retrieving it from the options or
@@ -227,5 +221,23 @@ module ActionController #:nodoc:
def default_action
@action ||= ACTIONS_FOR_VERBS[request.request_method_symbol]
end
+
+ # Check whether resource needs a specific definition of empty resource to be valid
+ #
+ def has_empty_resource_definition?
+ respond_to?("empty_#{format}_resource")
+ end
+
+ # Delegate to proper empty resource method
+ #
+ def empty_resource
+ send("empty_#{format}_resource")
+ end
+
+ # Return a valid empty JSON resource
+ #
+ def empty_json_resource
+ "{}"
+ end
end
end
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index d75b46dace..312dc8eb3e 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -69,10 +69,6 @@ module ActionController #:nodoc:
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
send_file_headers! options
- if options[:x_sendfile]
- ActiveSupport::Deprecation.warn(":x_sendfile is no longer needed in send_file", caller)
- end
-
self.status = options[:status] || 200
self.content_type = options[:content_type] if options.key?(:content_type)
self.response_body = File.open(path, "rb")
@@ -105,10 +101,6 @@ module ActionController #:nodoc:
# send_data image.data, :type => image.content_type, :disposition => 'inline'
#
# See +send_file+ for more information on HTTP Content-* headers and caching.
- #
- # <b>Tip:</b> if you want to stream large amounts of on-the-fly generated
- # data to the browser, then use <tt>render :text => proc { ... }</tt>
- # instead. See ActionController::Base#render for more information.
def send_data(data, options = {}) #:doc:
send_file_headers! options.dup
render options.slice(:status, :content_type).merge(:text => data)
@@ -121,10 +113,6 @@ module ActionController #:nodoc:
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
end
- if options.key?(:length)
- ActiveSupport::Deprecation.warn("You do not need to provide the file's length", caller)
- end
-
disposition = options[:disposition]
disposition += %(; filename="#{options[:filename]}") if options[:filename]
diff --git a/actionpack/lib/action_controller/metal/testing.rb b/actionpack/lib/action_controller/metal/testing.rb
index 4b8c452d50..f4efeb33ba 100644
--- a/actionpack/lib/action_controller/metal/testing.rb
+++ b/actionpack/lib/action_controller/metal/testing.rb
@@ -14,18 +14,9 @@ module ActionController
cookies.write(@_response)
end
@_response.prepare!
- set_test_assigns
ret
end
- def set_test_assigns
- @assigns = {}
- (instance_variable_names - self.class.protected_instance_variables).each do |var|
- name, value = var[1..-1], instance_variable_get(var)
- @assigns[name] = value
- end
- end
-
# TODO : Rewrite tests using controller.headers= to use Rack env
def headers=(new_headers)
@_response ||= ActionDispatch::Response.new
diff --git a/actionpack/lib/action_controller/metal/url_for.rb b/actionpack/lib/action_controller/metal/url_for.rb
index a51fc5b8e4..6fc0cf1fb8 100644
--- a/actionpack/lib/action_controller/metal/url_for.rb
+++ b/actionpack/lib/action_controller/metal/url_for.rb
@@ -2,27 +2,25 @@ module ActionController
module UrlFor
extend ActiveSupport::Concern
- include ActionDispatch::Routing::UrlFor
+ include AbstractController::UrlFor
def url_options
- super.reverse_merge(
- :host => request.host_with_port,
+ @_url_options ||= super.reverse_merge(
+ :host => request.host,
+ :port => request.optional_port,
:protocol => request.protocol,
:_path_segments => request.symbolized_path_parameters
- ).merge(:script_name => request.script_name)
- end
-
- def _routes
- raise "In order to use #url_for, you must include routing helpers explicitly. " \
- "For instance, `include Rails.application.routes.url_helpers"
- end
+ ).freeze
- module ClassMethods
- def action_methods
- @action_methods ||= begin
- super - _routes.named_routes.helper_names
+ if _routes.equal?(env["action_dispatch.routes"])
+ @_url_options.dup.tap do |options|
+ options[:script_name] = request.script_name.dup
+ options.freeze
end
+ else
+ @_url_options
end
end
+
end
end
diff --git a/actionpack/lib/action_controller/middleware.rb b/actionpack/lib/action_controller/middleware.rb
index 2115b07b3e..437fec3dc6 100644
--- a/actionpack/lib/action_controller/middleware.rb
+++ b/actionpack/lib/action_controller/middleware.rb
@@ -31,7 +31,7 @@ module ActionController
super()
@_app = app
end
-
+
def index
call(env)
end
diff --git a/actionpack/lib/action_controller/railtie.rb b/actionpack/lib/action_controller/railtie.rb
index cd2dfafbe6..f0c29825ba 100644
--- a/actionpack/lib/action_controller/railtie.rb
+++ b/actionpack/lib/action_controller/railtie.rb
@@ -2,35 +2,13 @@ require "rails"
require "action_controller"
require "action_dispatch/railtie"
require "action_view/railtie"
-require "active_support/deprecation/proxy_wrappers"
-require "active_support/deprecation"
+require "abstract_controller/railties/routes_helpers"
+require "action_controller/railties/paths"
module ActionController
class Railtie < Rails::Railtie
config.action_controller = ActiveSupport::OrderedOptions.new
- config.action_controller.singleton_class.tap do |d|
- d.send(:define_method, :session) do
- ActiveSupport::Deprecation.warn "config.action_controller.session has been deprecated. " <<
- "Please use Rails.application.config.session_store instead.", caller
- end
-
- d.send(:define_method, :session=) do |val|
- ActiveSupport::Deprecation.warn "config.action_controller.session= has been deprecated. " <<
- "Please use config.session_store(name, options) instead.", caller
- end
-
- d.send(:define_method, :session_store) do
- ActiveSupport::Deprecation.warn "config.action_controller.session_store has been deprecated. " <<
- "Please use Rails.application.config.session_store instead.", caller
- end
-
- d.send(:define_method, :session_store=) do |val|
- ActiveSupport::Deprecation.warn "config.action_controller.session_store= has been deprecated. " <<
- "Please use config.session_store(name, options) instead.", caller
- end
- end
-
initializer "action_controller.logger" do
ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger }
end
@@ -43,24 +21,27 @@ module ActionController
paths = app.config.paths
options = app.config.action_controller
- options.assets_dir ||= paths.public.to_a.first
- options.javascripts_dir ||= paths.public.javascripts.to_a.first
- options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
- options.page_cache_directory ||= paths.public.to_a.first
- options.helpers_path ||= paths.app.helpers.to_a
+ options.assets_dir ||= paths["public"].first
+ options.javascripts_dir ||= paths["public/javascripts"].first
+ options.stylesheets_dir ||= paths["public/stylesheets"].first
+ options.page_cache_directory ||= paths["public"].first
+
+ # make sure readers methods get compiled
+ options.asset_path ||= app.config.asset_path
+ options.asset_host ||= app.config.asset_host
ActiveSupport.on_load(:action_controller) do
- include app.routes.url_helpers
+ include app.routes.mounted_helpers
+ extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
+ extend ::ActionController::Railties::Paths.with(app)
options.each { |k,v| send("#{k}=", v) }
end
end
- initializer "action_controller.deprecated_routes" do |app|
- message = "ActionController::Routing::Routes is deprecated. " \
- "Instead, use Rails.application.routes"
-
- proxy = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(app.routes, message)
- ActionController::Routing::Routes = proxy
+ initializer "action_controller.compile_config_methods" do
+ ActiveSupport.on_load(:action_controller) do
+ config.compile_methods! if config.respond_to?(:compile_methods!)
+ end
end
end
-end \ No newline at end of file
+end
diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb
new file mode 100644
index 0000000000..dce3c2fe88
--- /dev/null
+++ b/actionpack/lib/action_controller/railties/paths.rb
@@ -0,0 +1,32 @@
+module ActionController
+ module Railties
+ module Paths
+ def self.with(app)
+ Module.new do
+ define_method(:inherited) do |klass|
+ super(klass)
+
+ if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
+ paths = namespace._railtie.paths["app/helpers"].existent
+ else
+ paths = app.config.helpers_paths
+ end
+
+ klass.helpers_path = paths
+ if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
+ klass.helper :all
+ end
+
+ if app.config.serve_static_assets && namespace
+ paths = namespace._railtie.config.paths
+
+ klass.config.assets_dir = paths["public"].first
+ klass.config.javascripts_dir = paths["public/javascripts"].first
+ klass.config.stylesheets_dir = paths["public/stylesheets"].first
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb
index e306697f4b..bc4f8bb9ce 100644
--- a/actionpack/lib/action_controller/test_case.rb
+++ b/actionpack/lib/action_controller/test_case.rb
@@ -1,6 +1,7 @@
require 'rack/session/abstract/id'
require 'active_support/core_ext/object/blank'
require 'active_support/core_ext/object/to_query'
+require 'active_support/core_ext/class/attribute'
module ActionController
module TemplateAssertions
@@ -40,6 +41,13 @@ module ActionController
ActiveSupport::Notifications.unsubscribe("!render_template.action_view")
end
+ def process(*args)
+ @partials = Hash.new(0)
+ @templates = Hash.new(0)
+ @layouts = Hash.new(0)
+ super
+ end
+
# Asserts that the request was rendered with the appropriate template file or partials.
#
# ==== Examples
@@ -127,7 +135,7 @@ module ActionController
class Result < ::Array #:nodoc:
def to_s() join '/' end
def self.new_escaped(strings)
- new strings.collect {|str| URI.unescape str}
+ new strings.collect {|str| uri_parser.unescape str}
end
end
@@ -164,9 +172,14 @@ module ActionController
end
def recycle!
+ write_cookies!
+ @env.delete('HTTP_COOKIE') if @cookies.blank?
+ @env.delete('action_dispatch.cookies')
+ @cookies = nil
@formats = nil
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
+ @symbolized_path_params = nil
@method = @request_method = nil
@fullpath = @ip = @remote_ip = nil
@env['action_dispatch.request.query_parameters'] = {}
@@ -186,20 +199,23 @@ module ActionController
end
end
- class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc:
- DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS
+ class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
+ DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
def initialize(session = {})
+ @env, @by = nil, nil
replace(session.stringify_keys)
@loaded = true
end
- def exists?; true; end
+ def exists?
+ true
+ end
end
# Superclass for ActionController functional tests. Functional tests allow you to
# test a single controller action per test method. This should not be confused with
- # integration tests (see ActionController::IntegrationTest), which are more like
+ # integration tests (see ActionDispatch::IntegrationTest), which are more like
# "stories" that can involve multiple controllers and multiple actions (i.e. multiple
# different HTTP requests).
#
@@ -244,7 +260,7 @@ module ActionController
# after calling +post+. If the various assert methods are not sufficient, then you
# may use this object to inspect the HTTP response in detail.
#
- # (Earlier versions of Rails required each functional test to subclass
+ # (Earlier versions of \Rails required each functional test to subclass
# Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
#
# == Controller is automatically inferred
@@ -257,7 +273,7 @@ module ActionController
# tests WidgetController
# end
#
- # == Testing controller internals
+ # == \Testing controller internals
#
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
# can be used against. These collections are:
@@ -265,7 +281,7 @@ module ActionController
# * assigns: Instance variables assigned in the action that are available for the view.
# * session: Objects being saved in the session.
# * flash: The flash objects currently in the session.
- # * cookies: Cookies being sent to the user on this request.
+ # * cookies: \Cookies being sent to the user on this request.
#
# These collections can be used just like any other hash:
#
@@ -289,9 +305,13 @@ module ActionController
# and cookies, though. For sessions, you just do:
#
# @request.session[:key] = "value"
- # @request.cookies["key"] = "value"
+ # @request.cookies[:key] = "value"
+ #
+ # To clear the cookies for a test just clear the request's cookies hash:
+ #
+ # @request.cookies.clear
#
- # == Testing named routes
+ # == \Testing named routes
#
# If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
# Example:
@@ -311,14 +331,14 @@ module ActionController
def tests(controller_class)
self.controller_class = controller_class
end
-
+
def controller_class=(new_class)
prepare_controller_class(new_class) if new_class
- write_inheritable_attribute(:controller_class, new_class)
+ self._controller_class = new_class
end
def controller_class
- if current_controller_class = read_inheritable_attribute(:controller_class)
+ if current_controller_class = self._controller_class
current_controller_class
else
self.controller_class = determine_default_controller_class(name)
@@ -393,16 +413,18 @@ module ActionController
parameters ||= {}
@request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
- @request.session = ActionController::TestSession.new(session) unless session.nil?
+ @request.session = ActionController::TestSession.new(session) if session
@request.session["flash"] = @request.flash.update(flash || {})
@request.session["flash"].sweep
@controller.request = @request
@controller.params.merge!(parameters)
build_request_uri(action, parameters)
- Base.class_eval { include Testing }
+ @controller.class.class_eval { include Testing }
@controller.process_with_new_base_test(@request, @response)
+ @assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
@request.session.delete('flash') if @request.session['flash'].blank?
+ @request.cookies.merge!(@response.cookies)
@response
end
@@ -416,7 +438,7 @@ module ActionController
@request.env.delete('PATH_INFO')
- if @controller
+ if defined?(@controller) && @controller
@controller.request = @request
@controller.params = {}
end
@@ -430,6 +452,7 @@ module ActionController
included do
include ActionController::TemplateAssertions
include ActionDispatch::Assertions
+ class_attribute :_controller_class
setup :setup_controller_request_and_response
end
@@ -437,7 +460,7 @@ module ActionController
def build_request_uri(action, parameters)
unless @request.env["PATH_INFO"]
- options = @controller.__send__(:url_options).merge(parameters)
+ options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
options.update(
:only_path => true,
:action => action,
@@ -461,9 +484,11 @@ module ActionController
# The exception is stored in the exception accessor for further inspection.
module RaiseActionExceptions
def self.included(base)
- base.class_eval do
- attr_accessor :exception
- protected :exception, :exception=
+ unless base.method_defined?(:exception) && base.method_defined?(:exception=)
+ base.class_eval do
+ attr_accessor :exception
+ protected :exception, :exception=
+ end
end
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb
index b8d73c350d..7fa3aead82 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/document.rb
@@ -48,7 +48,7 @@ EOF
end
end
end
-
+
# Search the tree for (and return) the first node that matches the given
# conditions. The conditions are interpreted differently for different node
# types, see HTML::Text#find and HTML::Tag#find.
@@ -62,7 +62,7 @@ EOF
def find_all(conditions)
@root.find_all(conditions)
end
-
+
end
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb
index a874519978..22b3243104 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/node.rb
@@ -1,7 +1,7 @@
require 'strscan'
module HTML #:nodoc:
-
+
class Conditions < Hash #:nodoc:
def initialize(hash)
super()
@@ -18,14 +18,14 @@ module HTML #:nodoc:
hash[k] = Conditions.new(v)
when :children
hash[k] = v = keys_to_symbols(v)
- v.each do |k,v2|
- case k
+ v.each do |key,value|
+ case key
when :count, :greater_than, :less_than
# keys are valid, and require no further processing
when :only
- v[k] = Conditions.new(v2)
+ v[key] = Conditions.new(value)
else
- raise "illegal key #{k.inspect} => #{v2.inspect}"
+ raise "illegal key #{key.inspect} => #{value.inspect}"
end
end
else
@@ -38,18 +38,14 @@ module HTML #:nodoc:
private
def keys_to_strings(hash)
- hash.keys.inject({}) do |h,k|
- h[k.to_s] = hash[k]
- h
- end
+ Hash[hash.keys.map {|k| [k.to_s, hash[k]]}]
end
def keys_to_symbols(hash)
- hash.keys.inject({}) do |h,k|
+ Hash[hash.keys.map do |k|
raise "illegal key #{k.inspect}" unless k.respond_to?(:to_sym)
- h[k.to_sym] = hash[k]
- h
- end
+ [k.to_sym, hash[k]]
+ end]
end
end
@@ -57,17 +53,17 @@ module HTML #:nodoc:
class Node #:nodoc:
# The array of children of this node. Not all nodes have children.
attr_reader :children
-
+
# The parent node of this node. All nodes have a parent, except for the
# root node.
attr_reader :parent
-
+
# The line number of the input where this node was begun
attr_reader :line
-
+
# The byte position in the input where this node was begun
attr_reader :position
-
+
# Create a new node as a child of the given parent.
def initialize(parent, line=0, pos=0)
@parent = parent
@@ -77,9 +73,7 @@ module HTML #:nodoc:
# Return a textual representation of the node.
def to_s
- s = ""
- @children.each { |child| s << child.to_s }
- s
+ @children.join()
end
# Return false (subclasses must override this to provide specific matching
@@ -92,7 +86,7 @@ module HTML #:nodoc:
# returns non +nil+. Returns the result of the #find call that succeeded.
def find(conditions)
conditions = validate_conditions(conditions)
- @children.each do |child|
+ @children.each do |child|
node = child.find(conditions)
return node if node
end
@@ -133,7 +127,7 @@ module HTML #:nodoc:
equivalent
end
-
+
class <<self
def parse(parent, line, pos, content, strict=true)
if content !~ /^<\S/
@@ -160,11 +154,11 @@ module HTML #:nodoc:
return CDATA.new(parent, line, pos, scanner.pre_match.gsub(/<!\[CDATA\[/, ''))
end
-
+
closing = ( scanner.scan(/\//) ? :close : nil )
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/)
name.downcase!
-
+
unless closing
scanner.skip(/\s*/)
attributes = {}
@@ -191,13 +185,13 @@ module HTML #:nodoc:
attributes[attr.downcase] = value
scanner.skip(/\s*/)
end
-
+
closing = ( scanner.scan(/\//) ? :self : nil )
end
-
+
unless scanner.scan(/\s*>/)
if strict
- raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
+ raise "expected > (got #{scanner.rest.inspect} for #{content}, #{attributes.inspect})"
else
# throw away all text until we find what we're looking for
scanner.skip_until(/>/) or scanner.terminate
@@ -212,9 +206,9 @@ module HTML #:nodoc:
# A node that represents text, rather than markup.
class Text < Node #:nodoc:
-
+
attr_reader :content
-
+
# Creates a new text node as a child of the given parent, with the given
# content.
def initialize(parent, line, pos, content)
@@ -240,7 +234,7 @@ module HTML #:nodoc:
def find(conditions)
match(conditions) && self
end
-
+
# Returns non-+nil+ if this node meets the given conditions, or +nil+
# otherwise. See the discussion of #find for the valid conditions.
def match(conditions)
@@ -268,7 +262,7 @@ module HTML #:nodoc:
content == node.content
end
end
-
+
# A CDATA node is simply a text node with a specialized way of displaying
# itself.
class CDATA < Text #:nodoc:
@@ -281,16 +275,16 @@ module HTML #:nodoc:
# closing tag, or a self-closing tag. It has a name, and may have a hash of
# attributes.
class Tag < Node #:nodoc:
-
+
# Either +nil+, <tt>:close</tt>, or <tt>:self</tt>
attr_reader :closing
-
+
# Either +nil+, or a hash of attributes for this node.
attr_reader :attributes
# The name of this tag.
attr_reader :name
-
+
# Create a new node as a child of the given parent, using the given content
# to describe the node. It will be parsed and the node name, attributes and
# closing status extracted.
@@ -344,7 +338,7 @@ module HTML #:nodoc:
def tag?
true
end
-
+
# Returns +true+ if the node meets any of the given conditions. The
# +conditions+ parameter must be a hash of any of the following keys
# (all are optional):
@@ -404,7 +398,7 @@ module HTML #:nodoc:
# node.match :descendant => { :tag => "strong" }
#
# # test if the node has between 2 and 4 span tags as immediate children
- # node.match :children => { :count => 2..4, :only => { :tag => "span" } }
+ # node.match :children => { :count => 2..4, :only => { :tag => "span" } }
#
# # get funky: test to see if the node is a "div", has a "ul" ancestor
# # and an "li" parent (with "class" = "enum"), and whether or not it has
@@ -439,7 +433,7 @@ module HTML #:nodoc:
# test children
return false unless children.find { |child| child.match(conditions[:child]) } if conditions[:child]
-
+
# test ancestors
if conditions[:ancestor]
return false unless catch :found do
@@ -457,13 +451,13 @@ module HTML #:nodoc:
child.match(:descendant => conditions[:descendant])
end
end
-
+
# count children
if opts = conditions[:children]
matches = children.select do |c|
(c.kind_of?(HTML::Tag) and (c.closing == :self or ! c.childless?))
end
-
+
matches = matches.select { |c| c.match(opts[:only]) } if opts[:only]
opts.each do |key, value|
next if key == :only
@@ -489,24 +483,24 @@ module HTML #:nodoc:
self_index = siblings.index(self)
if conditions[:sibling]
- return false unless siblings.detect do |s|
+ return false unless siblings.detect do |s|
s != self && s.match(conditions[:sibling])
end
end
if conditions[:before]
- return false unless siblings[self_index+1..-1].detect do |s|
+ return false unless siblings[self_index+1..-1].detect do |s|
s != self && s.match(conditions[:before])
end
end
if conditions[:after]
- return false unless siblings[0,self_index].detect do |s|
+ return false unless siblings[0,self_index].detect do |s|
s != self && s.match(conditions[:after])
end
end
end
-
+
true
end
@@ -515,7 +509,7 @@ module HTML #:nodoc:
return false unless closing == node.closing && self.name == node.name
attributes == node.attributes
end
-
+
private
# Match the given value to the given condition.
def match_condition(value, condition)
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
index 51e0868995..09dd08898c 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/sanitizer.rb
@@ -1,5 +1,5 @@
require 'set'
-require 'active_support/core_ext/class/inheritable_attributes'
+require 'active_support/core_ext/class/attribute'
module HTML
class Sanitizer
@@ -7,11 +7,11 @@ module HTML
return text unless sanitizeable?(text)
tokenize(text, options).join
end
-
+
def sanitizeable?(text)
!(text.nil? || text.empty? || !text.index("<"))
end
-
+
protected
def tokenize(text, options)
tokenizer = HTML::Tokenizer.new(text)
@@ -22,12 +22,12 @@ module HTML
end
result
end
-
+
def process_node(node, result, options)
result << node.to_s
end
end
-
+
class FullSanitizer < Sanitizer
def sanitize(text, options = {})
result = super
@@ -37,12 +37,12 @@ module HTML
# Recurse - handle all dirty nested tags
result == text ? result : sanitize(result, options)
end
-
+
def process_node(node, result, options)
result << node.to_s if node.class == HTML::Text
end
end
-
+
class LinkSanitizer < FullSanitizer
cattr_accessor :included_tags, :instance_writer => false
self.included_tags = Set.new(%w(a href))
@@ -50,51 +50,51 @@ module HTML
def sanitizeable?(text)
!(text.nil? || text.empty? || !((text.index("<a") || text.index("<href")) && text.index(">")))
end
-
+
protected
def process_node(node, result, options)
- result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
+ result << node.to_s unless node.is_a?(HTML::Tag) && included_tags.include?(node.name)
end
end
-
+
class WhiteListSanitizer < Sanitizer
[:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
:allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
- class_inheritable_accessor attr, :instance_writer => false
+ class_attribute attr, :instance_writer => false
end
# A regular expression of the valid characters used to separate protocols like
# the ':' in 'http://foo.com'
self.protocol_separator = /:|(&#0*58)|(&#x70)|(%|&#37;)3A/
-
+
# Specifies a Set of HTML attributes that can have URIs.
self.uri_attributes = Set.new(%w(href src cite action longdesc xlink:href lowsrc))
# Specifies a Set of 'bad' tags that the #sanitize helper will remove completely, as opposed
# to just escaping harmless tags like &lt;font&gt;
self.bad_tags = Set.new(%w(script))
-
+
# Specifies the default Set of tags that the #sanitize helper will allow unscathed.
- self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
- sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
+ self.allowed_tags = Set.new(%w(strong em b i p code pre tt samp kbd var sub
+ sup dfn cite big small address hr br div span h1 h2 h3 h4 h5 h6 ul ol li dl dt dd abbr
acronym a img blockquote del ins))
- # Specifies the default Set of html attributes that the #sanitize helper will leave
+ # Specifies the default Set of html attributes that the #sanitize helper will leave
# in the allowed tag.
self.allowed_attributes = Set.new(%w(href src width height alt cite datetime title class name xml:lang abbr))
-
+
# Specifies the default Set of acceptable css properties that #sanitize and #sanitize_css will accept.
- self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
+ self.allowed_protocols = Set.new(%w(ed2k ftp http https irc mailto news gopher nntp telnet webcal xmpp callto
feed svn urn aim rsync tag ssh sftp rtsp afs))
-
+
# Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
- self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
- border-color border-left-color border-right-color border-top-color clear color cursor direction display
+ self.allowed_css_properties = Set.new(%w(azimuth background-color border-bottom-color border-collapse
+ border-color border-left-color border-right-color border-top-color clear color cursor direction display
elevation float font font-family font-size font-style font-variant font-weight height letter-spacing line-height
overflow pause pause-after pause-before pitch pitch-range richness speak speak-header speak-numeral speak-punctuation
speech-rate stress text-align text-decoration text-indent unicode-bidi vertical-align voice-family volume white-space
width))
-
+
# Specifies the default Set of acceptable css keywords that #sanitize and #sanitize_css will accept.
self.allowed_css_keywords = Set.new(%w(auto aqua black block blue bold both bottom brown center
collapse dashed dotted fuchsia gray green !important italic left lime maroon medium none navy normal
@@ -118,9 +118,9 @@ module HTML
style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val|
if allowed_css_properties.include?(prop.downcase)
clean << prop + ': ' + val + ';'
- elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
+ elsif shorthand_css_properties.include?(prop.split('-')[0].downcase)
unless val.split().any? do |keyword|
- !allowed_css_keywords.include?(keyword) &&
+ !allowed_css_keywords.include?(keyword) &&
keyword !~ /^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$/
end
clean << prop + ': ' + val + ';'
@@ -146,7 +146,7 @@ module HTML
else
options[:parent].unshift node.name
end
-
+
process_attributes_for node, options
options[:tags].include?(node.name) ? node : nil
@@ -154,7 +154,7 @@ module HTML
bad_tags.include?(options[:parent].first) ? nil : node.to_s.gsub(/</, "&lt;")
end
end
-
+
def process_attributes_for(node, options)
return unless node.attributes
node.attributes.keys.each do |attr_name|
@@ -169,8 +169,8 @@ module HTML
end
def contains_bad_protocols?(attr_name, value)
- uri_attributes.include?(attr_name) &&
- (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first))
+ uri_attributes.include?(attr_name) &&
+ (value =~ /(^[^\/:]*):|(&#0*58)|(&#x70)|(%|&#37;)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase))
end
end
end
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
index e2c49c284f..0fe2e6d1a6 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb
@@ -182,7 +182,7 @@ module HTML
# not another using <tt>:not</tt>. For example:
# p:not(.post)
# Matches all paragraphs that do not have the class <tt>.post</tt>.
- #
+ #
# === Substitution Values
#
# You can use substitution with identifiers, class names and element values.
diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
index 240dc1890f..c252e01cf5 100644
--- a/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
+++ b/actionpack/lib/action_controller/vendor/html-scanner/html/tokenizer.rb
@@ -1,7 +1,7 @@
require 'strscan'
module HTML #:nodoc:
-
+
# A simple HTML tokenizer. It simply breaks a stream of text into tokens, where each
# token is a string. Each string represents either "text", or an HTML element.
#
@@ -14,13 +14,13 @@ module HTML #:nodoc:
# p token
# end
class Tokenizer #:nodoc:
-
+
# The current (byte) position in the text
attr_reader :position
-
+
# The current line number
attr_reader :line
-
+
# Create a new Tokenizer for the given text.
def initialize(text)
text.encode! if text.encoding_aware?
@@ -42,7 +42,7 @@ module HTML #:nodoc:
update_current_line(scan_text)
end
end
-
+
private
# Treat the text at the current position as a tag, and scan it. Supports
@@ -69,13 +69,13 @@ module HTML #:nodoc:
def scan_text
"#{@scanner.getch}#{@scanner.scan(/[^<]*/)}"
end
-
+
# Counts the number of newlines in the text and updates the current line
# accordingly.
def update_current_line(text)
text.scan(/\r?\n/) { @current_line += 1 }
end
-
+
# Skips over quoted strings, so that less-than and greater-than characters
# within the strings are ignored.
def consume_quoted_regions
@@ -103,5 +103,5 @@ module HTML #:nodoc:
text
end
end
-
+
end