aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_controller/routing
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_controller/routing')
-rw-r--r--actionpack/lib/action_controller/routing/builder.rb199
-rw-r--r--actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb210
-rw-r--r--actionpack/lib/action_controller/routing/generation/url_rewriter.rb204
-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
10 files changed, 0 insertions, 2694 deletions
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/generation/polymorphic_routes.rb b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb
deleted file mode 100644
index 2adf3575a7..0000000000
--- a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb
+++ /dev/null
@@ -1,210 +0,0 @@
-module ActionController
- # Polymorphic URL helpers are methods for smart resolution to a named route call when
- # given an Active Record model instance. They are to be used in combination with
- # ActionController::Resources.
- #
- # These methods are useful when you want to generate correct URL or path to a RESTful
- # resource without having to know the exact type of the record in question.
- #
- # Nested resources and/or namespaces are also supported, as illustrated in the example:
- #
- # polymorphic_url([:admin, @article, @comment])
- #
- # results in:
- #
- # admin_article_comment_url(@article, @comment)
- #
- # == Usage within the framework
- #
- # Polymorphic URL helpers are used in a number of places throughout the Rails framework:
- #
- # * <tt>url_for</tt>, so you can use it with a record as the argument, e.g.
- # <tt>url_for(@article)</tt>;
- # * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
- # <tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
- # action;
- # * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
- # <tt>redirect_to(post)</tt> in your controllers;
- # * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
- # for feed entries.
- #
- # == Prefixed polymorphic helpers
- #
- # In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a
- # number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt>
- # in options. Those are:
- #
- # * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
- # * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
- #
- # Example usage:
- #
- # edit_polymorphic_path(@post) # => "/posts/1/edit"
- # polymorphic_path(@post, :format => :pdf) # => "/posts/1.pdf"
- module PolymorphicRoutes
- # Constructs a call to a named RESTful route for the given record and returns the
- # resulting URL string. For example:
- #
- # # calls post_url(post)
- # polymorphic_url(post) # => "http://example.com/posts/1"
- # polymorphic_url([blog, post]) # => "http://example.com/blogs/1/posts/1"
- # polymorphic_url([:admin, blog, post]) # => "http://example.com/admin/blogs/1/posts/1"
- # polymorphic_url([user, :blog, post]) # => "http://example.com/users/1/blog/posts/1"
- # polymorphic_url(Comment) # => "http://example.com/comments"
- #
- # ==== Options
- #
- # * <tt>:action</tt> - Specifies the action prefix for the named route:
- # <tt>:new</tt> or <tt>:edit</tt>. Default is no prefix.
- # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
- # Default is <tt>:url</tt>.
- #
- # ==== Examples
- #
- # # an Article record
- # polymorphic_url(record) # same as article_url(record)
- #
- # # a Comment record
- # polymorphic_url(record) # same as comment_url(record)
- #
- # # it recognizes new records and maps to the collection
- # record = Comment.new
- # polymorphic_url(record) # same as comments_url()
- #
- # # the class of a record will also map to the collection
- # polymorphic_url(Comment) # same as comments_url()
- #
- def polymorphic_url(record_or_hash_or_array, options = {})
- if record_or_hash_or_array.kind_of?(Array)
- record_or_hash_or_array = record_or_hash_or_array.compact
- 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)
-
- args = case record_or_hash_or_array
- when Hash; [ record_or_hash_or_array ]
- when Array; record_or_hash_or_array.dup
- else [ record_or_hash_or_array ]
- end
-
- inflection = if options[:action].to_s == "new"
- args.pop
- :singular
- elsif (record.respond_to?(:new_record?) && record.new_record?) ||
- (record.respond_to?(:destroyed?) && record.destroyed?)
- args.pop
- :plural
- elsif record.is_a?(Class)
- args.pop
- :plural
- else
- :singular
- 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)
-
- url_options = options.except(:action, :routing_type)
- unless url_options.empty?
- args.last.kind_of?(Hash) ? args.last.merge!(url_options) : args << url_options
- end
-
- __send__(named_route, *args)
- end
-
- # Returns the path component of a URL for the given record. It uses
- # <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
- def polymorphic_path(record_or_hash_or_array, options = {})
- polymorphic_url(record_or_hash_or_array, options.merge(:routing_type => :path))
- end
-
- %w(edit new).each do |action|
- module_eval <<-EOT, __FILE__, __LINE__
- def #{action}_polymorphic_url(record_or_hash, options = {}) # def edit_polymorphic_url(record_or_hash, options = {})
- polymorphic_url( # polymorphic_url(
- record_or_hash, # record_or_hash,
- options.merge(:action => "#{action}")) # options.merge(:action => "edit"))
- end # end
- #
- def #{action}_polymorphic_path(record_or_hash, options = {}) # def edit_polymorphic_path(record_or_hash, options = {})
- polymorphic_url( # polymorphic_url(
- record_or_hash, # record_or_hash,
- options.merge(:action => "#{action}", :routing_type => :path)) # options.merge(:action => "edit", :routing_type => :path))
- end # end
- 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]}_" : ''
- end
-
- def routing_type(options)
- options[:routing_type] || :url
- end
-
- def build_named_route_call(records, namespace, inflection, options = {})
- unless records.is_a?(Array)
- record = extract_record(records)
- route = ''
- else
- record = records.pop
- route = records.inject("") do |string, parent|
- if parent.is_a?(Symbol) || parent.is_a?(String)
- string << "#{parent}_"
- else
- string << "#{RecordIdentifier.__send__("plural_class_name", parent)}".singularize
- string << "_"
- end
- end
- end
-
- if record.is_a?(Symbol) || record.is_a?(String)
- route << "#{record}_"
- else
- 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
- end
-
- def extract_record(record_or_hash_or_array)
- case record_or_hash_or_array
- when Array; record_or_hash_or_array.last
- when Hash; record_or_hash_or_array[:id]
- 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/generation/url_rewriter.rb b/actionpack/lib/action_controller/routing/generation/url_rewriter.rb
deleted file mode 100644
index 52b66c9303..0000000000
--- a/actionpack/lib/action_controller/routing/generation/url_rewriter.rb
+++ /dev/null
@@ -1,204 +0,0 @@
-module ActionController
- # In <b>routes.rb</b> one defines URL-to-controller mappings, but the reverse
- # is also possible: an URL can be generated from one of your routing definitions.
- # URL generation functionality is centralized in this module.
- #
- # See ActionController::Routing and ActionController::Resources for general
- # information about routing and routes.rb.
- #
- # <b>Tip:</b> If you need to generate URLs from your models or some other place,
- # then ActionController::UrlWriter is what you're looking for. Read on for
- # an introduction.
- #
- # == URL generation from parameters
- #
- # As you may know, some functions - such as ActionController::Base#url_for
- # and ActionView::Helpers::UrlHelper#link_to, can generate URLs given a set
- # of parameters. For example, you've probably had the chance to write code
- # like this in one of your views:
- #
- # <%= link_to('Click here', :controller => 'users',
- # :action => 'new', :message => 'Welcome!') %>
- #
- # #=> Generates a link to: /users/new?message=Welcome%21
- #
- # link_to, and all other functions that require URL generation functionality,
- # actually use ActionController::UrlWriter under the hood. And in particular,
- # they use the ActionController::UrlWriter#url_for method. One can generate
- # the same path as the above example by using the following code:
- #
- # include UrlWriter
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :only_path => true)
- # # => "/users/new?message=Welcome%21"
- #
- # Notice the <tt>:only_path => true</tt> part. This is because UrlWriter has no
- # information about the website hostname that your Rails app is serving. So if you
- # want to include the hostname as well, then you must also pass the <tt>:host</tt>
- # argument:
- #
- # include UrlWriter
- # url_for(:controller => 'users',
- # :action => 'new',
- # :message => 'Welcome!',
- # :host => 'www.example.com') # Changed this.
- # # => "http://www.example.com/users/new?message=Welcome%21"
- #
- # By default, all controllers and views have access to a special version of url_for,
- # that already knows what the current hostname is. So if you use url_for in your
- # controllers or your views, then you don't need to explicitly pass the <tt>:host</tt>
- # argument.
- #
- # For convenience reasons, mailers provide a shortcut for ActionController::UrlWriter#url_for.
- # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlWriter#url_for'
- # in full. However, mailers don't have hostname information, and what's why you'll still
- # have to specify the <tt>:host</tt> argument when generating URLs in mailers.
- #
- #
- # == URL generation for named routes
- #
- # UrlWriter also allows one to access methods that have been auto-generated from
- # named routes. For example, suppose that you have a 'users' resource in your
- # <b>routes.rb</b>:
- #
- # map.resources :users
- #
- # This generates, among other things, the method <tt>users_path</tt>. By default,
- # this method is accessible from your controllers, views and mailers. If you need
- # to access this auto-generated method from other places (such as a model), then
- # you can do that by including ActionController::UrlWriter in your class:
- #
- # class User < ActiveRecord::Base
- # include ActionController::UrlWriter
- #
- # def base_uri
- # user_path(self)
- # end
- # end
- #
- # User.find(1).base_uri # => "/users/1"
- module UrlWriter
- def self.included(base) #:nodoc:
- ActionController::Routing::Routes.install_helpers(base)
- base.mattr_accessor :default_url_options
-
- # The default options for urls written by this writer. Typically a <tt>:host</tt> pair is provided.
- base.default_url_options ||= {}
- end
-
- # Generate a url based on the options provided, default_url_options and the
- # routes defined in routes.rb. The following options are supported:
- #
- # * <tt>:only_path</tt> - If true, the relative url is returned. Defaults to +false+.
- # * <tt>:protocol</tt> - The protocol to connect to. Defaults to 'http'.
- # * <tt>:host</tt> - Specifies the host the link should be targeted at.
- # If <tt>:only_path</tt> is false, this option must be
- # provided either explicitly, or via +default_url_options+.
- # * <tt>:port</tt> - Optionally specify the port to connect to.
- # * <tt>:anchor</tt> - An anchor name to be appended to the path.
- # * <tt>:skip_relative_url_root</tt> - If true, the url is not constructed using the
- # +relative_url_root+ set in ActionController::Base.relative_url_root.
- # * <tt>:trailing_slash</tt> - If true, adds a trailing slash, as in "/archive/2009/"
- #
- # Any other key (<tt>:controller</tt>, <tt>:action</tt>, etc.) given to
- # +url_for+ is forwarded to the Routes module.
- #
- # Examples:
- #
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :port=>'8080' # => 'http://somehost.org:8080/tasks/testing'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :anchor => 'ok', :only_path => true # => '/tasks/testing#ok'
- # url_for :controller => 'tasks', :action => 'testing', :trailing_slash=>true # => 'http://somehost.org/tasks/testing/'
- # url_for :controller => 'tasks', :action => 'testing', :host=>'somehost.org', :number => '33' # => 'http://somehost.org/tasks/testing?number=33'
- def url_for(options)
- options = self.class.default_url_options.merge(options)
-
- url = ''
-
- unless options.delete(:only_path)
- url << (options.delete(:protocol) || 'http')
- url << '://' unless url.match("://")
-
- raise "Missing host to link to! Please provide :host parameter or set default_url_options[:host]" unless options[:host]
-
- url << options.delete(:host)
- url << ":#{options.delete(:port)}" if options.key?(:port)
- else
- # Delete the unused options to prevent their appearance in the query string.
- [:protocol, :host, :port, :skip_relative_url_root].each { |k| options.delete(k) }
- end
- trailing_slash = options.delete(:trailing_slash) if options.key?(:trailing_slash)
- url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
- anchor = "##{CGI.escape options.delete(:anchor).to_param.to_s}" if options[:anchor]
- generated = Routing::Routes.generate(options, {})
- url << (trailing_slash ? generated.sub(/\?|\z/) { "/" + $& } : generated)
- url << anchor if anchor
-
- url
- end
- end
-
- # Rewrites URLs for Base.redirect_to and Base.url_for in the controller.
- class UrlRewriter #:nodoc:
- RESERVED_OPTIONS = [:anchor, :params, :only_path, :host, :protocol, :port, :trailing_slash, :skip_relative_url_root]
- def initialize(request, parameters)
- @request, @parameters = request, parameters
- end
-
- def rewrite(options = {})
- rewrite_url(options)
- end
-
- def to_str
- "#{@request.protocol}, #{@request.host_with_port}, #{@request.path}, #{@parameters[:controller]}, #{@parameters[:action]}, #{@request.parameters.inspect}"
- end
-
- alias_method :to_s, :to_str
-
- private
- # Given a path and options, returns a rewritten URL string
- def rewrite_url(options)
- rewritten_url = ""
-
- unless options[:only_path]
- rewritten_url << (options[:protocol] || @request.protocol)
- rewritten_url << "://" unless rewritten_url.match("://")
- rewritten_url << rewrite_authentication(options)
- rewritten_url << (options[:host] || @request.host_with_port)
- rewritten_url << ":#{options.delete(:port)}" if options.key?(:port)
- end
-
- path = rewrite_path(options)
- rewritten_url << ActionController::Base.relative_url_root.to_s unless options[:skip_relative_url_root]
- rewritten_url << (options[:trailing_slash] ? path.sub(/\?|\z/) { "/" + $& } : path)
- rewritten_url << "##{CGI.escape(options[:anchor].to_param.to_s)}" if options[:anchor]
-
- rewritten_url
- end
-
- # Given a Hash of options, generates a route
- def rewrite_path(options)
- options = options.symbolize_keys
- options.update(options[:params].symbolize_keys) if options[:params]
-
- if (overwrite = options.delete(:overwrite_params))
- options.update(@parameters.symbolize_keys)
- options.update(overwrite.symbolize_keys)
- end
-
- RESERVED_OPTIONS.each { |k| options.delete(k) }
-
- # Generates the query string, too
- Routing::Routes.generate(options, @request.symbolized_path_parameters)
- end
-
- def rewrite_authentication(options)
- if options[:user] && options[:password]
- "#{CGI.escape(options.delete(:user))}:#{CGI.escape(options.delete(:password))}@"
- else
- ""
- 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