From a5db1488251304ec93256654859b430148f0c506 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 28 Jul 2008 13:41:42 -0500 Subject: Prepare Route#generate and Route#recognize early. Also refactor segments a bit to try to make immutable. --- .../lib/action_controller/routing/builder.rb | 29 +- .../lib/action_controller/routing/optimisations.rb | 1 + .../routing/recognition_optimisation.rb | 40 ++- actionpack/lib/action_controller/routing/route.rb | 300 +++++++++++---------- .../lib/action_controller/routing/route_set.rb | 3 +- .../lib/action_controller/routing/segments.rb | 29 +- actionpack/test/controller/routing_test.rb | 151 ++++------- actionpack/test/controller/test_test.rb | 4 +- 8 files changed, 259 insertions(+), 298 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb index 912999d845..03427e41de 100644 --- a/actionpack/lib/action_controller/routing/builder.rb +++ b/actionpack/lib/action_controller/routing/builder.rb @@ -48,14 +48,10 @@ module ActionController end when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional => true) when /\A\?(.*?)\?/ - returning segment = StaticSegment.new($1) do - segment.is_optional = true - end + StaticSegment.new($1, :optional => true) when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1) when Regexp.new(separator_pattern) then - returning segment = DividerSegment.new($&) do - segment.is_optional = (optional_separators.include? $&) - end + DividerSegment.new($&, :optional => (optional_separators.include? $&)) end [segment, $~.post_match] end @@ -176,29 +172,16 @@ module ActionController defaults, requirements, conditions = divide_route_options(segments, options) requirements = assign_route_options(segments, defaults, requirements) - route = Route.new - - route.segments = segments - route.requirements = requirements - route.conditions = conditions + # TODO: Segments should be frozen on initialize + segments.each { |segment| segment.freeze } - if !route.significant_keys.include?(:action) && !route.requirements[:action] - route.requirements[:action] = "index" - route.significant_keys << :action - end - - # Routes cannot use the current string interpolation method - # if there are user-supplied :requirements as the interpolation - # code won't raise RoutingErrors when generating - if options.key?(:requirements) || route.requirements.keys.to_set != Routing::ALLOWED_REQUIREMENTS_FOR_OPTIMISATION - route.optimise = false - end + route = Route.new(segments, requirements, conditions) if !route.significant_keys.include?(:controller) raise ArgumentError, "Illegal route: the :controller must be specified!" end - route + route.freeze end private diff --git a/actionpack/lib/action_controller/routing/optimisations.rb b/actionpack/lib/action_controller/routing/optimisations.rb index b44ebd5ca2..0fe836606c 100644 --- a/actionpack/lib/action_controller/routing/optimisations.rb +++ b/actionpack/lib/action_controller/routing/optimisations.rb @@ -20,6 +20,7 @@ module ActionController class Optimiser attr_reader :route, :kind + def initialize(route, kind) @route = route @kind = kind diff --git a/actionpack/lib/action_controller/routing/recognition_optimisation.rb b/actionpack/lib/action_controller/routing/recognition_optimisation.rb index 67d354a943..6d54d0334c 100644 --- a/actionpack/lib/action_controller/routing/recognition_optimisation.rb +++ b/actionpack/lib/action_controller/routing/recognition_optimisation.rb @@ -67,28 +67,6 @@ module ActionController end end - def recognize_optimized(path, env) - write_recognize_optimized - recognize_optimized(path, env) - end - - def write_recognize_optimized - tree = segment_tree(routes) - body = generate_code(tree) - 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 - }, __FILE__, __LINE__ - end - def segment_tree(routes) tree = [0] @@ -151,6 +129,24 @@ module ActionController segments << nil segments end + + private + def write_recognize_optimized! + tree = segment_tree(routes) + body = generate_code(tree) + 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 + }, __FILE__, __LINE__ + end end end end diff --git a/actionpack/lib/action_controller/routing/route.rb b/actionpack/lib/action_controller/routing/route.rb index a05c8c10ac..2106ac09e0 100644 --- a/actionpack/lib/action_controller/routing/route.rb +++ b/actionpack/lib/action_controller/routing/route.rb @@ -3,11 +3,25 @@ module ActionController class Route #:nodoc: attr_accessor :segments, :requirements, :conditions, :optimise - def initialize - @segments = [] - @requirements = {} - @conditions = {} - @optimise = true + 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 :requirements 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 @@ -22,129 +36,6 @@ module ActionController end.compact 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.) - - method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend" - instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" - - method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend" - instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" - 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] === 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 += Regexp.new(segment.regexp_chunk).number_of_captures - x - end - extraction.compact - end - - # Write the real generation implementation and then resend the message. - def generate(options, hash, expire_on = {}) - write_generation - generate options, hash, expire_on - end - - def generate_extras(options, hash, expire_on = {}) - write_generation - generate_extras options, hash, expire_on - 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 - # :controller 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 - # 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 @@ -161,12 +52,6 @@ module ActionController elements.empty? ? '' : "?#{elements.sort * '&'}" end - # Write the real recognition implementation and then resend the message. - def recognize(path, environment={}) - write_recognition - recognize path, environment - 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. # @@ -186,7 +71,7 @@ module ActionController # includes keys that appear inside the path, and keys that have requirements # placed upon them. def significant_keys - @significant_keys ||= returning [] do |sk| + @significant_keys ||= returning([]) do |sk| segments.each { |segment| sk << segment.key if segment.respond_to? :key } sk.concat requirements.keys sk.uniq! @@ -209,12 +94,7 @@ module ActionController end def matches_controller_and_action?(controller, action) - unless defined? @matching_prepared - @controller_requirement = requirement_for(:controller) - @action_requirement = requirement_for(:action) - @matching_prepared = true - end - + prepare_matching! (@controller_requirement.nil? || @controller_requirement === controller) && (@action_requirement.nil? || @action_requirement === action) end @@ -226,7 +106,23 @@ module ActionController end end - protected + # 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 + + private def requirement_for(key) return requirements[key] if requirements.key? key segments.each do |segment| @@ -234,6 +130,126 @@ module ActionController 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.) + + method_decl = "def generate(#{args})\npath, hash = generate_raw(options, hash, expire_on)\nappend_query_string(path, hash, extra_keys(options))\nend" + instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" + + method_decl = "def generate_extras(#{args})\npath, hash = generate_raw(options, hash, expire_on)\n[path, extra_keys(options)]\nend" + instance_eval method_decl, "generated code (#{__FILE__}:#{__LINE__})" + 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] === 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 += Regexp.new(segment.regexp_chunk).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 + # :controller 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 index 4a9bc259f3..8dfc22f94f 100644 --- a/actionpack/lib/action_controller/routing/route_set.rb +++ b/actionpack/lib/action_controller/routing/route_set.rb @@ -194,6 +194,8 @@ module ActionController def initialize self.routes = [] self.named_routes = NamedRouteCollection.new + + write_recognize_optimized! end # Subclasses and plugins may override this method to specify a different @@ -231,7 +233,6 @@ module ActionController Routing.use_controllers! nil # Clear the controller cache so we may discover new ones clear! load_routes! - install_helpers end # reload! will always force a reload whereas load checks the timestamp first diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index 18e76b6b82..75784c3b78 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -4,11 +4,12 @@ module ActionController RESERVED_PCHAR = ':@&=+$,;' UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze + # TODO: Convert :is_optional accessor to read only attr_accessor :is_optional alias_method :optional?, :is_optional def initialize - self.is_optional = false + @is_optional = false end def extraction_code @@ -63,12 +64,14 @@ module ActionController end class StaticSegment < Segment #:nodoc: - attr_accessor :value, :raw + attr_reader :value, :raw alias_method :raw?, :raw - def initialize(value = nil) + def initialize(value = nil, options = {}) super() - self.value = value + @value = value + @raw = options[:raw] if options.key?(:raw) + @is_optional = options[:optional] if options.key?(:optional) end def interpolation_chunk @@ -97,10 +100,8 @@ module ActionController end class DividerSegment < StaticSegment #:nodoc: - def initialize(value = nil) - super(value) - self.raw = true - self.is_optional = true + def initialize(value = nil, options = {}) + super(value, {:raw => true, :optional => true}.merge(options)) end def optionality_implied? @@ -109,13 +110,17 @@ module ActionController end class DynamicSegment < Segment #:nodoc: - attr_accessor :key, :default, :regexp + attr_reader :key + + # TODO: Convert these accessors to read only + attr_accessor :default, :regexp def initialize(key = nil, options = {}) super() - self.key = key - self.default = options[:default] if options.key? :default - self.is_optional = true if options[:optional] || options.key?(:default) + @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 diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 22e394dc95..6cf134c26f 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -67,66 +67,56 @@ class SegmentTest < Test::Unit::TestCase end def test_interpolation_statement - s = ROUTING::StaticSegment.new - s.value = "Hello" + s = ROUTING::StaticSegment.new("Hello") assert_equal "Hello", eval(s.interpolation_statement([])) assert_equal "HelloHello", eval(s.interpolation_statement([s])) - s2 = ROUTING::StaticSegment.new - s2.value = "-" + s2 = ROUTING::StaticSegment.new("-") assert_equal "Hello-Hello", eval(s.interpolation_statement([s, s2])) - s3 = ROUTING::StaticSegment.new - s3.value = "World" + s3 = ROUTING::StaticSegment.new("World") assert_equal "Hello-World", eval(s3.interpolation_statement([s, s2])) end end class StaticSegmentTest < Test::Unit::TestCase def test_interpolation_chunk_should_respect_raw - s = ROUTING::StaticSegment.new - s.value = 'Hello World' - assert ! s.raw? + s = ROUTING::StaticSegment.new('Hello World') + assert !s.raw? assert_equal 'Hello%20World', s.interpolation_chunk - s.raw = true + s = ROUTING::StaticSegment.new('Hello World', :raw => true) assert s.raw? assert_equal 'Hello World', s.interpolation_chunk end def test_regexp_chunk_should_escape_specials - s = ROUTING::StaticSegment.new - - s.value = 'Hello*World' + s = ROUTING::StaticSegment.new('Hello*World') assert_equal 'Hello\*World', s.regexp_chunk - s.value = 'HelloWorld' + s = ROUTING::StaticSegment.new('HelloWorld') assert_equal 'HelloWorld', s.regexp_chunk end def test_regexp_chunk_should_add_question_mark_for_optionals - s = ROUTING::StaticSegment.new - s.value = "/" - s.is_optional = true + s = ROUTING::StaticSegment.new("/", :optional => true) assert_equal "/?", s.regexp_chunk - s.value = "hello" + s = ROUTING::StaticSegment.new("hello", :optional => true) assert_equal "(?:hello)?", s.regexp_chunk end end class DynamicSegmentTest < Test::Unit::TestCase - def segment + def segment(options = {}) unless @segment - @segment = ROUTING::DynamicSegment.new - @segment.key = :a + @segment = ROUTING::DynamicSegment.new(:a, options) end @segment end def test_extract_value - s = ROUTING::DynamicSegment.new - s.key = :a + s = ROUTING::DynamicSegment.new(:a) hash = {:a => '10', :b => '20'} assert_equal '10', eval(s.extract_value) @@ -149,31 +139,31 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_regexp_value_check_rejects_nil - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) + a_value = nil - assert ! eval(segment.value_check) + assert !eval(segment.value_check) end def test_optional_regexp_value_check_should_accept_nil - segment.regexp = /\d+/ - segment.is_optional = true + segment = segment(:regexp => /\d+/, :optional => true) + a_value = nil assert eval(segment.value_check) end def test_regexp_value_check_rejects_no_match - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) a_value = "Hello20World" - assert ! eval(segment.value_check) + assert !eval(segment.value_check) a_value = "20Hi" - assert ! eval(segment.value_check) + assert !eval(segment.value_check) end def test_regexp_value_check_accepts_match - segment.regexp = /\d+/ - + segment = segment(:regexp => /\d+/) a_value = "30" assert eval(segment.value_check) end @@ -184,14 +174,14 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_optional_value_needs_no_check - segment.is_optional = true + segment = segment(:optional => true) + a_value = nil assert_equal nil, segment.value_check end def test_regexp_value_check_should_accept_match_with_default - segment.regexp = /\d+/ - segment.default = '200' + segment = segment(:regexp => /\d+/, :default => '200') a_value = '100' assert eval(segment.value_check) @@ -234,7 +224,7 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_extraction_code_should_return_on_mismatch - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) hash = merged = {:a => 'Hi', :b => '3'} options = {:b => '3'} a_value = nil @@ -292,7 +282,7 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_value_regexp_should_match_exacly - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) assert_no_match segment.value_regexp, "Hello 10 World" assert_no_match segment.value_regexp, "Hello 10" assert_no_match segment.value_regexp, "10 World" @@ -300,40 +290,36 @@ class DynamicSegmentTest < Test::Unit::TestCase end def test_regexp_chunk_should_return_string - segment.regexp = /\d+/ + segment = segment(:regexp => /\d+/) assert_kind_of String, segment.regexp_chunk end def test_build_pattern_non_optional_with_no_captures # Non optional - a_segment = ROUTING::DynamicSegment.new - a_segment.regexp = /\d+/ #number_of_captures is 0 + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /\d+/) assert_equal "(\\d+)stuff", a_segment.build_pattern('stuff') end def test_build_pattern_non_optional_with_captures # Non optional - a_segment = ROUTING::DynamicSegment.new - a_segment.regexp = /(\d+)(.*?)/ #number_of_captures is 2 + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /(\d+)(.*?)/) assert_equal "((\\d+)(.*?))stuff", a_segment.build_pattern('stuff') end def test_optionality_implied - a_segment = ROUTING::DynamicSegment.new - a_segment.key = :id + a_segment = ROUTING::DynamicSegment.new(:id) assert a_segment.optionality_implied? - a_segment.key = :action + a_segment = ROUTING::DynamicSegment.new(:action) assert a_segment.optionality_implied? end def test_modifiers_must_be_handled_sensibly - a_segment = ROUTING::DynamicSegment.new - a_segment.regexp = /david|jamis/i + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/i) assert_equal "((?i-mx:david|jamis))stuff", a_segment.build_pattern('stuff') - a_segment.regexp = /david|jamis/x + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/x) assert_equal "((?x-mi:david|jamis))stuff", a_segment.build_pattern('stuff') - a_segment.regexp = /david|jamis/ + a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/) assert_equal "(david|jamis)stuff", a_segment.build_pattern('stuff') end end @@ -560,7 +546,7 @@ class RouteBuilderTest < Test::Unit::TestCase action = segments[-4] assert_equal :action, action.key - action.regexp = /show|in/ # Use 'in' to check partial matches + segments[-4] = ROUTING::DynamicSegment.new(:action, :regexp => /show|in/) builder.assign_default_route_options(segments) @@ -661,10 +647,10 @@ class RoutingTest < Test::Unit::TestCase ActionController::Routing.controller_paths = [] assert_equal [], ActionController::Routing.possible_controllers - ActionController::Routing::Routes.load! ActionController::Routing.controller_paths = [ root, root + '/app/controllers', root + '/vendor/plugins/bad_plugin/lib' ] + ActionController::Routing::Routes.load! assert_equal ["admin/user", "plugin", "user"], ActionController::Routing.possible_controllers.sort ensure @@ -1374,34 +1360,20 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do end def slash_segment(is_optional = false) - returning ROUTING::DividerSegment.new('/') do |s| - s.is_optional = is_optional - end + ROUTING::DividerSegment.new('/', :optional => is_optional) end def default_route unless defined?(@default_route) - @default_route = ROUTING::Route.new - - @default_route.segments << (s = ROUTING::StaticSegment.new) - s.value = '/' - s.raw = true - - @default_route.segments << (s = ROUTING::DynamicSegment.new) - s.key = :controller - - @default_route.segments << slash_segment(:optional) - @default_route.segments << (s = ROUTING::DynamicSegment.new) - s.key = :action - s.default = 'index' - s.is_optional = true - - @default_route.segments << slash_segment(:optional) - @default_route.segments << (s = ROUTING::DynamicSegment.new) - s.key = :id - s.is_optional = true - - @default_route.segments << slash_segment(:optional) + segments = [] + segments << ROUTING::StaticSegment.new('/', :raw => true) + segments << ROUTING::DynamicSegment.new(:controller) + segments << slash_segment(:optional) + segments << ROUTING::DynamicSegment.new(:action, :default => 'index', :optional => true) + segments << slash_segment(:optional) + segments << ROUTING::DynamicSegment.new(:id, :optional => true) + segments << slash_segment(:optional) + @default_route = ROUTING::Route.new(segments).freeze end @default_route end @@ -1489,29 +1461,16 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do end def test_significant_keys - user_url = ROUTING::Route.new - user_url.segments << (s = ROUTING::StaticSegment.new) - s.value = '/' - s.raw = true - - user_url.segments << (s = ROUTING::StaticSegment.new) - s.value = 'user' - - user_url.segments << (s = ROUTING::StaticSegment.new) - s.value = '/' - s.raw = true - s.is_optional = true - - user_url.segments << (s = ROUTING::DynamicSegment.new) - s.key = :user - - user_url.segments << (s = ROUTING::StaticSegment.new) - s.value = '/' - s.raw = true - s.is_optional = true + segments = [] + segments << ROUTING::StaticSegment.new('/', :raw => true) + segments << ROUTING::StaticSegment.new('user') + segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true) + segments << ROUTING::DynamicSegment.new(:user) + segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true) - user_url.requirements = {:controller => 'users', :action => 'show'} + requirements = {:controller => 'users', :action => 'show'} + user_url = ROUTING::Route.new(segments, requirements) keys = user_url.significant_keys.sort_by { |k| k.to_s } assert_equal [:action, :controller, :user], keys end diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 61b8c83ee0..58d9ca537f 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -117,8 +117,8 @@ XML @controller = TestController.new @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new - ActionController::Routing::Routes.reload ActionController::Routing.use_controllers! %w(content admin/user test_test/test) + ActionController::Routing::Routes.load_routes! end def teardown @@ -412,7 +412,7 @@ XML def test_assert_routing_with_method with_routing do |set| - set.draw { |map| map.resources(:content) } + set.draw { |map| map.resources(:content) } assert_routing({ :method => 'post', :path => 'content' }, { :controller => 'content', :action => 'create' }) end end -- cgit v1.2.3