1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Prepare Route#generate and Route#recognize early. Also refactor segments a bit to try to make immutable.

This commit is contained in:
Joshua Peek 2008-07-28 13:41:42 -05:00
parent e0513e33c4
commit a5db148825
8 changed files with 259 additions and 298 deletions

View file

@ -48,14 +48,10 @@ module ActionController
end end
when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional => true) when /\A\*(\w+)/ then PathSegment.new($1.to_sym, :optional => true)
when /\A\?(.*?)\?/ when /\A\?(.*?)\?/
returning segment = StaticSegment.new($1) do StaticSegment.new($1, :optional => true)
segment.is_optional = true
end
when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1) when /\A(#{separator_pattern(:inverted)}+)/ then StaticSegment.new($1)
when Regexp.new(separator_pattern) then when Regexp.new(separator_pattern) then
returning segment = DividerSegment.new($&) do DividerSegment.new($&, :optional => (optional_separators.include? $&))
segment.is_optional = (optional_separators.include? $&)
end
end end
[segment, $~.post_match] [segment, $~.post_match]
end end
@ -176,29 +172,16 @@ module ActionController
defaults, requirements, conditions = divide_route_options(segments, options) defaults, requirements, conditions = divide_route_options(segments, options)
requirements = assign_route_options(segments, defaults, requirements) requirements = assign_route_options(segments, defaults, requirements)
route = Route.new # TODO: Segments should be frozen on initialize
segments.each { |segment| segment.freeze }
route.segments = segments route = Route.new(segments, requirements, conditions)
route.requirements = requirements
route.conditions = conditions
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 <tt>:requirements</tt> 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
if !route.significant_keys.include?(:controller) if !route.significant_keys.include?(:controller)
raise ArgumentError, "Illegal route: the :controller must be specified!" raise ArgumentError, "Illegal route: the :controller must be specified!"
end end
route route.freeze
end end
private private

View file

@ -20,6 +20,7 @@ module ActionController
class Optimiser class Optimiser
attr_reader :route, :kind attr_reader :route, :kind
def initialize(route, kind) def initialize(route, kind)
@route = route @route = route
@kind = kind @kind = kind

View file

@ -67,28 +67,6 @@ module ActionController
end end
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) def segment_tree(routes)
tree = [0] tree = [0]
@ -151,6 +129,24 @@ module ActionController
segments << nil segments << nil
segments segments
end 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 end
end end

View file

@ -3,12 +3,26 @@ module ActionController
class Route #:nodoc: class Route #:nodoc:
attr_accessor :segments, :requirements, :conditions, :optimise attr_accessor :segments, :requirements, :conditions, :optimise
def initialize def initialize(segments = [], requirements = {}, conditions = {})
@segments = [] @segments = segments
@requirements = {} @requirements = requirements
@conditions = {} @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 <tt>:requirements</tt> 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 @optimise = true
end end
end
# Indicates whether the routes should be optimised with the string interpolation # Indicates whether the routes should be optimised with the string interpolation
# version of the named routes methods. # version of the named routes methods.
@ -22,8 +36,103 @@ module ActionController
end.compact end.compact
end 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
# values.
def build_query_string(hash, only_keys = nil)
elements = []
(only_keys || hash.keys).each do |key|
if value = hash[key]
elements << value.to_query(key)
end
end
elements.empty? ? '' : "?#{elements.sort * '&'}"
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.
#
# For example, +{:controller => 'pages', :action => 'show'} is the shell for the route:
#
# map.connect '/page/:id', :controller => 'pages', :action => 'show', :id => /\d+/
#
def parameter_shell
@parameter_shell ||= returning({}) do |shell|
requirements.each do |key, requirement|
shell[key] = requirement unless requirement.is_a? Regexp
end
end
end
# Return an array containing all the keys that are used in this route. This
# includes keys that appear inside the path, and keys that have requirements
# placed upon them.
def significant_keys
@significant_keys ||= returning([]) do |sk|
segments.each { |segment| sk << segment.key if segment.respond_to? :key }
sk.concat requirements.keys
sk.uniq!
end
end
# Return a hash of key/value pairs representing the keys in the route that
# have defaults, or which are specified by non-regexp requirements.
def defaults
@defaults ||= returning({}) do |hash|
segments.each do |segment|
next unless segment.respond_to? :default
hash[segment.key] = segment.default unless segment.default.nil?
end
requirements.each do |key,req|
next if Regexp === req || req.nil?
hash[key] = req
end
end
end
def matches_controller_and_action?(controller, action)
prepare_matching!
(@controller_requirement.nil? || @controller_requirement === controller) &&
(@action_requirement.nil? || @action_requirement === action)
end
def to_s
@to_s ||= begin
segs = segments.inject("") { |str,s| str << s.to_s }
"%-6s %-40s %s" % [(conditions[:method] || :any).to_s.upcase, segs, requirements.inspect]
end
end
# 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|
return segment.regexp if segment.respond_to?(:key) && segment.key == key
end
nil
end
# Write and compile a +generate+ method for this Route. # Write and compile a +generate+ method for this Route.
def write_generation def write_generation!
# Build the main body of the generation # Build the main body of the generation
body = "expired = false\n#{generation_extraction}\n#{generation_structure}" body = "expired = false\n#{generation_extraction}\n#{generation_structure}"
@ -76,7 +185,7 @@ module ActionController
end end
# Write and compile a +recognize+ method for this Route. # Write and compile a +recognize+ method for this Route.
def write_recognition def write_recognition!
# Create an if structure to extract the params from a match if it occurs. # 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 = "params = parameter_shell.dup\n#{recognition_extraction * "\n"}\nparams"
body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend" body = "if #{recognition_conditions.join(" && ")}\n#{body}\nend"
@ -116,17 +225,6 @@ module ActionController
extraction.compact extraction.compact
end 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 # Generate the query string with any extra keys in the hash and append
# it to the given path, returning the new path. # it to the given path, returning the new path.
def append_query_string(path, hash, query_keys = nil) def append_query_string(path, hash, query_keys = nil)
@ -145,94 +243,12 @@ module ActionController
(hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys (hash || {}).keys.map { |k| k.to_sym } - (recall || {}).keys - significant_keys
end end
# Build a query string from the keys of the given hash. If +only_keys+ def prepare_matching!
# 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
# values.
def build_query_string(hash, only_keys = nil)
elements = []
(only_keys || hash.keys).each do |key|
if value = hash[key]
elements << value.to_query(key)
end
end
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.
#
# For example, +{:controller => 'pages', :action => 'show'} is the shell for the route:
#
# map.connect '/page/:id', :controller => 'pages', :action => 'show', :id => /\d+/
#
def parameter_shell
@parameter_shell ||= returning({}) do |shell|
requirements.each do |key, requirement|
shell[key] = requirement unless requirement.is_a? Regexp
end
end
end
# Return an array containing all the keys that are used in this route. This
# includes keys that appear inside the path, and keys that have requirements
# placed upon them.
def significant_keys
@significant_keys ||= returning [] do |sk|
segments.each { |segment| sk << segment.key if segment.respond_to? :key }
sk.concat requirements.keys
sk.uniq!
end
end
# Return a hash of key/value pairs representing the keys in the route that
# have defaults, or which are specified by non-regexp requirements.
def defaults
@defaults ||= returning({}) do |hash|
segments.each do |segment|
next unless segment.respond_to? :default
hash[segment.key] = segment.default unless segment.default.nil?
end
requirements.each do |key,req|
next if Regexp === req || req.nil?
hash[key] = req
end
end
end
def matches_controller_and_action?(controller, action)
unless defined? @matching_prepared unless defined? @matching_prepared
@controller_requirement = requirement_for(:controller) @controller_requirement = requirement_for(:controller)
@action_requirement = requirement_for(:action) @action_requirement = requirement_for(:action)
@matching_prepared = true @matching_prepared = true
end end
(@controller_requirement.nil? || @controller_requirement === controller) &&
(@action_requirement.nil? || @action_requirement === action)
end
def to_s
@to_s ||= begin
segs = segments.inject("") { |str,s| str << s.to_s }
"%-6s %-40s %s" % [(conditions[:method] || :any).to_s.upcase, segs, requirements.inspect]
end
end
protected
def requirement_for(key)
return requirements[key] if requirements.key? key
segments.each do |segment|
return segment.regexp if segment.respond_to?(:key) && segment.key == key
end
nil
end end
end end
end end

View file

@ -194,6 +194,8 @@ module ActionController
def initialize def initialize
self.routes = [] self.routes = []
self.named_routes = NamedRouteCollection.new self.named_routes = NamedRouteCollection.new
write_recognize_optimized!
end end
# Subclasses and plugins may override this method to specify a different # 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 Routing.use_controllers! nil # Clear the controller cache so we may discover new ones
clear! clear!
load_routes! load_routes!
install_helpers
end end
# reload! will always force a reload whereas load checks the timestamp first # reload! will always force a reload whereas load checks the timestamp first

View file

@ -4,11 +4,12 @@ module ActionController
RESERVED_PCHAR = ':@&=+$,;' RESERVED_PCHAR = ':@&=+$,;'
UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze 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 attr_accessor :is_optional
alias_method :optional?, :is_optional alias_method :optional?, :is_optional
def initialize def initialize
self.is_optional = false @is_optional = false
end end
def extraction_code def extraction_code
@ -63,12 +64,14 @@ module ActionController
end end
class StaticSegment < Segment #:nodoc: class StaticSegment < Segment #:nodoc:
attr_accessor :value, :raw attr_reader :value, :raw
alias_method :raw?, :raw alias_method :raw?, :raw
def initialize(value = nil) def initialize(value = nil, options = {})
super() super()
self.value = value @value = value
@raw = options[:raw] if options.key?(:raw)
@is_optional = options[:optional] if options.key?(:optional)
end end
def interpolation_chunk def interpolation_chunk
@ -97,10 +100,8 @@ module ActionController
end end
class DividerSegment < StaticSegment #:nodoc: class DividerSegment < StaticSegment #:nodoc:
def initialize(value = nil) def initialize(value = nil, options = {})
super(value) super(value, {:raw => true, :optional => true}.merge(options))
self.raw = true
self.is_optional = true
end end
def optionality_implied? def optionality_implied?
@ -109,13 +110,17 @@ module ActionController
end end
class DynamicSegment < Segment #:nodoc: 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 = {}) def initialize(key = nil, options = {})
super() super()
self.key = key @key = key
self.default = options[:default] if options.key? :default @default = options[:default] if options.key?(:default)
self.is_optional = true if options[:optional] || options.key?(:default) @regexp = options[:regexp] if options.key?(:regexp)
@is_optional = true if options[:optional] || options.key?(:default)
end end
def to_s def to_s

View file

@ -67,66 +67,56 @@ class SegmentTest < Test::Unit::TestCase
end end
def test_interpolation_statement def test_interpolation_statement
s = ROUTING::StaticSegment.new s = ROUTING::StaticSegment.new("Hello")
s.value = "Hello"
assert_equal "Hello", eval(s.interpolation_statement([])) assert_equal "Hello", eval(s.interpolation_statement([]))
assert_equal "HelloHello", eval(s.interpolation_statement([s])) assert_equal "HelloHello", eval(s.interpolation_statement([s]))
s2 = ROUTING::StaticSegment.new s2 = ROUTING::StaticSegment.new("-")
s2.value = "-"
assert_equal "Hello-Hello", eval(s.interpolation_statement([s, s2])) assert_equal "Hello-Hello", eval(s.interpolation_statement([s, s2]))
s3 = ROUTING::StaticSegment.new s3 = ROUTING::StaticSegment.new("World")
s3.value = "World"
assert_equal "Hello-World", eval(s3.interpolation_statement([s, s2])) assert_equal "Hello-World", eval(s3.interpolation_statement([s, s2]))
end end
end end
class StaticSegmentTest < Test::Unit::TestCase class StaticSegmentTest < Test::Unit::TestCase
def test_interpolation_chunk_should_respect_raw def test_interpolation_chunk_should_respect_raw
s = ROUTING::StaticSegment.new s = ROUTING::StaticSegment.new('Hello World')
s.value = 'Hello World'
assert !s.raw? assert !s.raw?
assert_equal 'Hello%20World', s.interpolation_chunk assert_equal 'Hello%20World', s.interpolation_chunk
s.raw = true s = ROUTING::StaticSegment.new('Hello World', :raw => true)
assert s.raw? assert s.raw?
assert_equal 'Hello World', s.interpolation_chunk assert_equal 'Hello World', s.interpolation_chunk
end end
def test_regexp_chunk_should_escape_specials def test_regexp_chunk_should_escape_specials
s = ROUTING::StaticSegment.new s = ROUTING::StaticSegment.new('Hello*World')
s.value = 'Hello*World'
assert_equal 'Hello\*World', s.regexp_chunk assert_equal 'Hello\*World', s.regexp_chunk
s.value = 'HelloWorld' s = ROUTING::StaticSegment.new('HelloWorld')
assert_equal 'HelloWorld', s.regexp_chunk assert_equal 'HelloWorld', s.regexp_chunk
end end
def test_regexp_chunk_should_add_question_mark_for_optionals def test_regexp_chunk_should_add_question_mark_for_optionals
s = ROUTING::StaticSegment.new s = ROUTING::StaticSegment.new("/", :optional => true)
s.value = "/"
s.is_optional = true
assert_equal "/?", s.regexp_chunk assert_equal "/?", s.regexp_chunk
s.value = "hello" s = ROUTING::StaticSegment.new("hello", :optional => true)
assert_equal "(?:hello)?", s.regexp_chunk assert_equal "(?:hello)?", s.regexp_chunk
end end
end end
class DynamicSegmentTest < Test::Unit::TestCase class DynamicSegmentTest < Test::Unit::TestCase
def segment def segment(options = {})
unless @segment unless @segment
@segment = ROUTING::DynamicSegment.new @segment = ROUTING::DynamicSegment.new(:a, options)
@segment.key = :a
end end
@segment @segment
end end
def test_extract_value def test_extract_value
s = ROUTING::DynamicSegment.new s = ROUTING::DynamicSegment.new(:a)
s.key = :a
hash = {:a => '10', :b => '20'} hash = {:a => '10', :b => '20'}
assert_equal '10', eval(s.extract_value) assert_equal '10', eval(s.extract_value)
@ -149,20 +139,21 @@ class DynamicSegmentTest < Test::Unit::TestCase
end end
def test_regexp_value_check_rejects_nil def test_regexp_value_check_rejects_nil
segment.regexp = /\d+/ segment = segment(:regexp => /\d+/)
a_value = nil a_value = nil
assert !eval(segment.value_check) assert !eval(segment.value_check)
end end
def test_optional_regexp_value_check_should_accept_nil def test_optional_regexp_value_check_should_accept_nil
segment.regexp = /\d+/ segment = segment(:regexp => /\d+/, :optional => true)
segment.is_optional = true
a_value = nil a_value = nil
assert eval(segment.value_check) assert eval(segment.value_check)
end end
def test_regexp_value_check_rejects_no_match def test_regexp_value_check_rejects_no_match
segment.regexp = /\d+/ segment = segment(:regexp => /\d+/)
a_value = "Hello20World" a_value = "Hello20World"
assert !eval(segment.value_check) assert !eval(segment.value_check)
@ -172,8 +163,7 @@ class DynamicSegmentTest < Test::Unit::TestCase
end end
def test_regexp_value_check_accepts_match def test_regexp_value_check_accepts_match
segment.regexp = /\d+/ segment = segment(:regexp => /\d+/)
a_value = "30" a_value = "30"
assert eval(segment.value_check) assert eval(segment.value_check)
end end
@ -184,14 +174,14 @@ class DynamicSegmentTest < Test::Unit::TestCase
end end
def test_optional_value_needs_no_check def test_optional_value_needs_no_check
segment.is_optional = true segment = segment(:optional => true)
a_value = nil a_value = nil
assert_equal nil, segment.value_check assert_equal nil, segment.value_check
end end
def test_regexp_value_check_should_accept_match_with_default def test_regexp_value_check_should_accept_match_with_default
segment.regexp = /\d+/ segment = segment(:regexp => /\d+/, :default => '200')
segment.default = '200'
a_value = '100' a_value = '100'
assert eval(segment.value_check) assert eval(segment.value_check)
@ -234,7 +224,7 @@ class DynamicSegmentTest < Test::Unit::TestCase
end end
def test_extraction_code_should_return_on_mismatch def test_extraction_code_should_return_on_mismatch
segment.regexp = /\d+/ segment = segment(:regexp => /\d+/)
hash = merged = {:a => 'Hi', :b => '3'} hash = merged = {:a => 'Hi', :b => '3'}
options = {:b => '3'} options = {:b => '3'}
a_value = nil a_value = nil
@ -292,7 +282,7 @@ class DynamicSegmentTest < Test::Unit::TestCase
end end
def test_value_regexp_should_match_exacly 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 World"
assert_no_match segment.value_regexp, "Hello 10" assert_no_match segment.value_regexp, "Hello 10"
assert_no_match segment.value_regexp, "10 World" assert_no_match segment.value_regexp, "10 World"
@ -300,40 +290,36 @@ class DynamicSegmentTest < Test::Unit::TestCase
end end
def test_regexp_chunk_should_return_string def test_regexp_chunk_should_return_string
segment.regexp = /\d+/ segment = segment(:regexp => /\d+/)
assert_kind_of String, segment.regexp_chunk assert_kind_of String, segment.regexp_chunk
end end
def test_build_pattern_non_optional_with_no_captures def test_build_pattern_non_optional_with_no_captures
# Non optional # Non optional
a_segment = ROUTING::DynamicSegment.new a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /\d+/)
a_segment.regexp = /\d+/ #number_of_captures is 0
assert_equal "(\\d+)stuff", a_segment.build_pattern('stuff') assert_equal "(\\d+)stuff", a_segment.build_pattern('stuff')
end end
def test_build_pattern_non_optional_with_captures def test_build_pattern_non_optional_with_captures
# Non optional # Non optional
a_segment = ROUTING::DynamicSegment.new a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /(\d+)(.*?)/)
a_segment.regexp = /(\d+)(.*?)/ #number_of_captures is 2
assert_equal "((\\d+)(.*?))stuff", a_segment.build_pattern('stuff') assert_equal "((\\d+)(.*?))stuff", a_segment.build_pattern('stuff')
end end
def test_optionality_implied def test_optionality_implied
a_segment = ROUTING::DynamicSegment.new a_segment = ROUTING::DynamicSegment.new(:id)
a_segment.key = :id
assert a_segment.optionality_implied? assert a_segment.optionality_implied?
a_segment.key = :action a_segment = ROUTING::DynamicSegment.new(:action)
assert a_segment.optionality_implied? assert a_segment.optionality_implied?
end end
def test_modifiers_must_be_handled_sensibly def test_modifiers_must_be_handled_sensibly
a_segment = ROUTING::DynamicSegment.new a_segment = ROUTING::DynamicSegment.new(nil, :regexp => /david|jamis/i)
a_segment.regexp = /david|jamis/i
assert_equal "((?i-mx:david|jamis))stuff", a_segment.build_pattern('stuff') 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') 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') assert_equal "(david|jamis)stuff", a_segment.build_pattern('stuff')
end end
end end
@ -560,7 +546,7 @@ class RouteBuilderTest < Test::Unit::TestCase
action = segments[-4] action = segments[-4]
assert_equal :action, action.key 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) builder.assign_default_route_options(segments)
@ -661,10 +647,10 @@ class RoutingTest < Test::Unit::TestCase
ActionController::Routing.controller_paths = [] ActionController::Routing.controller_paths = []
assert_equal [], ActionController::Routing.possible_controllers assert_equal [], ActionController::Routing.possible_controllers
ActionController::Routing::Routes.load!
ActionController::Routing.controller_paths = [ ActionController::Routing.controller_paths = [
root, root + '/app/controllers', root + '/vendor/plugins/bad_plugin/lib' 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 assert_equal ["admin/user", "plugin", "user"], ActionController::Routing.possible_controllers.sort
ensure ensure
@ -1374,34 +1360,20 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end end
def slash_segment(is_optional = false) def slash_segment(is_optional = false)
returning ROUTING::DividerSegment.new('/') do |s| ROUTING::DividerSegment.new('/', :optional => is_optional)
s.is_optional = is_optional
end
end end
def default_route def default_route
unless defined?(@default_route) unless defined?(@default_route)
@default_route = ROUTING::Route.new segments = []
segments << ROUTING::StaticSegment.new('/', :raw => true)
@default_route.segments << (s = ROUTING::StaticSegment.new) segments << ROUTING::DynamicSegment.new(:controller)
s.value = '/' segments << slash_segment(:optional)
s.raw = true segments << ROUTING::DynamicSegment.new(:action, :default => 'index', :optional => true)
segments << slash_segment(:optional)
@default_route.segments << (s = ROUTING::DynamicSegment.new) segments << ROUTING::DynamicSegment.new(:id, :optional => true)
s.key = :controller segments << slash_segment(:optional)
@default_route = ROUTING::Route.new(segments).freeze
@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)
end end
@default_route @default_route
end end
@ -1489,29 +1461,16 @@ uses_mocha 'LegacyRouteSet, Route, RouteSet and RouteLoading' do
end end
def test_significant_keys def test_significant_keys
user_url = ROUTING::Route.new segments = []
user_url.segments << (s = ROUTING::StaticSegment.new) segments << ROUTING::StaticSegment.new('/', :raw => true)
s.value = '/' segments << ROUTING::StaticSegment.new('user')
s.raw = true segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true)
segments << ROUTING::DynamicSegment.new(:user)
segments << ROUTING::StaticSegment.new('/', :raw => true, :optional => true)
user_url.segments << (s = ROUTING::StaticSegment.new) requirements = {:controller => 'users', :action => 'show'}
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
user_url.requirements = {:controller => 'users', :action => 'show'}
user_url = ROUTING::Route.new(segments, requirements)
keys = user_url.significant_keys.sort_by { |k| k.to_s } keys = user_url.significant_keys.sort_by { |k| k.to_s }
assert_equal [:action, :controller, :user], keys assert_equal [:action, :controller, :user], keys
end end

View file

@ -117,8 +117,8 @@ XML
@controller = TestController.new @controller = TestController.new
@request = ActionController::TestRequest.new @request = ActionController::TestRequest.new
@response = ActionController::TestResponse.new @response = ActionController::TestResponse.new
ActionController::Routing::Routes.reload
ActionController::Routing.use_controllers! %w(content admin/user test_test/test) ActionController::Routing.use_controllers! %w(content admin/user test_test/test)
ActionController::Routing::Routes.load_routes!
end end
def teardown def teardown