diff options
Diffstat (limited to 'actionpack/lib/action_dispatch/routing')
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 201 |
1 files changed, 85 insertions, 116 deletions
diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 23b13d1d5d..9aa34e7ba5 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -43,9 +43,10 @@ module ActionDispatch class Mapping #:nodoc: IGNORE_OPTIONS = [:to, :as, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix] - def initialize(set, scope, args) - @set, @scope = set, scope - @path, @options = extract_path_and_options(args) + def initialize(set, scope, path, options) + @set, @scope = set, scope + @options = (@scope[:options] || {}).merge(options) + @path = normalize_path(path) normalize_options! end @@ -54,28 +55,6 @@ module ActionDispatch end private - def extract_path_and_options(args) - options = args.extract_options! - - if using_to_shorthand?(args, options) - path, to = options.find { |name, value| name.is_a?(String) } - options.merge!(:to => to).delete(path) if path - else - path = args.first - end - - if path.match(':controller') - raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module] - - # Add a default constraint for :controller path segments that matches namespaced - # controllers with default routes like :controller/:action/:id(.:format), e.g: - # GET /admin/products/show/1 - # => { :controller => 'admin/products', :action => 'show', :id => '1' } - options.reverse_merge!(:controller => /.+?/) - end - - [ normalize_path(path), options ] - end def normalize_options! path_without_format = @path.sub(/\(\.:format\)$/, '') @@ -83,25 +62,39 @@ module ActionDispatch if using_match_shorthand?(path_without_format, @options) to_shorthand = @options[:to].blank? @options[:to] ||= path_without_format[1..-1].sub(%r{/([^/]*)$}, '#\1') - @options[:as] ||= path_without_format[1..-1].gsub("/", "_") + @options[:as] ||= Mapper.normalize_name(path_without_format) end @options.merge!(default_controller_and_action(to_shorthand)) end - # match "account" => "account#index" - def using_to_shorthand?(args, options) - args.empty? && options.present? - end - # match "account/overview" def using_match_shorthand?(path, options) path && options.except(:via, :anchor, :to, :as).empty? && path =~ %r{^/[\w\/]+$} end def normalize_path(path) - raise ArgumentError, "path is required" if @scope[:path].blank? && path.blank? - Mapper.normalize_path("#{@scope[:path]}/#{path}") + raise ArgumentError, "path is required" if path.blank? + path = Mapper.normalize_path(path) + + if path.match(':controller') + raise ArgumentError, ":controller segment is not allowed within a namespace block" if @scope[:module] + + # Add a default constraint for :controller path segments that matches namespaced + # controllers with default routes like :controller/:action/:id(.:format), e.g: + # GET /admin/products/show/1 + # => { :controller => 'admin/products', :action => 'show', :id => '1' } + @options.reverse_merge!(:controller => /.+?/) + end + + if @options[:format] == false + @options.delete(:format) + path + elsif path.include?(":format") + path + else + "#{path}(.:format)" + end end def app @@ -224,6 +217,10 @@ module ActionDispatch path end + def self.normalize_name(name) + normalize_path(name)[1..-1].gsub("/", "_") + end + module Base def initialize(set) #:nodoc: @set = set @@ -233,8 +230,8 @@ module ActionDispatch match '/', options.reverse_merge(:as => :root) end - def match(*args) - mapping = Mapping.new(@set, @scope, args).to_route + def match(path, options=nil) + mapping = Mapping.new(@set, @scope, path, options || {}).to_route @set.add_route(*mapping) self end @@ -250,7 +247,7 @@ module ActionDispatch raise "A rack application must be specified" unless path - match(path, options.merge(:to => app, :anchor => false)) + match(path, options.merge(:to => app, :anchor => false, :format => false)) self end @@ -332,13 +329,7 @@ module ActionDispatch ActiveSupport::Deprecation.warn ":name_prefix was deprecated in the new router syntax. Use :as instead.", caller end - case args.first - when String - options[:path] = args.first - when Symbol - options[:controller] = args.first - end - + options[:path] = args.first if args.first.is_a?(String) recover = {} options[:constraints] ||= {} @@ -370,8 +361,9 @@ module ActionDispatch @scope[:blocks] = recover[:block] end - def controller(controller) - scope(controller.to_sym) { yield } + def controller(controller, options={}) + options[:controller] = controller + scope(options) { yield } end def namespace(path, options = {}) @@ -389,21 +381,6 @@ module ActionDispatch scope(:defaults => defaults) { yield } end - def match(*args) - options = args.extract_options! - - options = (@scope[:options] || {}).merge(options) - - if @scope[:as] && !options[:as].blank? - options[:as] = "#{@scope[:as]}_#{options[:as]}" - elsif @scope[:as] && options[:as] == "" - options[:as] = @scope[:as].to_s - end - - args.push(options) - super(*args) - end - private def scope_options @scope_options ||= private_methods.grep(/^merge_(.+)_scope$/) { $1.to_sym } @@ -467,9 +444,9 @@ module ActionDispatch module Resources # CANONICAL_ACTIONS holds all actions that does not need a prefix or # a path appended since they fit properly in their scope level. - VALID_ON_OPTIONS = [:new, :collection, :member] - CANONICAL_ACTIONS = [:index, :create, :new, :show, :update, :destroy] - RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except] + VALID_ON_OPTIONS = [:new, :collection, :member] + RESOURCE_OPTIONS = [:as, :controller, :path, :only, :except] + CANONICAL_ACTIONS = %w(index create new show update destroy) class Resource #:nodoc: DEFAULT_ACTIONS = [:index, :create, :new, :show, :update, :destroy, :edit] @@ -723,42 +700,27 @@ module ActionDispatch return member { match(*args) } end - path = options.delete(:path) - action = args.first - - if action.is_a?(Symbol) - path = path_for_action(action, path) - options[:to] ||= action - options[:as] = name_for_action(action, options[:as]) - - with_exclusive_scope do - return super(path, options) - end - elsif resource_method_scope? - path = path_for_custom_action - options[:action] ||= action - options[:as] = name_for_action(options[:as]) if options[:as] - args.push(options) - - with_exclusive_scope do - scope(path) do - return super - end - end - end - if resource_scope? raise ArgumentError, "can't define route directly in resource(s) scope" end - args.push(options) - super + action = args.first + path = path_for_action(action, options.delete(:path)) + + if action.to_s =~ /^[\w\/]+$/ + options[:action] ||= action unless action.to_s.include?("/") + options[:as] = name_for_action(action, options[:as]) + else + options[:as] = name_for_action(options[:as]) + end + + super(path, options) end def root(options={}) if @scope[:scope_level] == :resources - with_scope_level(:nested) do - scope(parent_resource.path, :as => parent_resource.collection_name) do + with_scope_level(:root) do + scope(parent_resource.path) do super(options) end end @@ -895,7 +857,7 @@ module ActionDispatch end def canonical_action?(action, flag) - flag && CANONICAL_ACTIONS.include?(action) + flag && resource_method_scope? && CANONICAL_ACTIONS.include?(action.to_s) end def shallow_scoping? @@ -906,18 +868,10 @@ module ActionDispatch prefix = shallow_scoping? ? "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" : @scope[:path] - if canonical_action?(action, path.blank?) - "#{prefix}(.:format)" - else - "#{prefix}/#{action_path(action, path)}(.:format)" - end - end - - def path_for_custom_action - if shallow_scoping? - "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" + path = if canonical_action?(action, path.blank?) + prefix.to_s else - @scope[:path] + "#{prefix}/#{action_path(action, path)}" end end @@ -927,44 +881,59 @@ module ActionDispatch def prefix_name_for_action(action, as) if as.present? - "#{as}_" + as.to_s elsif as - "" + nil elsif !canonical_action?(action, @scope[:scope_level]) - "#{action}_" + action.to_s end end def name_for_action(action, as=nil) prefix = prefix_name_for_action(action, as) + prefix = Mapper.normalize_name(prefix) if prefix name_prefix = @scope[:as] if parent_resource collection_name = parent_resource.collection_name member_name = parent_resource.member_name - name_prefix = "#{name_prefix}_" if name_prefix.present? end - case @scope[:scope_level] + name = case @scope[:scope_level] when :collection - "#{prefix}#{name_prefix}#{collection_name}" + [prefix, name_prefix, collection_name] when :new - "#{prefix}new_#{name_prefix}#{member_name}" + [prefix, :new, name_prefix, member_name] + when :member + [prefix, shallow_scoping? ? @scope[:shallow_prefix] : name_prefix, member_name] + when :root + [name_prefix, collection_name, prefix] else - if shallow_scoping? - shallow_prefix = "#{@scope[:shallow_prefix]}_" if @scope[:shallow_prefix].present? - "#{prefix}#{shallow_prefix}#{member_name}" - else - "#{prefix}#{name_prefix}#{member_name}" - end + [name_prefix, member_name, prefix] end + + name.select(&:present?).join("_").presence end end + module Shorthand + def match(*args) + if args.size == 1 && args.last.is_a?(Hash) + options = args.pop + path, to = options.find { |name, value| name.is_a?(String) } + options.merge!(:to => to).delete(path) + super(path, options) + else + super + end + end + end + include Base include HttpHelpers include Scoping include Resources + include Shorthand end end end |