aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_dispatch
diff options
context:
space:
mode:
authorJoshua Peek <josh@joshpeek.com>2009-10-20 10:46:27 -0500
committerJoshua Peek <josh@joshpeek.com>2009-10-20 10:46:27 -0500
commitdf68cae0c0837fbf23fdfc3f04162307ffa8f2c1 (patch)
treef827d3eeb9116967531fe962bcbcddc7fb40459a /actionpack/lib/action_dispatch
parent35576a237e2c721dca8be0f8f0d653ae8bc07389 (diff)
downloadrails-df68cae0c0837fbf23fdfc3f04162307ffa8f2c1.tar.gz
rails-df68cae0c0837fbf23fdfc3f04162307ffa8f2c1.tar.bz2
rails-df68cae0c0837fbf23fdfc3f04162307ffa8f2c1.zip
Group together all the old routing dsl logic
Diffstat (limited to 'actionpack/lib/action_dispatch')
-rw-r--r--actionpack/lib/action_dispatch/routing.rb2
-rw-r--r--actionpack/lib/action_dispatch/routing/mapper.rb (renamed from actionpack/lib/action_dispatch/routing/resources.rb)200
-rw-r--r--actionpack/lib/action_dispatch/routing/route_set.rb206
3 files changed, 199 insertions, 209 deletions
diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb
index 5a8df76326..0647d051cb 100644
--- a/actionpack/lib/action_dispatch/routing.rb
+++ b/actionpack/lib/action_dispatch/routing.rb
@@ -260,7 +260,7 @@ module ActionDispatch
# Run <tt>rake routes</tt>.
#
module Routing
- autoload :Resources, 'action_dispatch/routing/resources'
+ autoload :Mapper, 'action_dispatch/routing/mapper'
autoload :RouteSet, 'action_dispatch/routing/route_set'
SEPARATORS = %w( / . ? )
diff --git a/actionpack/lib/action_dispatch/routing/resources.rb b/actionpack/lib/action_dispatch/routing/mapper.rb
index ada0d0a648..44afbb9cd7 100644
--- a/actionpack/lib/action_dispatch/routing/resources.rb
+++ b/actionpack/lib/action_dispatch/routing/mapper.rb
@@ -1,8 +1,11 @@
-require 'active_support/core_ext/hash/slice'
-require 'active_support/core_ext/object/try'
-
module ActionDispatch
module Routing
+ # 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.
+ #
# == Overview
#
# ActionController::Resources are a way of defining RESTful \resources. A RESTful \resource, in basic terms,
@@ -45,7 +48,196 @@ module ActionDispatch
# 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
+ class Mapper #:doc:
+ def initialize(set) #:nodoc:
+ @set = set
+ end
+
+ # Create an unnamed route with the provided +path+ and +options+. See
+ # ActionDispatch::Routing for an introduction to routes.
+ def connect(path, options = {})
+ options = options.dup
+
+ if conditions = options.delete(:conditions)
+ conditions = conditions.dup
+ method = [conditions.delete(:method)].flatten.compact
+ method.map! { |m|
+ m = m.to_s.upcase
+
+ 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.downcase.to_sym)
+ raise ArgumentError, "Invalid HTTP method specified in route conditions"
+ end
+
+ m
+ }
+
+ if method.length > 1
+ method = Regexp.union(*method)
+ elsif method.length == 1
+ method = method.first
+ else
+ method = nil
+ end
+ end
+
+ path_prefix = options.delete(:path_prefix)
+ name_prefix = options.delete(:name_prefix)
+ namespace = options.delete(:namespace)
+
+ name = options.delete(:_name)
+ name = "#{name_prefix}#{name}" if name_prefix
+
+ requirements = options.delete(:requirements) || {}
+ defaults = options.delete(:defaults) || {}
+ options.each do |k, v|
+ if v.is_a?(Regexp)
+ if value = options.delete(k)
+ requirements[k.to_sym] = value
+ end
+ else
+ value = options.delete(k)
+ defaults[k.to_sym] = value.is_a?(Symbol) ? value : value.to_param
+ end
+ end
+
+ requirements.each do |_, requirement|
+ 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
+ end
+
+ possible_names = Routing.possible_controllers.collect { |n| Regexp.escape(n) }
+ requirements[:controller] ||= Regexp.union(*possible_names)
+
+ if defaults[:controller]
+ defaults[:action] ||= 'index'
+ defaults[:controller] = defaults[:controller].to_s
+ defaults[:controller] = "#{namespace}#{defaults[:controller]}" if namespace
+ end
+
+ if defaults[:action]
+ defaults[:action] = defaults[:action].to_s
+ end
+
+ if path.is_a?(String)
+ path = "#{path_prefix}/#{path}" if path_prefix
+ path = path.gsub('.:format', '(.:format)')
+ path = optionalize_trailing_dynamic_segments(path, requirements, defaults)
+ glob = $1.to_sym if path =~ /\/\*(\w+)$/
+ path = ::Rack::Mount::Utils.normalize_path(path)
+ path = ::Rack::Mount::Strexp.compile(path, requirements, %w( / . ? ))
+
+ if glob && !defaults[glob].blank?
+ raise ActionController::RoutingError, "paths cannot have non-empty default values"
+ end
+ end
+
+ app = Routing::RouteSet::Dispatcher.new(:defaults => defaults, :glob => glob)
+
+ conditions = {}
+ conditions[:request_method] = method if method
+ conditions[:path_info] = path if path
+
+ @set.add_route(app, conditions, defaults, name)
+ end
+
+ def optionalize_trailing_dynamic_segments(path, requirements, defaults) #:nodoc:
+ path = (path =~ /^\//) ? path.dup : "/#{path}"
+ optional, segments = true, []
+
+ required_segments = requirements.keys
+ required_segments -= defaults.keys.compact
+
+ old_segments = path.split('/')
+ old_segments.shift
+ length = old_segments.length
+
+ old_segments.reverse.each_with_index do |segment, index|
+ required_segments.each do |required|
+ if segment =~ /#{required}/
+ optional = false
+ break
+ end
+ end
+
+ if optional
+ if segment == ":id" && segments.include?(":action")
+ optional = false
+ elsif segment == ":controller" || segment == ":action" || segment == ":id"
+ # Ignore
+ elsif !(segment =~ /^:\w+$/) &&
+ !(segment =~ /^:\w+\(\.:format\)$/)
+ optional = false
+ elsif segment =~ /^:(\w+)$/
+ if defaults.has_key?($1.to_sym)
+ defaults.delete($1.to_sym)
+ else
+ optional = false
+ end
+ end
+ end
+
+ if optional && index < length - 1
+ segments.unshift('(/', segment)
+ segments.push(')')
+ elsif optional
+ segments.unshift('/(', segment)
+ segments.push(')')
+ else
+ segments.unshift('/', segment)
+ end
+ end
+
+ segments.join
+ end
+ private :optionalize_trailing_dynamic_segments
+
+ # 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:
+ options[:_name] = name
+ connect(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?
+ named_route(route_name, *args)
+ end
+
INHERITABLE_OPTIONS = :namespace, :shallow
class Resource #:nodoc:
diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb
index 9e40108d00..a6e46b1c78 100644
--- a/actionpack/lib/action_dispatch/routing/route_set.rb
+++ b/actionpack/lib/action_dispatch/routing/route_set.rb
@@ -53,63 +53,6 @@ module ActionDispatch
end
end
- # 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 Routing::Resources
-
- def initialize(set) #:nodoc:
- @set = set
- end
-
- # Create an unnamed route with the provided +path+ and +options+. See
- # ActionDispatch::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.
@@ -347,109 +290,14 @@ module ActionDispatch
routes_changed_at
end
- def add_route(path, options = {})
- options = options.dup
-
- if conditions = options.delete(:conditions)
- conditions = conditions.dup
- method = [conditions.delete(:method)].flatten.compact
- method.map! { |m|
- m = m.to_s.upcase
-
- 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.downcase.to_sym)
- raise ArgumentError, "Invalid HTTP method specified in route conditions"
- end
-
- m
- }
-
- if method.length > 1
- method = Regexp.union(*method)
- elsif method.length == 1
- method = method.first
- else
- method = nil
- end
- end
-
- path_prefix = options.delete(:path_prefix)
- name_prefix = options.delete(:name_prefix)
- namespace = options.delete(:namespace)
-
- name = options.delete(:_name)
- name = "#{name_prefix}#{name}" if name_prefix
-
- requirements = options.delete(:requirements) || {}
- defaults = options.delete(:defaults) || {}
- options.each do |k, v|
- if v.is_a?(Regexp)
- if value = options.delete(k)
- requirements[k.to_sym] = value
- end
- else
- value = options.delete(k)
- defaults[k.to_sym] = value.is_a?(Symbol) ? value : value.to_param
- end
- end
-
- requirements.each do |_, requirement|
- 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
- end
-
- possible_names = Routing.possible_controllers.collect { |n| Regexp.escape(n) }
- requirements[:controller] ||= Regexp.union(*possible_names)
-
- if defaults[:controller]
- defaults[:action] ||= 'index'
- defaults[:controller] = defaults[:controller].to_s
- defaults[:controller] = "#{namespace}#{defaults[:controller]}" if namespace
- end
-
- if defaults[:action]
- defaults[:action] = defaults[:action].to_s
- end
-
- if path.is_a?(String)
- path = "#{path_prefix}/#{path}" if path_prefix
- path = path.gsub('.:format', '(.:format)')
- path = optionalize_trailing_dynamic_segments(path, requirements, defaults)
- glob = $1.to_sym if path =~ /\/\*(\w+)$/
- path = ::Rack::Mount::Utils.normalize_path(path)
- path = ::Rack::Mount::Strexp.compile(path, requirements, %w( / . ? ))
-
- if glob && !defaults[glob].blank?
- raise ActionController::RoutingError, "paths cannot have non-empty default values"
- end
- end
-
- app = Dispatcher.new(:defaults => defaults, :glob => glob)
-
- conditions = {}
- conditions[:request_method] = method if method
- conditions[:path_info] = path if path
-
+ def add_route(app, conditions = {}, defaults = {}, name = nil)
route = @set.add_route(app, conditions, defaults, name)
route.extend(RouteExtensions)
+ named_routes[name] = route if name
routes << route
route
end
- def add_named_route(name, path, options = {})
- options[:_name] = name
- route = add_route(path, options)
- named_routes[route.name] = route
- route
- end
-
def options_as_params(options)
# If an explicit :controller was given, always make :action explicit
# too, so that action expiry works as expected for things like
@@ -644,56 +492,6 @@ module ActionDispatch
_escape ? Rack::Mount::Utils.escape_uri(v) : v.to_s
end
end
-
- def optionalize_trailing_dynamic_segments(path, requirements, defaults)
- path = (path =~ /^\//) ? path.dup : "/#{path}"
- optional, segments = true, []
-
- required_segments = requirements.keys
- required_segments -= defaults.keys.compact
-
- old_segments = path.split('/')
- old_segments.shift
- length = old_segments.length
-
- old_segments.reverse.each_with_index do |segment, index|
- required_segments.each do |required|
- if segment =~ /#{required}/
- optional = false
- break
- end
- end
-
- if optional
- if segment == ":id" && segments.include?(":action")
- optional = false
- elsif segment == ":controller" || segment == ":action" || segment == ":id"
- # Ignore
- elsif !(segment =~ /^:\w+$/) &&
- !(segment =~ /^:\w+\(\.:format\)$/)
- optional = false
- elsif segment =~ /^:(\w+)$/
- if defaults.has_key?($1.to_sym)
- defaults.delete($1.to_sym)
- else
- optional = false
- end
- end
- end
-
- if optional && index < length - 1
- segments.unshift('(/', segment)
- segments.push(')')
- elsif optional
- segments.unshift('/(', segment)
- segments.push(')')
- else
- segments.unshift('/', segment)
- end
- end
-
- segments.join
- end
end
end
end