From 1f80f540a7618e0d9c853319f6488e25ac201b2c Mon Sep 17 00:00:00 2001 From: Jamis Buck Date: Fri, 21 Apr 2006 15:17:02 +0000 Subject: Add support in routes for semicolon delimited "subpaths", like /books/:id;:action git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4242 5ecf4fe2-1ee6-0310-87b1-e25e094e27de --- .../lib/action_controller/code_generation.rb | 26 +++++---- actionpack/lib/action_controller/routing.rb | 63 +++++++++++++++++++--- 2 files changed, 73 insertions(+), 16 deletions(-) (limited to 'actionpack/lib/action_controller') diff --git a/actionpack/lib/action_controller/code_generation.rb b/actionpack/lib/action_controller/code_generation.rb index 6762b7610d..312dff6be0 100644 --- a/actionpack/lib/action_controller/code_generation.rb +++ b/actionpack/lib/action_controller/code_generation.rb @@ -65,7 +65,7 @@ module ActionController copy = self.class.new(source) self.class::FieldsToDuplicate.each do |sym| value = self.send(sym) - value = value.dup unless value.nil? || value.is_a?(Numeric) + value = value.dup unless value.nil? || value.is_a?(Numeric) || value.is_a?(Symbol) copy.send("#{sym}=", value) end return copy @@ -73,7 +73,7 @@ module ActionController end class RecognitionGenerator < CodeGenerator #:nodoc: - Attributes = [:after, :before, :current, :results, :constants, :depth, :move_ahead, :finish_statement] + Attributes = [:after, :before, :current, :results, :constants, :depth, :move_ahead, :finish_statement, :path_name, :base_segment_name, :base_index_name] attr_accessor(*Attributes) FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes @@ -85,6 +85,9 @@ module ActionController @depth = 0 @move_ahead = nil @finish_statement = Proc.new {|hash_expr| hash_expr} + @path_name = :path + @base_segment_name = :segment + @base_index_name = :index end def if_next_matches(string, &block) @@ -118,11 +121,10 @@ module ActionController return code.to_s end - def segment_name() "segment#{depth}".to_sym end - def path_name() :path end + def segment_name() "#{base_segment_name}#{depth}".to_sym end def index_name move_ahead, @move_ahead = @move_ahead, nil - move_ahead ? "index += #{move_ahead}" : 'index' + move_ahead ? "#{base_index_name} += #{move_ahead}" : base_index_name end def continue @@ -162,9 +164,9 @@ module ActionController end end end - + class GenerationGenerator < CodeGenerator #:nodoc: - Attributes = [:after, :before, :current, :segments] + Attributes = [:after, :before, :current, :segments, :subpath_at] attr_accessor(*Attributes) FieldsToDuplicate = CodeGenerator::FieldsToDuplicate + Attributes @@ -173,6 +175,7 @@ module ActionController @after, @before = [], [] @current = nil @segments = [] + @subpath_at = nil end def hash_name() 'hash' end @@ -202,7 +205,7 @@ module ActionController d.segments.concat segments yield d end - + def go if current then current.write_generation(self) else self.finish @@ -215,8 +218,13 @@ module ActionController d.current = d.after.shift d.go end - + + def start_subpath! + @subpath_at ||= segments.length + end + def finish + segments[subpath_at..-1] = [segments[subpath_at..-1].join(";")] if subpath_at line %("/#{segments.join('/')}") end diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index b25f99c37f..4fa1acf95e 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -60,6 +60,7 @@ module ActionController def self.new(string, *args) return super(string, *args) unless self == Component case string + when /.*;.*/ then SubpathComponent.new(string.split(/;/), *args) when ':controller' then ControllerComponent.new(:controller, *args) when /^:(\w+)$/ then DynamicComponent.new($1, *args) when /^\*(\w+)$/ then PathComponent.new($1, *args) @@ -68,6 +69,53 @@ module ActionController end end + class SubpathComponent < Component #:nodoc: + attr_reader :parts + + def initialize(parts, *args) + @parts = parts.map { |part| Component.new(part, *args) } + end + + def write_recognition(g) + raise RoutingError, "Subpath components must occur last" unless g.after.empty? + g.next_segment + g.line "subindex, subpath = 0, #{g.next_segment}.split(/;/)" + tweak_recognizer(g).go + g.move_forward { |gg| gg.continue } + end + + def write_generation(g) + raise RoutingError, "Subpath components must occur last" unless g.after.empty? + tweak_generator(g).go + end + + def key + parts.map { |p| p.key } + end + + private + + def tweak_recognizer(g) + gg = g.dup + + gg.path_name = :subpath + gg.base_segment_name = :subsegment + gg.base_index_name = :subindex + gg.depth = 0 + + gg.before, gg.current, gg.after = [], parts.first, (parts[1..-1] || []) + + gg + end + + def tweak_generator(g) + gg = g.dup + gg.before, gg.current, gg.after = [], parts.first, (parts[1..-1] || []) + gg.start_subpath! + gg + end + end + class StaticComponent < Component #:nodoc: attr_reader :value @@ -337,7 +385,7 @@ module ActionController g = generator.dup g.share_locals_with generator g.before, g.current, g.after = [], components.first, (components[1..-1] || []) - + known.each do |key, value| if key == :controller then ControllerComponent.assign_controller(g, value) else g.constant_result(key, value) @@ -354,7 +402,7 @@ module ActionController end def initialize_keys - @keys = (components.collect {|c| c.key} + known.keys).compact + @keys = (components.collect {|c| c.key} + known.keys).flatten.compact @keys.freeze end @@ -379,12 +427,12 @@ module ActionController end def initialize_hashes(options) - path_keys = components.collect {|c| c.key }.compact + path_keys = components.collect {|c| c.key }.flatten.compact self.known = {} defaults = options.delete(:defaults) || {} conditions = options.delete(:require) || {} conditions.update(options.delete(:requirements) || {}) - + options.each do |k, v| if path_keys.include?(k) then (v.is_a?(Regexp) ? conditions : defaults)[k] = v else known[k] = v @@ -394,7 +442,8 @@ module ActionController end def configure_components(defaults, conditions) - components.each do |component| + all_components = components.map { |c| SubpathComponent === c ? c.parts : c }.flatten + all_components.each do |component| if defaults.key?(component.key) then component.default = defaults[component.key] elsif component.key == :action then component.default = 'index' elsif component.key == :id then component.default = nil @@ -405,7 +454,7 @@ module ActionController end def add_default_requirements - component_keys = components.collect {|c| c.key} + component_keys = components.collect {|c| c.key}.flatten known[:action] ||= 'index' unless component_keys.include? :action end end @@ -505,7 +554,7 @@ module ActionController route.write_recognition(g) end end - + eval g.to_s, nil, 'generated/routing/recognition.rb' return g.to_s end -- cgit v1.2.3