diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/routing')
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 213 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/route_set.rb | 8 |
2 files changed, 139 insertions, 82 deletions
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index d480af876d..e655d6a708 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -19,9 +19,9 @@ module ActionDispatch @constraints.each { |constraint| if constraint.respond_to?(:matches?) && !constraint.matches?(req) - return [417, {}, []] + return [ 404, {'X-Cascade' => 'pass'}, [] ] elsif constraint.respond_to?(:call) && !constraint.call(req) - return [417, {}, []] + return [ 404, {'X-Cascade' => 'pass'}, [] ] end } @@ -29,90 +29,138 @@ module ActionDispatch end end - module Base - def initialize(set) - @set = set + class Mapping + def initialize(set, scope, args) + @set, @scope = set, scope + @path, @options = extract_path_and_options(args) end - def root(options = {}) - match '/', options.merge(:as => :root) + def to_route + [ app, conditions, requirements, defaults, @options[:as] ] end - def match(*args) - options = args.extract_options! + private + def extract_path_and_options(args) + options = args.extract_options! - path = args.first + if args.empty? + path, to = options.find { |name, value| name.is_a?(String) } + options.merge!(:to => to).delete(path) if path + else + path = args.first + end - conditions, defaults = {}, {} + [ normalize_path(path), options ] + end - path = nil if path == "" - path = "#{@scope[:path]}#{path}" if @scope[:path] - path = Rack::Mount::Utils.normalize_path(path) if path + def normalize_path(path) + path = nil if path == "" + path = "#{@scope[:path]}#{path}" if @scope[:path] + path = Rack::Mount::Utils.normalize_path(path) if path - raise ArgumentError, "path is required" unless path + raise ArgumentError, "path is required" unless path - constraints = options[:constraints] || {} - unless constraints.is_a?(Hash) - block, constraints = constraints, {} + path end - blocks = ((@scope[:blocks] || []) + [block]).compact - constraints = (@scope[:constraints] || {}).merge(constraints) - options.each { |k, v| constraints[k] = v if v.is_a?(Regexp) } - conditions[:path_info] = path - requirements = constraints.dup - path_regexp = Rack::Mount::Strexp.compile(path, constraints, SEPARATORS) - segment_keys = Rack::Mount::RegexpWithNamedGroups.new(path_regexp).names - constraints.reject! { |k, v| segment_keys.include?(k.to_s) } - conditions.merge!(constraints) + def app + Constraints.new( + to.respond_to?(:call) ? to : Routing::RouteSet::Dispatcher.new(:defaults => defaults), + blocks + ) + end - requirements[:controller] ||= @set.controller_constraints + def conditions + { :path_info => @path }.merge(constraints).merge(request_method_condition) + end - if via = options[:via] - via = Array(via).map { |m| m.to_s.upcase } - conditions[:request_method] = Regexp.union(*via) + def requirements + @requirements ||= returning(@options[:constraints] || {}) do |requirements| + requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints] + @options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) } + requirements[:controller] ||= @set.controller_constraints + end end - defaults[:controller] ||= @scope[:controller].to_s if @scope[:controller] + def defaults + @defaults ||= if to.respond_to?(:call) + { } + else + defaults = case to + when String + controller, action = to.split('#') + { :controller => controller, :action => action } + when Symbol + { :action => to.to_s }.merge(default_controller ? { :controller => default_controller } : {}) + else + default_controller ? { :controller => default_controller } : {} + end - app = initialize_app_endpoint(options, defaults) - validate_defaults!(app, defaults, segment_keys) - app = Constraints.new(app, blocks) + if defaults[:controller].blank? && segment_keys.exclude?("controller") + raise ArgumentError, "missing :controller" + end - @set.add_route(app, conditions, requirements, defaults, options[:as]) + if defaults[:action].blank? && segment_keys.exclude?("action") + raise ArgumentError, "missing :action" + end - self - end + defaults + end + end - private - def initialize_app_endpoint(options, defaults) - app = nil - - if options[:to].respond_to?(:call) - app = options[:to] - defaults.delete(:controller) - defaults.delete(:action) - elsif options[:to].is_a?(String) - defaults[:controller], defaults[:action] = options[:to].split('#') - elsif options[:to].is_a?(Symbol) - defaults[:action] = options[:to].to_s + + def blocks + if @options[:constraints].present? && !@options[:constraints].is_a?(Hash) + block = @options[:constraints] + else + block = nil end - app || Routing::RouteSet::Dispatcher.new(:defaults => defaults) + ((@scope[:blocks] || []) + [ block ]).compact end - def validate_defaults!(app, defaults, segment_keys) - return unless app.is_a?(Routing::RouteSet::Dispatcher) + def constraints + @constraints ||= requirements.reject { |k, v| segment_keys.include?(k.to_s) || k == :controller } + end - unless defaults.include?(:controller) || segment_keys.include?("controller") - raise ArgumentError, "missing :controller" + def request_method_condition + if via = @options[:via] + via = Array(via).map { |m| m.to_s.upcase } + { :request_method => Regexp.union(*via) } + else + { } end + end - unless defaults.include?(:action) || segment_keys.include?("action") - raise ArgumentError, "missing :action" - end + def segment_keys + @segment_keys ||= Rack::Mount::RegexpWithNamedGroups.new( + Rack::Mount::Strexp.compile(@path, requirements, SEPARATORS) + ).names + end + + def to + @options[:to] end + + def default_controller + @scope[:controller].to_s if @scope[:controller] + end + end + + module Base + def initialize(set) + @set = set + end + + def root(options = {}) + match '/', options.reverse_merge(:as => :root) + end + + def match(*args) + @set.add_route(*Mapping.new(@set, @scope, args).to_route) + self + end end module HttpHelpers @@ -132,13 +180,20 @@ module ActionDispatch map_method(:delete, *args, &block) end - def redirect(path, options = {}) - status = options[:status] || 301 - lambda { |env| - req = Rack::Request.new(env) - url = req.scheme + '://' + req.host + path - [status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently']] - } + def redirect(*args, &block) + options = args.last.is_a?(Hash) ? args.pop : {} + + path = args.shift || block + path_proc = path.is_a?(Proc) ? path : proc { |params| path % params } + status = options[:status] || 301 + + lambda do |env| + req = Rack::Request.new(env) + params = path_proc.call(env["action_dispatch.request.path_parameters"]) + url = req.scheme + '://' + req.host + params + + [ status, {'Location' => url, 'Content-Type' => 'text/html'}, ['Moved Permanently'] ] + end end private @@ -201,11 +256,11 @@ module ActionDispatch self ensure - @scope[:path] = path if path_set + @scope[:path] = path if path_set @scope[:name_prefix] = name_prefix if name_prefix_set - @scope[:controller] = controller if controller_set - @scope[:options] = options - @scope[:blocks] = blocks + @scope[:controller] = controller if controller_set + @scope[:options] = options + @scope[:blocks] = blocks @scope[:constraints] = constraints end @@ -301,12 +356,12 @@ module ActionDispatch with_scope_level(:resource, resource) do yield if block_given? - get "(.:format)", :to => :show, :as => resource.member_name - post "(.:format)", :to => :create - put "(.:format)", :to => :update - delete "(.:format)", :to => :destroy - get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}" - get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}" + get "(.:format)", :to => :show, :as => resource.member_name + post "(.:format)", :to => :create + put "(.:format)", :to => :update + delete "(.:format)", :to => :destroy + get "/new(.:format)", :to => :new, :as => "new_#{resource.singular}" + get "/edit(.:format)", :to => :edit, :as => "edit_#{resource.singular}" end end @@ -336,8 +391,9 @@ module ActionDispatch yield if block_given? with_scope_level(:collection) do - get "(.:format)", :to => :index, :as => resource.collection_name + get "(.:format)", :to => :index, :as => resource.collection_name post "(.:format)", :to => :create + with_exclusive_name_prefix :new do get "/new(.:format)", :to => :new, :as => resource.singular end @@ -345,9 +401,10 @@ module ActionDispatch with_scope_level(:member) do scope("/:id") do - get "(.:format)", :to => :show, :as => resource.member_name - put "(.:format)", :to => :update + get "(.:format)", :to => :show, :as => resource.member_name + put "(.:format)", :to => :update delete "(.:format)", :to => :destroy + with_exclusive_name_prefix :edit do get "/edit(.:format)", :to => :edit, :as => resource.singular end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index bf2443c1be..bd397432ce 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -5,7 +5,7 @@ module ActionDispatch module Routing class RouteSet #:nodoc: NotFound = lambda { |env| - raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect} with #{env.inspect}" + raise ActionController::RoutingError, "No route matches #{env['PATH_INFO'].inspect}" } PARAMETERS_KEY = 'action_dispatch.request.path_parameters' @@ -21,7 +21,7 @@ module ActionDispatch prepare_params!(params) unless controller = controller(params) - return [417, {}, []] + return [404, {'X-Cascade' => 'pass'}, []] end controller.action(params[:action]).call(env) @@ -273,7 +273,7 @@ module ActionDispatch # TODO: Move this into Railties if defined?(Rails.application) # Find namespaces in controllers/ directory - Rails.application.configuration.controller_paths.each do |load_path| + Rails.application.config.controller_paths.each do |load_path| load_path = File.expand_path(load_path) Dir["#{load_path}/**/*_controller.rb"].collect do |path| namespaces << File.dirname(path).sub(/#{load_path}\/?/, '') @@ -426,7 +426,7 @@ module ActionDispatch end end - raise ActionController::RoutingError, "No route matches #{path.inspect} with #{environment.inspect}" + raise ActionController::RoutingError, "No route matches #{path.inspect}" end end end |