vendor/rails/actionpack/lib/action_controller/routing/segments.rb in radiant-0.7.2 vs vendor/rails/actionpack/lib/action_controller/routing/segments.rb in radiant-0.8.0
- old
+ new
@@ -1,18 +1,28 @@
module ActionController
module Routing
class Segment #:nodoc:
RESERVED_PCHAR = ':@&=+$,;'
- UNSAFE_PCHAR = Regexp.new("[^#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}]", false, 'N').freeze
+ SAFE_PCHAR = "#{URI::REGEXP::PATTERN::UNRESERVED}#{RESERVED_PCHAR}"
+ if RUBY_VERSION >= '1.9'
+ UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false).freeze
+ else
+ UNSAFE_PCHAR = Regexp.new("[^#{SAFE_PCHAR}]", false, 'N').freeze
+ end
+ # 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 number_of_captures
+ Regexp.new(regexp_chunk).number_of_captures
+ end
+
def extraction_code
nil
end
# Continue generating string for the prior segments.
@@ -61,16 +71,18 @@
false
end
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
raw? ? value : super
end
@@ -78,10 +90,14 @@
def regexp_chunk
chunk = Regexp.escape(value)
optional? ? Regexp.optionalize(chunk) : chunk
end
+ def number_of_captures
+ 0
+ end
+
def build_pattern(pattern)
escaped = Regexp.escape(value)
if optional? && ! pattern.empty?
"(?:#{Regexp.optionalize escaped}\\Z|#{escaped}#{Regexp.unoptionalize pattern})"
elsif optional?
@@ -95,29 +111,31 @@
value
end
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?
true
end
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
":#{key}"
end
@@ -128,10 +146,11 @@
end
def extract_value
"#{local_name} = hash[:#{key}] && hash[:#{key}].to_param #{"|| #{default.inspect}" if default}"
end
+
def value_check
if default # Then we know it won't be nil
"#{value_regexp.inspect} =~ #{local_name}" if regexp
elsif optional?
# If we have a regexp check that the value is not given, or that it matches.
@@ -139,10 +158,11 @@
"#{local_name}.nil? || #{value_regexp.inspect} =~ #{local_name}" if regexp
else # Then it must be present, and if we have a regexp, it must match too.
"#{local_name} #{"&& #{value_regexp.inspect} =~ #{local_name}" if regexp}"
end
end
+
def expiry_statement
"expired, hash = true, options if !expired && expire_on[:#{key}]"
end
def extraction_code
@@ -150,11 +170,11 @@
vc = value_check
s << "\nreturn [nil,nil] unless #{vc}" if vc
s << "\n#{expiry_statement}"
end
- def interpolation_chunk(value_code = "#{local_name}")
+ def interpolation_chunk(value_code = local_name)
"\#{URI.escape(#{value_code}.to_s, ActionController::Routing::Segment::UNSAFE_PCHAR)}"
end
def string_structure(prior_segments)
if optional? # We have a conditional to do...
@@ -173,25 +193,27 @@
def value_regexp
Regexp.new "\\A#{regexp.to_s}\\Z" if regexp
end
def regexp_chunk
- if regexp
- if regexp_has_modifiers?
- "(#{regexp.to_s})"
- else
- "(#{regexp.source})"
- end
- else
- "([^#{Routing::SEPARATORS.join}]+)"
- end
+ regexp ? regexp_string : default_regexp_chunk
end
+ def regexp_string
+ regexp_has_modifiers? ? "(#{regexp.to_s})" : "(#{regexp.source})"
+ end
+
+ def default_regexp_chunk
+ "([^#{Routing::SEPARATORS.join}]+)"
+ end
+
+ def number_of_captures
+ regexp ? regexp.number_of_captures + 1 : 1
+ end
+
def build_pattern(pattern)
- chunk = regexp_chunk
- chunk = "(#{chunk})" if Regexp.new(chunk).number_of_captures == 0
- pattern = "#{chunk}#{pattern}"
+ pattern = "#{regexp_chunk}#{pattern}"
optional? ? Regexp.optionalize(pattern) : pattern
end
def match_extraction(next_capture)
# All non code-related keys (such as :id, :slug) are URI-unescaped as
@@ -212,21 +234,20 @@
end
def regexp_has_modifiers?
regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0
end
-
end
class ControllerSegment < DynamicSegment #:nodoc:
def regexp_chunk
possible_names = Routing.possible_controllers.collect { |name| Regexp.escape name }
"(?i-:(#{(regexp || Regexp.union(*possible_names)).source}))"
end
# Don't URI.escape the controller name since it may contain slashes.
- def interpolation_chunk(value_code = "#{local_name}")
+ def interpolation_chunk(value_code = local_name)
"\#{#{value_code}.to_s}"
end
# Make sure controller names like Admin/Content are correctly normalized to
# admin/content
@@ -242,11 +263,11 @@
end
end
end
class PathSegment < DynamicSegment #:nodoc:
- def interpolation_chunk(value_code = "#{local_name}")
+ def interpolation_chunk(value_code = local_name)
"\#{#{value_code}}"
end
def extract_value
"#{local_name} = hash[:#{key}] && Array(hash[:#{key}]).collect { |path_component| URI.escape(path_component.to_param, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}"
@@ -262,14 +283,18 @@
def match_extraction(next_capture)
"params[:#{key}] = PathSegment::Result.new_escaped((match[#{next_capture}]#{" || " + default.inspect if default}).split('/'))#{" if match[" + next_capture + "]" if !default}"
end
- def regexp_chunk
- regexp || "(.*)"
+ def default_regexp_chunk
+ "(.*)"
end
+ def number_of_captures
+ regexp ? regexp.number_of_captures : 1
+ end
+
def optionality_implied?
true
end
class Result < ::Array #:nodoc:
@@ -277,7 +302,42 @@
def self.new_escaped(strings)
new strings.collect {|str| URI.unescape str}
end
end
end
+
+ # The OptionalFormatSegment allows for any resource route to have an optional
+ # :format, which decreases the amount of routes created by 50%.
+ class OptionalFormatSegment < DynamicSegment
+
+ def initialize(key = nil, options = {})
+ super(:format, {:optional => true}.merge(options))
+ end
+
+ def interpolation_chunk
+ "." + super
+ end
+
+ def regexp_chunk
+ '/|(\.[^/?\.]+)?'
+ end
+
+ def to_s
+ '(.:format)?'
+ end
+
+ def extract_value
+ "#{local_name} = options[:#{key}] && options[:#{key}].to_s.downcase"
+ end
+
+ #the value should not include the period (.)
+ def match_extraction(next_capture)
+ %[
+ if (m = match[#{next_capture}])
+ params[:#{key}] = URI.unescape(m.from(1))
+ end
+ ]
+ end
+ end
+
end
end