aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller
diff options
context:
space:
mode:
authorPratik Naik <pratiknaik@gmail.com>2009-11-17 22:47:23 +0000
committerPratik Naik <pratiknaik@gmail.com>2009-11-17 22:47:23 +0000
commit5446d5cb05b50a9a3f317ded774be438e0eff909 (patch)
tree6b0b87efe3e95783763208215a3159fb63217a6d /actionpack/lib/action_controller
parent9754debb9a72f9385950e5282f3642b995ab76d8 (diff)
parentf8877d4b2a2a6f68770b376f0b1391a6295f62f2 (diff)
downloadrails-5446d5cb05b50a9a3f317ded774be438e0eff909.tar.gz
rails-5446d5cb05b50a9a3f317ded774be438e0eff909.tar.bz2
rails-5446d5cb05b50a9a3f317ded774be438e0eff909.zip
Merge remote branch 'mainstream/master'
Conflicts: activesupport/lib/active_support/core_ext/hash/conversions.rb
Diffstat (limited to 'actionpack/lib/action_controller')
-rw-r--r--actionpack/lib/action_controller/base.rb1
-rw-r--r--actionpack/lib/action_controller/caching.rb61
-rw-r--r--actionpack/lib/action_controller/caching/actions.rb213
-rw-r--r--actionpack/lib/action_controller/caching/fragments.rb26
-rw-r--r--actionpack/lib/action_controller/caching/pages.rb10
-rw-r--r--actionpack/lib/action_controller/caching/sweeping.rb2
-rw-r--r--actionpack/lib/action_controller/deprecated.rb2
-rw-r--r--actionpack/lib/action_controller/dispatch/dispatcher.rb2
-rw-r--r--actionpack/lib/action_controller/legacy/layout.rb256
-rw-r--r--actionpack/lib/action_controller/metal.rb10
-rw-r--r--actionpack/lib/action_controller/metal/benchmarking.rb23
-rw-r--r--actionpack/lib/action_controller/metal/compatibility.rb8
-rw-r--r--actionpack/lib/action_controller/metal/conditional_get.rb41
-rw-r--r--actionpack/lib/action_controller/metal/configuration.rb28
-rw-r--r--actionpack/lib/action_controller/metal/cookies.rb47
-rw-r--r--actionpack/lib/action_controller/metal/filter_parameter_logging.rb65
-rw-r--r--actionpack/lib/action_controller/metal/flash.rb48
-rw-r--r--actionpack/lib/action_controller/metal/head.rb27
-rw-r--r--actionpack/lib/action_controller/metal/helpers.rb71
-rw-r--r--actionpack/lib/action_controller/metal/layouts.rb21
-rw-r--r--actionpack/lib/action_controller/metal/mime_responds.rb9
-rw-r--r--actionpack/lib/action_controller/metal/rack_convenience.rb2
-rw-r--r--actionpack/lib/action_controller/metal/redirector.rb2
-rw-r--r--actionpack/lib/action_controller/metal/rendering_controller.rb64
-rw-r--r--actionpack/lib/action_controller/metal/request_forgery_protection.rb56
-rw-r--r--actionpack/lib/action_controller/metal/responder.rb46
-rw-r--r--actionpack/lib/action_controller/metal/session_management.rb15
-rw-r--r--actionpack/lib/action_controller/metal/streaming.rb2
-rw-r--r--actionpack/lib/action_controller/metal/verification.rb4
-rw-r--r--actionpack/lib/action_controller/middleware.rb40
-rw-r--r--actionpack/lib/action_controller/notifications.rb10
-rw-r--r--actionpack/lib/action_controller/polymorphic_routes.rb (renamed from actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb)41
-rw-r--r--actionpack/lib/action_controller/routing.rb389
-rw-r--r--actionpack/lib/action_controller/routing/builder.rb199
-rw-r--r--actionpack/lib/action_controller/routing/optimisations.rb130
-rw-r--r--actionpack/lib/action_controller/routing/recognition_optimisation.rb167
-rw-r--r--actionpack/lib/action_controller/routing/resources.rb685
-rw-r--r--actionpack/lib/action_controller/routing/route.rb267
-rw-r--r--actionpack/lib/action_controller/routing/route_set.rb485
-rw-r--r--actionpack/lib/action_controller/routing/routing_ext.rb4
-rw-r--r--actionpack/lib/action_controller/routing/segments.rb343
-rw-r--r--actionpack/lib/action_controller/testing/process.rb2
-rw-r--r--actionpack/lib/action_controller/testing/test_case.rb9
-rw-r--r--actionpack/lib/action_controller/translation.rb4
-rw-r--r--actionpack/lib/action_controller/url_rewriter.rb (renamed from actionpack/lib/action_controller/routing/generation/url_rewriter.rb)0
45 files changed, 420 insertions, 3517 deletions
diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb
index 5338a70104..4c026fe5f7 100644
--- a/actionpack/lib/action_controller/base.rb
+++ b/actionpack/lib/action_controller/base.rb
@@ -15,6 +15,7 @@ module ActionController
include ActionController::ConditionalGet
include ActionController::RackConvenience
include ActionController::Benchmarking
+ include ActionController::Configuration
# Legacy modules
include SessionManagement
diff --git a/actionpack/lib/action_controller/caching.rb b/actionpack/lib/action_controller/caching.rb
index 38cf1da6a8..3caf759032 100644
--- a/actionpack/lib/action_controller/caching.rb
+++ b/actionpack/lib/action_controller/caching.rb
@@ -3,26 +3,31 @@ require 'uri'
require 'set'
module ActionController #:nodoc:
- # 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.
+ # 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.
#
- # You can read more about each approach and the sweeping assistance by clicking the modules below.
- #
- # Note: To turn off all caching and sweeping, set Base.perform_caching = false.
+ # You can read more about each approach and the sweeping assistance by clicking the
+ # modules below.
#
+ # Note: To turn off all caching and sweeping, set
+ # config.action_controller.perform_caching = false.
#
# == 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 as page caching is always written to disk.
+ # 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
+ # as page caching is always written to disk.
#
# Configuration examples (MemoryStore is the default):
#
- # ActionController::Base.cache_store = :memory_store
- # ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
- # ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
- # ActionController::Base.cache_store = :mem_cache_store, "localhost"
- # ActionController::Base.cache_store = MyOwnStore.new("parameter")
+ # config.action_controller.cache_store = :memory_store
+ # config.action_controller.cache_store = :file_store, "/path/to/cache/directory"
+ # config.action_controller.cache_store = :drb_store, "druby://localhost:9192"
+ # config.action_controller.cache_store = :mem_cache_store, "localhost"
+ # config.action_controller.cache_store = :mem_cache_store, Memcached::Rails.new("localhost:11211")
+ # config.action_controller.cache_store = MyOwnStore.new("parameter")
module Caching
extend ActiveSupport::Concern
@@ -46,25 +51,31 @@ module ActionController #:nodoc:
@@perform_caching = true
cattr_accessor :perform_caching
+ end
- def self.cache_configured?
+ module ClassMethods
+ def cache_configured?
perform_caching && cache_store
end
end
- protected
- # Convenience accessor
- def cache(key, options = {}, &block)
- if cache_configured?
- cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
- else
- yield
- end
- end
+ def caching_allowed?
+ request.get? && response.status == 200
+ end
- private
- def cache_configured?
- self.class.cache_configured?
+ protected
+ # Convenience accessor
+ def cache(key, options = {}, &block)
+ if cache_configured?
+ cache_store.fetch(ActiveSupport::Cache.expand_cache_key(key, :controller), options, &block)
+ else
+ yield
end
+ end
+
+ private
+ def cache_configured?
+ self.class.cache_configured?
+ end
end
end
diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb
index cb0c3a1384..35111a4b92 100644
--- a/actionpack/lib/action_controller/caching/actions.rb
+++ b/actionpack/lib/action_controller/caching/actions.rb
@@ -2,9 +2,12 @@ require 'set'
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:
+ # 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:
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
@@ -12,45 +15,64 @@ module ActionController #:nodoc:
# caches_action :index, :show, :feed
# 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.
+ # 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 the subdomain-as-account-key pattern.
+ # 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
+ # 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>
- # 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>.
+ # Different representations of the same resource, e.g.
+ # <tt>http://david.somewhere.com/lists</tt> and
+ # <tt>http://david.somewhere.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.
+ # 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.
#
- # And you can also use :if (or :unless) 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 :expires_in.
#
# class ListsController < ApplicationController
# before_filter :authenticate, :except => :public
# caches_page :public
- # caches_action :index, :if => Proc.new { |c| !c.request.format.json? } # cache if is not a JSON request
- # caches_action :show, :cache_path => { :project => 1 }, :expires_in => 1.hour
- # caches_action :feed, :cache_path => Proc.new { |controller|
- # controller.params[:user_id] ?
- # controller.send(:user_list_url, controller.params[:user_id], controller.params[:id]) :
- # controller.send(:list_url, controller.params[:id]) }
+ # caches_action :index, :if => proc do |c|
+ # !c.request.format.json? # cache if is not a JSON request
+ # end
+ #
+ # 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])
+ # else
+ # controller.send(:list_url, controller.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 :layout => false, it will only cache your action
+ # content. It is useful when your layout has dynamic information.
#
module Actions
- def self.included(base) #:nodoc:
- base.extend(ClassMethods)
- base.class_eval do
- attr_accessor :rendered_action_cache, :action_cache_path
- end
- end
+ extend ActiveSupport::Concern
module ClassMethods
# Declares that +actions+ should be cached.
@@ -58,120 +80,83 @@ module ActionController #:nodoc:
def caches_action(*actions)
return unless cache_configured?
options = actions.extract_options!
- filter_options = { :only => actions, :if => options.delete(:if), :unless => options.delete(:unless) }
-
- cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options)
+ filter_options = options.extract!(:if, :unless).merge(:only => actions)
+ cache_options = options.extract!(:layout, :cache_path).merge(:store_options => options)
- around_filter cache_filter, filter_options
+ around_filter ActionCacheFilter.new(cache_options), filter_options
end
end
- protected
- def expire_action(options = {})
- return unless cache_configured?
+ def _render_cache_fragment(cache, extension, layout)
+ render :text => cache, :layout => layout, :content_type => Mime[extension || :html]
+ end
- if options[:action].is_a?(Array)
- options[:action].dup.each do |action|
- expire_fragment(ActionCachePath.path_for(self, options.merge({ :action => action }), false))
- end
- else
- expire_fragment(ActionCachePath.path_for(self, options, false))
- end
+ def _save_fragment(name, layout, options)
+ return unless caching_allowed?
+
+ content = layout ? view_context.content_for(:layout) : response_body
+ write_fragment(name, content, options)
+ end
+
+ protected
+ def expire_action(options = {})
+ return unless cache_configured?
+
+ actions = options[:action]
+ if actions.is_a?(Array)
+ actions.each {|action| expire_action(options.merge(:action => action)) }
+ else
+ expire_fragment(ActionCachePath.new(self, options, false).path)
end
+ end
class ActionCacheFilter #:nodoc:
def initialize(options, &block)
- @options = options
+ @cache_path, @store_options, @layout =
+ options.values_at(:cache_path, :store_options, :layout)
end
def filter(controller)
- should_continue = before(controller)
- yield if should_continue
- after(controller)
- end
-
- def before(controller)
- cache_path = ActionCachePath.new(controller, path_options_for(controller, @options.slice(:cache_path)))
-
- if cache = controller.read_fragment(cache_path.path, @options[:store_options])
- controller.rendered_action_cache = true
- set_content_type!(controller, cache_path.extension)
- options = { :text => cache }
- options.merge!(:layout => true) if cache_layout?
- controller.__send__(:render, options)
- false
+ path_options = if @cache_path.respond_to?(:call)
+ controller.instance_exec(controller, &@cache_path)
else
- controller.action_cache_path = cache_path
- end
- end
-
- def after(controller)
- return if controller.rendered_action_cache || !caching_allowed(controller)
- action_content = cache_layout? ? content_for_layout(controller) : controller.response.body
- controller.write_fragment(controller.action_cache_path.path, action_content, @options[:store_options])
- end
-
- private
- def set_content_type!(controller, extension)
- controller.response.content_type = Mime::Type.lookup_by_extension(extension).to_s if extension
+ @cache_path
end
- def path_options_for(controller, options)
- ((path_options = options[:cache_path]).respond_to?(:call) ? path_options.call(controller) : path_options) || {}
- end
-
- def caching_allowed(controller)
- controller.request.get? && controller.response.status.to_i == 200
- end
-
- def cache_layout?
- @options[:layout] == false
- end
+ cache_path = ActionCachePath.new(controller, path_options || {})
- def content_for_layout(controller)
- template = controller.view_context
- template.layout && template.instance_variable_get('@cached_content_for_layout')
+ if cache = controller.read_fragment(cache_path.path, @store_options)
+ controller._render_cache_fragment(cache, cache_path.extension, @layout == false)
+ else
+ yield
+ controller._save_fragment(cache_path.path, @layout == false, @store_options)
end
+ end
end
class ActionCachePath
attr_reader :path, :extension
- class << self
- def path_for(controller, options, infer_extension = true)
- new(controller, options, infer_extension).path
- end
- end
-
- # 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 expiring the cache -
- # expire_action should expire the same files regardless of the request format.
+ # 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
+ # expiring the cache - expire_action should expire the same files regardless of the
+ # request format.
def initialize(controller, options = {}, infer_extension = true)
if infer_extension
- extract_extension(controller.request)
- options = options.reverse_merge(:format => @extension) if options.is_a?(Hash)
+ @extension = controller.params[:format]
+ options.reverse_merge!(:format => @extension) if options.is_a?(Hash)
end
- path = controller.url_for(options).split('://').last
- normalize!(path)
- add_extension!(path, @extension)
- @path = URI.unescape(path)
+ path = controller.url_for(options).split(%r{://}).last
+ @path = normalize!(path)
end
- private
- def normalize!(path)
- path << 'index' if path[-1] == ?/
- end
-
- def add_extension!(path, extension)
- path << ".#{extension}" if extension and !path.ends_with?(extension)
- end
-
- def extract_extension(request)
- # Don't want just what comes after the last '.' to accommodate multi part extensions
- # such as tar.gz.
- @extension = request.path[/^[^.]+\.(.+)$/, 1] || request.cache_format
- end
+ private
+ def normalize!(path)
+ path << 'index' if path[-1] == ?/
+ path << ".#{extension}" if extension and !path.ends_with?(extension)
+ URI.unescape(path)
+ end
end
end
end
diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb
index 59e24619e3..8c1167d526 100644
--- a/actionpack/lib/action_controller/caching/fragments.rb
+++ b/actionpack/lib/action_controller/caching/fragments.rb
@@ -51,40 +51,32 @@ module ActionController #:nodoc:
# 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)
- event = ActiveSupport::Orchestra.instrument(:write_fragment, :key => key) do
+
+ ActiveSupport::Notifications.instrument(:write_fragment, :key => key) do
cache_store.write(key, content, options)
end
-
- self.class.log_with_time("Cached fragment miss: #{key}", event.duration)
content
end
# 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?
-
key = fragment_cache_key(key)
- event = ActiveSupport::Orchestra.instrument(:read_fragment, :key => key) do
+
+ ActiveSupport::Notifications.instrument(:read_fragment, :key => key) do
cache_store.read(key, options)
end
-
- self.class.log_with_time("Cached fragment hit: #{key}", event.duration)
- event.result
end
# 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)
- event = ActiveSupport::Orchestra.instrument(:fragment_exist?, :key => key) do
+
+ ActiveSupport::Notifications.instrument(:fragment_exist?, :key => key) do
cache_store.exist?(key, options)
end
-
- self.class.log_with_time("Cached fragment exists?: #{key}", event.duration)
- event.result
end
# Removes fragments from the cache.
@@ -106,11 +98,10 @@ module ActionController #:nodoc:
# method (or <tt>delete_matched</tt>, for Regexp keys.)
def expire_fragment(key, options = nil)
return unless cache_configured?
-
key = fragment_cache_key(key) unless key.is_a?(Regexp)
message = nil
- event = ActiveSupport::Orchestra.instrument(:expire_fragment, :key => key) do
+ ActiveSupport::Notifications.instrument(:expire_fragment, :key => key) do
if key.is_a?(Regexp)
message = "Expired fragments matching: #{key.source}"
cache_store.delete_matched(key, options)
@@ -119,9 +110,6 @@ module ActionController #:nodoc:
cache_store.delete(key, options)
end
end
-
- self.class.log_with_time(message, event.duration)
- event.result
end
end
end
diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb
index 4fb154470f..d46f528c7e 100644
--- a/actionpack/lib/action_controller/caching/pages.rb
+++ b/actionpack/lib/action_controller/caching/pages.rb
@@ -64,12 +64,9 @@ module ActionController #:nodoc:
return unless perform_caching
path = page_cache_path(path)
- event = ActiveSupport::Orchestra.instrument(:expire_page, :path => path) do
+ ActiveSupport::Notifications.instrument(:expire_page, :path => path) do
File.delete(path) if File.exist?(path)
end
-
- log_with_time("Expired page: #{path}", event.duration)
- event.result
end
# Manually cache the +content+ in the key determined by +path+. Example:
@@ -78,13 +75,10 @@ module ActionController #:nodoc:
return unless perform_caching
path = page_cache_path(path)
- event = ActiveSupport::Orchestra.instrument(:cache_page, :path => path) do
+ ActiveSupport::Notifications.instrument(:cache_page, :path => path) do
FileUtils.makedirs(File.dirname(path))
File.open(path, "wb+") { |f| f.write(content) }
end
-
- log_with_time("Cached page: #{path}", event.duration)
- event.result
end
# Caches the +actions+ using the page-caching approach that'll store the cache in a path within the page_cache_directory that
diff --git a/actionpack/lib/action_controller/caching/sweeping.rb b/actionpack/lib/action_controller/caching/sweeping.rb
index c1be264ffb..871f41bfdd 100644
--- a/actionpack/lib/action_controller/caching/sweeping.rb
+++ b/actionpack/lib/action_controller/caching/sweeping.rb
@@ -70,7 +70,7 @@ module ActionController #:nodoc:
protected
# gets the action cache path for the given options.
def action_path_for(options)
- ActionController::Caching::Actions::ActionCachePath.path_for(controller, options)
+ Actions::ActionCachePath.new(controller, options).path
end
# Retrieve instance variables set in the controller.
diff --git a/actionpack/lib/action_controller/deprecated.rb b/actionpack/lib/action_controller/deprecated.rb
index d98e9ac7bd..589061e77c 100644
--- a/actionpack/lib/action_controller/deprecated.rb
+++ b/actionpack/lib/action_controller/deprecated.rb
@@ -1,2 +1,4 @@
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
+ActionController::Routing = ActionDispatch::Routing
+ActionController::Routing::Routes = ActionDispatch::Routing::RouteSet.new
diff --git a/actionpack/lib/action_controller/dispatch/dispatcher.rb b/actionpack/lib/action_controller/dispatch/dispatcher.rb
index 008fb54715..e04da42637 100644
--- a/actionpack/lib/action_controller/dispatch/dispatcher.rb
+++ b/actionpack/lib/action_controller/dispatch/dispatcher.rb
@@ -50,7 +50,7 @@ module ActionController
def new
# DEPRECATE Rails application fallback
- Rails.application.new
+ Rails.application
end
end
end
diff --git a/actionpack/lib/action_controller/legacy/layout.rb b/actionpack/lib/action_controller/legacy/layout.rb
deleted file mode 100644
index 53762158fc..0000000000
--- a/actionpack/lib/action_controller/legacy/layout.rb
+++ /dev/null
@@ -1,256 +0,0 @@
-require 'active_support/core_ext/enumerable'
-require 'active_support/core_ext/class'
-require 'active_support/core_ext/class/delegating_attributes'
-require 'active_support/core_ext/class/inheritable_attributes'
-
-module ActionController #:nodoc:
- # MegasuperultraHAX
- # plz refactor ActionMailer
- class Base
- @@exempt_from_layout = [ActionView::TemplateHandlers::RJS]
- cattr_accessor :exempt_from_layout
- end
-
- module Layout #:nodoc:
- def self.included(base)
- base.extend(ClassMethods)
- base.class_inheritable_accessor :layout_name, :layout_conditions
- end
-
- # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
- # repeated setups. The inclusion pattern has pages that look like this:
- #
- # <%= render "shared/header" %>
- # Hello World
- # <%= render "shared/footer" %>
- #
- # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
- # and if you ever want to change the structure of these two includes, you'll have to change all the templates.
- #
- # With layouts, you can flip it around and have the common structure know where to insert changing content. This means
- # that the header and footer are only mentioned in one place, like this:
- #
- # // The header part of this layout
- # <%= yield %>
- # // The footer part of this layout
- #
- # And then you have content pages that look like this:
- #
- # hello world
- #
- # At rendering time, the content page is computed and then inserted in the layout, like this:
- #
- # // The header part of this layout
- # hello world
- # // The footer part of this layout
- #
- # == Accessing shared variables
- #
- # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
- # references that won't materialize before rendering time:
- #
- # <h1><%= @page_title %></h1>
- # <%= yield %>
- #
- # ...and content pages that fulfill these references _at_ rendering time:
- #
- # <% @page_title = "Welcome" %>
- # Off-world colonies offers you a chance to start a new life
- #
- # The result after rendering is:
- #
- # <h1>Welcome</h1>
- # Off-world colonies offers you a chance to start a new life
- #
- # == Automatic layout assignment
- #
- # If there is a template in <tt>app/views/layouts/</tt> with the same name as the current controller then it will be automatically
- # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named
- # <tt>app/views/layouts/weblog.erb</tt> or <tt>app/views/layouts/weblog.builder</tt> exists then it will be automatically set as
- # the layout for your WeblogController. You can create a layout with the name <tt>application.erb</tt> or <tt>application.builder</tt>
- # and this will be set as the default controller if there is no layout with the same name as the current controller and there is
- # no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout.
- # assignment. So an Admin::WeblogController will look for a template named <tt>app/views/layouts/admin/weblog.erb</tt>.
- # Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set.
- # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignment if the child
- # class has a layout with the same name.
- #
- # == Inheritance for layouts
- #
- # Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples:
- #
- # class BankController < ActionController::Base
- # layout "bank_standard"
- #
- # class InformationController < BankController
- #
- # class VaultController < BankController
- # layout :access_level_layout
- #
- # class EmployeeController < BankController
- # layout nil
- #
- # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites
- # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
- #
- # == Types of layouts
- #
- # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
- # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
- # be done either by specifying a method reference as a symbol or using an inline method (as a proc).
- #
- # The method reference is the preferred approach to variable layouts and is used like this:
- #
- # class WeblogController < ActionController::Base
- # layout :writers_and_readers
- #
- # def index
- # # fetching posts
- # end
- #
- # private
- # def writers_and_readers
- # logged_in? ? "writer_layout" : "reader_layout"
- # end
- #
- # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
- # is logged in or not.
- #
- # If you want to use an inline method, such as a proc, do something like this:
- #
- # class WeblogController < ActionController::Base
- # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
- #
- # Of course, the most common way of specifying a layout is still just as a plain template name:
- #
- # class WeblogController < ActionController::Base
- # layout "weblog_standard"
- #
- # If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
- # Otherwise, it will be looked up relative to the template root.
- #
- # == Conditional layouts
- #
- # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
- # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
- # <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
- #
- # class WeblogController < ActionController::Base
- # layout "weblog_standard", :except => :rss
- #
- # # ...
- #
- # end
- #
- # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
- # around the rendered view.
- #
- # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
- # #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
- #
- # == Using a different layout in the action render call
- #
- # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
- # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
- # You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
- #
- # class WeblogController < ActionController::Base
- # layout "weblog_standard"
- #
- # def help
- # render :action => "help", :layout => "help"
- # end
- # end
- #
- # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
- module ClassMethods
- extend ActiveSupport::Memoizable
-
- # If a layout is specified, all rendered actions will have their result rendered
- # when the layout <tt>yield</tt>s. This layout can itself depend on instance variables assigned during action
- # performance and have access to them as any normal template would.
- def layout(template_name, conditions = {}, auto = false)
- add_layout_conditions(conditions)
- self.layout_name = template_name
- end
-
- def memoized_default_layout(formats) #:nodoc:
- self.layout_name || begin
- layout = default_layout_name
- layout.is_a?(String) ? find_layout(layout, formats) : layout
- rescue ActionView::MissingTemplate
- end
- end
-
- def default_layout(*args)
- memoized_default_layout(*args)
- @_memoized_default_layout ||= ::ActiveSupport::ConcurrentHash.new
- @_memoized_default_layout[args] ||= memoized_default_layout(*args)
- end
-
- def memoized_find_layout(layout, formats) #:nodoc:
- return layout if layout.nil? || layout.respond_to?(:render)
- prefix = layout.to_s =~ /layouts\// ? nil : "layouts"
- find_template(layout.to_s, {:formats => formats}, :_prefix => prefix)
- end
-
- def find_layout(*args)
- @_memoized_find_layout ||= ::ActiveSupport::ConcurrentHash.new
- @_memoized_find_layout[args] ||= memoized_find_layout(*args)
- end
-
- def layout_list #:nodoc:
- Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
- end
- memoize :layout_list
-
- def default_layout_name
- layout_match = name.underscore.sub(/_controller$/, '')
- if layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
- superclass.default_layout_name if superclass.respond_to?(:default_layout_name)
- else
- layout_match
- end
- end
- memoize :default_layout_name
-
- private
- def add_layout_conditions(conditions)
- # :except => :foo == :except => [:foo] == :except => "foo" == :except => ["foo"]
- conditions.each {|k, v| conditions[k] = Array(v).map {|a| a.to_s} }
- write_inheritable_hash(:layout_conditions, conditions)
- end
- end
-
- def active_layout(name)
- name = self.class.default_layout(formats) if name == true
-
- layout_name = case name
- when Symbol then __send__(name)
- when Proc then name.call(self)
- else name
- end
-
- self.class.find_layout(layout_name, formats)
- end
-
- def _pick_layout(layout_name = nil, implicit = false)
- return unless layout_name || implicit
- layout_name = true if layout_name.nil?
- active_layout(layout_name) if action_has_layout? && layout_name
- end
-
- private
- def action_has_layout?
- if conditions = self.class.layout_conditions
- if only = conditions[:only]
- return only.include?(action_name)
- elsif except = conditions[:except]
- return !except.include?(action_name)
- end
- end
- true
- end
-
- end
-end
diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb
index e9007d3631..60b3f9a89b 100644
--- a/actionpack/lib/action_controller/metal.rb
+++ b/actionpack/lib/action_controller/metal.rb
@@ -49,7 +49,7 @@ module ActionController
# and response object available. You might wish to control the
# environment and response manually for performance reasons.
- attr_internal :status, :headers, :content_type, :app, :response
+ attr_internal :status, :headers, :content_type, :response
def initialize(*)
@_headers = {}
@@ -69,7 +69,7 @@ module ActionController
end
# :api: private
- def call(name, env)
+ def dispatch(name, env)
@_env = env
process(name)
to_a
@@ -95,7 +95,7 @@ module ActionController
end
def call(env)
- controller = @controller.new.call(@action, env)
+ @controller.new.dispatch(@action, env)
end
end
@@ -109,6 +109,10 @@ module ActionController
middleware_stack
end
+ def self.call(env)
+ action(env['action_dispatch.request.path_parameters'][:action]).call(env)
+ end
+
# Return a rack endpoint for the given action. Memoize the endpoint, so
# multiple calls into MyController.action will return the same object
# for the same action.
diff --git a/actionpack/lib/action_controller/metal/benchmarking.rb b/actionpack/lib/action_controller/metal/benchmarking.rb
index d4cb1e122d..e58df69172 100644
--- a/actionpack/lib/action_controller/metal/benchmarking.rb
+++ b/actionpack/lib/action_controller/metal/benchmarking.rb
@@ -1,4 +1,4 @@
-require 'benchmark'
+require 'active_support/core_ext/benchmark'
module ActionController #:nodoc:
# The benchmarking module times the performance of actions and reports to the logger. If the Active Record
@@ -6,25 +6,6 @@ module ActionController #:nodoc:
module Benchmarking #:nodoc:
extend ActiveSupport::Concern
- module ClassMethods
- # Log and benchmark the workings of a single block and silence whatever logging that may have happened inside it
- # (unless <tt>use_silence</tt> is set to false).
- #
- # The benchmark is only recorded if the current level of the logger matches the <tt>log_level</tt>, which makes it
- # easy to include benchmarking statements in production software that will remain inexpensive because the benchmark
- # will only be conducted if the log level is low enough.
- def benchmark(title, log_level = Logger::DEBUG, use_silence = true)
- if logger && logger.level == log_level
- result = nil
- ms = Benchmark.ms { result = use_silence ? silence { yield } : yield }
- logger.add(log_level, "#{title} (#{('%.1f' % ms)}ms)")
- result
- else
- yield
- end
- end
- end
-
protected
def render(*args, &block)
if logger
@@ -45,7 +26,7 @@ module ActionController #:nodoc:
else
super
end
- end
+ end
private
def process_action(*args)
diff --git a/actionpack/lib/action_controller/metal/compatibility.rb b/actionpack/lib/action_controller/metal/compatibility.rb
index 22f9ab219c..c251d79f4e 100644
--- a/actionpack/lib/action_controller/metal/compatibility.rb
+++ b/actionpack/lib/action_controller/metal/compatibility.rb
@@ -25,9 +25,11 @@ module ActionController
# cattr_reader :protected_instance_variables
cattr_accessor :protected_instance_variables
- self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render @variables_added @request_origin @url @parent_controller
- @action_name @before_filter_chain_aborted @action_cache_path @_headers @_params
- @_flash @_response)
+ self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render
+ @variables_added @request_origin @url
+ @parent_controller @action_name
+ @before_filter_chain_aborted @_headers @_params
+ @_flash @_response)
# Indicates whether or not optimise the generated named
# route helper methods
diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb
index 8575d30335..5156fbc1d5 100644
--- a/actionpack/lib/action_controller/metal/conditional_get.rb
+++ b/actionpack/lib/action_controller/metal/conditional_get.rb
@@ -3,6 +3,7 @@ module ActionController
extend ActiveSupport::Concern
include RackConvenience
+ 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.
@@ -27,43 +28,9 @@ module ActionController
response.etag = options[:etag] if options[:etag]
response.last_modified = options[:last_modified] if options[:last_modified]
+ response.cache_control[:public] = true if options[:public]
- if options[:public]
- response.cache_control[:public] = true
- end
-
- if request.fresh?(response)
- head :not_modified
- end
- end
-
- # Return a response that has no content (merely headers). The options
- # argument is interpreted to be a hash of header names and values.
- # This allows you to easily return a response that consists only of
- # significant headers:
- #
- # head :created, :location => person_path(@person)
- #
- # It can also be used to return exceptional conditions:
- #
- # return head(:method_not_allowed) unless request.post?
- # return head(:bad_request) unless valid_request?
- # render
- def head(*args)
- if args.length > 2
- raise ArgumentError, "too many arguments to head"
- elsif args.empty?
- raise ArgumentError, "too few arguments to head"
- end
- options = args.extract_options!
- status = args.shift || options.delete(:status) || :ok
- location = options.delete(:location)
-
- options.each do |key, value|
- headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
- end
-
- render :nothing => true, :status => status, :location => location
+ head :not_modified if request.fresh?(response)
end
# Sets the etag and/or last_modified on the response and checks it against
@@ -113,7 +80,7 @@ module ActionController
# Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
# intermediate caches (like caching proxy servers).
def expires_now #:doc:
- response.headers["Cache-Control"] = "no-cache"
+ response.cache_control.replace(:no_cache => true)
end
end
end
diff --git a/actionpack/lib/action_controller/metal/configuration.rb b/actionpack/lib/action_controller/metal/configuration.rb
new file mode 100644
index 0000000000..5c829853b7
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/configuration.rb
@@ -0,0 +1,28 @@
+module ActionController
+ module Configuration
+ extend ActiveSupport::Concern
+
+ def config
+ @config ||= self.class.config
+ end
+
+ def config=(config)
+ @config = config
+ end
+
+ module ClassMethods
+ def default_config
+ @default_config ||= {}
+ end
+
+ def config
+ self.config ||= default_config
+ end
+
+ def config=(config)
+ @config = ActiveSupport::OrderedHash.new
+ @config.merge!(config)
+ end
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/cookies.rb b/actionpack/lib/action_controller/metal/cookies.rb
index d4806623c3..6855ca1478 100644
--- a/actionpack/lib/action_controller/metal/cookies.rb
+++ b/actionpack/lib/action_controller/metal/cookies.rb
@@ -44,24 +44,31 @@ module ActionController #:nodoc:
# * <tt>:httponly</tt> - Whether this cookie is accessible via scripting or
# only HTTP. Defaults to +false+.
module Cookies
- def self.included(base)
- base.helper_method :cookies
+ extend ActiveSupport::Concern
+
+ include RackConvenience
+
+ included do
+ helper_method :cookies
end
- protected
- # Returns the cookie container, which operates as described above.
- def cookies
- @cookies ||= CookieJar.new(self)
- end
+ protected
+ # Returns the cookie container, which operates as described above.
+ def cookies
+ @cookies ||= CookieJar.build(request, response)
+ end
end
class CookieJar < Hash #:nodoc:
- def initialize(controller)
- @controller, @cookies = controller, controller.request.cookies
- super()
- update(@cookies)
+ def self.build(request, response)
+ new.tap do |hash|
+ hash.update(request.cookies)
+ hash.response = response
+ end
end
+ attr_accessor :response
+
# Returns the value of the cookie by +name+, or +nil+ if no such cookie exists.
def [](name)
super(name.to_s)
@@ -72,13 +79,16 @@ module ActionController #:nodoc:
def []=(key, options)
if options.is_a?(Hash)
options.symbolize_keys!
+ value = options[:value]
else
- options = { :value => options }
+ value = options
+ options = { :value => value }
end
- options[:path] = "/" unless options.has_key?(:path)
- super(key.to_s, options[:value])
- @controller.response.set_cookie(key, options)
+ super(key.to_s, value)
+
+ options[:path] ||= "/"
+ response.set_cookie(key, options)
end
# Removes the cookie on the client machine by setting the value to an empty string
@@ -86,9 +96,10 @@ module ActionController #:nodoc:
# an options hash to delete cookies with extra data such as a <tt>:path</tt>.
def delete(key, options = {})
options.symbolize_keys!
- options[:path] = "/" unless options.has_key?(:path)
- super(key.to_s)
- @controller.response.delete_cookie(key, options)
+ options[:path] ||= "/"
+ value = super(key.to_s)
+ response.delete_cookie(key, options)
+ value
end
end
end
diff --git a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb
index 4259d9de19..a53c052075 100644
--- a/actionpack/lib/action_controller/metal/filter_parameter_logging.rb
+++ b/actionpack/lib/action_controller/metal/filter_parameter_logging.rb
@@ -4,10 +4,6 @@ module ActionController
include AbstractController::Logger
- included do
- include InstanceMethodsForNewBase
- end
-
module ClassMethods
# Replace sensitive parameter data from the request log.
# Filters parameters that have any of the arguments as a substring.
@@ -17,8 +13,6 @@ module ActionController
# can be replaced using String#replace or similar method.
#
# Examples:
- # filter_parameter_logging
- # => Does nothing, just slows the logging process down
#
# filter_parameter_logging :password
# => replaces the value to all keys matching /password/i with "[FILTERED]"
@@ -33,64 +27,51 @@ module ActionController
# => reverses the value to all keys matching /secret/i, and
# replaces the value to all keys matching /foo|bar/i with "[FILTERED]"
def filter_parameter_logging(*filter_words, &block)
- parameter_filter = Regexp.new(filter_words.collect{ |s| s.to_s }.join('|'), true) if filter_words.length > 0
+ raise "You must filter at least one word from logging" if filter_words.empty?
+
+ parameter_filter = Regexp.new(filter_words.join('|'), true)
- define_method(:filter_parameters) do |unfiltered_parameters|
- filtered_parameters = {}
+ define_method(:filter_parameters) do |original_params|
+ filtered_params = {}
- unfiltered_parameters.each do |key, value|
+ original_params.each do |key, value|
if key =~ parameter_filter
- filtered_parameters[key] = '[FILTERED]'
+ value = '[FILTERED]'
elsif value.is_a?(Hash)
- filtered_parameters[key] = filter_parameters(value)
+ value = filter_parameters(value)
elsif value.is_a?(Array)
- filtered_parameters[key] = value.collect do |item|
- filter_parameters(item)
- end
+ value = value.map { |item| filter_parameters(item) }
elsif block_given?
key = key.dup
value = value.dup if value.duplicable?
yield key, value
- filtered_parameters[key] = value
- else
- filtered_parameters[key] = value
end
+
+ filtered_params[key] = value
end
- filtered_parameters
+ filtered_params
end
protected :filter_parameters
end
end
- module InstanceMethodsForNewBase
- # TODO : Fix the order of information inside such that it's exactly same as the old base
- def process(*)
- ret = super
-
- if logger
- parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
- parameters = parameters.except!(:controller, :action, :format, :_method, :only_path)
+ INTERNAL_PARAMS = [:controller, :action, :format, :_method, :only_path]
- unless parameters.empty?
- # TODO : Move DelayedLog to AS
- log = AbstractController::Logger::DelayedLog.new { " Parameters: #{parameters.inspect}" }
- logger.info(log)
- end
- end
-
- ret
+ def process(*)
+ response = super
+ if logger
+ parameters = filter_parameters(params).except!(*INTERNAL_PARAMS)
+ logger.info { " Parameters: #{parameters.inspect}" } unless parameters.empty?
end
+ response
end
- private
+ protected
- # TODO : This method is not needed for the new base
- def log_processing_for_parameters
- parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
- parameters = parameters.except!(:controller, :action, :format, :_method)
-
- logger.info " Parameters: #{parameters.inspect}" unless parameters.empty?
+ def filter_parameters(params)
+ params.dup
end
+
end
end
diff --git a/actionpack/lib/action_controller/metal/flash.rb b/actionpack/lib/action_controller/metal/flash.rb
index 8753253dc6..feb066a6f6 100644
--- a/actionpack/lib/action_controller/metal/flash.rb
+++ b/actionpack/lib/action_controller/metal/flash.rb
@@ -49,7 +49,7 @@ module ActionController #:nodoc:
class FlashHash < Hash
def initialize #:nodoc:
super
- @used = {}
+ @used = Set.new
end
def []=(k, v) #:nodoc:
@@ -65,7 +65,7 @@ module ActionController #:nodoc:
alias :merge! :update
def replace(h) #:nodoc:
- @used = {}
+ @used = Set.new
super
end
@@ -104,8 +104,8 @@ module ActionController #:nodoc:
# This method is called automatically by filters, so you generally don't need to care about it.
def sweep #:nodoc:
keys.each do |k|
- unless @used[k]
- use(k)
+ unless @used.include?(k)
+ @used << k
else
delete(k)
@used.delete(k)
@@ -113,47 +113,45 @@ module ActionController #:nodoc:
end
# clean up after keys that could have been left over by calling reject! or shift on the flash
- (@used.keys - keys).each{ |k| @used.delete(k) }
+ (@used - keys).each{ |k| @used.delete(k) }
end
- def store(session, key = "flash")
+ def store(session)
return if self.empty?
- session[key] = self
+ session["flash"] = self
end
- private
- # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
- # use() # marks the entire flash as used
- # use('msg') # marks the "msg" entry as used
- # use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
- # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
- # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself
- # if no key is passed.
- def use(key = nil, used = true)
- Array(key || keys).each { |k| @used[k] = used }
- return key ? self[key] : self
- end
+ private
+ # Used internally by the <tt>keep</tt> and <tt>discard</tt> methods
+ # use() # marks the entire flash as used
+ # use('msg') # marks the "msg" entry as used
+ # use(nil, false) # marks the entire flash as unused (keeps it around for one more action)
+ # use('msg', false) # marks the "msg" entry as unused (keeps it around for one more action)
+ # Returns the single value for the key you asked to be marked (un)used or the FlashHash itself
+ # if no key is passed.
+ def use(key = nil, used = true)
+ Array(key || keys).each { |k| used ? @used << k : @used.delete(k) }
+ return key ? self[key] : self
+ end
end
protected
def process_action(method_name)
super
- if defined? @_flash
- @_flash.store(session)
- remove_instance_variable(:@_flash)
- end
+ @_flash.store(session) if @_flash
+ @_flash = nil
end
def reset_session
super
- remove_instance_variable(:@_flash) if defined?(@_flash)
+ @_flash = nil
end
# Access the contents of the flash. Use <tt>flash["notice"]</tt> to
# read a notice you put there or <tt>flash["notice"] = "hello"</tt>
# to put a new one.
def flash #:doc:
- if !defined?(@_flash)
+ unless @_flash
@_flash = session["flash"] || FlashHash.new
@_flash.sweep
end
diff --git a/actionpack/lib/action_controller/metal/head.rb b/actionpack/lib/action_controller/metal/head.rb
new file mode 100644
index 0000000000..68fa0a0402
--- /dev/null
+++ b/actionpack/lib/action_controller/metal/head.rb
@@ -0,0 +1,27 @@
+module ActionController
+ module Head
+ # Return a response that has no content (merely headers). The options
+ # argument is interpreted to be a hash of header names and values.
+ # This allows you to easily return a response that consists only of
+ # significant headers:
+ #
+ # head :created, :location => person_path(@person)
+ #
+ # It can also be used to return exceptional conditions:
+ #
+ # return head(:method_not_allowed) unless request.post?
+ # return head(:bad_request) unless valid_request?
+ # render
+ def head(status, options = {})
+ options, status = status, nil if status.is_a?(Hash)
+ status ||= options.delete(:status) || :ok
+ location = options.delete(:location)
+
+ options.each do |key, value|
+ headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
+ end
+
+ render :nothing => true, :status => status, :location => location
+ end
+ end
+end \ No newline at end of file
diff --git a/actionpack/lib/action_controller/metal/helpers.rb b/actionpack/lib/action_controller/metal/helpers.rb
index 7c52779064..b4325e24ad 100644
--- a/actionpack/lib/action_controller/metal/helpers.rb
+++ b/actionpack/lib/action_controller/metal/helpers.rb
@@ -1,5 +1,3 @@
-require 'active_support/dependencies'
-
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
@@ -54,7 +52,7 @@ module ActionController
included do
# Set the default directory for helpers
extlib_inheritable_accessor(:helpers_dir) do
- defined?(RAILS_ROOT) ? "#{RAILS_ROOT}/app/helpers" : "app/helpers"
+ defined?(Rails) ? "#{Rails.root}/app/helpers" : "app/helpers"
end
end
@@ -64,46 +62,6 @@ module ActionController
super
end
- # The +helper+ class method can take a series of helper module names, a block, or both.
- #
- # ==== Parameters
- # *args<Array[Module, Symbol, String, :all]>
- # block<Block>:: A block defining helper methods
- #
- # ==== Examples
- # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file
- # and include the module in the template class. The second form illustrates how to include custom helpers
- # when working with namespaced controllers, or other cases where the file containing the helper definition is not
- # in one of Rails' standard load paths:
- # helper :foo # => requires 'foo_helper' and includes FooHelper
- # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper
- #
- # When the argument is a module it will be included directly in the template class.
- # helper FooHelper # => includes FooHelper
- #
- # When the argument is the symbol <tt>:all</tt>, the controller will include all helpers beneath
- # <tt>ActionController::Base.helpers_dir</tt> (defaults to <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT).
- # helper :all
- #
- # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available
- # to the template.
- # # One line
- # helper { def hello() "Hello, world!" end }
- # # Multi-line
- # helper do
- # def foo(bar)
- # "#{bar} is the very best"
- # end
- # end
- #
- # Finally, all the above styles can be mixed together, and the +helper+ method can be invoked with a mix of
- # +symbols+, +strings+, +modules+ and blocks.
- # helper(:three, BlindHelper) { def mice() 'mice' end }
- #
- def helper(*args, &block)
- super(*_modules_for_helpers(args), &block)
- 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:
@@ -123,15 +81,8 @@ module ActionController
end
private
- # Returns a list of modules, normalized from the acceptable kinds of
- # helpers with the following behavior:
- # String or Symbol:: :FooBar or "FooBar" becomes "foo_bar_helper",
- # and "foo_bar_helper.rb" is loaded using require_dependency.
- # :all:: Loads all modules in the #helpers_dir
- # Module:: No further processing
- #
- # After loading the appropriate files, the corresponding modules
- # are returned.
+ # Overwrite _modules_for_helpers to accept :all as argument, which loads
+ # all helpers in helpers_dir.
#
# ==== Parameters
# args<Array[String, Symbol, Module, all]>:: A list of helpers
@@ -140,20 +91,8 @@ module ActionController
# Array[Module]:: A normalized list of modules for the list of
# helpers provided.
def _modules_for_helpers(args)
- args.flatten.map! do |arg|
- case arg
- when :all
- _modules_for_helpers all_application_helpers
- when String, Symbol
- file_name = "#{arg.to_s.underscore}_helper"
- require_dependency(file_name, "Missing helper file helpers/%s.rb")
- file_name.camelize.constantize
- when Module
- arg
- else
- raise ArgumentError, "helper must be a String, Symbol, or Module"
- end
- end
+ args += all_application_helpers if args.delete(:all)
+ super(args)
end
def default_helper_module!
diff --git a/actionpack/lib/action_controller/metal/layouts.rb b/actionpack/lib/action_controller/metal/layouts.rb
index cac529b1ae..cc7088248a 100644
--- a/actionpack/lib/action_controller/metal/layouts.rb
+++ b/actionpack/lib/action_controller/metal/layouts.rb
@@ -167,26 +167,5 @@ module ActionController
controller_path
end
end
-
- private
- def _determine_template(options)
- super
-
- return if (options.key?(:text) || options.key?(:inline) || options.key?(:partial)) && !options.key?(:layout)
- layout = options.key?(:layout) ? options[:layout] : :default
- options[:_layout] = _layout_for_option(layout, options[:_template].details)
- end
-
- def _layout_for_option(name, details)
- case name
- when String then _layout_for_name(name, details)
- when true then _default_layout(details, true)
- when :default then _default_layout(details, false)
- when false, nil then nil
- else
- raise ArgumentError,
- "String, true, or false, expected for `layout'; you passed #{name.inspect}"
- end
- end
end
end
diff --git a/actionpack/lib/action_controller/metal/mime_responds.rb b/actionpack/lib/action_controller/metal/mime_responds.rb
index 3026067868..468c5f4fae 100644
--- a/actionpack/lib/action_controller/metal/mime_responds.rb
+++ b/actionpack/lib/action_controller/metal/mime_responds.rb
@@ -3,7 +3,8 @@ module ActionController #:nodoc:
extend ActiveSupport::Concern
included do
- class_inheritable_reader :mimes_for_respond_to
+ extlib_inheritable_accessor :responder, :mimes_for_respond_to, :instance_writer => false
+ self.responder = ActionController::Responder
clear_respond_to
end
@@ -46,7 +47,7 @@ module ActionController #:nodoc:
# Clear all mimes in respond_to.
#
def clear_respond_to
- write_inheritable_attribute(:mimes_for_respond_to, ActiveSupport::OrderedHash.new)
+ self.mimes_for_respond_to = ActiveSupport::OrderedHash.new
end
end
@@ -221,10 +222,6 @@ module ActionController #:nodoc:
end
end
- def responder
- ActionController::Responder
- end
-
protected
# Collect mimes declared in the class method respond_to valid for the
diff --git a/actionpack/lib/action_controller/metal/rack_convenience.rb b/actionpack/lib/action_controller/metal/rack_convenience.rb
index a80569c530..131d20114d 100644
--- a/actionpack/lib/action_controller/metal/rack_convenience.rb
+++ b/actionpack/lib/action_controller/metal/rack_convenience.rb
@@ -8,7 +8,7 @@ module ActionController
attr_internal :request
end
- def call(name, env)
+ def dispatch(action, env)
@_request = ActionDispatch::Request.new(env)
@_response = ActionDispatch::Response.new
@_response.request = request
diff --git a/actionpack/lib/action_controller/metal/redirector.rb b/actionpack/lib/action_controller/metal/redirector.rb
index f79fd54acd..b55f5e7bfc 100644
--- a/actionpack/lib/action_controller/metal/redirector.rb
+++ b/actionpack/lib/action_controller/metal/redirector.rb
@@ -16,7 +16,7 @@ module ActionController
logger.info("Redirected to #{url}") if logger && logger.info?
self.status = status
self.location = url.gsub(/[\r\n]/, '')
- self.response_body = "<html><body>You are being <a href=\"#{CGI.escapeHTML(url)}\">redirected</a>.</body></html>"
+ self.response_body = "<html><body>You are being <a href=\"#{ERB::Util.h(url)}\">redirected</a>.</body></html>"
end
end
end
diff --git a/actionpack/lib/action_controller/metal/rendering_controller.rb b/actionpack/lib/action_controller/metal/rendering_controller.rb
index 4da32ca1b3..237299cd30 100644
--- a/actionpack/lib/action_controller/metal/rendering_controller.rb
+++ b/actionpack/lib/action_controller/metal/rendering_controller.rb
@@ -1,54 +1,18 @@
module ActionController
- class HashKey
- @hash_keys = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = {} } }
-
- def self.get(klass, formats, locale)
- @hash_keys[klass][formats][locale] ||= new(klass, formats, locale)
- end
-
- attr_accessor :hash
- def initialize(klass, formats, locale)
- @formats, @locale = formats, locale
- @hash = [formats, locale].hash
- end
-
- alias_method :eql?, :equal?
-
- def inspect
- "#<HashKey -- formats: #{@formats} locale: #{@locale}>"
- end
- end
-
module RenderingController
extend ActiveSupport::Concern
- include AbstractController::RenderingController
-
- module ClassMethods
- def clear_template_caches!
- ActionView::Partials::PartialRenderer::TEMPLATES.clear
- template_cache.clear
- super
- end
-
- def template_cache
- @template_cache ||= Hash.new {|h,k| h[k] = {} }
- end
+ included do
+ include AbstractController::RenderingController
+ include AbstractController::LocalizedCache
end
def process_action(*)
self.formats = request.formats.map {|x| x.to_sym}
-
- super
- end
-
- def _determine_template(*)
super
end
def render(options)
- Thread.current[:format_locale_key] = HashKey.get(self.class, formats, I18n.locale)
-
super
self.content_type ||= options[:_template].mime_type.to_s
response_body
@@ -70,29 +34,19 @@ module ActionController
controller_path
end
- def with_template_cache(name)
- self.class.template_cache[Thread.current[:format_locale_key]][name] ||= super
- end
-
def _determine_template(options)
- if options.key?(:text)
- options[:_template] = ActionView::TextTemplate.new(options[:text], formats.first)
- elsif options.key?(:inline)
- handler = ActionView::Template.handler_class_for_extension(options[:type] || "erb")
- template = ActionView::Template.new(options[:inline], "inline #{options[:inline].inspect}", handler, {})
- options[:_template] = template
- elsif options.key?(:template)
- options[:_template_name] = options[:template]
- elsif options.key?(:file)
- options[:_template_name] = options[:file]
- elsif !options.key?(:partial)
- options[:_template_name] = (options[:action] || action_name).to_s
+ if (options.keys & [:partial, :file, :template, :text, :inline]).empty?
+ options[:_template_name] ||= options[:action]
options[:_prefix] = _prefix
end
super
end
+ def format_for_text
+ formats.first
+ end
+
def _process_options(options)
status, content_type, location = options.values_at(:status, :content_type, :location)
self.status = status if status
diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
index ad06657f86..113c20a758 100644
--- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb
+++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb
@@ -5,7 +5,6 @@ module ActionController #:nodoc:
module RequestForgeryProtection
extend ActiveSupport::Concern
- # TODO : Remove the defined? check when new base is the main base
include AbstractController::Helpers, Session
included do
@@ -21,26 +20,26 @@ module ActionController #:nodoc:
helper_method :protect_against_forgery?
end
- # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
- # forged link from another site, is done by embedding a token based on a random 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.
+ # 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.
#
# 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.
+ # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the
+ # error message in production by editing public/422.html. A call to this method in ApplicationController is
+ # generated by default in post-Rails 2.0 applications.
#
- # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
- # use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to include a hidden field named like that and
- # set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
- # make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
+ # 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>.
#
- # <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
- #
- # 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:
+ # 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
@@ -57,7 +56,8 @@ module ActionController #:nodoc:
# * 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"
+ # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look
+ # for "Expires: at end of session"
#
module ClassMethods
# Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
@@ -76,10 +76,7 @@ module ActionController #:nodoc:
# * <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, :only => options.delete(:only), :except => options.delete(:except)
- if options[:secret] || options[:digest]
- ActiveSupport::Deprecation.warn("protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect", caller)
- end
+ before_filter :verify_authenticity_token, options
end
end
@@ -88,31 +85,24 @@ module ActionController #:nodoc:
def verify_authenticity_token
verified_request? || raise(ActionController::InvalidAuthenticityToken)
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?
def verified_request?
- !protect_against_forgery? ||
- request.method == :get ||
- request.xhr? ||
- !verifiable_request_format? ||
+ !protect_against_forgery? || request.forgery_whitelisted? ||
form_authenticity_token == params[request_forgery_protection_token]
end
-
- def verifiable_request_format?
- !request.content_type.nil? && request.content_type.verify_request?
- end
-
+
# Sets the token value for the current session.
def form_authenticity_token
session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
end
def protect_against_forgery?
- allow_forgery_protection && request_forgery_protection_token
+ allow_forgery_protection
end
end
end
diff --git a/actionpack/lib/action_controller/metal/responder.rb b/actionpack/lib/action_controller/metal/responder.rb
index a16ed97131..e8e88e7479 100644
--- a/actionpack/lib/action_controller/metal/responder.rb
+++ b/actionpack/lib/action_controller/metal/responder.rb
@@ -14,12 +14,11 @@ module ActionController #:nodoc:
#
# When a request comes, for example with format :xml, three steps happen:
#
- # 1) respond_with searches for a template at people/index.xml;
+ # 1) responder searches for a template at people/index.xml;
#
- # 2) if the template is not available, it will create a responder, passing
- # the controller and the resource and invoke :to_xml on it;
+ # 2) if the template is not available, it will invoke :to_xml in the given resource;
#
- # 3) if the responder does not respond_to :to_xml, call to_format on it.
+ # 3) if the responder does not respond_to :to_xml, call :to_format on it.
#
# === Builtin HTTP verb semantics
#
@@ -88,14 +87,16 @@ module ActionController #:nodoc:
@resource = resources.is_a?(Array) ? resources.last : resources
@resources = resources
@options = options
+ @action = options.delete(:action)
@default_response = options.delete(:default_response)
end
delegate :head, :render, :redirect_to, :to => :controller
delegate :get?, :post?, :put?, :delete?, :to => :request
- # Undefine :to_json since it's defined on Object
- undef_method :to_json
+ # 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)
# Initializes a new responder an invoke the proper format. If the format is
# not defined, call to_format.
@@ -111,14 +112,8 @@ module ActionController #:nodoc:
#
def to_html
default_render
- rescue ActionView::MissingTemplate
- if get?
- raise
- elsif has_errors?
- render :action => default_action
- else
- redirect_to resource_location
- end
+ rescue ActionView::MissingTemplate => e
+ navigation_behavior(e)
end
# All others formats follow the procedure below. First we try to render a
@@ -127,9 +122,26 @@ module ActionController #:nodoc:
#
def to_format
default_render
- rescue ActionView::MissingTemplate
+ rescue ActionView::MissingTemplate => e
raise unless resourceful?
+ api_behavior(e)
+ end
+ protected
+
+ # This is the common behavior for "navigation" requests, like :html, :iphone and so forth.
+ def navigation_behavior(error)
+ if get?
+ raise error
+ elsif has_errors?
+ render :action => default_action
+ else
+ redirect_to resource_location
+ end
+ end
+
+ # This is the common behavior for "API" requests, like :xml and :json.
+ def api_behavior(error)
if get?
display resource
elsif has_errors?
@@ -141,8 +153,6 @@ module ActionController #:nodoc:
end
end
- protected
-
# Checks whether the resource responds to the current format or not.
#
def resourceful?
@@ -194,7 +204,7 @@ module ActionController #:nodoc:
# the verb is post.
#
def default_action
- request.post? ? :new : :edit
+ @action || (request.post? ? :new : :edit)
end
end
end
diff --git a/actionpack/lib/action_controller/metal/session_management.rb b/actionpack/lib/action_controller/metal/session_management.rb
index 654aa08cd3..d70f40ce7a 100644
--- a/actionpack/lib/action_controller/metal/session_management.rb
+++ b/actionpack/lib/action_controller/metal/session_management.rb
@@ -1,10 +1,8 @@
module ActionController #:nodoc:
module SessionManagement #:nodoc:
- def self.included(base)
- base.class_eval do
- extend ClassMethods
- end
- end
+ extend ActiveSupport::Concern
+
+ include ActionController::Configuration
module ClassMethods
# Set the session store to be used for keeping the session data between requests.
@@ -35,13 +33,6 @@ module ActionController #:nodoc:
session_options.merge!(options)
end
- # Returns the hash used to configure the session. Example use:
- #
- # ActionController::Base.session_options[:secure] = true # session only available over HTTPS
- def session_options
- @session_options ||= {}
- end
-
def session(*args)
ActiveSupport::Deprecation.warn(
"Disabling sessions for a single controller has been deprecated. " +
diff --git a/actionpack/lib/action_controller/metal/streaming.rb b/actionpack/lib/action_controller/metal/streaming.rb
index 4761763a26..43c661bef4 100644
--- a/actionpack/lib/action_controller/metal/streaming.rb
+++ b/actionpack/lib/action_controller/metal/streaming.rb
@@ -1,5 +1,3 @@
-require 'active_support/core_ext/string/bytesize'
-
module ActionController #:nodoc:
# Methods for sending arbitrary data and for streaming files to the browser,
# instead of rendering.
diff --git a/actionpack/lib/action_controller/metal/verification.rb b/actionpack/lib/action_controller/metal/verification.rb
index d3d78e3749..500cced539 100644
--- a/actionpack/lib/action_controller/metal/verification.rb
+++ b/actionpack/lib/action_controller/metal/verification.rb
@@ -79,8 +79,8 @@ module ActionController #:nodoc:
# do not apply this verification to the actions specified in the associated
# array (may also be a single value).
def verify(options={})
- before_filter :only => options[:only], :except => options[:except] do |c|
- c.__send__ :verify_action, options
+ before_filter :only => options[:only], :except => options[:except] do
+ verify_action options
end
end
end
diff --git a/actionpack/lib/action_controller/middleware.rb b/actionpack/lib/action_controller/middleware.rb
index fac0ed2645..17275793b7 100644
--- a/actionpack/lib/action_controller/middleware.rb
+++ b/actionpack/lib/action_controller/middleware.rb
@@ -1,34 +1,34 @@
module ActionController
class Middleware < Metal
class ActionMiddleware
- def initialize(controller)
- @controller = controller
+ def initialize(controller, app)
+ @controller, @app = controller, app
end
def call(env)
- controller = @controller.allocate
- controller.send(:initialize)
- controller.app = @app
- controller._call(env)
+ @controller.build(@app).dispatch(:index, env)
end
+ end
+
+ class << self
+ alias build new
- def app=(app)
- @app = app
+ def new(app)
+ ActionMiddleware.new(self, app)
end
end
-
- def self.new(app)
- middleware = ActionMiddleware.new(self)
- middleware.app = app
- middleware
+
+ attr_internal :app
+
+ def process(action)
+ response = super
+ self.status, self.headers, self.response_body = response if response.is_a?(Array)
+ response
end
-
- def _call(env)
- @_env = env
- @_request = ActionDispatch::Request.new(env)
- @_response = ActionDispatch::Response.new
- @_response.request = @_request
- process(:index)
+
+ def initialize(app)
+ super()
+ @_app = app
end
def index
diff --git a/actionpack/lib/action_controller/notifications.rb b/actionpack/lib/action_controller/notifications.rb
new file mode 100644
index 0000000000..1a4f29e0e2
--- /dev/null
+++ b/actionpack/lib/action_controller/notifications.rb
@@ -0,0 +1,10 @@
+require 'active_support/notifications'
+
+ActiveSupport::Notifications.subscribe(/(read|write|cache|expire|exist)_(fragment|page)\??/) do |*args|
+ event = ActiveSupport::Notifications::Event.new(*args)
+
+ if logger = ActionController::Base.logger
+ human_name = event.name.to_s.humanize
+ logger.info("#{human_name} (%.1fms)" % event.duration)
+ end
+end
diff --git a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb
index 2adf3575a7..eaed00cfb7 100644
--- a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb
+++ b/actionpack/lib/action_controller/polymorphic_routes.rb
@@ -80,9 +80,8 @@ module ActionController
record_or_hash_or_array = record_or_hash_or_array[0] if record_or_hash_or_array.size == 1
end
- record = extract_record(record_or_hash_or_array)
- record = record.to_model if record.respond_to?(:to_model)
- namespace = extract_namespace(record_or_hash_or_array)
+ record = extract_record(record_or_hash_or_array)
+ record = record.to_model if record.respond_to?(:to_model)
args = case record_or_hash_or_array
when Hash; [ record_or_hash_or_array ]
@@ -105,8 +104,7 @@ module ActionController
end
args.delete_if {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
-
- named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
+ named_route = build_named_route_call(record_or_hash_or_array, inflection, options)
url_options = options.except(:action, :routing_type)
unless url_options.empty?
@@ -138,18 +136,6 @@ module ActionController
EOT
end
- def formatted_polymorphic_url(record_or_hash, options = {})
- ActiveSupport::Deprecation.warn("formatted_polymorphic_url has been deprecated. Please pass :format to the polymorphic_url method instead", caller)
- options[:format] = record_or_hash.pop if Array === record_or_hash
- polymorphic_url(record_or_hash, options)
- end
-
- def formatted_polymorphic_path(record_or_hash, options = {})
- ActiveSupport::Deprecation.warn("formatted_polymorphic_path has been deprecated. Please pass :format to the polymorphic_path method instead", caller)
- options[:format] = record_or_hash.pop if record_or_hash === Array
- polymorphic_url(record_or_hash, options.merge(:routing_type => :path))
- end
-
private
def action_prefix(options)
options[:action] ? "#{options[:action]}_" : ''
@@ -159,7 +145,7 @@ module ActionController
options[:routing_type] || :url
end
- def build_named_route_call(records, namespace, inflection, options = {})
+ def build_named_route_call(records, inflection, options = {})
unless records.is_a?(Array)
record = extract_record(records)
route = ''
@@ -169,7 +155,7 @@ module ActionController
if parent.is_a?(Symbol) || parent.is_a?(String)
string << "#{parent}_"
else
- string << "#{RecordIdentifier.__send__("plural_class_name", parent)}".singularize
+ string << RecordIdentifier.__send__("plural_class_name", parent).singularize
string << "_"
end
end
@@ -178,12 +164,12 @@ module ActionController
if record.is_a?(Symbol) || record.is_a?(String)
route << "#{record}_"
else
- route << "#{RecordIdentifier.__send__("plural_class_name", record)}"
+ route << RecordIdentifier.__send__("plural_class_name", record)
route = route.singularize if inflection == :singular
route << "_"
end
- action_prefix(options) + namespace + route + routing_type(options).to_s
+ action_prefix(options) + route + routing_type(options).to_s
end
def extract_record(record_or_hash_or_array)
@@ -193,18 +179,5 @@ module ActionController
else record_or_hash_or_array
end
end
-
- # Remove the first symbols from the array and return the url prefix
- # implied by those symbols.
- def extract_namespace(record_or_hash_or_array)
- return "" unless record_or_hash_or_array.is_a?(Array)
-
- namespace_keys = []
- while (key = record_or_hash_or_array.first) && key.is_a?(String) || key.is_a?(Symbol)
- namespace_keys << record_or_hash_or_array.shift
- end
-
- namespace_keys.map {|k| "#{k}_"}.join
- end
end
end
diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb
deleted file mode 100644
index 5b9ded83dd..0000000000
--- a/actionpack/lib/action_controller/routing.rb
+++ /dev/null
@@ -1,389 +0,0 @@
-require 'cgi'
-require 'uri'
-require 'set'
-
-require 'active_support/core_ext/module/aliasing'
-require 'active_support/core_ext/module/attribute_accessors'
-require 'action_controller/routing/optimisations'
-require 'action_controller/routing/routing_ext'
-require 'action_controller/routing/route'
-require 'action_controller/routing/segments'
-require 'action_controller/routing/builder'
-require 'action_controller/routing/route_set'
-require 'action_controller/routing/recognition_optimisation'
-
-module ActionController
- # == Routing
- #
- # The routing module provides URL rewriting in native Ruby. It's a way to
- # redirect incoming requests to controllers and actions. This replaces
- # mod_rewrite rules. Best of all, Rails' Routing works with any web server.
- # Routes are defined in <tt>config/routes.rb</tt>.
- #
- # Consider the following route, installed by Rails when you generate your
- # application:
- #
- # map.connect ':controller/:action/:id'
- #
- # This route states that it expects requests to consist of a
- # <tt>:controller</tt> followed by an <tt>:action</tt> that in turn is fed
- # some <tt>:id</tt>.
- #
- # Suppose you get an incoming request for <tt>/blog/edit/22</tt>, you'll end up
- # with:
- #
- # params = { :controller => 'blog',
- # :action => 'edit',
- # :id => '22'
- # }
- #
- # Think of creating routes as drawing a map for your requests. The map tells
- # them where to go based on some predefined pattern:
- #
- # ActionController::Routing::Routes.draw do |map|
- # Pattern 1 tells some request to go to one place
- # Pattern 2 tell them to go to another
- # ...
- # end
- #
- # The following symbols are special:
- #
- # :controller maps to your controller name
- # :action maps to an action with your controllers
- #
- # Other names simply map to a parameter as in the case of <tt>:id</tt>.
- #
- # == Route priority
- #
- # Not all routes are created equally. Routes have priority defined by the
- # order of appearance of the routes in the <tt>config/routes.rb</tt> file. The priority goes
- # from top to bottom. The last route in that file is at the lowest priority
- # and will be applied last. If no route matches, 404 is returned.
- #
- # Within blocks, the empty pattern is at the highest priority.
- # In practice this works out nicely:
- #
- # ActionController::Routing::Routes.draw do |map|
- # map.with_options :controller => 'blog' do |blog|
- # blog.show '', :action => 'list'
- # end
- # map.connect ':controller/:action/:view'
- # end
- #
- # In this case, invoking blog controller (with an URL like '/blog/')
- # without parameters will activate the 'list' action by default.
- #
- # == Defaults routes and default parameters
- #
- # Setting a default route is straightforward in Rails - you simply append a
- # Hash at the end of your mapping to set any default parameters.
- #
- # Example:
- #
- # ActionController::Routing:Routes.draw do |map|
- # map.connect ':controller/:action/:id', :controller => 'blog'
- # end
- #
- # This sets up +blog+ as the default controller if no other is specified.
- # This means visiting '/' would invoke the blog controller.
- #
- # More formally, you can include arbitrary parameters in the route, thus:
- #
- # map.connect ':controller/:action/:id', :action => 'show', :page => 'Dashboard'
- #
- # This will pass the :page parameter to all incoming requests that match this route.
- #
- # Note: The default routes, as provided by the Rails generator, make all actions in every
- # controller accessible via GET requests. You should consider removing them or commenting
- # them out if you're using named routes and resources.
- #
- # == Named routes
- #
- # Routes can be named with the syntax <tt>map.name_of_route options</tt>,
- # allowing for easy reference within your source as +name_of_route_url+
- # for the full URL and +name_of_route_path+ for the URI path.
- #
- # Example:
- #
- # # In routes.rb
- # map.login 'login', :controller => 'accounts', :action => 'login'
- #
- # # With render, redirect_to, tests, etc.
- # redirect_to login_url
- #
- # Arguments can be passed as well.
- #
- # redirect_to show_item_path(:id => 25)
- #
- # Use <tt>map.root</tt> as a shorthand to name a route for the root path "".
- #
- # # In routes.rb
- # map.root :controller => 'blogs'
- #
- # # would recognize http://www.example.com/ as
- # params = { :controller => 'blogs', :action => 'index' }
- #
- # # and provide these named routes
- # root_url # => 'http://www.example.com/'
- # root_path # => ''
- #
- # You can also specify an already-defined named route in your <tt>map.root</tt> call:
- #
- # # In routes.rb
- # map.new_session :controller => 'sessions', :action => 'new'
- # map.root :new_session
- #
- # Note: when using +with_options+, the route is simply named after the
- # method you call on the block parameter rather than map.
- #
- # # In routes.rb
- # map.with_options :controller => 'blog' do |blog|
- # blog.show '', :action => 'list'
- # blog.delete 'delete/:id', :action => 'delete'
- # blog.edit 'edit/:id', :action => 'edit'
- # end
- #
- # # provides named routes for show, delete, and edit
- # link_to @article.title, show_path(:id => @article.id)
- #
- # == Pretty URLs
- #
- # Routes can generate pretty URLs. For example:
- #
- # map.connect 'articles/:year/:month/:day',
- # :controller => 'articles',
- # :action => 'find_by_date',
- # :year => /\d{4}/,
- # :month => /\d{1,2}/,
- # :day => /\d{1,2}/
- #
- # Using the route above, the URL "http://localhost:3000/articles/2005/11/06"
- # maps to
- #
- # params = {:year => '2005', :month => '11', :day => '06'}
- #
- # == Regular Expressions and parameters
- # You can specify a regular expression to define a format for a parameter.
- #
- # map.geocode 'geocode/:postalcode', :controller => 'geocode',
- # :action => 'show', :postalcode => /\d{5}(-\d{4})?/
- #
- # or, more formally:
- #
- # map.geocode 'geocode/:postalcode', :controller => 'geocode',
- # :action => 'show', :requirements => { :postalcode => /\d{5}(-\d{4})?/ }
- #
- # Formats can include the 'ignorecase' and 'extended syntax' regular
- # expression modifiers:
- #
- # map.geocode 'geocode/:postalcode', :controller => 'geocode',
- # :action => 'show', :postalcode => /hx\d\d\s\d[a-z]{2}/i
- #
- # map.geocode 'geocode/:postalcode', :controller => 'geocode',
- # :action => 'show',:requirements => {
- # :postalcode => /# Postcode format
- # \d{5} #Prefix
- # (-\d{4})? #Suffix
- # /x
- # }
- #
- # Using the multiline match modifier will raise an ArgumentError.
- # Encoding regular expression modifiers are silently ignored. The
- # match will always use the default encoding or ASCII.
- #
- # == Route globbing
- #
- # Specifying <tt>*[string]</tt> as part of a rule like:
- #
- # map.connect '*path' , :controller => 'blog' , :action => 'unrecognized?'
- #
- # will glob all remaining parts of the route that were not recognized earlier.
- # The globbed values are in <tt>params[:path]</tt> as an array of path segments.
- #
- # == Route conditions
- #
- # With conditions you can define restrictions on routes. Currently the only valid condition is <tt>:method</tt>.
- #
- # * <tt>:method</tt> - Allows you to specify which method can access the route. Possible values are <tt>:post</tt>,
- # <tt>:get</tt>, <tt>:put</tt>, <tt>:delete</tt> and <tt>:any</tt>. The default value is <tt>:any</tt>,
- # <tt>:any</tt> means that any method can access the route.
- #
- # Example:
- #
- # map.connect 'post/:id', :controller => 'posts', :action => 'show',
- # :conditions => { :method => :get }
- # map.connect 'post/:id', :controller => 'posts', :action => 'create_comment',
- # :conditions => { :method => :post }
- #
- # Now, if you POST to <tt>/posts/:id</tt>, it will route to the <tt>create_comment</tt> action. A GET on the same
- # URL will route to the <tt>show</tt> action.
- #
- # == Reloading routes
- #
- # You can reload routes if you feel you must:
- #
- # ActionController::Routing::Routes.reload
- #
- # This will clear all named routes and reload routes.rb if the file has been modified from
- # last load. To absolutely force reloading, use <tt>reload!</tt>.
- #
- # == Testing Routes
- #
- # The two main methods for testing your routes:
- #
- # === +assert_routing+
- #
- # def test_movie_route_properly_splits
- # opts = {:controller => "plugin", :action => "checkout", :id => "2"}
- # assert_routing "plugin/checkout/2", opts
- # end
- #
- # +assert_routing+ lets you test whether or not the route properly resolves into options.
- #
- # === +assert_recognizes+
- #
- # def test_route_has_options
- # opts = {:controller => "plugin", :action => "show", :id => "12"}
- # assert_recognizes opts, "/plugins/show/12"
- # end
- #
- # Note the subtle difference between the two: +assert_routing+ tests that
- # a URL fits options while +assert_recognizes+ tests that a URL
- # breaks into parameters properly.
- #
- # In tests you can simply pass the URL or named route to +get+ or +post+.
- #
- # def send_to_jail
- # get '/jail'
- # assert_response :success
- # assert_template "jail/front"
- # end
- #
- # def goes_to_login
- # get login_url
- # #...
- # end
- #
- # == View a list of all your routes
- #
- # Run <tt>rake routes</tt>.
- #
- module Routing
- SEPARATORS = %w( / . ? )
-
- HTTP_METHODS = [:get, :head, :post, :put, :delete, :options]
-
- ALLOWED_REQUIREMENTS_FOR_OPTIMISATION = [:controller, :action].to_set
-
- # The root paths which may contain controller files
- mattr_accessor :controller_paths
- self.controller_paths = []
-
- # A helper module to hold URL related helpers.
- module Helpers
- include PolymorphicRoutes
- end
-
- class << self
- # Expects an array of controller names as the first argument.
- # Executes the passed block with only the named controllers named available.
- # This method is used in internal Rails testing.
- def with_controllers(names)
- prior_controllers = @possible_controllers
- use_controllers! names
- yield
- ensure
- use_controllers! prior_controllers
- end
-
- # Returns an array of paths, cleaned of double-slashes and relative path references.
- # * "\\\" and "//" become "\\" or "/".
- # * "/foo/bar/../config" becomes "/foo/config".
- # The returned array is sorted by length, descending.
- def normalize_paths(paths)
- # do the hokey-pokey of path normalization...
- paths = paths.collect do |path|
- path = path.
- gsub("//", "/"). # replace double / chars with a single
- gsub("\\\\", "\\"). # replace double \ chars with a single
- gsub(%r{(.)[\\/]$}, '\1') # drop final / or \ if path ends with it
-
- # eliminate .. paths where possible
- re = %r{[^/\\]+[/\\]\.\.[/\\]}
- path.gsub!(re, "") while path.match(re)
- path
- end
-
- # start with longest path, first
- paths = paths.uniq.sort_by { |path| - path.length }
- end
-
- # Returns the array of controller names currently available to ActionController::Routing.
- def possible_controllers
- unless @possible_controllers
- @possible_controllers = []
-
- paths = controller_paths.select { |path| File.directory?(path) && path != "." }
-
- seen_paths = Hash.new {|h, k| h[k] = true; false}
- normalize_paths(paths).each do |load_path|
- Dir["#{load_path}/**/*_controller.rb"].collect do |path|
- next if seen_paths[path.gsub(%r{^\.[/\\]}, "")]
-
- controller_name = path[(load_path.length + 1)..-1]
-
- controller_name.gsub!(/_controller\.rb\Z/, '')
- @possible_controllers << controller_name
- end
- end
-
- # remove duplicates
- @possible_controllers.uniq!
- end
- @possible_controllers
- end
-
- # Replaces the internal list of controllers available to ActionController::Routing with the passed argument.
- # ActionController::Routing.use_controllers!([ "posts", "comments", "admin/comments" ])
- def use_controllers!(controller_names)
- @possible_controllers = controller_names
- end
-
- # Returns a controller path for a new +controller+ based on a +previous+ controller path.
- # Handles 4 scenarios:
- #
- # * stay in the previous controller:
- # controller_relative_to( nil, "groups/discussion" ) # => "groups/discussion"
- #
- # * stay in the previous namespace:
- # controller_relative_to( "posts", "groups/discussion" ) # => "groups/posts"
- #
- # * forced move to the root namespace:
- # controller_relative_to( "/posts", "groups/discussion" ) # => "posts"
- #
- # * previous namespace is root:
- # controller_relative_to( "posts", "anything_with_no_slashes" ) # =>"posts"
- #
- def controller_relative_to(controller, previous)
- if controller.nil? then previous
- elsif controller[0] == ?/ then controller[1..-1]
- elsif %r{^(.*)/} =~ previous then "#{$1}/#{controller}"
- else controller
- end
- end
- end
-
- Routes = RouteSet.new
-
- ActiveSupport::Inflector.module_eval do
- # Ensures that routes are reloaded when Rails inflections are updated.
- def inflections_with_route_reloading(&block)
- returning(inflections_without_route_reloading(&block)) {
- ActionController::Routing::Routes.reload! if block_given?
- }
- end
-
- alias_method_chain :inflections, :route_reloading
- end
- end
-end
diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb
deleted file mode 100644
index 42ad12e1ea..0000000000
--- a/actionpack/lib/action_controller/routing/builder.rb
+++ /dev/null
@@ -1,199 +0,0 @@
-require 'active_support/core_ext/hash/except'
-
-module ActionController
- module Routing
- class RouteBuilder #:nodoc:
- attr_reader :separators, :optional_separators
- attr_reader :separator_regexp, :nonseparator_regexp, :interval_regexp
-
- def initialize
- @separators = Routing::SEPARATORS
- @optional_separators = %w( / )
-
- @separator_regexp = /[#{Regexp.escape(separators.join)}]/
- @nonseparator_regexp = /\A([^#{Regexp.escape(separators.join)}]+)/
- @interval_regexp = /(.*?)(#{separator_regexp}|$)/
- end
-
- # Accepts a "route path" (a string defining a route), and returns the array
- # of segments that corresponds to it. Note that the segment array is only
- # partially initialized--the defaults and requirements, for instance, need
- # to be set separately, via the +assign_route_options+ method, and the
- # <tt>optional?</tt> method for each segment will not be reliable until after
- # +assign_route_options+ is called, as well.
- def segments_for_route_path(path)
- rest, segments = path, []
-
- until rest.empty?
- segment, rest = segment_for(rest)
- segments << segment
- end
- segments
- end
-
- # A factory method that returns a new segment instance appropriate for the
- # format of the given string.
- def segment_for(string)
- segment =
- case string
- when /\A\.(:format)?\//
- OptionalFormatSegment.new
- when /\A:(\w+)/
- key = $1.to_sym
- key == :controller ? ControllerSegment.new(key) : DynamicSegment.new(key)
- when /\A\*(\w+)/
- PathSegment.new($1.to_sym, :optional => true)
- when /\A\?(.*?)\?/
- StaticSegment.new($1, :optional => true)
- when nonseparator_regexp
- StaticSegment.new($1)
- when separator_regexp
- DividerSegment.new($&, :optional => optional_separators.include?($&))
- end
- [segment, $~.post_match]
- end
-
- # Split the given hash of options into requirement and default hashes. The
- # segments are passed alongside in order to distinguish between default values
- # and requirements.
- def divide_route_options(segments, options)
- options = options.except(:path_prefix, :name_prefix)
-
- if options[:namespace]
- options[:controller] = "#{options.delete(:namespace).sub(/\/$/, '')}/#{options[:controller]}"
- end
-
- requirements = (options.delete(:requirements) || {}).dup
- defaults = (options.delete(:defaults) || {}).dup
- conditions = (options.delete(:conditions) || {}).dup
-
- validate_route_conditions(conditions)
-
- path_keys = segments.collect { |segment| segment.key if segment.respond_to?(:key) }.compact
- options.each do |key, value|
- hash = (path_keys.include?(key) && ! value.is_a?(Regexp)) ? defaults : requirements
- hash[key] = value
- end
-
- [defaults, requirements, conditions]
- end
-
- # Takes a hash of defaults and a hash of requirements, and assigns them to
- # the segments. Any unused requirements (which do not correspond to a segment)
- # are returned as a hash.
- def assign_route_options(segments, defaults, requirements)
- route_requirements = {} # Requirements that do not belong to a segment
-
- segment_named = Proc.new do |key|
- segments.detect { |segment| segment.key == key if segment.respond_to?(:key) }
- end
-
- requirements.each do |key, requirement|
- segment = segment_named[key]
- if segment
- raise TypeError, "#{key}: requirements on a path segment must be regular expressions" unless requirement.is_a?(Regexp)
- if requirement.source =~ %r{\A(\\A|\^)|(\\Z|\\z|\$)\Z}
- raise ArgumentError, "Regexp anchor characters are not allowed in routing requirements: #{requirement.inspect}"
- end
- if requirement.multiline?
- raise ArgumentError, "Regexp multiline option not allowed in routing requirements: #{requirement.inspect}"
- end
- segment.regexp = requirement
- else
- route_requirements[key] = requirement
- end
- end
-
- defaults.each do |key, default|
- segment = segment_named[key]
- raise ArgumentError, "#{key}: No matching segment exists; cannot assign default" unless segment
- segment.is_optional = true
- segment.default = default.to_param if default
- end
-
- assign_default_route_options(segments)
- ensure_required_segments(segments)
- route_requirements
- end
-
- # Assign default options, such as 'index' as a default for <tt>:action</tt>. This
- # method must be run *after* user supplied requirements and defaults have
- # been applied to the segments.
- def assign_default_route_options(segments)
- segments.each do |segment|
- next unless segment.is_a? DynamicSegment
- case segment.key
- when :action
- if segment.regexp.nil? || segment.regexp.match('index').to_s == 'index'
- segment.default ||= 'index'
- segment.is_optional = true
- end
- when :id
- if segment.default.nil? && segment.regexp.nil? || segment.regexp =~ ''
- segment.is_optional = true
- end
- end
- end
- end
-
- # Makes sure that there are no optional segments that precede a required
- # segment. If any are found that precede a required segment, they are
- # made required.
- def ensure_required_segments(segments)
- allow_optional = true
- segments.reverse_each do |segment|
- allow_optional &&= segment.optional?
- if !allow_optional && segment.optional?
- unless segment.optionality_implied?
- warn "Route segment \"#{segment.to_s}\" cannot be optional because it precedes a required segment. This segment will be required."
- end
- segment.is_optional = false
- elsif allow_optional && segment.respond_to?(:default) && segment.default
- # if a segment has a default, then it is optional
- segment.is_optional = true
- end
- end
- end
-
- # Construct and return a route with the given path and options.
- def build(path, options)
- # Wrap the path with slashes
- path = "/#{path}" unless path[0] == ?/
- path = "#{path}/" unless path[-1] == ?/
-
- prefix = options[:path_prefix].to_s.gsub(/^\//,'')
- path = "/#{prefix}#{path}" unless prefix.blank?
-
- segments = segments_for_route_path(path)
- defaults, requirements, conditions = divide_route_options(segments, options)
- requirements = assign_route_options(segments, defaults, requirements)
-
- # TODO: Segments should be frozen on initialize
- segments.each { |segment| segment.freeze }
-
- route = Route.new(segments, requirements, conditions)
-
- if !route.significant_keys.include?(:controller)
- raise ArgumentError, "Illegal route: the :controller must be specified!"
- end
-
- route.freeze
- end
-
- private
- def validate_route_conditions(conditions)
- if method = conditions[:method]
- [method].flatten.each do |m|
- if m == :head
- raise ArgumentError, "HTTP method HEAD is invalid in route conditions. Rails processes HEAD requests the same as GETs, returning just the response headers"
- end
-
- unless HTTP_METHODS.include?(m.to_sym)
- raise ArgumentError, "Invalid HTTP method specified in route conditions: #{conditions.inspect}"
- end
- end
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/routing/optimisations.rb b/actionpack/lib/action_controller/routing/optimisations.rb
deleted file mode 100644
index 714cf97861..0000000000
--- a/actionpack/lib/action_controller/routing/optimisations.rb
+++ /dev/null
@@ -1,130 +0,0 @@
-module ActionController
- module Routing
- # Much of the slow performance from routes comes from the
- # complexity of expiry, <tt>:requirements</tt> matching, defaults providing
- # and figuring out which url pattern to use. With named routes
- # we can avoid the expense of finding the right route. So if
- # they've provided the right number of arguments, and have no
- # <tt>:requirements</tt>, we can just build up a string and return it.
- #
- # To support building optimisations for other common cases, the
- # generation code is separated into several classes
- module Optimisation
- def generate_optimisation_block(route, kind)
- return "" unless route.optimise?
- OPTIMISERS.inject("") do |memo, klazz|
- memo << klazz.new(route, kind).source_code
- memo
- end
- end
-
- class Optimiser
- attr_reader :route, :kind
- GLOBAL_GUARD_CONDITIONS = [
- "(!defined?(default_url_options) || default_url_options.blank?)",
- "(!defined?(controller.default_url_options) || controller.default_url_options.blank?)",
- "defined?(request)",
- "request"
- ]
-
- def initialize(route, kind)
- @route = route
- @kind = kind
- end
-
- def guard_conditions
- ["false"]
- end
-
- def generation_code
- 'nil'
- end
-
- def source_code
- if applicable?
- guard_condition = (GLOBAL_GUARD_CONDITIONS + guard_conditions).join(" && ")
- "return #{generation_code} if #{guard_condition}\n"
- else
- "\n"
- end
- end
-
- # Temporarily disabled <tt>:url</tt> optimisation pending proper solution to
- # Issues around request.host etc.
- def applicable?
- true
- end
- end
-
- # Given a route
- #
- # map.person '/people/:id'
- #
- # If the user calls <tt>person_url(@person)</tt>, we can simply
- # return a string like "/people/#{@person.to_param}"
- # rather than triggering the expensive logic in +url_for+.
- class PositionalArguments < Optimiser
- def guard_conditions
- number_of_arguments = route.required_segment_keys.size
- # if they're using foo_url(:id=>2) it's one
- # argument, but we don't want to generate /foos/id2
- if number_of_arguments == 1
- ["args.size == 1", "!args.first.is_a?(Hash)"]
- else
- ["args.size == #{number_of_arguments}"]
- end
- end
-
- def generation_code
- elements = []
- idx = 0
-
- if kind == :url
- elements << '#{request.protocol}'
- elements << '#{request.host_with_port}'
- end
-
- elements << '#{ActionController::Base.relative_url_root if ActionController::Base.relative_url_root}'
-
- # The last entry in <tt>route.segments</tt> appears to *always* be a
- # 'divider segment' for '/' but we have assertions to ensure that
- # we don't include the trailing slashes, so skip them.
- (route.segments.size == 1 ? route.segments : route.segments[0..-2]).each do |segment|
- if segment.is_a?(DynamicSegment)
- elements << segment.interpolation_chunk("args[#{idx}].to_param")
- idx += 1
- else
- elements << segment.interpolation_chunk
- end
- end
- %("#{elements * ''}")
- end
- end
-
- # This case is mostly the same as the positional arguments case
- # above, but it supports additional query parameters as the last
- # argument
- class PositionalArgumentsWithAdditionalParams < PositionalArguments
- def guard_conditions
- ["args.size == #{route.segment_keys.size + 1}"] +
- UrlRewriter::RESERVED_OPTIONS.collect{ |key| "!args.last.has_key?(:#{key})" }
- end
-
- # This case uses almost the same code as positional arguments,
- # but add a question mark and args.last.to_query on the end,
- # unless the last arg is empty
- def generation_code
- super.insert(-2, '#{\'?\' + args.last.to_query unless args.last.empty?}')
- end
-
- # To avoid generating "http://localhost/?host=foo.example.com" we
- # can't use this optimisation on routes without any segments
- def applicable?
- super && route.segment_keys.size > 0
- end
- end
-
- OPTIMISERS = [PositionalArguments, PositionalArgumentsWithAdditionalParams]
- end
- end
-end
diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb
deleted file mode 100644
index 9bfebff0c0..0000000000
--- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb
+++ /dev/null
@@ -1,167 +0,0 @@
-module ActionController
- module Routing
- # BEFORE: 0.191446860631307 ms/url
- # AFTER: 0.029847304022858 ms/url
- # Speed up: 6.4 times
- #
- # Route recognition is slow due to one-by-one iterating over
- # a whole routeset (each map.resources generates at least 14 routes)
- # and matching weird regexps on each step.
- #
- # We optimize this by skipping all URI segments that 100% sure can't
- # be matched, moving deeper in a tree of routes (where node == segment)
- # until first possible match is accured. In such case, we start walking
- # a flat list of routes, matching them with accurate matcher.
- # So, first step: search a segment tree for the first relevant index.
- # Second step: iterate routes starting with that index.
- #
- # How tree is walked? We can do a recursive tests, but it's smarter:
- # We just create a tree of if-s and elsif-s matching segments.
- #
- # We have segments of 3 flavors:
- # 1) nil (no segment, route finished)
- # 2) const-dot-dynamic (like "/posts.:xml", "/preview.:size.jpg")
- # 3) const (like "/posts", "/comments")
- # 4) dynamic ("/:id", "file.:size.:extension")
- #
- # We split incoming string into segments and iterate over them.
- # When segment is nil, we drop immediately, on a current node index.
- # When segment is equal to some const, we step into branch.
- # If none constants matched, we step into 'dynamic' branch (it's a last).
- # If we can't match anything, we drop to last index on a level.
- #
- # Note: we maintain the original routes order, so we finish building
- # steps on a first dynamic segment.
- #
- #
- # Example. Given the routes:
- # 0 /posts/
- # 1 /posts/:id
- # 2 /posts/:id/comments
- # 3 /posts/blah
- # 4 /users/
- # 5 /users/:id
- # 6 /users/:id/profile
- #
- # request_uri = /users/123
- #
- # There will be only 4 iterations:
- # 1) segm test for /posts prefix, skip all /posts/* routes
- # 2) segm test for /users/
- # 3) segm test for /users/:id
- # (jump to list index = 5)
- # 4) full test for /users/:id => here we are!
- class RouteSet
- def recognize_path(path, environment={})
- result = recognize_optimized(path, environment) and return result
-
- # Route was not recognized. Try to find out why (maybe wrong verb).
- allows = HTTP_METHODS.select { |verb| routes.find { |r| r.recognize(path, environment.merge(:method => verb)) } }
-
- if environment[:method] && !HTTP_METHODS.include?(environment[:method])
- raise NotImplemented.new(*allows)
- elsif !allows.empty?
- raise MethodNotAllowed.new(*allows)
- else
- raise RoutingError, "No route matches #{path.inspect} with #{environment.inspect}"
- end
- end
-
- def segment_tree(routes)
- tree = [0]
-
- i = -1
- routes.each do |route|
- i += 1
- # not fast, but runs only once
- segments = to_plain_segments(route.segments.inject("") { |str,s| str << s.to_s })
-
- node = tree
- segments.each do |seg|
- seg = :dynamic if seg && seg[0] == ?:
- node << [seg, [i]] if node.empty? || node[node.size - 1][0] != seg
- node = node[node.size - 1][1]
- end
- end
- tree
- end
-
- def generate_code(list, padding=' ', level = 0)
- # a digit
- return padding + "#{list[0]}\n" if list.size == 1 && !(Array === list[0])
-
- body = padding + "(seg = segments[#{level}]; \n"
-
- i = 0
- was_nil = false
- list.each do |item|
- if Array === item
- i += 1
- start = (i == 1)
- tag, sub = item
- if tag == :dynamic
- body += padding + "#{start ? 'if' : 'elsif'} true\n"
- body += generate_code(sub, padding + " ", level + 1)
- break
- elsif tag == nil && !was_nil
- was_nil = true
- body += padding + "#{start ? 'if' : 'elsif'} seg.nil?\n"
- body += generate_code(sub, padding + " ", level + 1)
- else
- body += padding + "#{start ? 'if' : 'elsif'} seg == '#{tag}'\n"
- body += generate_code(sub, padding + " ", level + 1)
- end
- end
- end
- body += padding + "else\n"
- body += padding + " #{list[0]}\n"
- body += padding + "end)\n"
- body
- end
-
- # this must be really fast
- def to_plain_segments(str)
- str = str.dup
- str.sub!(/^\/+/,'')
- str.sub!(/\/+$/,'')
- segments = str.split(/\.[^\/]+\/+|\/+|\.[^\/]+\Z/) # cut off ".format" also
- segments << nil
- segments
- end
-
- private
- def write_recognize_optimized!
- tree = segment_tree(routes)
- body = generate_code(tree)
-
- remove_recognize_optimized!
-
- instance_eval %{
- def recognize_optimized(path, env)
- segments = to_plain_segments(path)
- index = #{body}
- return nil unless index
- while index < routes.size
- result = routes[index].recognize(path, env) and return result
- index += 1
- end
- nil
- end
- }, '(recognize_optimized)', 1
- end
-
- def clear_recognize_optimized!
- remove_recognize_optimized!
- write_recognize_optimized!
- end
-
- def remove_recognize_optimized!
- if respond_to?(:recognize_optimized)
- class << self
- remove_method :recognize_optimized
- end
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/routing/resources.rb b/actionpack/lib/action_controller/routing/resources.rb
deleted file mode 100644
index 06506435a2..0000000000
--- a/actionpack/lib/action_controller/routing/resources.rb
+++ /dev/null
@@ -1,685 +0,0 @@
-require 'active_support/core_ext/hash/slice'
-require 'active_support/core_ext/object/try'
-
-module ActionController
- # == Overview
- #
- # ActionController::Resources are a way of defining RESTful \resources. A RESTful \resource, in basic terms,
- # is something that can be pointed at and it will respond with a representation of the data requested.
- # In real terms this could mean a user with a browser requests an HTML page, or that a desktop application
- # requests XML data.
- #
- # RESTful design is based on the assumption that there are four generic verbs that a user of an
- # application can request from a \resource (the noun).
- #
- # \Resources can be requested using four basic HTTP verbs (GET, POST, PUT, DELETE), the method used
- # denotes the type of action that should take place.
- #
- # === The Different Methods and their Usage
- #
- # * GET - Requests for a \resource, no saving or editing of a \resource should occur in a GET request.
- # * POST - Creation of \resources.
- # * PUT - Editing of attributes on a \resource.
- # * DELETE - Deletion of a \resource.
- #
- # === Examples
- #
- # # A GET request on the Posts resource is asking for all Posts
- # GET /posts
- #
- # # A GET request on a single Post resource is asking for that particular Post
- # GET /posts/1
- #
- # # A POST request on the Posts resource is asking for a Post to be created with the supplied details
- # POST /posts # with => { :post => { :title => "My Whizzy New Post", :body => "I've got a brand new combine harvester" } }
- #
- # # A PUT request on a single Post resource is asking for a Post to be updated
- # PUT /posts # with => { :id => 1, :post => { :title => "Changed Whizzy Title" } }
- #
- # # A DELETE request on a single Post resource is asking for it to be deleted
- # DELETE /posts # with => { :id => 1 }
- #
- # By using the REST convention, users of our application can assume certain things about how the data
- # is requested and how it is returned. Rails simplifies the routing part of RESTful design by
- # supplying you with methods to create them in your routes.rb file.
- #
- # Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer
- module Resources
- INHERITABLE_OPTIONS = :namespace, :shallow
-
- class Resource #:nodoc:
- DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy
-
- attr_reader :collection_methods, :member_methods, :new_methods
- attr_reader :path_prefix, :name_prefix, :path_segment
- attr_reader :plural, :singular
- attr_reader :options
-
- def initialize(entities, options)
- @plural ||= entities
- @singular ||= options[:singular] || plural.to_s.singularize
- @path_segment = options.delete(:as) || @plural
-
- @options = options
-
- arrange_actions
- add_default_actions
- set_allowed_actions
- set_prefixes
- end
-
- def controller
- @controller ||= "#{options[:namespace]}#{(options[:controller] || plural).to_s}"
- end
-
- def requirements(with_id = false)
- @requirements ||= @options[:requirements] || {}
- @id_requirement ||= { :id => @requirements.delete(:id) || /[^#{Routing::SEPARATORS.join}]+/ }
-
- with_id ? @requirements.merge(@id_requirement) : @requirements
- end
-
- def conditions
- @conditions ||= @options[:conditions] || {}
- end
-
- def path
- @path ||= "#{path_prefix}/#{path_segment}"
- end
-
- def new_path
- new_action = self.options[:path_names][:new] if self.options[:path_names]
- new_action ||= Base.resources_path_names[:new]
- @new_path ||= "#{path}/#{new_action}"
- end
-
- def shallow_path_prefix
- @shallow_path_prefix ||= @options[:shallow] ? @options[:namespace].try(:sub, /\/$/, '') : path_prefix
- end
-
- def member_path
- @member_path ||= "#{shallow_path_prefix}/#{path_segment}/:id"
- end
-
- def nesting_path_prefix
- @nesting_path_prefix ||= "#{shallow_path_prefix}/#{path_segment}/:#{singular}_id"
- end
-
- def shallow_name_prefix
- @shallow_name_prefix ||= @options[:shallow] ? @options[:namespace].try(:gsub, /\//, '_') : name_prefix
- end
-
- def nesting_name_prefix
- "#{shallow_name_prefix}#{singular}_"
- end
-
- def action_separator
- @action_separator ||= Base.resource_action_separator
- end
-
- def uncountable?
- @singular.to_s == @plural.to_s
- end
-
- def has_action?(action)
- !DEFAULT_ACTIONS.include?(action) || action_allowed?(action)
- end
-
- protected
- def arrange_actions
- @collection_methods = arrange_actions_by_methods(options.delete(:collection))
- @member_methods = arrange_actions_by_methods(options.delete(:member))
- @new_methods = arrange_actions_by_methods(options.delete(:new))
- end
-
- def add_default_actions
- add_default_action(member_methods, :get, :edit)
- add_default_action(new_methods, :get, :new)
- end
-
- def set_allowed_actions
- only, except = @options.values_at(:only, :except)
- @allowed_actions ||= {}
-
- if only == :all || except == :none
- only = nil
- except = []
- elsif only == :none || except == :all
- only = []
- except = nil
- end
-
- if only
- @allowed_actions[:only] = Array(only).map {|a| a.to_sym }
- elsif except
- @allowed_actions[:except] = Array(except).map {|a| a.to_sym }
- end
- end
-
- def action_allowed?(action)
- only, except = @allowed_actions.values_at(:only, :except)
- (!only || only.include?(action)) && (!except || !except.include?(action))
- end
-
- def set_prefixes
- @path_prefix = options.delete(:path_prefix)
- @name_prefix = options.delete(:name_prefix)
- end
-
- def arrange_actions_by_methods(actions)
- (actions || {}).inject({}) do |flipped_hash, (key, value)|
- (flipped_hash[value] ||= []) << key
- flipped_hash
- end
- end
-
- def add_default_action(collection, method, action)
- (collection[method] ||= []).unshift(action)
- end
- end
-
- class SingletonResource < Resource #:nodoc:
- def initialize(entity, options)
- @singular = @plural = entity
- options[:controller] ||= @singular.to_s.pluralize
- super
- end
-
- alias_method :shallow_path_prefix, :path_prefix
- alias_method :shallow_name_prefix, :name_prefix
- alias_method :member_path, :path
- alias_method :nesting_path_prefix, :path
- end
-
- # Creates named routes for implementing verb-oriented controllers
- # for a collection \resource.
- #
- # For example:
- #
- # map.resources :messages
- #
- # will map the following actions in the corresponding controller:
- #
- # class MessagesController < ActionController::Base
- # # GET messages_url
- # def index
- # # return all messages
- # end
- #
- # # GET new_message_url
- # def new
- # # return an HTML form for describing a new message
- # end
- #
- # # POST messages_url
- # def create
- # # create a new message
- # end
- #
- # # GET message_url(:id => 1)
- # def show
- # # find and return a specific message
- # end
- #
- # # GET edit_message_url(:id => 1)
- # def edit
- # # return an HTML form for editing a specific message
- # end
- #
- # # PUT message_url(:id => 1)
- # def update
- # # find and update a specific message
- # end
- #
- # # DELETE message_url(:id => 1)
- # def destroy
- # # delete a specific message
- # end
- # end
- #
- # Along with the routes themselves, +resources+ generates named routes for use in
- # controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers:
- #
- # Named Route Helpers
- # ============ =====================================================
- # messages messages_url, hash_for_messages_url,
- # messages_path, hash_for_messages_path
- #
- # message message_url(id), hash_for_message_url(id),
- # message_path(id), hash_for_message_path(id)
- #
- # new_message new_message_url, hash_for_new_message_url,
- # new_message_path, hash_for_new_message_path
- #
- # edit_message edit_message_url(id), hash_for_edit_message_url(id),
- # edit_message_path(id), hash_for_edit_message_path(id)
- #
- # You can use these helpers instead of +url_for+ or methods that take +url_for+ parameters. For example:
- #
- # redirect_to :controller => 'messages', :action => 'index'
- # # and
- # <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
- #
- # now become:
- #
- # redirect_to messages_url
- # # and
- # <%= link_to "edit this message", edit_message_url(@message) # calls @message.id automatically
- #
- # Since web browsers don't support the PUT and DELETE verbs, you will need to add a parameter '_method' to your
- # form tags. The form helpers make this a little easier. For an update form with a <tt>@message</tt> object:
- #
- # <%= form_tag message_path(@message), :method => :put %>
- #
- # or
- #
- # <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
- #
- # or
- #
- # <% form_for @message do |f| %>
- #
- # which takes into account whether <tt>@message</tt> is a new record or not and generates the
- # path and method accordingly.
- #
- # The +resources+ method accepts the following options to customize the resulting routes:
- # * <tt>:collection</tt> - Add named routes for other actions that operate on the collection.
- # Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>,
- # an array of any of the previous, or <tt>:any</tt> if the method does not matter.
- # These routes map to a URL like /messages/rss, with a route of +rss_messages_url+.
- # * <tt>:member</tt> - Same as <tt>:collection</tt>, but for actions that operate on a specific member.
- # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action.
- # * <tt>:controller</tt> - Specify the controller name for the routes.
- # * <tt>:singular</tt> - Specify the singular name used in the member routes.
- # * <tt>:requirements</tt> - Set custom routing parameter requirements; this is a hash of either
- # regular expressions (which must match for the route to match) or extra parameters. For example:
- #
- # map.resource :profile, :path_prefix => ':name', :requirements => { :name => /[a-zA-Z]+/, :extra => 'value' }
- #
- # will only match if the first part is alphabetic, and will pass the parameter :extra to the controller.
- # * <tt>:conditions</tt> - Specify custom routing recognition conditions. \Resources sets the <tt>:method</tt> value for the method-specific routes.
- # * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example:
- # # products_path == '/productos'
- # map.resources :products, :as => 'productos' do |product|
- # # product_reviews_path(product) == '/productos/1234/comentarios'
- # product.resources :product_reviews, :as => 'comentarios'
- # end
- #
- # * <tt>:has_one</tt> - Specify nested \resources, this is a shorthand for mapping singleton \resources beneath the current.
- # * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural \resources.
- #
- # You may directly specify the routing association with +has_one+ and +has_many+ like:
- #
- # map.resources :notes, :has_one => :author, :has_many => [:comments, :attachments]
- #
- # This is the same as:
- #
- # map.resources :notes do |notes|
- # notes.resource :author
- # notes.resources :comments
- # notes.resources :attachments
- # end
- #
- # * <tt>:path_names</tt> - Specify different path names for the actions. For example:
- # # new_products_path == '/productos/nuevo'
- # # bids_product_path(1) == '/productos/1/licitacoes'
- # map.resources :products, :as => 'productos', :member => { :bids => :get }, :path_names => { :new => 'nuevo', :bids => 'licitacoes' }
- #
- # You can also set default action names from an environment, like this:
- # config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
- #
- # * <tt>:path_prefix</tt> - Set a prefix to the routes with required route variables.
- #
- # Weblog comments usually belong to a post, so you might use +resources+ like:
- #
- # map.resources :articles
- # map.resources :comments, :path_prefix => '/articles/:article_id'
- #
- # You can nest +resources+ calls to set this automatically:
- #
- # map.resources :articles do |article|
- # article.resources :comments
- # end
- #
- # The comment \resources work the same, but must now include a value for <tt>:article_id</tt>.
- #
- # article_comments_url(@article)
- # article_comment_url(@article, @comment)
- #
- # article_comments_url(:article_id => @article)
- # article_comment_url(:article_id => @article, :id => @comment)
- #
- # If you don't want to load all objects from the database you might want to use the <tt>article_id</tt> directly:
- #
- # articles_comments_url(@comment.article_id, @comment)
- #
- # * <tt>:name_prefix</tt> - Define a prefix for all generated routes, usually ending in an underscore.
- # Use this if you have named routes that may clash.
- #
- # map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
- # map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
- #
- # You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested \resource:
- #
- # map.resources :articles do |article|
- # article.resources :comments, :name_prefix => nil
- # end
- #
- # This will yield named \resources like so:
- #
- # comments_url(@article)
- # comment_url(@article, @comment)
- #
- # * <tt>:shallow</tt> - If true, paths for nested resources which reference a specific member
- # (ie. those with an :id parameter) will not use the parent path prefix or name prefix.
- #
- # The <tt>:shallow</tt> option is inherited by any nested resource(s).
- #
- # For example, 'users', 'posts' and 'comments' all use shallow paths with the following nested resources:
- #
- # map.resources :users, :shallow => true do |user|
- # user.resources :posts do |post|
- # post.resources :comments
- # end
- # end
- # # --> GET /users/1/posts (maps to the PostsController#index action as usual)
- # # also adds the usual named route called "user_posts"
- # # --> GET /posts/2 (maps to the PostsController#show action as if it were not nested)
- # # also adds the named route called "post"
- # # --> GET /posts/2/comments (maps to the CommentsController#index action)
- # # also adds the named route called "post_comments"
- # # --> GET /comments/2 (maps to the CommentsController#show action as if it were not nested)
- # # also adds the named route called "comment"
- #
- # You may also use <tt>:shallow</tt> in combination with the +has_one+ and +has_many+ shorthand notations like:
- #
- # map.resources :users, :has_many => { :posts => :comments }, :shallow => true
- #
- # * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to.
- #
- # <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a
- # list of action names. By default, routes are generated for all seven actions.
- #
- # For example:
- #
- # map.resources :posts, :only => [:index, :show] do |post|
- # post.resources :comments, :except => [:update, :destroy]
- # end
- # # --> GET /posts (maps to the PostsController#index action)
- # # --> POST /posts (fails)
- # # --> GET /posts/1 (maps to the PostsController#show action)
- # # --> DELETE /posts/1 (fails)
- # # --> POST /posts/1/comments (maps to the CommentsController#create action)
- # # --> PUT /posts/1/comments/1 (fails)
- #
- # If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
- #
- # Examples:
- #
- # map.resources :messages, :path_prefix => "/thread/:thread_id"
- # # --> GET /thread/7/messages/1
- #
- # map.resources :messages, :collection => { :rss => :get }
- # # --> GET /messages/rss (maps to the #rss action)
- # # also adds a named route called "rss_messages"
- #
- # map.resources :messages, :member => { :mark => :post }
- # # --> POST /messages/1/mark (maps to the #mark action)
- # # also adds a named route called "mark_message"
- #
- # map.resources :messages, :new => { :preview => :post }
- # # --> POST /messages/new/preview (maps to the #preview action)
- # # also adds a named route called "preview_new_message"
- #
- # map.resources :messages, :new => { :new => :any, :preview => :post }
- # # --> POST /messages/new/preview (maps to the #preview action)
- # # also adds a named route called "preview_new_message"
- # # --> /messages/new can be invoked via any request method
- #
- # map.resources :messages, :controller => "categories",
- # :path_prefix => "/category/:category_id",
- # :name_prefix => "category_"
- # # --> GET /categories/7/messages/1
- # # has named route "category_message"
- #
- # The +resources+ method sets HTTP method restrictions on the routes it generates. For example, making an
- # HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
- # <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for \resource routes.
- def resources(*entities, &block)
- options = entities.extract_options!
- entities.each { |entity| map_resource(entity, options.dup, &block) }
- end
-
- # Creates named routes for implementing verb-oriented controllers for a singleton \resource.
- # A singleton \resource is global to its current context. For unnested singleton \resources,
- # the \resource is global to the current user visiting the application, such as a user's
- # <tt>/account</tt> profile. For nested singleton \resources, the \resource is global to its parent
- # \resource, such as a <tt>projects</tt> \resource that <tt>has_one :project_manager</tt>.
- # The <tt>project_manager</tt> should be mapped as a singleton \resource under <tt>projects</tt>:
- #
- # map.resources :projects do |project|
- # project.resource :project_manager
- # end
- #
- # See +resources+ for general conventions. These are the main differences:
- # * A singular name is given to <tt>map.resource</tt>. The default controller name is still taken from the plural name.
- # * To specify a custom plural name, use the <tt>:plural</tt> option. There is no <tt>:singular</tt> option.
- # * No default index route is created for the singleton \resource controller.
- # * When nesting singleton \resources, only the singular name is used as the path prefix (example: 'account/messages/1')
- #
- # For example:
- #
- # map.resource :account
- #
- # maps these actions in the Accounts controller:
- #
- # class AccountsController < ActionController::Base
- # # GET new_account_url
- # def new
- # # return an HTML form for describing the new account
- # end
- #
- # # POST account_url
- # def create
- # # create an account
- # end
- #
- # # GET account_url
- # def show
- # # find and return the account
- # end
- #
- # # GET edit_account_url
- # def edit
- # # return an HTML form for editing the account
- # end
- #
- # # PUT account_url
- # def update
- # # find and update the account
- # end
- #
- # # DELETE account_url
- # def destroy
- # # delete the account
- # end
- # end
- #
- # Along with the routes themselves, +resource+ generates named routes for
- # use in controllers and views. <tt>map.resource :account</tt> produces
- # these named routes and helpers:
- #
- # Named Route Helpers
- # ============ =============================================
- # account account_url, hash_for_account_url,
- # account_path, hash_for_account_path
- #
- # new_account new_account_url, hash_for_new_account_url,
- # new_account_path, hash_for_new_account_path
- #
- # edit_account edit_account_url, hash_for_edit_account_url,
- # edit_account_path, hash_for_edit_account_path
- def resource(*entities, &block)
- options = entities.extract_options!
- entities.each { |entity| map_singleton_resource(entity, options.dup, &block) }
- end
-
- private
- def map_resource(entities, options = {}, &block)
- resource = Resource.new(entities, options)
-
- with_options :controller => resource.controller do |map|
- map_associations(resource, options)
-
- if block_given?
- with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
- end
-
- map_collection_actions(map, resource)
- map_default_collection_actions(map, resource)
- map_new_actions(map, resource)
- map_member_actions(map, resource)
- end
- end
-
- def map_singleton_resource(entities, options = {}, &block)
- resource = SingletonResource.new(entities, options)
-
- with_options :controller => resource.controller do |map|
- map_associations(resource, options)
-
- if block_given?
- with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
- end
-
- map_collection_actions(map, resource)
- map_new_actions(map, resource)
- map_member_actions(map, resource)
- map_default_singleton_actions(map, resource)
- end
- end
-
- def map_associations(resource, options)
- map_has_many_associations(resource, options.delete(:has_many), options) if options[:has_many]
-
- path_prefix = "#{options.delete(:path_prefix)}#{resource.nesting_path_prefix}"
- name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}"
-
- Array(options[:has_one]).each do |association|
- resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix))
- end
- end
-
- def map_has_many_associations(resource, associations, options)
- case associations
- when Hash
- associations.each do |association,has_many|
- map_has_many_associations(resource, association, options.merge(:has_many => has_many))
- end
- when Array
- associations.each do |association|
- map_has_many_associations(resource, association, options)
- end
- when Symbol, String
- resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many]))
- else
- end
- end
-
- def map_collection_actions(map, resource)
- resource.collection_methods.each do |method, actions|
- actions.each do |action|
- [method].flatten.each do |m|
- action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
- action_path ||= action
-
- map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
- end
- end
- end
- end
-
- def map_default_collection_actions(map, resource)
- index_route_name = "#{resource.name_prefix}#{resource.plural}"
-
- if resource.uncountable?
- index_route_name << "_index"
- end
-
- map_resource_routes(map, resource, :index, resource.path, index_route_name)
- map_resource_routes(map, resource, :create, resource.path, index_route_name)
- end
-
- def map_default_singleton_actions(map, resource)
- map_resource_routes(map, resource, :create, resource.path, "#{resource.shallow_name_prefix}#{resource.singular}")
- end
-
- def map_new_actions(map, resource)
- resource.new_methods.each do |method, actions|
- actions.each do |action|
- route_path = resource.new_path
- route_name = "new_#{resource.name_prefix}#{resource.singular}"
-
- unless action == :new
- route_path = "#{route_path}#{resource.action_separator}#{action}"
- route_name = "#{action}_#{route_name}"
- end
-
- map_resource_routes(map, resource, action, route_path, route_name, method)
- end
- end
- end
-
- def map_member_actions(map, resource)
- resource.member_methods.each do |method, actions|
- actions.each do |action|
- [method].flatten.each do |m|
- action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
- action_path ||= Base.resources_path_names[action] || action
-
- map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true })
- end
- end
- end
-
- route_path = "#{resource.shallow_name_prefix}#{resource.singular}"
- map_resource_routes(map, resource, :show, resource.member_path, route_path)
- map_resource_routes(map, resource, :update, resource.member_path, route_path)
- map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
- end
-
- def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} )
- if resource.has_action?(action)
- action_options = action_options_for(action, resource, method, resource_options)
- formatted_route_path = "#{route_path}.:format"
-
- if route_name && @set.named_routes[route_name.to_sym].nil?
- map.named_route(route_name, formatted_route_path, action_options)
- else
- map.connect(formatted_route_path, action_options)
- end
- end
- end
-
- def add_conditions_for(conditions, method)
- returning({:conditions => conditions.dup}) do |options|
- options[:conditions][:method] = method unless method == :any
- end
- end
-
- def action_options_for(action, resource, method = nil, resource_options = {})
- default_options = { :action => action.to_s }
- require_id = !resource.kind_of?(SingletonResource)
- force_id = resource_options[:force_id] && !resource.kind_of?(SingletonResource)
-
- case default_options[:action]
- when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
- when "create"; default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements)
- when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
- when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
- when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
- else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(force_id))
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/routing/route.rb b/actionpack/lib/action_controller/routing/route.rb
deleted file mode 100644
index eba05a3c5a..0000000000
--- a/actionpack/lib/action_controller/routing/route.rb
+++ /dev/null
@@ -1,267 +0,0 @@
-require 'active_support/core_ext/object/misc'
-
-module ActionController
- module Routing
- class Route #:nodoc:
- attr_accessor :segments, :requirements, :conditions, :optimise
-
- def initialize(segments = [], requirements = {}, conditions = {})
- @segments = segments
- @requirements = requirements
- @conditions = conditions
-
- if !significant_keys.include?(:action) && !requirements[:action]
- @requirements[:action] = "index"
- @significant_keys << :action
- end
-
- # Routes cannot use the current string interpolation method
- # if there are user-supplied <tt>:requirements</tt> as the interpolation
- # code won't raise RoutingErrors when generating
- has_requirements = @segments.detect { |segment| segment.respond_to?(:regexp) && segment.regexp }
- if has_requirements || @requirements.keys.to_set != Routing::ALLOWED_REQUIREMENTS_FOR_OPTIMISATION
- @optimise = false
- else
- @optimise = true
- end
- end
-
- # Indicates whether the routes should be optimised with the string interpolation
- # version of the named routes methods.
- def optimise?
- @optimise && ActionController::Base::optimise_named_routes
- end
-
- def segment_keys
- segments.collect do |segment|
- segment.key if segment.respond_to? :key
- end.compact
- end
-
- def required_segment_keys
- required_segments = segments.select {|seg| (!seg.optional? && !seg.is_a?(DividerSegment)) || seg.is_a?(PathSegment) }
- required_segments.collect { |seg| seg.key if seg.respond_to?(:key)}.compact
- end
-
- # Build a query string from the keys of the given hash. If +only_keys+
- # is given (as an array), only the keys indicated will be used to build
- # the query string. The query string will correctly build array parameter
- # values.
- def build_query_string(hash, only_keys = nil)
- elements = []
-
- (only_keys || hash.keys).each do |key|
- if value = hash[key]
- elements << value.to_query(key)
- end
- end
-
- elements.empty? ? '' : "?#{elements.sort * '&'}"
- end
-
- # A route's parameter shell contains parameter values that are not in the
- # route's path, but should be placed in the recognized hash.
- #
- # For example, +{:controller => 'pages', :action => 'show'} is the shell for the route:
- #
- # map.connect '/page/:id', :controller => 'pages', :action => 'show', :id => /\d+/
- #
- def parameter_shell
- @parameter_shell ||= {}.tap do |shell|
- requirements.each do |key, requirement|
- shell[key] = requirement unless requirement.is_a? Regexp
- end
- end
- end
-
- # Return an array containing all the keys that are used in this route. This
- # includes keys that appear inside the path, and keys that have requirements
- # placed upon them.
- def significant_keys
- @significant_keys ||= [].tap do |sk|
- segments.each { |segment| sk << segment.key if segment.respond_to? :key }
- sk.concat requirements.keys
- sk.uniq!
- end
- end
-
- # Return a hash of key/value pairs representing the keys in the route that
- # have defaults, or which are specified by non-regexp requirements.
- def defaults
- @defaults ||= {}.tap do |hash|
- segments.each do |segment|
- next unless segment.respond_to? :default
- hash[segment.key] = segment.default unless segment.default.nil?
- end
- requirements.each do |key,req|
- next if Regexp === req || req.nil?
- hash[key] = req
- end
- end
- end
-
- def matches_controller_and_action?(controller, action)
- prepare_matching!
- (@controller_requirement.nil? || @controller_requirement === controller) &&
- (@action_requirement.nil? || @action_requirement === action)
- end
-
- def to_s
- @to_s ||= begin
- segs = segments.inject("") { |str,s| str << s.to_s }
- "%-6s %-40s %s" % [(conditions[:method] || :any).to_s.upcase, segs, requirements.inspect]
- end
- end
-
- # TODO: Route should be prepared and frozen on initialize
- def freeze
- unless frozen?
- write_generation!
- write_recognition!
- prepare_matching!
-
- parameter_shell
- significant_keys
- defaults
- to_s
- end
-
- super
- end
-
- def generate(options, hash, expire_on = {})
- path, hash = generate_raw(options, hash, expire_on)
- append_query_string(path, hash, extra_keys(options))
- end
-
- def generate_extras(options, hash, expire_on = {})
- path, hash = generate_raw(options, hash, expire_on)
- [path, extra_keys(options)]
- end
-
- private
- def requirement_for(key)
- return requirements[key] if requirements.key? key
- segments.each do |segment|
- return segment.regexp if segment.respond_to?(:key) && segment.key == key
- end
- nil
- end
-
- # Write and compile a +generate+ method for this Route.
- def write_generation!
- # Build the main body of the generation
- body = "expired = false\n#{generation_extraction}\n#{generation_structure}"
-
- # If we have conditions that must be tested first, nest the body inside an if
- body = "if #{generation_requirements}\n#{body}\nend" if generation_requirements
- args = "options, hash, expire_on = {}"
-
- # Nest the body inside of a def block, and then compile it.
- raw_method = method_decl = "def generate_raw(#{args})\npath = begin\n#{body}\nend\n[path, hash]\nend"
- instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
-
- # expire_on.keys == recall.keys; in other words, the keys in the expire_on hash
- # are the same as the keys that were recalled from the previous request. Thus,
- # we can use the expire_on.keys to determine which keys ought to be used to build
- # the query string. (Never use keys from the recalled request when building the
- # query string.)
-
- raw_method
- end
-
- # Build several lines of code that extract values from the options hash. If any
- # of the values are missing or rejected then a return will be executed.
- def generation_extraction
- segments.collect do |segment|
- segment.extraction_code
- end.compact * "\n"
- end
-
- # Produce a condition expression that will check the requirements of this route
- # upon generation.
- def generation_requirements
- requirement_conditions = requirements.collect do |key, req|
- if req.is_a? Regexp
- value_regexp = Regexp.new "\\A#{req.to_s}\\Z"
- "hash[:#{key}] && #{value_regexp.inspect} =~ options[:#{key}]"
- else
- "hash[:#{key}] == #{req.inspect}"
- end
- end
- requirement_conditions * ' && ' unless requirement_conditions.empty?
- end
-
- def generation_structure
- segments.last.string_structure segments[0..-2]
- end
-
- # Write and compile a +recognize+ method for this Route.
- def write_recognition!
- # Create an if structure to extract the params from a match if it occurs.
- body = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams"
- body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend"
-
- # Build the method declaration and compile it
- method_decl = "def recognize(path, env = {})\n#{body}\nend"
- instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})"
- method_decl
- end
-
- # Plugins may override this method to add other conditions, like checks on
- # host, subdomain, and so forth. Note that changes here only affect route
- # recognition, not generation.
- def recognition_conditions
- result = ["(match = #{Regexp.new(recognition_pattern).inspect}.match(path))"]
- result << "[conditions[:method]].flatten.include?(env[:method])" if conditions[:method]
- result
- end
-
- # Build the regular expression pattern that will match this route.
- def recognition_pattern(wrap = true)
- pattern = ''
- segments.reverse_each do |segment|
- pattern = segment.build_pattern pattern
- end
- wrap ? ("\\A" + pattern + "\\Z") : pattern
- end
-
- # Write the code to extract the parameters from a matched route.
- def recognition_extraction
- next_capture = 1
- extraction = segments.collect do |segment|
- x = segment.match_extraction(next_capture)
- next_capture += segment.number_of_captures
- x
- end
- extraction.compact
- end
-
- # Generate the query string with any extra keys in the hash and append
- # it to the given path, returning the new path.
- def append_query_string(path, hash, query_keys = nil)
- return nil unless path
- query_keys ||= extra_keys(hash)
- "#{path}#{build_query_string(hash, query_keys)}"
- end
-
- # Determine which keys in the given hash are "extra". Extra keys are
- # those that were not used to generate a particular route. The extra
- # keys also do not include those recalled from the prior request, nor
- # do they include any keys that were implied in the route (like a
- # <tt>:controller</tt> that is required, but not explicitly used in the
- # text of the route.)
- def extra_keys(hash, recall = {})
- (hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys
- end
-
- def prepare_matching!
- unless defined? @matching_prepared
- @controller_requirement = requirement_for(:controller)
- @action_requirement = requirement_for(:action)
- @matching_prepared = true
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/routing/route_set.rb b/actionpack/lib/action_controller/routing/route_set.rb
deleted file mode 100644
index 25fdbf480e..0000000000
--- a/actionpack/lib/action_controller/routing/route_set.rb
+++ /dev/null
@@ -1,485 +0,0 @@
-module ActionController
- module Routing
- class RouteSet #:nodoc:
- # Mapper instances are used to build routes. The object passed to the draw
- # block in config/routes.rb is a Mapper instance.
- #
- # Mapper instances have relatively few instance methods, in order to avoid
- # clashes with named routes.
- class Mapper #:doc:
- include ActionController::Resources
-
- def initialize(set) #:nodoc:
- @set = set
- end
-
- # Create an unnamed route with the provided +path+ and +options+. See
- # ActionController::Routing for an introduction to routes.
- def connect(path, options = {})
- @set.add_route(path, options)
- end
-
- # Creates a named route called "root" for matching the root level request.
- def root(options = {})
- if options.is_a?(Symbol)
- if source_route = @set.named_routes.routes[options]
- options = source_route.defaults.merge({ :conditions => source_route.conditions })
- end
- end
- named_route("root", '', options)
- end
-
- def named_route(name, path, options = {}) #:nodoc:
- @set.add_named_route(name, path, options)
- end
-
- # Enables the use of resources in a module by setting the name_prefix, path_prefix, and namespace for the model.
- # Example:
- #
- # map.namespace(:admin) do |admin|
- # admin.resources :products,
- # :has_many => [ :tags, :images, :variants ]
- # end
- #
- # This will create +admin_products_url+ pointing to "admin/products", which will look for an Admin::ProductsController.
- # It'll also create +admin_product_tags_url+ pointing to "admin/products/#{product_id}/tags", which will look for
- # Admin::TagsController.
- def namespace(name, options = {}, &block)
- if options[:namespace]
- with_options({:path_prefix => "#{options.delete(:path_prefix)}/#{name}", :name_prefix => "#{options.delete(:name_prefix)}#{name}_", :namespace => "#{options.delete(:namespace)}#{name}/" }.merge(options), &block)
- else
- with_options({:path_prefix => name, :name_prefix => "#{name}_", :namespace => "#{name}/" }.merge(options), &block)
- end
- end
-
- def method_missing(route_name, *args, &proc) #:nodoc:
- super unless args.length >= 1 && proc.nil?
- @set.add_named_route(route_name, *args)
- end
- end
-
- # A NamedRouteCollection instance is a collection of named routes, and also
- # maintains an anonymous module that can be used to install helpers for the
- # named routes.
- class NamedRouteCollection #:nodoc:
- include Enumerable
- include ActionController::Routing::Optimisation
- attr_reader :routes, :helpers
-
- def initialize
- clear!
- end
-
- def clear!
- @routes = {}
- @helpers = []
-
- @module ||= Module.new
- @module.instance_methods.each do |selector|
- @module.class_eval { remove_method selector }
- end
- end
-
- def add(name, route)
- routes[name.to_sym] = route
- define_named_route_methods(name, route)
- end
-
- def get(name)
- routes[name.to_sym]
- end
-
- alias []= add
- alias [] get
- alias clear clear!
-
- def each
- routes.each { |name, route| yield name, route }
- self
- end
-
- def names
- routes.keys
- end
-
- def length
- routes.length
- end
-
- def reset!
- old_routes = routes.dup
- clear!
- old_routes.each do |name, route|
- add(name, route)
- end
- end
-
- def install(destinations = [ActionController::Base, ActionView::Base], regenerate = false)
- reset! if regenerate
- Array(destinations).each do |dest|
- dest.__send__(:include, @module)
- end
- end
-
- private
- def url_helper_name(name, kind = :url)
- :"#{name}_#{kind}"
- end
-
- def hash_access_name(name, kind = :url)
- :"hash_for_#{name}_#{kind}"
- end
-
- def define_named_route_methods(name, route)
- {:url => {:only_path => false}, :path => {:only_path => true}}.each do |kind, opts|
- hash = route.defaults.merge(:use_route => name).merge(opts)
- define_hash_access route, name, kind, hash
- define_url_helper route, name, kind, hash
- end
- end
-
- def named_helper_module_eval(code, *args)
- @module.module_eval(code, *args)
- end
-
- def define_hash_access(route, name, kind, options)
- selector = hash_access_name(name, kind)
- named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
- def #{selector}(options = nil) # def hash_for_users_url(options = nil)
- options ? #{options.inspect}.merge(options) : #{options.inspect} # options ? {:only_path=>false}.merge(options) : {:only_path=>false}
- end # end
- protected :#{selector} # protected :hash_for_users_url
- end_eval
- helpers << selector
- end
-
- def define_url_helper(route, name, kind, options)
- selector = url_helper_name(name, kind)
- # The segment keys used for positional parameters
-
- hash_access_method = hash_access_name(name, kind)
-
- # allow ordered parameters to be associated with corresponding
- # dynamic segments, so you can do
- #
- # foo_url(bar, baz, bang)
- #
- # instead of
- #
- # foo_url(:bar => bar, :baz => baz, :bang => bang)
- #
- # Also allow options hash, so you can do
- #
- # foo_url(bar, baz, bang, :sort_by => 'baz')
- #
- named_helper_module_eval <<-end_eval # We use module_eval to avoid leaks
- def #{selector}(*args) # def users_url(*args)
- #
- #{generate_optimisation_block(route, kind)} # #{generate_optimisation_block(route, kind)}
- #
- opts = if args.empty? || Hash === args.first # opts = if args.empty? || Hash === args.first
- args.first || {} # args.first || {}
- else # else
- options = args.extract_options! # options = args.extract_options!
- args = args.zip(#{route.segment_keys.inspect}).inject({}) do |h, (v, k)| # args = args.zip([]).inject({}) do |h, (v, k)|
- h[k] = v # h[k] = v
- h # h
- end # end
- options.merge(args) # options.merge(args)
- end # end
- #
- url_for(#{hash_access_method}(opts)) # url_for(hash_for_users_url(opts))
- #
- end # end
- #Add an alias to support the now deprecated formatted_* URL. # #Add an alias to support the now deprecated formatted_* URL.
- def formatted_#{selector}(*args) # def formatted_users_url(*args)
- ActiveSupport::Deprecation.warn( # ActiveSupport::Deprecation.warn(
- "formatted_#{selector}() has been deprecated. " + # "formatted_users_url() has been deprecated. " +
- "Please pass format to the standard " + # "Please pass format to the standard " +
- "#{selector} method instead.", caller) # "users_url method instead.", caller)
- #{selector}(*args) # users_url(*args)
- end # end
- protected :#{selector} # protected :users_url
- end_eval
- helpers << selector
- end
- end
-
- attr_accessor :routes, :named_routes, :configuration_files
-
- def initialize
- self.configuration_files = []
-
- self.routes = []
- self.named_routes = NamedRouteCollection.new
-
- clear!
- end
-
- # Subclasses and plugins may override this method to specify a different
- # RouteBuilder instance, so that other route DSL's can be created.
- def builder
- @builder ||= RouteBuilder.new
- end
-
- def draw
- clear!
- yield Mapper.new(self)
- install_helpers
- end
-
- def clear!
- routes.clear
- named_routes.clear
-
- @combined_regexp = nil
- @routes_by_controller = nil
-
- # This will force routing/recognition_optimization.rb
- # to refresh optimisations.
- clear_recognize_optimized!
- end
-
- def install_helpers(destinations = [ActionController::Base, ActionView::Base], regenerate_code = false)
- Array(destinations).each { |d| d.module_eval { include Helpers } }
- named_routes.install(destinations, regenerate_code)
- end
-
- def empty?
- routes.empty?
- end
-
- def add_configuration_file(path)
- self.configuration_files << path
- end
-
- # Deprecated accessor
- def configuration_file=(path)
- add_configuration_file(path)
- end
-
- # Deprecated accessor
- def configuration_file
- configuration_files
- end
-
- def load!
- Routing.use_controllers!(nil) # Clear the controller cache so we may discover new ones
- load_routes!
- end
-
- # reload! will always force a reload whereas load checks the timestamp first
- alias reload! load!
-
- def reload
- if configuration_files.any? && @routes_last_modified
- if routes_changed_at == @routes_last_modified
- return # routes didn't change, don't reload
- else
- @routes_last_modified = routes_changed_at
- end
- end
-
- load!
- end
-
- def load_routes!
- if configuration_files.any?
- configuration_files.each { |config| load(config) }
- @routes_last_modified = routes_changed_at
- else
- draw do |map|
- map.connect ":controller/:action/:id"
- end
- end
- end
-
- def routes_changed_at
- routes_changed_at = nil
-
- configuration_files.each do |config|
- config_changed_at = File.stat(config).mtime
-
- if routes_changed_at.nil? || config_changed_at > routes_changed_at
- routes_changed_at = config_changed_at
- end
- end
-
- routes_changed_at
- end
-
- def add_route(path, options = {})
- options.each { |k, v| options[k] = v.to_s if [:controller, :action].include?(k) && v.is_a?(Symbol) }
- route = builder.build(path, options)
- routes << route
- route
- end
-
- def add_named_route(name, path, options = {})
- # TODO - is options EVER used?
- name = options[:name_prefix] + name.to_s if options[:name_prefix]
- named_routes[name.to_sym] = add_route(path, options)
- end
-
- def options_as_params(options)
- # If an explicit :controller was given, always make :action explicit
- # too, so that action expiry works as expected for things like
- #
- # generate({:controller => 'content'}, {:controller => 'content', :action => 'show'})
- #
- # (the above is from the unit tests). In the above case, because the
- # controller was explicitly given, but no action, the action is implied to
- # be "index", not the recalled action of "show".
- #
- # great fun, eh?
-
- options_as_params = options.clone
- options_as_params[:action] ||= 'index' if options[:controller]
- options_as_params[:action] = options_as_params[:action].to_s if options_as_params[:action]
- options_as_params
- end
-
- def build_expiry(options, recall)
- recall.inject({}) do |expiry, (key, recalled_value)|
- expiry[key] = (options.key?(key) && options[key].to_param != recalled_value.to_param)
- expiry
- end
- end
-
- # Generate the path indicated by the arguments, and return an array of
- # the keys that were not used to generate it.
- def extra_keys(options, recall={})
- generate_extras(options, recall).last
- end
-
- def generate_extras(options, recall={})
- generate(options, recall, :generate_extras)
- end
-
- def generate(options, recall = {}, method=:generate)
- named_route_name = options.delete(:use_route)
- generate_all = options.delete(:generate_all)
- if named_route_name
- named_route = named_routes[named_route_name]
- options = named_route.parameter_shell.merge(options)
- end
-
- options = options_as_params(options)
- expire_on = build_expiry(options, recall)
-
- if options[:controller]
- options[:controller] = options[:controller].to_s
- end
- # if the controller has changed, make sure it changes relative to the
- # current controller module, if any. In other words, if we're currently
- # on admin/get, and the new controller is 'set', the new controller
- # should really be admin/set.
- if !named_route && expire_on[:controller] && options[:controller] && options[:controller][0] != ?/
- old_parts = recall[:controller].split('/')
- new_parts = options[:controller].split('/')
- parts = old_parts[0..-(new_parts.length + 1)] + new_parts
- options[:controller] = parts.join('/')
- end
-
- # drop the leading '/' on the controller name
- options[:controller] = options[:controller][1..-1] if options[:controller] && options[:controller][0] == ?/
- merged = recall.merge(options)
-
- if named_route
- path = named_route.generate(options, merged, expire_on)
- if path.nil?
- raise_named_route_error(options, named_route, named_route_name)
- else
- return path
- end
- else
- merged[:action] ||= 'index'
- options[:action] ||= 'index'
-
- controller = merged[:controller]
- action = merged[:action]
-
- raise RoutingError, "Need controller and action!" unless controller && action
-
- if generate_all
- # Used by caching to expire all paths for a resource
- return routes.collect do |route|
- route.__send__(method, options, merged, expire_on)
- end.compact
- end
-
- # don't use the recalled keys when determining which routes to check
- routes = routes_by_controller[controller][action][options.reject {|k,v| !v}.keys.sort_by { |x| x.object_id }]
-
- routes.each_with_index do |route, index|
- results = route.__send__(method, options, merged, expire_on)
- if results && (!results.is_a?(Array) || results.first)
- return results
- end
- end
- end
-
- raise RoutingError, "No route matches #{options.inspect}"
- end
-
- # try to give a helpful error message when named route generation fails
- def raise_named_route_error(options, named_route, named_route_name)
- diff = named_route.requirements.diff(options)
- unless diff.empty?
- raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect}, expected: #{named_route.requirements.inspect}, diff: #{named_route.requirements.diff(options).inspect}"
- else
- required_segments = named_route.segments.select {|seg| (!seg.optional?) && (!seg.is_a?(DividerSegment)) }
- required_keys_or_values = required_segments.map { |seg| seg.key rescue seg.value } # we want either the key or the value from the segment
- raise RoutingError, "#{named_route_name}_url failed to generate from #{options.inspect} - you may have ambiguous routes, or you may need to supply additional parameters for this route. content_url has the following required parameters: #{required_keys_or_values.inspect} - are they all satisfied?"
- end
- end
-
- def call(env)
- request = ActionDispatch::Request.new(env)
- app = Routing::Routes.recognize(request)
- app.action(request.parameters[:action] || 'index').call(env)
- end
-
- def recognize(request)
- params = recognize_path(request.path, extract_request_environment(request))
- request.path_parameters = params.with_indifferent_access
- "#{params[:controller].to_s.camelize}Controller".constantize
- end
-
- def recognize_path(path, environment={})
- raise "Not optimized! Check that routing/recognition_optimisation overrides RouteSet#recognize_path."
- end
-
- def routes_by_controller
- @routes_by_controller ||= Hash.new do |controller_hash, controller|
- controller_hash[controller] = Hash.new do |action_hash, action|
- action_hash[action] = Hash.new do |key_hash, keys|
- key_hash[keys] = routes_for_controller_and_action_and_keys(controller, action, keys)
- end
- end
- end
- end
-
- def routes_for(options, merged, expire_on)
- raise "Need controller and action!" unless controller && action
- controller = merged[:controller]
- merged = options if expire_on[:controller]
- action = merged[:action] || 'index'
-
- routes_by_controller[controller][action][merged.keys][1]
- end
-
- def routes_for_controller_and_action_and_keys(controller, action, keys)
- routes.select do |route|
- route.matches_controller_and_action? controller, action
- end
- end
-
- # Subclasses and plugins may override this method to extract further attributes
- # from the request, for use by route conditions and such.
- def extract_request_environment(request)
- { :method => request.method }
- end
- end
- end
-end
diff --git a/actionpack/lib/action_controller/routing/routing_ext.rb b/actionpack/lib/action_controller/routing/routing_ext.rb
deleted file mode 100644
index 5e5b22b6c2..0000000000
--- a/actionpack/lib/action_controller/routing/routing_ext.rb
+++ /dev/null
@@ -1,4 +0,0 @@
-require 'active_support/core_ext/object/conversions'
-require 'active_support/core_ext/boolean/conversions'
-require 'active_support/core_ext/nil/conversions'
-require 'active_support/core_ext/regexp'
diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb
deleted file mode 100644
index 2603855476..0000000000
--- a/actionpack/lib/action_controller/routing/segments.rb
+++ /dev/null
@@ -1,343 +0,0 @@
-module ActionController
- module Routing
- class Segment #:nodoc:
- RESERVED_PCHAR = ':@&=+$,;%'
- SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
- if RUBY_VERSION >= '1.9'
- UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze
- else
- UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
- end
-
- # TODO: Convert :is_optional accessor to read only
- attr_accessor :is_optional
- alias_method :optional?, :is_optional
-
- def initialize
- @is_optional = false
- end
-
- def number_of_captures
- Regexp.new(regexp_chunk).number_of_captures
- end
-
- def extraction_code
- nil
- end
-
- # Continue generating string for the prior segments.
- def continue_string_structure(prior_segments)
- if prior_segments.empty?
- interpolation_statement(prior_segments)
- else
- new_priors = prior_segments[0..-2]
- prior_segments.last.string_structure(new_priors)
- end
- end
-
- def interpolation_chunk
- URI.escape(value, UNSAFE_PCHAR)
- end
-
- # Return a string interpolation statement for this segment and those before it.
- def interpolation_statement(prior_segments)
- chunks = prior_segments.collect { |s| s.interpolation_chunk }
- chunks << interpolation_chunk
- "\"#{chunks * ''}\"#{all_optionals_available_condition(prior_segments)}"
- end
-
- def string_structure(prior_segments)
- optional? ? continue_string_structure(prior_segments) : interpolation_statement(prior_segments)
- end
-
- # Return an if condition that is true if all the prior segments can be generated.
- # If there are no optional segments before this one, then nil is returned.
- def all_optionals_available_condition(prior_segments)
- optional_locals = prior_segments.collect { |s| s.local_name if s.optional? && s.respond_to?(:local_name) }.compact
- optional_locals.empty? ? nil : " if #{optional_locals * ' && '}"
- end
-
- # Recognition
-
- def match_extraction(next_capture)
- nil
- end
-
- # Warning
-
- # Returns true if this segment is optional? because of a default. If so, then
- # no warning will be emitted regarding this segment.
- def optionality_implied?
- false
- end
- end
-
- class StaticSegment < Segment #:nodoc:
- attr_reader :value, :raw
- alias_method :raw?, :raw
-
- def initialize(value = nil, options = {})
- super()
- @value = value
- @raw = options[:raw] if options.key?(:raw)
- @is_optional = options[:optional] if options.key?(:optional)
- end
-
- def interpolation_chunk
- raw? ? value : super
- end
-
- def regexp_chunk
- chunk = Regexp.escape(value)
- optional? ? Regexp.optionalize(chunk) : chunk
- end
-
- def number_of_captures
- 0
- end
-
- def build_pattern(pattern)
- escaped = Regexp.escape(value)
- if optional? && ! pattern.empty?
- "(?:#{Regexp.optionalize escaped}\\Z|#{escaped}#{Regexp.unoptionalize pattern})"
- elsif optional?
- Regexp.optionalize escaped
- else
- escaped + pattern
- end
- end
-
- def to_s
- value
- end
- end
-
- class DividerSegment < StaticSegment #:nodoc:
- def initialize(value = nil, options = {})
- super(value, {:raw => true, :optional => true}.merge(options))
- end
-
- def optionality_implied?
- true
- end
- end
-
- class DynamicSegment < Segment #:nodoc:
- attr_reader :key
-
- # TODO: Convert these accessors to read only
- attr_accessor :default, :regexp
-
- def initialize(key = nil, options = {})
- super()
- @key = key
- @default = options[:default] if options.key?(:default)
- @regexp = options[:regexp] if options.key?(:regexp)
- @is_optional = true if options[:optional] || options.key?(:default)
- end
-
- def to_s
- ":#{key}"
- end
-
- # The local variable name that the value of this segment will be extracted to.
- def local_name
- "#{key}_value"
- end
-
- def extract_value
- "#{local_name} = hash[:#{key}] && hash[:#{key}].to_param #{"|| #{default.inspect}" if default}"
- end
-
- def value_check
- if default # Then we know it won't be nil
- "#{value_regexp.inspect} =~ #{local_name}" if regexp
- elsif optional?
- # If we have a regexp check that the value is not given, or that it matches.
- # If we have no regexp, return nil since we do not require a condition.
- "#{local_name}.nil? || #{value_regexp.inspect} =~ #{local_name}" if regexp
- else # Then it must be present, and if we have a regexp, it must match too.
- "#{local_name} #{"&& #{value_regexp.inspect} =~ #{local_name}" if regexp}"
- end
- end
-
- def expiry_statement
- "expired, hash = true, options if !expired && expire_on[:#{key}]"
- end
-
- def extraction_code
- s = extract_value
- vc = value_check
- s << "\nreturn [nil,nil] unless #{vc}" if vc
- s << "\n#{expiry_statement}"
- end
-
- def interpolation_chunk(value_code = local_name)
- "\#{URI.escape(#{value_code}.to_s, ActionController::Routing::Segment::UNSAFE_PCHAR)}"
- end
-
- def string_structure(prior_segments)
- if optional? # We have a conditional to do...
- # If we should not appear in the url, just write the code for the prior
- # segments. This occurs if our value is the default value, or, if we are
- # optional, if we have nil as our value.
- "if #{local_name} == #{default.inspect}\n" +
- continue_string_structure(prior_segments) +
- "\nelse\n" + # Otherwise, write the code up to here
- "#{interpolation_statement(prior_segments)}\nend"
- else
- interpolation_statement(prior_segments)
- end
- end
-
- def value_regexp
- Regexp.new "\\A#{regexp.to_s}\\Z" if regexp
- end
-
- def regexp_chunk
- regexp ? regexp_string : default_regexp_chunk
- end
-
- def regexp_string
- regexp_has_modifiers? ? "(#{regexp.to_s})" : "(#{regexp.source})"
- end
-
- def default_regexp_chunk
- "([^#{Routing::SEPARATORS.join}]+)"
- end
-
- def number_of_captures
- regexp ? regexp.number_of_captures + 1 : 1
- end
-
- def build_pattern(pattern)
- pattern = "#{regexp_chunk}#{pattern}"
- optional? ? Regexp.optionalize(pattern) : pattern
- end
-
- def match_extraction(next_capture)
- # All non code-related keys (such as :id, :slug) are URI-unescaped as
- # path parameters.
- default_value = default ? default.inspect : nil
- %[
- value = if (m = match[#{next_capture}])
- URI.unescape(m)
- else
- #{default_value}
- end
- params[:#{key}] = value if value
- ]
- end
-
- def optionality_implied?
- [:action, :id].include? key
- end
-
- def regexp_has_modifiers?
- regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0
- end
- end
-
- class ControllerSegment < DynamicSegment #:nodoc:
- def regexp_chunk
- possible_names = Routing.possible_controllers.collect { |name| Regexp.escape name }
- "(?i-:(#{(regexp || Regexp.union(*possible_names)).source}))"
- end
-
- # Don't URI.escape the controller name since it may contain slashes.
- def interpolation_chunk(value_code = local_name)
- "\#{#{value_code}.to_s}"
- end
-
- # Make sure controller names like Admin/Content are correctly normalized to
- # admin/content
- def extract_value
- "#{local_name} = (hash[:#{key}] #{"|| #{default.inspect}" if default}).downcase"
- end
-
- def match_extraction(next_capture)
- if default
- "params[:#{key}] = match[#{next_capture}] ? match[#{next_capture}].downcase : '#{default}'"
- else
- "params[:#{key}] = match[#{next_capture}].downcase if match[#{next_capture}]"
- end
- end
- end
-
- class PathSegment < DynamicSegment #:nodoc:
- def interpolation_chunk(value_code = local_name)
- "\#{#{value_code}}"
- end
-
- def extract_value
- "#{local_name} = hash[:#{key}] && Array(hash[:#{key}]).collect { |path_component| URI.escape(path_component.to_param, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
- end
-
- def default
- ''
- end
-
- def default=(path)
- raise RoutingError, "paths cannot have non-empty default values" unless path.blank?
- end
-
- def match_extraction(next_capture)
- "params[:#{key}] = PathSegment::Result.new_escaped((match[#{next_capture}]#{" || " + default.inspect if default}).split('/'))#{" if match[" + next_capture + "]" if !default}"
- end
-
- def default_regexp_chunk
- "(.*)"
- end
-
- def number_of_captures
- regexp ? regexp.number_of_captures : 1
- end
-
- def optionality_implied?
- true
- end
-
- class Result < ::Array #:nodoc:
- def to_s() join '/' end
- def self.new_escaped(strings)
- new strings.collect {|str| URI.unescape str}
- end
- end
- end
-
- # The OptionalFormatSegment allows for any resource route to have an optional
- # :format, which decreases the amount of routes created by 50%.
- class OptionalFormatSegment < DynamicSegment
-
- def initialize(key = nil, options = {})
- super(:format, {:optional => true}.merge(options))
- end
-
- def interpolation_chunk
- "." + super
- end
-
- def regexp_chunk
- '/|(\.[^/?\.]+)?'
- end
-
- def to_s
- '(.:format)?'
- end
-
- def extract_value
- "#{local_name} = options[:#{key}] && options[:#{key}].to_s.downcase"
- end
-
- #the value should not include the period (.)
- def match_extraction(next_capture)
- %[
- if (m = match[#{next_capture}])
- params[:#{key}] = URI.unescape(m.from(1))
- end
- ]
- end
- end
-
- end
-end
diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb
index bbc7f3c8f9..323cce6a2f 100644
--- a/actionpack/lib/action_controller/testing/process.rb
+++ b/actionpack/lib/action_controller/testing/process.rb
@@ -35,7 +35,7 @@ module ActionController #:nodoc:
end
def cookies
- @response.cookies
+ @request.cookies.merge(@response.cookies)
end
def redirect_to_url
diff --git a/actionpack/lib/action_controller/testing/test_case.rb b/actionpack/lib/action_controller/testing/test_case.rb
index 178e3477a6..01a55fe930 100644
--- a/actionpack/lib/action_controller/testing/test_case.rb
+++ b/actionpack/lib/action_controller/testing/test_case.rb
@@ -10,6 +10,13 @@ module ActionController
self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => ActiveSupport::SecureRandom.hex(16))
end
+ class Result < ::Array #:nodoc:
+ def to_s() join '/' end
+ def self.new_escaped(strings)
+ new strings.collect {|str| URI.unescape str}
+ end
+ end
+
def assign_parameters(controller_path, action, parameters = {})
parameters = parameters.symbolize_keys.merge(:controller => controller_path, :action => action)
extra_keys = ActionController::Routing::Routes.extra_keys(parameters)
@@ -18,7 +25,7 @@ module ActionController
if value.is_a? Fixnum
value = value.to_s
elsif value.is_a? Array
- value = ActionController::Routing::PathSegment::Result.new(value)
+ value = Result.new(value)
end
if extra_keys.include?(key.to_sym)
diff --git a/actionpack/lib/action_controller/translation.rb b/actionpack/lib/action_controller/translation.rb
index 9bb63cdb15..65e9eddb0a 100644
--- a/actionpack/lib/action_controller/translation.rb
+++ b/actionpack/lib/action_controller/translation.rb
@@ -1,12 +1,12 @@
module ActionController
module Translation
def translate(*args)
- I18n.translate *args
+ I18n.translate(*args)
end
alias :t :translate
def localize(*args)
- I18n.localize *args
+ I18n.localize(*args)
end
alias :l :localize
end
diff --git a/actionpack/lib/action_controller/routing/generation/url_rewriter.rb b/actionpack/lib/action_controller/url_rewriter.rb
index 52b66c9303..52b66c9303 100644
--- a/actionpack/lib/action_controller/routing/generation/url_rewriter.rb
+++ b/actionpack/lib/action_controller/url_rewriter.rb