diff options
37 files changed, 4016 insertions, 3 deletions
@@ -1,7 +1,7 @@ source 'http://rubygems.org' gem "arel", :git => "git://github.com/rails/arel.git" -gem "rack-mount", :git => "git://github.com/rails/rack-mount.git" +#gem "rack-mount", :git => "git://github.com/rails/rack-mount.git" gem "rails", :path => File.dirname(__FILE__) gem "rake", ">= 0.8.7" diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index 1dede257f9..54e8ad9acb 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -25,7 +25,7 @@ Gem::Specification.new do |s| s.add_dependency('i18n', '~> 0.4.1') s.add_dependency('rack', '~> 1.1.0') s.add_dependency('rack-test', '~> 0.5.4') - s.add_dependency('rack-mount', '~> 0.6.5') + #s.add_dependency('rack-mount', '~> 0.6.6') s.add_dependency('tzinfo', '~> 0.3.16') s.add_dependency('erubis', '~> 2.6.5') end diff --git a/actionpack/lib/action_dispatch/routing/route_set.rb b/actionpack/lib/action_dispatch/routing/route_set.rb index 7be79d3200..05200f0338 100644 --- a/actionpack/lib/action_dispatch/routing/route_set.rb +++ b/actionpack/lib/action_dispatch/routing/route_set.rb @@ -1,8 +1,10 @@ -require 'rack/mount' require 'forwardable' require 'active_support/core_ext/object/to_query' require 'action_dispatch/routing/deprecated_mapper' +$: << File.expand_path('../../vendor/rack-mount-0.6.6.pre', __FILE__) +require 'rack/mount' + module ActionDispatch module Routing class RouteSet #:nodoc: diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount.rb new file mode 100644 index 0000000000..9fbf707724 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount.rb @@ -0,0 +1,32 @@ +require 'rack' + +module Rack #:nodoc: + # A stackable dynamic tree based Rack router. + # + # Rack::Mount supports Rack's Cascade style of trying several routes until + # it finds one that is not a 404. This allows multiple routes to be nested + # or stacked on top of each other. Since the application endpoint can + # trigger the router to continue matching, middleware can be used to add + # arbitrary conditions to any route. This allows you to route based on + # other request attributes, session information, or even data dynamically + # pulled from a database. + module Mount + autoload :CodeGeneration, 'rack/mount/code_generation' + autoload :GeneratableRegexp, 'rack/mount/generatable_regexp' + autoload :Multimap, 'rack/mount/multimap' + autoload :Prefix, 'rack/mount/prefix' + autoload :RegexpWithNamedGroups, 'rack/mount/regexp_with_named_groups' + autoload :Route, 'rack/mount/route' + autoload :RouteSet, 'rack/mount/route_set' + autoload :RoutingError, 'rack/mount/route_set' + autoload :Strexp, 'rack/mount/strexp' + autoload :Utils, 'rack/mount/utils' + autoload :Version, 'rack/mount/version' + + module Analysis #:nodoc: + autoload :Frequency, 'rack/mount/analysis/frequency' + autoload :Histogram, 'rack/mount/analysis/histogram' + autoload :Splitting, 'rack/mount/analysis/splitting' + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb new file mode 100644 index 0000000000..671258f807 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb @@ -0,0 +1,60 @@ +require 'rack/mount/utils' + +module Rack::Mount + module Analysis + class Frequency #:nodoc: + def initialize(*keys) + clear + keys.each { |key| self << key } + end + + def clear + @raw_keys = [] + @key_frequency = Analysis::Histogram.new + self + end + + def <<(key) + raise ArgumentError unless key.is_a?(Hash) + @raw_keys << key + nil + end + + def possible_keys + @possible_keys ||= begin + @raw_keys.map do |key| + key.inject({}) { |requirements, (method, requirement)| + process_key(requirements, method, requirement) + requirements + } + end + end + end + + def process_key(requirements, method, requirement) + if requirement.is_a?(Regexp) + expression = Utils.parse_regexp(requirement) + + if expression.is_a?(Regin::Expression) && expression.anchored_to_line? + expression = Regin::Expression.new(expression.reject { |e| e.is_a?(Regin::Anchor) }) + return requirements[method] = expression.to_s if expression.literal? + end + end + + requirements[method] = requirement + end + + def report + @report ||= begin + possible_keys.each { |keys| keys.each_pair { |key, _| @key_frequency << key } } + return [] if @key_frequency.count <= 1 + @key_frequency.keys_in_upper_quartile + end + end + + def expire! + @possible_keys = @report = nil + end + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb new file mode 100644 index 0000000000..20aaa132f9 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb @@ -0,0 +1,74 @@ +module Rack::Mount + module Analysis + class Histogram < Hash #:nodoc: + attr_reader :count + + def initialize + @count = 0 + super(0) + expire_caches! + end + + def <<(value) + @count += 1 + self[value] += 1 if value + expire_caches! + self + end + + def sorted_by_frequency + sort_by { |_, value| value }.reverse! + end + + def max + @max ||= values.max || 0 + end + + def min + @min ||= values.min || 0 + end + + def mean + @mean ||= calculate_mean + end + + def standard_deviation + @standard_deviation ||= calculate_standard_deviation + end + + def upper_quartile_limit + @upper_quartile_limit ||= calculate_upper_quartile_limit + end + + def keys_in_upper_quartile + @keys_in_upper_quartile ||= compute_keys_in_upper_quartile + end + + private + def calculate_mean + count / size + end + + def calculate_variance + values.inject(0) { |sum, e| sum + (e - mean) ** 2 } / count.to_f + end + + def calculate_standard_deviation + Math.sqrt(calculate_variance) + end + + def calculate_upper_quartile_limit + mean + standard_deviation + end + + def compute_keys_in_upper_quartile + sorted_by_frequency.select { |_, value| value >= upper_quartile_limit }.map! { |key, _| key } + end + + def expire_caches! + @max = @min = @mean = @standard_deviation = nil + @keys_in_upper_quartile = nil + end + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb new file mode 100644 index 0000000000..8a8c551302 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb @@ -0,0 +1,159 @@ +require 'rack/mount/utils' + +module Rack::Mount + module Analysis + class Splitting < Frequency + NULL = "\0".freeze + + class Key < Struct.new(:method, :index, :separators) + def self.split(value, separator_pattern) + keys = value.split(separator_pattern) + keys.shift if keys[0] == '' + keys << NULL + keys + end + + def call(cache, obj) + (cache[method] ||= self.class.split(obj.send(method), separators))[index] + end + + def call_source(cache, obj) + "(#{cache}[:#{method}] ||= Analysis::Splitting::Key.split(#{obj}.#{method}, #{separators.inspect}))[#{index}]" + end + + def inspect + "#{method}[#{index}]" + end + end + + def clear + @boundaries = {} + super + end + + def <<(key) + super + key.each_pair do |k, v| + analyze_capture_boundaries(v, @boundaries[k] ||= Histogram.new) + end + end + + def separators(key) + (@boundaries[key].keys_in_upper_quartile + ['/']).uniq + end + + def process_key(requirements, method, requirement) + separators = separators(method) + if requirement.is_a?(Regexp) && separators.any? + generate_split_keys(requirement, separators).each_with_index do |value, index| + requirements[Key.new(method, index, Regexp.union(*separators))] = value + end + else + super + end + end + + private + def analyze_capture_boundaries(regexp, boundaries) #:nodoc: + return boundaries unless regexp.is_a?(Regexp) + + parts = Utils.parse_regexp(regexp) + parts.each_with_index do |part, index| + if part.is_a?(Regin::Group) + if index > 0 + previous = parts[index-1] + if previous.is_a?(Regin::Character) && previous.literal? + boundaries << previous.to_s + end + end + + if inside = part.expression[0] + if inside.is_a?(Regin::Character) && inside.literal? + boundaries << inside.to_s + end + end + + if index < parts.length + following = parts[index+1] + if following.is_a?(Regin::Character) && following.literal? + boundaries << following.to_s + end + end + end + end + + boundaries + end + + def generate_split_keys(regexp, separators) #:nodoc: + segments = [] + buf = nil + parts = Utils.parse_regexp(regexp) + parts.each_with_index do |part, index| + case part + when Regin::Anchor + if part.value == '$' || part.value == '\Z' + segments << join_buffer(buf, regexp) if buf + segments << NULL + buf = nil + break + end + when Regin::CharacterClass + break if separators.any? { |s| part.include?(s) } + buf = nil + segments << part.to_regexp(true) + when Regin::Character + if separators.any? { |s| part.include?(s) } + segments << join_buffer(buf, regexp) if buf + peek = parts[index+1] + if peek.is_a?(Regin::Character) && separators.include?(peek.value) + segments << '' + end + buf = nil + else + buf ||= Regin::Expression.new([]) + buf += [part] + end + when Regin::Group + if part.quantifier == '?' + value = part.expression.first + if separators.any? { |s| value.include?(s) } + segments << join_buffer(buf, regexp) if buf + buf = nil + end + break + elsif part.quantifier == nil + break if separators.any? { |s| part.include?(s) } + buf = nil + segments << part.to_regexp(true) + else + break + end + else + break + end + + if index + 1 == parts.size + segments << join_buffer(buf, regexp) if buf + buf = nil + break + end + end + + while segments.length > 0 && (segments.last.nil? || segments.last == '') + segments.pop + end + + segments + end + + def join_buffer(parts, regexp) + if parts.literal? + parts.to_s + else + parts.to_regexp(true) + end + end + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb new file mode 100644 index 0000000000..903c79fdc6 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb @@ -0,0 +1,113 @@ +module Rack::Mount + module CodeGeneration #:nodoc: + def _expired_recognize(env) #:nodoc: + raise 'route set not finalized' + end + + def rehash + super + optimize_recognize! + end + + private + def expire! + if @optimized_recognize_defined + remove_metaclass_method :recognize + + class << self + alias_method :recognize, :_expired_recognize + end + + @optimized_recognize_defined = false + end + + super + end + + def optimize_container_iterator(container) + body = [] + + container.each_with_index { |route, i| + body << "route = self[#{i}]" + body << 'matches = {}' + body << 'params = route.defaults.dup' + + conditions = [] + route.conditions.each do |method, condition| + b = [] + if condition.is_a?(Regexp) + b << "if m = obj.#{method}.match(#{condition.inspect})" + b << "matches[:#{method}] = m" + if (named_captures = route.named_captures[method]) && named_captures.any? + b << 'captures = m.captures' + b << 'p = nil' + b << named_captures.map { |k, j| "params[#{k.inspect}] = p if p = captures[#{j}]" }.join('; ') + end + else + b << "if m = obj.#{method} == route.conditions[:#{method}]" + end + b << 'true' + b << 'end' + conditions << "(#{b.join('; ')})" + end + + body << <<-RUBY + if #{conditions.join(' && ')} + yield route, matches, params + end + RUBY + } + + container.instance_eval(<<-RUBY, __FILE__, __LINE__) + def optimized_each(obj) + #{body.join("\n")} + nil + end + RUBY + end + + def optimize_recognize! + keys = @recognition_keys.map { |key| + if key.respond_to?(:call_source) + key.call_source(:cache, :obj) + else + "obj.#{key}" + end + }.join(', ') + + @optimized_recognize_defined = true + + remove_metaclass_method :recognize + + instance_eval(<<-RUBY, __FILE__, __LINE__) + def recognize(obj) + cache = {} + container = @recognition_graph[#{keys}] + optimize_container_iterator(container) unless container.respond_to?(:optimized_each) + + if block_given? + container.optimized_each(obj) do |route, matches, params| + yield route, matches, params + end + else + container.optimized_each(obj) do |route, matches, params| + return route, matches, params + end + end + + nil + end + RUBY + end + + # method_defined? can't distinguish between instance + # and meta methods. So we have to rescue if the method + # has not been defined in the metaclass yet. + def remove_metaclass_method(symbol) + metaclass = class << self; self; end + metaclass.send(:remove_method, symbol) + rescue NameError => e + nil + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb new file mode 100644 index 0000000000..47bbab3784 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb @@ -0,0 +1,210 @@ +require 'rack/mount/utils' + +module Rack::Mount + class GeneratableRegexp < Regexp #:nodoc: + class DynamicSegment #:nodoc: + attr_reader :name, :requirement + + def initialize(name, requirement) + @name, @requirement = name.to_sym, requirement + freeze + end + + def ==(obj) + @name == obj.name && @requirement == obj.requirement + end + + def =~(str) + @requirement =~ str + end + + def to_hash + { @name => @requirement } + end + + def inspect + "/(?<#{@name}>#{@requirement.source})/" + end + end + + module InstanceMethods + def self.extended(obj) + obj.segments + end + + def defaults=(defaults) + @required_captures = nil + @required_params = nil + @required_defaults = nil + @defaults = defaults + end + + def defaults + @defaults ||= {} + end + + def generatable? + segments.any? + end + + def generate(params = {}, recall = {}, options = {}) + return nil unless generatable? + + merged = recall.merge(params) + return nil unless required_params.all? { |p| merged.include?(p) } + return nil unless required_defaults.all? { |k, v| merged[k] == v } + + generate_from_segments(segments, params, merged, options) + end + + def segments + @segments ||= begin + defaults + segments = [] + catch(:halt) do + expression = Utils.parse_regexp(self) + segments = parse_segments(expression) + end + segments + end + end + + def captures + segments.flatten.find_all { |s| s.is_a?(DynamicSegment) } + end + + def required_captures + @required_captures ||= segments.find_all { |s| + s.is_a?(DynamicSegment) && !@defaults.include?(s.name) + }.freeze + end + + def required_params + @required_params ||= required_captures.map { |s| s.name }.freeze + end + + def required_defaults + @required_defaults ||= begin + required_defaults = @defaults.dup + captures.inject({}) { |h, s| h.merge!(s.to_hash) }.keys.each { |name| + required_defaults.delete(name) + } + required_defaults + end + end + + def freeze + segments + captures + required_captures + required_params + required_defaults + super + end + + private + def parse_segments(segments) + s = [] + segments.each_with_index do |part, index| + case part + when Regin::Anchor + # ignore + when Regin::Character + throw :halt unless part.literal? + + if s.last.is_a?(String) + s.last << part.value.dup + else + s << part.value.dup + end + when Regin::Group + if part.name + s << DynamicSegment.new(part.name, part.expression.to_regexp(true)) + else + s << parse_segments(part.expression) + end + when Regin::Expression + return parse_segments(part) + else + throw :halt + end + end + + s + end + + EMPTY_STRING = ''.freeze + + def generate_from_segments(segments, params, merged, options, optional = false) + if optional + return EMPTY_STRING if segments.all? { |s| s.is_a?(String) } + return EMPTY_STRING unless segments.flatten.any? { |s| + params.has_key?(s.name) if s.is_a?(DynamicSegment) + } + return EMPTY_STRING if segments.any? { |segment| + if segment.is_a?(DynamicSegment) + value = merged[segment.name] || @defaults[segment.name] + value = parameterize(segment.name, value, options) + + merged_value = parameterize(segment.name, merged[segment.name], options) + default_value = parameterize(segment.name, @defaults[segment.name], options) + + if value.nil? || segment !~ value + true + elsif merged_value == default_value + # Nasty control flow + return :clear_remaining_segments + else + false + end + end + } + end + + generated = segments.map do |segment| + case segment + when String + segment + when DynamicSegment + value = params[segment.name] || merged[segment.name] || @defaults[segment.name] + value = parameterize(segment.name, value, options) + if value && segment =~ value.to_s + value + else + return + end + when Array + value = generate_from_segments(segment, params, merged, options, true) + if value == :clear_remaining_segments + segment.each { |s| params.delete(s.name) if s.is_a?(DynamicSegment) } + EMPTY_STRING + elsif value.nil? + EMPTY_STRING + else + value + end + end + end + + # Delete any used items from the params + segments.each { |s| params.delete(s.name) if s.is_a?(DynamicSegment) } + + generated.join + end + + def parameterize(name, value, options) + if block = options[:parameterize] + block.call(name, value) + else + value + end + end + end + include InstanceMethods + + def initialize(regexp) + super + segments + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb new file mode 100644 index 0000000000..0f8eaaec67 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb @@ -0,0 +1,53 @@ +begin + require 'nested_multimap' +rescue LoadError + $: << File.expand_path(File.join(File.dirname(__FILE__), 'vendor/multimap')) + require 'nested_multimap' +end + +module Rack::Mount + class Multimap < NestedMultimap #:nodoc: + def store(*args) + keys = args.dup + value = keys.pop + key = keys.shift + + raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value + + unless key.respond_to?(:=~) + raise ArgumentError, "unsupported key: #{args.first.inspect}" + end + + if key.is_a?(Regexp) + if keys.empty? + @hash.each_pair { |k, l| l << value if k =~ key } + self.default << value + else + @hash.each_pair { |k, _| + if k =~ key + args[0] = k + super(*args) + end + } + + self.default = self.class.new(default) unless default.is_a?(self.class) + default[*keys.dup] = value + end + else + super(*args) + end + end + alias_method :[]=, :store + + undef :index, :invert + + def height + containers_with_default.max { |a, b| a.length <=> b.length }.length + end + + def average_height + lengths = containers_with_default.map { |e| e.length } + lengths.inject(0) { |sum, len| sum += len }.to_f / lengths.size + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb new file mode 100644 index 0000000000..58892e4c4f --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb @@ -0,0 +1,36 @@ +require 'rack/mount/utils' + +module Rack::Mount + class Prefix #:nodoc: + EMPTY_STRING = ''.freeze + PATH_INFO = 'PATH_INFO'.freeze + SCRIPT_NAME = 'SCRIPT_NAME'.freeze + SLASH = '/'.freeze + + KEY = 'rack.mount.prefix'.freeze + + def initialize(app, prefix = nil) + @app, @prefix = app, prefix.freeze + freeze + end + + def call(env) + if prefix = env[KEY] || @prefix + old_path_info = env[PATH_INFO].dup + old_script_name = env[SCRIPT_NAME].dup + + begin + env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO].sub(prefix, EMPTY_STRING)) + env[PATH_INFO] = EMPTY_STRING if env[PATH_INFO] == SLASH + env[SCRIPT_NAME] = Utils.normalize_path(env[SCRIPT_NAME].to_s + prefix) + @app.call(env) + ensure + env[PATH_INFO] = old_path_info + env[SCRIPT_NAME] = old_script_name + end + else + @app.call(env) + end + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/regexp_with_named_groups.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/regexp_with_named_groups.rb new file mode 100644 index 0000000000..c11292b2a2 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/regexp_with_named_groups.rb @@ -0,0 +1,69 @@ +module Rack::Mount + if Regin.regexp_supports_named_captures? + RegexpWithNamedGroups = Regexp + else + require 'strscan' + + # A wrapper that adds shim named capture support to older + # versions of Ruby. + # + # Because the named capture syntax causes a parse error, an + # alternate syntax is used to indicate named captures. + # + # Ruby 1.9+ named capture syntax: + # + # /(?<foo>[a-z]+)/ + # + # Ruby 1.8 shim syntax: + # + # /(?:<foo>[a-z]+)/ + class RegexpWithNamedGroups < Regexp + def self.new(regexp) #:nodoc: + if regexp.is_a?(RegexpWithNamedGroups) + regexp + else + super + end + end + + # Wraps Regexp with named capture support. + def initialize(regexp) + regexp = Regexp.compile(regexp) unless regexp.is_a?(Regexp) + source, options = regexp.source, regexp.options + @names, scanner = [], StringScanner.new(source) + + while scanner.skip_until(/\(/) + if scanner.scan(/\?:<([^>]+)>/) + @names << scanner[1] + elsif scanner.scan(/\?(i?m?x?\-?i?m?x?)?:/) + # ignore noncapture + else + @names << nil + end + end + source.gsub!(/\?:<([^>]+)>/, '') + + @names = [] unless @names.any? + @names.freeze + + super(source, options) + end + + def names + @names.dup + end + + def named_captures + named_captures = {} + names.each_with_index { |n, i| + named_captures[n] = [i+1] if n + } + named_captures + end + + def eql?(other) + super && @names.eql?(other.names) + end + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb new file mode 100644 index 0000000000..680c40f147 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb @@ -0,0 +1,130 @@ +require 'rack/mount/generatable_regexp' +require 'rack/mount/regexp_with_named_groups' +require 'rack/mount/utils' + +module Rack::Mount + # Route is an internal class used to wrap a single route attributes. + # + # Plugins should not depend on any method on this class or instantiate + # new Route objects. Instead use the factory method, RouteSet#add_route + # to create new routes and add them to the set. + class Route + # Valid rack application to call if conditions are met + attr_reader :app + + # A hash of conditions to match against. Conditions may be expressed + # as strings or regexps to match against. + attr_reader :conditions + + # A hash of values that always gets merged into the parameters hash + attr_reader :defaults + + # Symbol identifier for the route used with named route generations + attr_reader :name + + attr_reader :named_captures + + def initialize(app, conditions, defaults, name) + unless app.respond_to?(:call) + raise ArgumentError, 'app must be a valid rack application' \ + ' and respond to call' + end + @app = app + + @name = name ? name.to_sym : nil + @defaults = (defaults || {}).freeze + + @conditions = {} + + conditions.each do |method, pattern| + next unless method && pattern + + pattern = Regexp.compile("\\A#{Regexp.escape(pattern)}\\Z") if pattern.is_a?(String) + + if pattern.is_a?(Regexp) + pattern = Utils.normalize_extended_expression(pattern) + pattern = RegexpWithNamedGroups.new(pattern) + pattern.extend(GeneratableRegexp::InstanceMethods) + pattern.defaults = @defaults + end + + @conditions[method] = pattern.freeze + end + + @named_captures = {} + @conditions.map { |method, condition| + next unless condition.respond_to?(:named_captures) + @named_captures[method] = condition.named_captures.inject({}) { |named_captures, (k, v)| + named_captures[k.to_sym] = v.last - 1 + named_captures + }.freeze + } + @named_captures.freeze + + @has_significant_params = @conditions.any? { |method, condition| + (condition.respond_to?(:required_params) && condition.required_params.any?) || + (condition.respond_to?(:required_defaults) && condition.required_defaults.any?) + } + + if @conditions.has_key?(:path_info) && + !Utils.regexp_anchored?(@conditions[:path_info]) + @prefix = true + @app = Prefix.new(@app) + else + @prefix = false + end + + @conditions.freeze + end + + def prefix? + @prefix + end + + + def generation_keys + @conditions.inject({}) { |keys, (method, condition)| + if condition.respond_to?(:required_defaults) + keys.merge!(condition.required_defaults) + else + keys + end + } + end + + def significant_params? + @has_significant_params + end + + def generate(method, params = {}, recall = {}, options = {}) + if method.nil? + result = @conditions.inject({}) { |h, (m, condition)| + if condition.respond_to?(:generate) + h[m] = condition.generate(params, recall, options) + end + h + } + return nil if result.values.compact.empty? + else + if condition = @conditions[method] + if condition.respond_to?(:generate) + result = condition.generate(params, recall, options) + end + end + end + + if result + @defaults.each do |key, value| + params.delete(key) if params[key] == value + end + end + + result + end + + + def inspect #:nodoc: + "#<#{self.class.name} @app=#{@app.inspect} @conditions=#{@conditions.inspect} @defaults=#{@defaults.inspect} @name=#{@name.inspect}>" + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb new file mode 100644 index 0000000000..0e5a65a640 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb @@ -0,0 +1,409 @@ +require 'rack/mount/multimap' +require 'rack/mount/route' +require 'rack/mount/utils' + +module Rack::Mount + class RoutingError < StandardError; end + + class RouteSet + # Initialize a new RouteSet without optimizations + def self.new_without_optimizations(options = {}, &block) + new(options.merge(:_optimize => false), &block) + end + + # Basic RouteSet initializer. + # + # If a block is given, the set is yielded and finalized. + # + # See other aspects for other valid options: + # - <tt>Generation::RouteSet.new</tt> + # - <tt>Recognition::RouteSet.new</tt> + def initialize(options = {}, &block) + @parameters_key = options.delete(:parameters_key) || 'rack.routing_args' + @parameters_key.freeze + + @named_routes = {} + + @recognition_key_analyzer = Analysis::Splitting.new + @generation_key_analyzer = Analysis::Frequency.new + + @request_class = options.delete(:request_class) || Rack::Request + @valid_conditions = @request_class.public_instance_methods.map! { |m| m.to_sym } + + extend CodeGeneration unless options[:_optimize] == false + @optimized_recognize_defined = false + + @routes = [] + expire! + + if block_given? + yield self + rehash + end + end + + # Builder method to add a route to the set + # + # <tt>app</tt>:: A valid Rack app to call if the conditions are met. + # <tt>conditions</tt>:: A hash of conditions to match against. + # Conditions may be expressed as strings or + # regexps to match against. + # <tt>defaults</tt>:: A hash of values that always gets merged in + # <tt>name</tt>:: Symbol identifier for the route used with named + # route generations + def add_route(app, conditions = {}, defaults = {}, name = nil) + unless conditions.is_a?(Hash) + raise ArgumentError, 'conditions must be a Hash' + end + + unless conditions.all? { |method, pattern| + @valid_conditions.include?(method) + } + raise ArgumentError, 'conditions may only include ' + + @valid_conditions.inspect + end + + route = Route.new(app, conditions, defaults, name) + @routes << route + + @recognition_key_analyzer << route.conditions + + @named_routes[route.name] = route if route.name + @generation_key_analyzer << route.generation_keys + + expire! + route + end + + def recognize(obj) + raise 'route set not finalized' unless @recognition_graph + + cache = {} + keys = @recognition_keys.map { |key| + if key.respond_to?(:call) + key.call(cache, obj) + else + obj.send(key) + end + } + + @recognition_graph[*keys].each do |route| + matches = {} + params = route.defaults.dup + + if route.conditions.all? { |method, condition| + value = obj.send(method) + if condition.is_a?(Regexp) && (m = value.match(condition)) + matches[method] = m + captures = m.captures + route.named_captures[method].each do |k, i| + if v = captures[i] + params[k] = v + end + end + true + elsif value == condition + true + else + false + end + } + if block_given? + yield route, matches, params + else + return route, matches, params + end + end + end + + nil + end + + X_CASCADE = 'X-Cascade'.freeze + PASS = 'pass'.freeze + PATH_INFO = 'PATH_INFO'.freeze + + # Rack compatible recognition and dispatching method. Routes are + # tried until one returns a non-catch status code. If no routes + # match, the catch status code is returned. + # + # This method can only be invoked after the RouteSet has been + # finalized. + def call(env) + raise 'route set not finalized' unless @recognition_graph + + env[PATH_INFO] = Utils.normalize_path(env[PATH_INFO]) + + request = nil + req = @request_class.new(env) + recognize(req) do |route, matches, params| + # TODO: We only want to unescape params from uri related methods + params.each { |k, v| params[k] = Utils.unescape_uri(v) if v.is_a?(String) } + + if route.prefix? + env[Prefix::KEY] = matches[:path_info].to_s + end + + env[@parameters_key] = params + result = route.app.call(env) + return result unless result[1][X_CASCADE] == PASS + end + + request || [404, {'Content-Type' => 'text/html', 'X-Cascade' => 'pass'}, ['Not Found']] + end + + # Generates a url from Rack env and identifiers or significant keys. + # + # To generate a url by named route, pass the name in as a +Symbol+. + # url(env, :dashboard) # => "/dashboard" + # + # Additional parameters can be passed in as a hash + # url(env, :people, :id => "1") # => "/people/1" + # + # If no name route is given, it will fall back to a slower + # generation search. + # url(env, :controller => "people", :action => "show", :id => "1") + # # => "/people/1" + def url(env, *args) + named_route, params = nil, {} + + case args.length + when 2 + named_route, params = args[0], args[1].dup + when 1 + if args[0].is_a?(Hash) + params = args[0].dup + else + named_route = args[0] + end + else + raise ArgumentError + end + + only_path = params.delete(:only_path) + recall = env[@parameters_key] || {} + + unless result = generate(:all, named_route, params, recall, + :parameterize => lambda { |name, param| Utils.escape_uri(param) }) + return + end + + parts, params = result + return unless parts + + params.each do |k, v| + if v + params[k] = v + else + params.delete(k) + end + end + + req = stubbed_request_class.new(env) + req._stubbed_values = parts.merge(:query_string => Utils.build_nested_query(params)) + only_path ? req.fullpath : req.url + end + + def generate(method, *args) #:nodoc: + raise 'route set not finalized' unless @generation_graph + + method = nil if method == :all + named_route, params, recall, options = extract_params!(*args) + merged = recall.merge(params) + route = nil + + if named_route + if route = @named_routes[named_route.to_sym] + recall = route.defaults.merge(recall) + url = route.generate(method, params, recall, options) + [url, params] + else + raise RoutingError, "#{named_route} failed to generate from #{params.inspect}" + end + else + keys = @generation_keys.map { |key| + if k = merged[key] + k.to_s + else + nil + end + } + @generation_graph[*keys].each do |r| + next unless r.significant_params? + if url = r.generate(method, params, recall, options) + return [url, params] + end + end + + raise RoutingError, "No route matches #{params.inspect}" + end + end + + # Number of routes in the set + def length + @routes.length + end + + def rehash #:nodoc: + @recognition_keys = build_recognition_keys + @recognition_graph = build_recognition_graph + @generation_keys = build_generation_keys + @generation_graph = build_generation_graph + end + + # Finalizes the set and builds optimized data structures. You *must* + # freeze the set before you can use <tt>call</tt> and <tt>url</tt>. + # So remember to call freeze after you are done adding routes. + def freeze + unless frozen? + rehash + + @recognition_key_analyzer = nil + @generation_key_analyzer = nil + @valid_conditions = nil + + @routes.each { |route| route.freeze } + @routes.freeze + end + + super + end + + def marshal_dump #:nodoc: + hash = {} + + instance_variables_to_serialize.each do |ivar| + hash[ivar] = instance_variable_get(ivar) + end + + if graph = hash[:@recognition_graph] + hash[:@recognition_graph] = graph.dup + end + + hash + end + + def marshal_load(hash) #:nodoc: + hash.each do |ivar, value| + instance_variable_set(ivar, value) + end + end + + protected + def recognition_stats + { :keys => @recognition_keys, + :keys_size => @recognition_keys.size, + :graph_size => @recognition_graph.size, + :graph_height => @recognition_graph.height, + :graph_average_height => @recognition_graph.average_height } + end + + private + def expire! #:nodoc: + @recognition_keys = @recognition_graph = nil + @recognition_key_analyzer.expire! + + @generation_keys = @generation_graph = nil + @generation_key_analyzer.expire! + end + + def instance_variables_to_serialize + instance_variables.map { |ivar| ivar.to_sym } - [:@stubbed_request_class, :@optimized_recognize_defined] + end + + # An internal helper method for constructing a nested set from + # the linear route set. + # + # build_nested_route_set([:request_method, :path_info]) { |route, method| + # route.send(method) + # } + def build_nested_route_set(keys, &block) + graph = Multimap.new + @routes.each_with_index do |route, index| + catch(:skip) do + k = keys.map { |key| block.call(key, index) } + Utils.pop_trailing_nils!(k) + k.map! { |key| key || /.+/ } + graph[*k] = route + end + end + graph + end + + def build_recognition_graph + build_nested_route_set(@recognition_keys) { |k, i| + @recognition_key_analyzer.possible_keys[i][k] + } + end + + def build_recognition_keys + @recognition_key_analyzer.report + end + + def build_generation_graph + build_nested_route_set(@generation_keys) { |k, i| + throw :skip unless @routes[i].significant_params? + + if k = @generation_key_analyzer.possible_keys[i][k] + k.to_s + else + nil + end + } + end + + def build_generation_keys + @generation_key_analyzer.report + end + + def extract_params!(*args) + case args.length + when 4 + named_route, params, recall, options = args + when 3 + if args[0].is_a?(Hash) + params, recall, options = args + else + named_route, params, recall = args + end + when 2 + if args[0].is_a?(Hash) + params, recall = args + else + named_route, params = args + end + when 1 + if args[0].is_a?(Hash) + params = args[0] + else + named_route = args[0] + end + else + raise ArgumentError + end + + named_route ||= nil + params ||= {} + recall ||= {} + options ||= {} + + [named_route, params.dup, recall.dup, options.dup] + end + + def stubbed_request_class + @stubbed_request_class ||= begin + klass = Class.new(@request_class) + klass.public_instance_methods.each do |method| + next if method =~ /^__|object_id/ + klass.class_eval <<-RUBY + def #{method}(*args, &block) + @_stubbed_values[:#{method}] || super + end + RUBY + end + klass.class_eval { attr_accessor :_stubbed_values } + klass + end + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb new file mode 100644 index 0000000000..d0d8797008 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb @@ -0,0 +1,68 @@ +require 'rack/mount/strexp/parser' + +module Rack::Mount + class Strexp + class << self + # Parses segmented string expression and converts it into a Regexp + # + # Strexp.compile('foo') + # # => %r{\Afoo\Z} + # + # Strexp.compile('foo/:bar', {}, ['/']) + # # => %r{\Afoo/(?<bar>[^/]+)\Z} + # + # Strexp.compile(':foo.example.com') + # # => %r{\A(?<foo>.+)\.example\.com\Z} + # + # Strexp.compile('foo/:bar', {:bar => /[a-z]+/}, ['/']) + # # => %r{\Afoo/(?<bar>[a-z]+)\Z} + # + # Strexp.compile('foo(.:extension)') + # # => %r{\Afoo(\.(?<extension>.+))?\Z} + # + # Strexp.compile('src/*files') + # # => %r{\Asrc/(?<files>.+)\Z} + def compile(str, requirements = {}, separators = [], anchor = true) + return Regexp.compile(str) if str.is_a?(Regexp) + + requirements = requirements ? requirements.dup : {} + normalize_requirements!(requirements, separators) + + parser = StrexpParser.new + parser.anchor = anchor + parser.requirements = requirements + + begin + re = parser.scan_str(str) + rescue Racc::ParseError => e + raise RegexpError, e.message + end + + Regexp.compile(re) + end + alias_method :new, :compile + + private + def normalize_requirements!(requirements, separators) + requirements.each do |key, value| + if value.is_a?(Regexp) + if regexp_has_modifiers?(value) + requirements[key] = value + else + requirements[key] = value.source + end + else + requirements[key] = Regexp.escape(value) + end + end + requirements.default ||= separators.any? ? + "[^#{separators.join}]+" : '.+' + requirements + end + + def regexp_has_modifiers?(regexp) + regexp.options & (Regexp::IGNORECASE | Regexp::EXTENDED) != 0 + end + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb new file mode 100644 index 0000000000..cfe05afc61 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb @@ -0,0 +1,160 @@ +# +# DO NOT MODIFY!!!! +# This file is automatically generated by Racc 1.4.6 +# from Racc grammer file "". +# + +require 'racc/parser.rb' + +require 'rack/mount/utils' +require 'rack/mount/strexp/tokenizer' + +module Rack + module Mount + class StrexpParser < Racc::Parser + + +if Regin.regexp_supports_named_captures? + REGEXP_NAMED_CAPTURE = '(?<%s>%s)'.freeze +else + REGEXP_NAMED_CAPTURE = '(?:<%s>%s)'.freeze +end + +attr_accessor :anchor, :requirements +##### State transition tables begin ### + +racc_action_table = [ + 1, 2, 3, 9, 4, 1, 2, 3, 12, 4, + 1, 2, 3, 11, 4, 1, 2, 3, nil, 4 ] + +racc_action_check = [ + 0, 0, 0, 5, 0, 3, 3, 3, 9, 3, + 8, 8, 8, 8, 8, 6, 6, 6, nil, 6 ] + +racc_action_pointer = [ + -2, nil, nil, 3, nil, 3, 13, nil, 8, 8, + nil, nil, nil ] + +racc_action_default = [ + -8, -4, -5, -8, -7, -8, -1, -3, -8, -8, + -2, -6, 13 ] + +racc_goto_table = [ + 6, 5, 10, 8, 10 ] + +racc_goto_check = [ + 2, 1, 3, 2, 3 ] + +racc_goto_pointer = [ + nil, 1, 0, -4 ] + +racc_goto_default = [ + nil, nil, nil, 7 ] + +racc_reduce_table = [ + 0, 0, :racc_error, + 1, 8, :_reduce_1, + 2, 9, :_reduce_2, + 1, 9, :_reduce_none, + 1, 10, :_reduce_4, + 1, 10, :_reduce_5, + 3, 10, :_reduce_6, + 1, 10, :_reduce_7 ] + +racc_reduce_n = 8 + +racc_shift_n = 13 + +racc_token_table = { + false => 0, + :error => 1, + :PARAM => 2, + :GLOB => 3, + :LPAREN => 4, + :RPAREN => 5, + :CHAR => 6 } + +racc_nt_base = 7 + +racc_use_result_var = true + +Racc_arg = [ + racc_action_table, + racc_action_check, + racc_action_default, + racc_action_pointer, + racc_goto_table, + racc_goto_check, + racc_goto_default, + racc_goto_pointer, + racc_nt_base, + racc_reduce_table, + racc_token_table, + racc_shift_n, + racc_reduce_n, + racc_use_result_var ] + +Racc_token_to_s_table = [ + "$end", + "error", + "PARAM", + "GLOB", + "LPAREN", + "RPAREN", + "CHAR", + "$start", + "target", + "expr", + "token" ] + +Racc_debug_parser = false + +##### State transition tables end ##### + +# reduce 0 omitted + +def _reduce_1(val, _values, result) + result = anchor ? "\\A#{val.join}\\Z" : "\\A#{val.join}" + result +end + +def _reduce_2(val, _values, result) + result = val.join + result +end + +# reduce 3 omitted + +def _reduce_4(val, _values, result) + name = val[0].to_sym + requirement = requirements[name] + result = REGEXP_NAMED_CAPTURE % [name, requirement] + + result +end + +def _reduce_5(val, _values, result) + name = val[0].to_sym + requirement = requirements[name] + result = REGEXP_NAMED_CAPTURE % [name, '.+' || requirement] + + result +end + +def _reduce_6(val, _values, result) + result = "(?:#{val[1]})?" + result +end + +def _reduce_7(val, _values, result) + result = Regexp.escape(val[0]) + result +end + +def _reduce_none(val, _values, result) + val[0] +end + + end # class StrexpParser + end # module Mount +end # module Rack diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y new file mode 100644 index 0000000000..ffbd9fae11 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y @@ -0,0 +1,34 @@ +class Rack::Mount::StrexpParser +rule + target: expr { result = anchor ? "\\A#{val.join}\\Z" : "\\A#{val.join}" } + + expr: expr token { result = val.join } + | token + + token: PARAM { + name = val[0].to_sym + requirement = requirements[name] + result = REGEXP_NAMED_CAPTURE % [name, requirement] + } + | GLOB { + name = val[0].to_sym + requirement = requirements[name] + result = REGEXP_NAMED_CAPTURE % [name, '.+' || requirement] + } + | LPAREN expr RPAREN { result = "(?:#{val[1]})?" } + | CHAR { result = Regexp.escape(val[0]) } +end + +---- header ---- +require 'rack/mount/utils' +require 'rack/mount/strexp/tokenizer' + +---- inner + +if Regin.regexp_supports_named_captures? + REGEXP_NAMED_CAPTURE = '(?<%s>%s)'.freeze +else + REGEXP_NAMED_CAPTURE = '(?:<%s>%s)'.freeze +end + +attr_accessor :anchor, :requirements diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb new file mode 100644 index 0000000000..0ff7f67661 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb @@ -0,0 +1,83 @@ +#-- +# DO NOT MODIFY!!!! +# This file is automatically generated by rex 1.0.5.beta1 +# from lexical definition file "lib/rack/mount/strexp/tokenizer.rex". +#++ + +require 'racc/parser' +class Rack::Mount::StrexpParser < Racc::Parser + require 'strscan' + + class ScanError < StandardError ; end + + attr_reader :lineno + attr_reader :filename + attr_accessor :state + + def scan_setup(str) + @ss = StringScanner.new(str) + @lineno = 1 + @state = nil + end + + def action + yield + end + + def scan_str(str) + scan_setup(str) + do_parse + end + alias :scan :scan_str + + def load_file( filename ) + @filename = filename + open(filename, "r") do |f| + scan_setup(f.read) + end + end + + def scan_file( filename ) + load_file(filename) + do_parse + end + + + def next_token + return if @ss.eos? + + text = @ss.peek(1) + @lineno += 1 if text == "\n" + token = case @state + when nil + case + when (text = @ss.scan(/\\(\(|\)|:|\*)/)) + action { [:CHAR, @ss[1]] } + + when (text = @ss.scan(/\:([a-zA-Z_]\w*)/)) + action { [:PARAM, @ss[1]] } + + when (text = @ss.scan(/\*([a-zA-Z_]\w*)/)) + action { [:GLOB, @ss[1]] } + + when (text = @ss.scan(/\(/)) + action { [:LPAREN, text] } + + when (text = @ss.scan(/\)/)) + action { [:RPAREN, text] } + + when (text = @ss.scan(/./)) + action { [:CHAR, text] } + + else + text = @ss.string[@ss.pos .. -1] + raise ScanError, "can not match: '" + text + "'" + end # if + + else + raise ScanError, "undefined state: '" + state.to_s + "'" + end # case state + token + end # def next_token + +end # class diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex new file mode 100644 index 0000000000..473bd096e1 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex @@ -0,0 +1,12 @@ +class Rack::Mount::StrexpParser +macro + RESERVED \(|\)|:|\* + ALPHA_U [a-zA-Z_] +rule + \\({RESERVED}) { [:CHAR, @ss[1]] } + \:({ALPHA_U}\w*) { [:PARAM, @ss[1]] } + \*({ALPHA_U}\w*) { [:GLOB, @ss[1]] } + \( { [:LPAREN, text] } + \) { [:RPAREN, text] } + . { [:CHAR, text] } +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb new file mode 100644 index 0000000000..aa23b1162f --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb @@ -0,0 +1,148 @@ +begin + require 'regin' +rescue LoadError + $: << File.expand_path(File.join(File.dirname(__FILE__), 'vendor/regin')) + require 'regin' +end + +require 'uri' + +module Rack::Mount + # Private utility methods used throughout Rack::Mount. + #-- + # This module is a trash can. Try to move these functions into + # more appropriate contexts. + #++ + module Utils + # Normalizes URI path. + # + # Strips off trailing slash and ensures there is a leading slash. + # + # normalize_path("/foo") # => "/foo" + # normalize_path("/foo/") # => "/foo" + # normalize_path("foo") # => "/foo" + # normalize_path("") # => "/" + def normalize_path(path) + path = "/#{path}" + path.squeeze!('/') + path.sub!(%r{/+\Z}, '') + path = '/' if path == '' + path + end + module_function :normalize_path + + # Removes trailing nils from array. + # + # pop_trailing_nils!([1, 2, 3]) # => [1, 2, 3] + # pop_trailing_nils!([1, 2, 3, nil, nil]) # => [1, 2, 3] + # pop_trailing_nils!([nil]) # => [] + def pop_trailing_nils!(ary) + while ary.length > 0 && ary.last.nil? + ary.pop + end + ary + end + module_function :pop_trailing_nils! + + RESERVED_PCHAR = ':@&=+$,;%' + 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 + + Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI + + def escape_uri(uri) + Parser.escape(uri.to_s, UNSAFE_PCHAR) + end + module_function :escape_uri + + if ''.respond_to?(:force_encoding) + def unescape_uri(uri) + Parser.unescape(uri).force_encoding('utf-8') + end + else + def unescape_uri(uri) + URI.unescape(uri) + end + end + module_function :unescape_uri + + # Taken from Rack 1.1.x to build nested query strings + def build_nested_query(value, prefix = nil) #:nodoc: + case value + when Array + value.map { |v| + build_nested_query(v, "#{prefix}[]") + }.join("&") + when Hash + value.map { |k, v| + build_nested_query(v, prefix ? "#{prefix}[#{Rack::Utils.escape(k)}]" : Rack::Utils.escape(k)) + }.join("&") + when String + raise ArgumentError, "value must be a Hash" if prefix.nil? + "#{prefix}=#{Rack::Utils.escape(value)}" + else + prefix + end + end + module_function :build_nested_query + + # Determines whether the regexp must match the entire string. + # + # regexp_anchored?(/^foo$/) # => true + # regexp_anchored?(/foo/) # => false + # regexp_anchored?(/^foo/) # => false + # regexp_anchored?(/foo$/) # => false + def regexp_anchored?(regexp) + regexp.source =~ /\A(\\A|\^).*(\\Z|\$)\Z/m ? true : false + end + module_function :regexp_anchored? + + def normalize_extended_expression(regexp) + return regexp unless regexp.options & Regexp::EXTENDED != 0 + source = regexp.source + source.gsub!(/#.+$/, '') + source.gsub!(/\s+/, '') + source.gsub!(/\\\//, '/') + Regexp.compile(source) + end + module_function :normalize_extended_expression + + def parse_regexp(regexp) + cache = @@_parse_regexp_cache ||= {} + + if expression = cache[regexp] + return expression + end + + unless regexp.is_a?(RegexpWithNamedGroups) + regexp = RegexpWithNamedGroups.new(regexp) + end + + expression = Regin.parse(regexp) + + unless Regin.regexp_supports_named_captures? + tag_captures = Proc.new do |group| + case group + when Regin::Group + # TODO: dup instead of mutating + group.instance_variable_set('@name', regexp.names[group.index]) if group.index + tag_captures.call(group.expression) + when Regin::Expression + group.each { |child| tag_captures.call(child) } + end + end + tag_captures.call(expression) + end + + cache[regexp] = expression.freeze + expression + rescue Racc::ParseError, Regin::Parser::ScanError + [] + end + module_function :parse_regexp + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb new file mode 100644 index 0000000000..0b49b49280 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb @@ -0,0 +1,569 @@ +require 'forwardable' +require 'multiset' + +# Multimap is a generalization of a map or associative array +# abstract data type in which more than one value may be associated +# with and returned for a given key. +# +# == Example +# +# require 'multimap' +# map = Multimap.new +# map["a"] = 100 +# map["b"] = 200 +# map["a"] = 300 +# map["a"] # -> [100, 300] +# map["b"] # -> [200] +# map.keys # -> #<Multiset: {a, a, b}> +class Multimap + extend Forwardable + + include Enumerable + + # call-seq: + # Multimap[ [key =>|, value]* ] => multimap + # + # Creates a new multimap populated with the given objects. + # + # Multimap["a", 100, "b", 200] #=> {"a"=>[100], "b"=>[200]} + # Multimap["a" => 100, "b" => 200] #=> {"a"=>[100], "b"=>[200]} + def self.[](*args) + default = [] + + if args.size == 2 && args.last.is_a?(Hash) + default = args.shift + elsif !args.first.is_a?(Hash) && args.size % 2 == 1 + default = args.shift + end + + if args.size == 1 && args.first.is_a?(Hash) + args[0] = args.first.inject({}) { |hash, (key, value)| + unless value.is_a?(default.class) + value = (default.dup << value) + end + hash[key] = value + hash + } + else + index = 0 + args.map! { |value| + unless index % 2 == 0 || value.is_a?(default.class) + value = (default.dup << value) + end + index += 1 + value + } + end + + map = new + map.instance_variable_set(:@hash, Hash[*args]) + map.default = default + map + end + + # call-seq: + # Multimap.new => multimap + # Multimap.new(default) => multimap + # + # Returns a new, empty multimap. + # + # map = Multimap.new(Set.new) + # h["a"] = 100 + # h["b"] = 200 + # h["a"] #=> [100].to_set + # h["c"] #=> [].to_set + def initialize(default = []) + @hash = Hash.new(default) + end + + def initialize_copy(original) #:nodoc: + @hash = Hash.new(original.default.dup) + original._internal_hash.each_pair do |key, container| + @hash[key] = container.dup + end + end + + def_delegators :@hash, :clear, :default, :default=, :empty?, + :fetch, :has_key?, :key? + + # Retrieves the <i>value</i> object corresponding to the + # <i>*keys</i> object. + def [](key) + @hash[key] + end + + # call-seq: + # map[key] = value => value + # map.store(key, value) => value + # + # Associates the value given by <i>value</i> with the key + # given by <i>key</i>. Unlike a regular hash, multiple can be + # assoicated with the same value. + # + # map = Multimap["a" => 100, "b" => 200] + # map["a"] = 9 + # map["c"] = 4 + # map #=> {"a" => [100, 9], "b" => [200], "c" => [4]} + def store(key, value) + update_container(key) do |container| + container << value + container + end + end + alias_method :[]=, :store + + # call-seq: + # map.delete(key, value) => value + # map.delete(key) => value + # + # Deletes and returns a key-value pair from <i>map</i>. If only + # <i>key</i> is given, all the values matching that key will be + # deleted. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.delete("b", 300) #=> 300 + # map.delete("a") #=> [100] + def delete(key, value = nil) + if value + @hash[key].delete(value) + else + @hash.delete(key) + end + end + + # call-seq: + # map.each { |key, value| block } => map + # + # Calls <i>block</i> for each key/value pair in <i>map</i>, passing + # the key and value to the block as a two-element array. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.each { |key, value| puts "#{key} is #{value}" } + # + # <em>produces:</em> + # + # a is 100 + # b is 200 + # b is 300 + def each + each_pair do |key, value| + yield [key, value] + end + end + + # call-seq: + # map.each_association { |key, container| block } => map + # + # Calls <i>block</i> once for each key/container in <i>map</i>, passing + # the key and container to the block as parameters. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.each_association { |key, container| puts "#{key} is #{container}" } + # + # <em>produces:</em> + # + # a is [100] + # b is [200, 300] + def each_association(&block) + @hash.each_pair(&block) + end + + # call-seq: + # map.each_container { |container| block } => map + # + # Calls <i>block</i> for each container in <i>map</i>, passing the + # container as a parameter. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.each_container { |container| puts container } + # + # <em>produces:</em> + # + # [100] + # [200, 300] + def each_container + each_association do |_, container| + yield container + end + end + + # call-seq: + # map.each_key { |key| block } => map + # + # Calls <i>block</i> for each key in <i>hsh</i>, passing the key + # as a parameter. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.each_key { |key| puts key } + # + # <em>produces:</em> + # + # a + # b + # b + def each_key + each_pair do |key, _| + yield key + end + end + + # call-seq: + # map.each_pair { |key_value_array| block } => map + # + # Calls <i>block</i> for each key/value pair in <i>map</i>, + # passing the key and value as parameters. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.each_pair { |key, value| puts "#{key} is #{value}" } + # + # <em>produces:</em> + # + # a is 100 + # b is 200 + # b is 300 + def each_pair + each_association do |key, values| + values.each do |value| + yield key, value + end + end + end + + # call-seq: + # map.each_value { |value| block } => map + # + # Calls <i>block</i> for each key in <i>map</i>, passing the + # value as a parameter. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.each_value { |value| puts value } + # + # <em>produces:</em> + # + # 100 + # 200 + # 300 + def each_value + each_pair do |_, value| + yield value + end + end + + def ==(other) #:nodoc: + case other + when Multimap + @hash == other._internal_hash + else + @hash == other + end + end + + def eql?(other) #:nodoc: + case other + when Multimap + @hash.eql?(other._internal_hash) + else + @hash.eql?(other) + end + end + + def freeze #:nodoc: + each_container { |container| container.freeze } + default.freeze + super + end + + # call-seq: + # map.has_value?(value) => true or false + # map.value?(value) => true or false + # + # Returns <tt>true</tt> if the given value is present for any key + # in <i>map</i>. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.has_value?(300) #=> true + # map.has_value?(999) #=> false + def has_value?(value) + values.include?(value) + end + alias_method :value?, :has_value? + + # call-seq: + # map.index(value) => key + # + # Returns the key for a given value. If not found, returns + # <tt>nil</tt>. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.index(100) #=> "a" + # map.index(200) #=> "b" + # map.index(999) #=> nil + def index(value) + invert[value] + end + + # call-seq: + # map.delete_if {| key, value | block } -> map + # + # Deletes every key-value pair from <i>map</i> for which <i>block</i> + # evaluates to <code>true</code>. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.delete_if {|key, value| value >= 300 } + # #=> Multimap["a" => 100, "b" => 200] + # + def delete_if + each_association do |key, container| + container.delete_if do |value| + yield [key, value] + end + end + self + end + + # call-seq: + # map.reject {| key, value | block } -> map + # + # Same as <code>Multimap#delete_if</code>, but works on (and returns) a + # copy of the <i>map</i>. Equivalent to + # <code><i>map</i>.dup.delete_if</code>. + # + def reject(&block) + dup.delete_if(&block) + end + + # call-seq: + # map.reject! {| key, value | block } -> map or nil + # + # Equivalent to <code>Multimap#delete_if</code>, but returns + # <code>nil</code> if no changes were made. + # + def reject!(&block) + old_size = size + delete_if(&block) + old_size == size ? nil : self + end + + # call-seq: + # map.replace(other_map) => map + # + # Replaces the contents of <i>map</i> with the contents of + # <i>other_map</i>. + # + # map = Multimap["a" => 100, "b" => 200] + # map.replace({ "c" => 300, "d" => 400 }) + # #=> Multimap["c" => 300, "d" => 400] + def replace(other) + case other + when Array + @hash.replace(self.class[self.default, *other]) + when Hash + @hash.replace(self.class[self.default, other]) + when self.class + @hash.replace(other) + else + raise ArgumentError + end + end + + # call-seq: + # map.invert => multimap + # + # Returns a new multimap created by using <i>map</i>'s values as keys, + # and the keys as values. + # + # map = Multimap["n" => 100, "m" => 100, "d" => [200, 300]] + # map.invert #=> Multimap[100 => ["n", "m"], 200 => "d", 300 => "d"] + def invert + h = self.class.new(default.dup) + each_pair { |key, value| h[value] = key } + h + end + + # call-seq: + # map.keys => multiset + # + # Returns a new +Multiset+ populated with the keys from this hash. See also + # <tt>Multimap#values</tt> and <tt>Multimap#containers</tt>. + # + # map = Multimap["a" => 100, "b" => [200, 300], "c" => 400] + # map.keys #=> Multiset.new(["a", "b", "b", "c"]) + def keys + keys = Multiset.new + each_key { |key| keys << key } + keys + end + + # Returns true if the given key is present in Multimap. + def include?(key) + keys.include?(key) + end + alias_method :member?, :include? + + # call-seq: + # map.length => fixnum + # map.size => fixnum + # + # Returns the number of key-value pairs in the map. + # + # map = Multimap["a" => 100, "b" => [200, 300], "c" => 400] + # map.length #=> 4 + # map.delete("a") #=> 100 + # map.length #=> 3 + def size + values.size + end + alias_method :length, :size + + # call-seq: + # map.merge(other_map) => multimap + # + # Returns a new multimap containing the contents of <i>other_map</i> and + # the contents of <i>map</i>. + # + # map1 = Multimap["a" => 100, "b" => 200] + # map2 = Multimap["a" => 254, "c" => 300] + # map2.merge(map2) #=> Multimap["a" => 100, "b" => [200, 254], "c" => 300] + # map1 #=> Multimap["a" => 100, "b" => 200] + def merge(other) + dup.update(other) + end + + # call-seq: + # map.merge!(other_map) => multimap + # map.update(other_map) => multimap + # + # Adds each pair from <i>other_map</i> to <i>map</i>. + # + # map1 = Multimap["a" => 100, "b" => 200] + # map2 = Multimap["b" => 254, "c" => 300] + # + # map1.merge!(map2) + # #=> Multimap["a" => 100, "b" => [200, 254], "c" => 300] + def update(other) + case other + when self.class + other.each_pair { |key, value| store(key, value) } + when Hash + update(self.class[self.default, other]) + else + raise ArgumentError + end + self + end + alias_method :merge!, :update + + # call-seq: + # map.select { |key, value| block } => multimap + # + # Returns a new Multimap consisting of the pairs for which the + # block returns true. + # + # map = Multimap["a" => 100, "b" => 200, "c" => 300] + # map.select { |k,v| k > "a" } #=> Multimap["b" => 200, "c" => 300] + # map.select { |k,v| v < 200 } #=> Multimap["a" => 100] + def select + inject(self.class.new) { |map, (key, value)| + map[key] = value if yield([key, value]) + map + } + end + + # call-seq: + # map.to_a => array + # + # Converts <i>map</i> to a nested array of [<i>key, + # value</i>] arrays. + # + # map = Multimap["a" => 100, "b" => [200, 300], "c" => 400] + # map.to_a #=> [["a", 100], ["b", 200], ["b", 300], ["c", 400]] + def to_a + ary = [] + each_pair do |key, value| + ary << [key, value] + end + ary + end + + # call-seq: + # map.to_hash => hash + # + # Converts <i>map</i> to a basic hash. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.to_hash #=> { "a" => [100], "b" => [200, 300] } + def to_hash + @hash.dup + end + + # call-seq: + # map.containers => array + # + # Returns a new array populated with the containers from <i>map</i>. See + # also <tt>Multimap#keys</tt> and <tt>Multimap#values</tt>. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.containers #=> [[100], [200, 300]] + def containers + containers = [] + each_container { |container| containers << container } + containers + end + + # call-seq: + # map.values => array + # + # Returns a new array populated with the values from <i>map</i>. See + # also <tt>Multimap#keys</tt> and <tt>Multimap#containers</tt>. + # + # map = Multimap["a" => 100, "b" => [200, 300]] + # map.values #=> [100, 200, 300] + def values + values = [] + each_value { |value| values << value } + values + end + + # Return an array containing the values associated with the given keys. + def values_at(*keys) + @hash.values_at(*keys) + end + + def marshal_dump #:nodoc: + @hash + end + + def marshal_load(hash) #:nodoc: + @hash = hash + end + + def to_yaml(opts = {}) #:nodoc: + YAML::quick_emit(self, opts) do |out| + out.map(taguri, to_yaml_style) do |map| + @hash.each do |k, v| + map.add(k, v) + end + map.add('__default__', @hash.default) + end + end + end + + def yaml_initialize(tag, val) #:nodoc: + default = val.delete('__default__') + @hash = val + @hash.default = default + self + end + + protected + def _internal_hash #:nodoc: + @hash + end + + def update_container(key) #:nodoc: + container = @hash[key] + container = container.dup if container.equal?(default) + container = yield(container) + @hash[key] = container + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb new file mode 100644 index 0000000000..119bf12646 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb @@ -0,0 +1,185 @@ +require 'set' + +# Multiset implements a collection of unordered values and +# allows duplicates. +# +# == Example +# +# require 'multiset' +# s1 = Multiset.new [1, 2] # -> #<Multiset: {1, 2}> +# s1.add(2) # -> #<Multiset: {1, 2, 2}> +# s1.merge([2, 6]) # -> #<Multiset: {1, 2, 2, 2, 3}> +# s1.multiplicity(2) # -> 3 +# s1.multiplicity(3) # -> 1 +class Multiset < Set + def initialize(*args, &block) #:nodoc: + @hash = Hash.new(0) + super + end + + # Returns the number of times an element belongs to the multiset. + def multiplicity(e) + @hash[e] + end + + # Returns the total number of elements in a multiset, including + # repeated memberships + def cardinality + @hash.inject(0) { |s, (e, m)| s += m } + end + alias_method :size, :cardinality + alias_method :length, :cardinality + + # Converts the set to an array. The order of elements is uncertain. + def to_a + inject([]) { |ary, (key, _)| ary << key } + end + + # Returns true if the set is a superset of the given set. + def superset?(set) + set.is_a?(self.class) or raise ArgumentError, "value must be a set" + return false if cardinality < set.cardinality + set.all? { |o| set.multiplicity(o) <= multiplicity(o) } + end + + # Returns true if the set is a proper superset of the given set. + def proper_superset?(set) + set.is_a?(self.class) or raise ArgumentError, "value must be a set" + return false if cardinality <= set.cardinality + set.all? { |o| set.multiplicity(o) <= multiplicity(o) } + end + + # Returns true if the set is a subset of the given set. + def subset?(set) + set.is_a?(self.class) or raise ArgumentError, "value must be a set" + return false if set.cardinality < cardinality + all? { |o| multiplicity(o) <= set.multiplicity(o) } + end + + # Returns true if the set is a proper subset of the given set. + def proper_subset?(set) + set.is_a?(self.class) or raise ArgumentError, "value must be a set" + return false if set.cardinality <= cardinality + all? { |o| multiplicity(o) <= set.multiplicity(o) } + end + + # Calls the given block once for each element in the set, passing + # the element as parameter. Returns an enumerator if no block is + # given. + def each + @hash.each_pair do |key, multiplicity| + multiplicity.times do + yield(key) + end + end + self + end + + # Adds the given object to the set and returns self. Use +merge+ to + # add many elements at once. + def add(o) + @hash[o] ||= 0 + @hash[o] += 1 + self + end + alias << add + + undef :add? + + # Deletes all the identical object from the set and returns self. + # If +n+ is given, it will remove that amount of identical objects + # from the set. Use +subtract+ to delete many different items at + # once. + def delete(o, n = nil) + if n + @hash[o] ||= 0 + @hash[o] -= n if @hash[o] > 0 + @hash.delete(o) if @hash[o] == 0 + else + @hash.delete(o) + end + self + end + + undef :delete? + + # Deletes every element of the set for which block evaluates to + # true, and returns self. + def delete_if + each { |o| delete(o) if yield(o) } + self + end + + # Merges the elements of the given enumerable object to the set and + # returns self. + def merge(enum) + enum.each { |o| add(o) } + self + end + + # Deletes every element that appears in the given enumerable object + # and returns self. + def subtract(enum) + enum.each { |o| delete(o, 1) } + self + end + + # Returns a new set containing elements common to the set and the + # given enumerable object. + def &(enum) + s = dup + n = self.class.new + enum.each { |o| + if s.include?(o) + s.delete(o, 1) + n.add(o) + end + } + n + end + alias intersection & + + # Returns a new set containing elements exclusive between the set + # and the given enumerable object. (set ^ enum) is equivalent to + # ((set | enum) - (set & enum)). + def ^(enum) + n = self.class.new(enum) + each { |o| n.include?(o) ? n.delete(o, 1) : n.add(o) } + n + end + + # Returns true if two sets are equal. Two multisets are equal if + # they have the same cardinalities and each element has the same + # multiplicity in both sets. The equality of each element inside + # the multiset is defined according to Object#eql?. + def eql?(set) + return true if equal?(set) + set = self.class.new(set) unless set.is_a?(self.class) + return false unless cardinality == set.cardinality + superset?(set) && subset?(set) + end + alias_method :==, :eql? + + def marshal_dump #:nodoc: + @hash + end + + def marshal_load(hash) #:nodoc: + @hash = hash + end + + def to_yaml(opts = {}) #:nodoc: + YAML::quick_emit(self, opts) do |out| + out.map(taguri, to_yaml_style) do |map| + @hash.each do |k, v| + map.add(k, v) + end + end + end + end + + def yaml_initialize(tag, val) #:nodoc: + @hash = val + self + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/nested_multimap.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/nested_multimap.rb new file mode 100644 index 0000000000..4eb088b91a --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/nested_multimap.rb @@ -0,0 +1,158 @@ +require 'multimap' + +# NestedMultimap allows values to be assoicated with a nested +# set of keys. +class NestedMultimap < Multimap + # call-seq: + # multimap[*keys] = value => value + # multimap.store(*keys, value) => value + # + # Associates the value given by <i>value</i> with multiple key + # given by <i>keys</i>. + # + # map = NestedMultimap.new + # map["a"] = 100 + # map["a", "b"] = 101 + # map["a"] = 102 + # map #=> {"a"=>{"b"=>[100, 101, 102], default => [100, 102]}} + def store(*args) + keys = args + value = args.pop + + raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value + + if keys.length > 1 + update_container(keys.shift) do |container| + container = self.class.new(container) unless container.is_a?(self.class) + container[*keys] = value + container + end + elsif keys.length == 1 + super(keys.first, value) + else + self << value + end + end + alias_method :[]=, :store + + # call-seq: + # multimap << obj => multimap + # + # Pushes the given object on to the end of all the containers. + # + # map = NestedMultimap["a" => [100], "b" => [200, 300]] + # map << 300 + # map["a"] #=> [100, 300] + # map["c"] #=> [300] + def <<(value) + @hash.each_value { |container| container << value } + self.default << value + self + end + + # call-seq: + # multimap[*keys] => value + # multimap[key1, key2, key3] => value + # + # Retrieves the <i>value</i> object corresponding to the + # <i>*keys</i> object. + def [](*keys) + i, l, r, k = 0, keys.length, self, self.class + while r.is_a?(k) + r = i < l ? r._internal_hash[keys[i]] : r.default + i += 1 + end + r + end + + # call-seq: + # multimap.each_association { |key, container| block } => multimap + # + # Calls <i>block</i> once for each key/container in <i>map</i>, passing + # the key and container to the block as parameters. + # + # map = NestedMultimap.new + # map["a"] = 100 + # map["a", "b"] = 101 + # map["a"] = 102 + # map["c"] = 200 + # map.each_association { |key, container| puts "#{key} is #{container}" } + # + # <em>produces:</em> + # + # ["a", "b"] is [100, 101, 102] + # "c" is [200] + def each_association + super() do |key, container| + if container.respond_to?(:each_association) + container.each_association do |nested_key, value| + yield [key, nested_key].flatten, value + end + else + yield key, container + end + end + end + + # call-seq: + # multimap.each_container_with_default { |container| block } => map + # + # Calls <i>block</i> for every container in <i>map</i> including + # the default, passing the container as a parameter. + # + # map = NestedMultimap.new + # map["a"] = 100 + # map["a", "b"] = 101 + # map["a"] = 102 + # map.each_container_with_default { |container| puts container } + # + # <em>produces:</em> + # + # [100, 101, 102] + # [100, 102] + # [] + def each_container_with_default(&block) + @hash.each_value do |container| + iterate_over_container(container, &block) + end + iterate_over_container(default, &block) + self + end + + # call-seq: + # multimap.containers_with_default => array + # + # Returns a new array populated with all the containers from + # <i>map</i> including the default. + # + # map = NestedMultimap.new + # map["a"] = 100 + # map["a", "b"] = 101 + # map["a"] = 102 + # map.containers_with_default #=> [[100, 101, 102], [100, 102], []] + def containers_with_default + containers = [] + each_container_with_default { |container| containers << container } + containers + end + + def inspect #:nodoc: + super.gsub(/\}$/, ", default => #{default.inspect}}") + end + + private + def iterate_over_container(container) + if container.respond_to?(:each_container_with_default) + container.each_container_with_default do |value| + yield value + end + else + yield container + end + end +end + +begin + require 'nested_multimap_ext' +rescue LoadError +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb new file mode 100644 index 0000000000..d38922bcc6 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb @@ -0,0 +1,45 @@ +module Regin + autoload :Alternation, 'regin/alternation' + autoload :Anchor, 'regin/anchor' + autoload :Atom, 'regin/atom' + autoload :Character, 'regin/character' + autoload :CharacterClass, 'regin/character_class' + autoload :Collection, 'regin/collection' + autoload :Expression, 'regin/expression' + autoload :Group, 'regin/group' + autoload :Options, 'regin/options' + autoload :Parser, 'regin/parser' + + class << self + begin + eval('foo = /(?<foo>.*)/').named_captures + + # Returns true if the interpreter is using the Oniguruma Regexp lib + # and supports named captures. + # + # /(?<foo>bar)/ + def regexp_supports_named_captures? + true + end + rescue SyntaxError, NoMethodError + def regexp_supports_named_captures? #:nodoc: + false + end + end + + # Parses Regexp and returns a Expression data structure. + def parse(regexp) + Parser.parse_regexp(regexp) + end + + # Recompiles Regexp by parsing it and turning it back into a Regexp. + # + # (In the future Regin will perform some Regexp optimizations + # such as removing unnecessary captures and options) + def compile(source) + regexp = Regexp.compile(source) + expression = parse(regexp) + Regexp.compile(expression.to_s(true), expression.flags) + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/alternation.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/alternation.rb new file mode 100644 index 0000000000..ce4f52bfdb --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/alternation.rb @@ -0,0 +1,40 @@ +module Regin + class Alternation < Collection + def initialize(*args) + args, options = extract_options(args) + + if args.length == 1 && args.first.instance_of?(Array) + super(args.first) + else + super(args) + end + + if options.key?(:ignorecase) + @array.map! { |e| e.dup(:ignorecase => options[:ignorecase]) } + end + end + + # Returns true if expression could be treated as a literal string. + # + # Alternation groups are never literal. + def literal? + false + end + + def flags + 0 + end + + def dup(options = {}) + self.class.new(to_a, options) + end + + def to_s(parent = false) + map { |e| e.to_s(parent) }.join('|') + end + + def inspect #:nodoc: + to_s.inspect + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/anchor.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/anchor.rb new file mode 100644 index 0000000000..05520dd5e0 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/anchor.rb @@ -0,0 +1,4 @@ +module Regin + class Anchor < Atom + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb new file mode 100644 index 0000000000..eb1923a5a1 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb @@ -0,0 +1,59 @@ +module Regin + class Atom + attr_reader :value, :ignorecase + + def initialize(value, options = {}) + @value = value + @ignorecase = options[:ignorecase] + end + + def option_names + %w( ignorecase ) + end + + # Returns true if expression could be treated as a literal string. + def literal? + false + end + + def casefold? + ignorecase ? true : false + end + + def dup(options = {}) + original_options = option_names.inject({}) do |h, m| + h[m.to_sym] = send(m) + h + end + self.class.new(value, original_options.merge(options)) + end + + def to_s(parent = false) + "#{value}" + end + + def inspect #:nodoc: + "#<#{self.class.to_s.sub('Regin::', '')} #{to_s.inspect}>" + end + + def ==(other) #:nodoc: + case other + when String + other == to_s + else + eql?(other) + end + end + + def eql?(other) #:nodoc: + other.instance_of?(self.class) && + self.value.eql?(other.value) && + (!!self.ignorecase).eql?(!!other.ignorecase) + end + + def freeze #:nodoc: + value.freeze + super + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character.rb new file mode 100644 index 0000000000..12a9199d2a --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character.rb @@ -0,0 +1,56 @@ +module Regin + class Character < Atom + attr_reader :quantifier + + def initialize(value, options = {}) + @quantifier = options[:quantifier] + super + end + + def option_names + %w( quantifier ) + super + end + + # Returns true if expression could be treated as a literal string. + # + # A Character is literal is there is no quantifier attached to it. + def literal? + quantifier.nil? && !ignorecase + end + + def to_s(parent = false) + if !parent && ignorecase + "(?i-mx:#{value})#{quantifier}" + else + "#{value}#{quantifier}" + end + end + + def to_regexp(anchored = false) + re = to_s(true) + re = "\\A#{re}\\Z" if anchored + Regexp.compile(re, ignorecase) + end + + def match(char) + to_regexp(true).match(char) + end + + def include?(char) + if ignorecase + value.downcase == char.downcase + else + value == char + end + end + + def eql?(other) #:nodoc: + super && quantifier.eql?(other.quantifier) + end + + def freeze #:nodoc: + quantifier.freeze if quantifier + super + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character_class.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character_class.rb new file mode 100644 index 0000000000..caed5ef9d0 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character_class.rb @@ -0,0 +1,55 @@ +module Regin + class CharacterClass < Character + def initialize(value, options = {}) + @negate = options[:negate] + super + end + + def option_names + %w( negate ) + super + end + + attr_reader :negate + + def negated? + negate ? true : false + end + + # Returns true if expression could be treated as a literal string. + # + # A CharacterClass is never literal. + def literal? + false + end + + def bracketed? + value != '.' && value !~ /^\\[dDsSwW]$/ + end + + def to_s(parent = false) + if bracketed? + if !parent && ignorecase + "(?i-mx:[#{negate && '^'}#{value}])#{quantifier}" + else + "[#{negate && '^'}#{value}]#{quantifier}" + end + else + super + end + end + + def include?(char) + re = quantifier ? to_s.sub(/#{Regexp.escape(quantifier)}$/, '') : to_s + Regexp.compile("\\A#{re}\\Z").match(char) + end + + def eql?(other) #:nodoc: + super && negate == other.negate + end + + def freeze #:nodoc: + negate.freeze if negate + super + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/collection.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/collection.rb new file mode 100644 index 0000000000..b60353268a --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/collection.rb @@ -0,0 +1,83 @@ +module Regin + class Collection + include Enumerable + + def initialize(*args) + @array = Array.new(*args) + end + + def each + @array.each{ |item| yield item } + end + + def [](i) + @array[i] + end + + def length + @array.length + end + alias_method :size, :length + + def first + @array.first + end + + def last + @array.last + end + + def +(other) + ary = other.is_a?(self.class) ? other.internal_array : other + self.class.new(@array + ary) + end + + def to_regexp(anchored = false) + re = to_s(true) + re = "\\A#{re}\\Z" if anchored + Regexp.compile(re, flags) + end + + def match(char) + to_regexp.match(char) + end + + def include?(char) + any? { |e| e.include?(char) } + end + + def ==(other) #:nodoc: + case other + when String + other == to_s + when Array + other == @array + else + eql?(other) + end + end + + def eql?(other) #:nodoc: + other.instance_of?(self.class) && @array.eql?(other.internal_array) + end + + def freeze #:nodoc: + each { |e| e.freeze } + @array.freeze + super + end + + protected + def internal_array #:nodoc: + @array + end + + def extract_options(args) + if args.last.is_a?(Hash) + return args[0..-2], args.last + else + return args, {} + end + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/expression.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/expression.rb new file mode 100644 index 0000000000..18e4965097 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/expression.rb @@ -0,0 +1,126 @@ +module Regin + class Expression < Collection + attr_reader :ignorecase, :multiline, :extended + + def initialize(*args) + args, options = extract_options(args) + + @multiline = @ignorecase = @extended = nil + + if args.length == 1 && args.first.instance_of?(Array) + super(args.first) + else + args = args.map { |e| e.instance_of?(String) ? Character.new(e) : e } + super(args) + end + + self.multiline = options[:multiline] if options.key?(:multiline) + self.ignorecase = options[:ignorecase] if options.key?(:ignorecase) + self.extended = options[:extended] if options.key?(:extended) + end + + # Returns true if expression could be treated as a literal string. + # + # A Expression is literal if all its elements are literal. + def literal? + !ignorecase && all? { |e| e.literal? } + end + + def anchored? + anchored_to_start? && anchored_to_end? + end + + def anchored_to_start? + first.is_a?(Anchor) && first == '\A' + end + + def anchored_to_end? + last.is_a?(Anchor) && last == '\Z' + end + + def anchored_to_line? + anchored_to_start_of_line? && anchored_to_end_of_line? + end + + def anchored_to_start_of_line? + anchored_to_start? || (first.is_a?(Anchor) && first == '^') + end + + def anchored_to_end_of_line? + anchored_to_end? || (last.is_a?(Anchor) && last == '$') + end + + def options? + options.any?(true) + end + + def flags + options.to_i + end + + def +(other) + ary = other.is_a?(self.class) ? other.internal_array : other + ary = @array + ary + [options.to_h(true)] + self.class.new(*ary) + end + + def dup(options = {}) + expression = super() + expression.multiline = options[:multiline] if options.key?(:multiline) + expression.ignorecase = options[:ignorecase] if options.key?(:ignorecase) + expression.extended = options[:extended] if options.key?(:extended) + expression + end + + def to_s(parent = false) + if parent || !options? + map { |e| e.to_s(parent) }.join + else + with, without = [], [] + multiline ? (with << 'm') : (without << 'm') + ignorecase ? (with << 'i') : (without << 'i') + extended ? (with << 'x') : (without << 'x') + + with = with.join + without = without.any? ? "-#{without.join}" : '' + + "(?#{with}#{without}:#{map { |e| e.to_s(true) }.join})" + end + end + + def inspect #:nodoc: + "#<Expression #{to_s.inspect}>" + end + + def casefold? + ignorecase + end + + def eql?(other) #:nodoc: + super && + !!self.multiline == !!other.multiline && + !!self.ignorecase == !!other.ignorecase && + !!self.extended == !!other.extended + end + + protected + def options + Options.new(multiline, ignorecase, extended) + end + + def multiline=(multiline) + @multiline = multiline + end + + def ignorecase=(ignorecase) + if @ignorecase.nil? + @array.map! { |e| e.dup(:ignorecase => ignorecase) } + @ignorecase = ignorecase + end + end + + def extended=(extended) + @extended = extended + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/group.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/group.rb new file mode 100644 index 0000000000..d682148bd9 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/group.rb @@ -0,0 +1,90 @@ +module Regin + class Group + attr_reader :expression, :quantifier, :capture, :index, :name + + def initialize(expression, options = {}) + @quantifier = @index = @name = nil + @capture = true + @expression = expression.dup(options) + + @quantifier = options[:quantifier] if options.key?(:quantifier) + @capture = options[:capture] if options.key?(:capture) + @index = options[:index] if options.key?(:index) + @name = options[:name] if options.key?(:name) + end + + def option_names + %w( quantifier capture index name ) + end + + # Returns true if expression could be treated as a literal string. + # + # A Group is literal if its expression is literal and it has no quantifier. + def literal? + quantifier.nil? && expression.literal? + end + + def to_s(parent = false) + if !expression.options? + "(#{capture ? '' : '?:'}#{expression.to_s(parent)})#{quantifier}" + elsif capture == false + "#{expression.to_s}#{quantifier}" + else + "(#{expression.to_s})#{quantifier}" + end + end + + def to_regexp(anchored = false) + re = to_s + re = "\\A#{re}\\Z" if anchored + Regexp.compile(re) + end + + def dup(options = {}) + original_options = option_names.inject({}) do |h, m| + h[m.to_sym] = send(m) + h + end + self.class.new(expression, original_options.merge(options)) + end + + def inspect #:nodoc: + to_s.inspect + end + + def match(char) + to_regexp.match(char) + end + + def include?(char) + expression.include?(char) + end + + def capture? + capture + end + + def ==(other) #:nodoc: + case other + when String + other == to_s + else + eql?(other) + end + end + + def eql?(other) #:nodoc: + other.is_a?(self.class) && + self.expression == other.expression && + self.quantifier == other.quantifier && + self.capture == other.capture && + self.index == other.index && + self.name == other.name + end + + def freeze #:nodoc: + expression.freeze if expression + super + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/options.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/options.rb new file mode 100644 index 0000000000..03ba29d9a5 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/options.rb @@ -0,0 +1,55 @@ +module Regin + class Options + def self.from_int(flags) + multiline = flags & Regexp::MULTILINE != 0 + ignorecase = flags & Regexp::IGNORECASE != 0 + extended = flags & Regexp::EXTENDED != 0 + + new(multiline, ignorecase, extended) + end + + attr_reader :multiline, :ignorecase, :extended + + def initialize(*args) + if args.first.is_a?(Hash) + @multiline = args[0][:multiline] + @ignorecase = args[0][:ignorecase] + @extended = args[0][:extended] + else + @multiline = args[0] + @ignorecase = args[1] + @extended = args[2] + end + end + + def any?(explicit = false) + if explicit + !multiline.nil? || !ignorecase.nil? || !extended.nil? + else + multiline || ignorecase || extended + end + end + + def to_h(explicit = false) + if explicit + options = {} + options[:multiline] = multiline unless multiline.nil? + options[:ignorecase] = ignorecase unless ignorecase.nil? + options[:extended] = extended unless extended.nil? + options + else + { :multiline => multiline, + :ignorecase => ignorecase, + :extended => extended } + end + end + + def to_i + flag = 0 + flag |= Regexp::MULTILINE if multiline + flag |= Regexp::IGNORECASE if ignorecase + flag |= Regexp::EXTENDED if extended + flag + end + end +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb new file mode 100644 index 0000000000..0bb9b87e9c --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb @@ -0,0 +1,415 @@ +# +# DO NOT MODIFY!!!! +# This file is automatically generated by Racc 1.4.6 +# from Racc grammer file "". +# + +require 'racc/parser.rb' +module Regin + class Parser < Racc::Parser #:nodoc: all + +def self.parse_regexp(regexp) + options = Options.from_int(regexp.options) + + parser = new + parser.options_stack << options.to_h + + expression = parser.scan_str(regexp.source) + expression = expression.dup(options.to_h) if options.any? + expression +end + +attr_accessor :options_stack + +def initialize + @capture_index = 0 + @capture_index_stack = [] + @options_stack = [] +end + +##### State transition tables begin ### + +racc_action_table = [ + 2, 18, 19, 19, 8, 10, 11, 13, 48, 19, + 2, 45, 3, 5, 8, 10, 11, 13, 64, 47, + 2, 55, 3, 5, 8, 10, 11, 13, 29, 19, + 2, 16, 3, 5, 8, 10, 11, 13, 61, 19, + 2, 63, 3, 5, 8, 10, 11, 13, 60, 36, + 2, 34, 3, 5, 8, 10, 11, 13, 28, 49, + 2, nil, 3, 5, 8, 10, 11, 13, nil, nil, + 2, nil, 3, 5, 8, 10, 11, 13, nil, 26, + 42, 43, 3, 5, 37, 38, 40, 21, 44, 37, + 38, 40, 22, 23, 24, 14, nil, 15, 31, 32, + 16, nil, 33, 46, 32, nil, nil, 33, 51, 37, + 38, 40, 58, 37, 38, 40, 37, 38, 40, 37, + 38, 40, 37, 38, 40, 37, 38, 40, 37, 38, + 40 ] + +racc_action_check = [ + 0, 4, 27, 4, 0, 0, 0, 0, 36, 56, + 49, 27, 0, 0, 49, 49, 49, 49, 56, 36, + 43, 48, 49, 49, 43, 43, 43, 43, 15, 53, + 6, 15, 43, 43, 6, 6, 6, 6, 53, 52, + 42, 55, 6, 6, 42, 42, 42, 42, 52, 24, + 35, 18, 42, 42, 35, 35, 35, 35, 14, 39, + 19, nil, 35, 35, 19, 19, 19, 19, nil, nil, + 13, nil, 19, 19, 13, 13, 13, 13, nil, 13, + 26, 26, 13, 13, 54, 54, 54, 9, 26, 26, + 26, 26, 9, 9, 9, 2, nil, 2, 17, 17, + 2, nil, 17, 30, 30, nil, nil, 30, 41, 41, + 41, 41, 50, 50, 50, 50, 44, 44, 44, 59, + 59, 59, 58, 58, 58, 51, 51, 51, 62, 62, + 62 ] + +racc_action_pointer = [ + -3, nil, 91, nil, 1, nil, 27, nil, nil, 75, + nil, nil, nil, 67, 53, 22, nil, 93, 51, 57, + nil, nil, nil, nil, 40, nil, 67, 0, nil, nil, + 98, nil, nil, nil, nil, 47, -1, nil, nil, 46, + nil, 87, 37, 17, 94, nil, nil, nil, 12, 7, + 91, 103, 37, 27, 62, 21, 7, nil, 100, 97, + nil, nil, 106, nil, nil, nil, nil, nil ] + +racc_action_default = [ + -37, -13, -37, -19, -37, -20, -2, -4, -11, -6, + -12, -14, -7, -37, -37, -29, -28, -37, -37, -37, + -3, -23, -21, -22, -37, -5, -37, -37, -8, -29, + -37, -9, -27, -26, 68, -1, -37, -34, -35, -37, + -36, -37, -37, -37, -37, -15, -10, -25, -37, -37, + -37, -37, -37, -37, -37, -37, -37, -33, -37, -37, + -17, -18, -37, -24, -16, -32, -31, -30 ] + +racc_goto_table = [ + 4, 41, 20, 35, 17, 39, 25, nil, nil, nil, + nil, nil, nil, 27, nil, nil, 50, 30, nil, 54, + nil, nil, nil, nil, nil, 57, 59, nil, nil, 62, + nil, 20, nil, 65, 66, nil, nil, 67, nil, nil, + nil, nil, 52, 53, nil, nil, nil, nil, nil, 56 ] + +racc_goto_check = [ + 1, 10, 3, 2, 7, 9, 5, nil, nil, nil, + nil, nil, nil, 1, nil, nil, 10, 7, nil, 10, + nil, nil, nil, nil, nil, 10, 10, nil, nil, 10, + nil, 3, nil, 10, 10, nil, nil, 10, nil, nil, + nil, nil, 1, 1, nil, nil, nil, nil, nil, 1 ] + +racc_goto_pointer = [ + nil, 0, -16, -4, nil, -3, nil, 2, nil, -21, + -25 ] + +racc_goto_default = [ + nil, nil, 6, 7, 9, nil, 12, nil, 1, nil, + nil ] + +racc_reduce_table = [ + 0, 0, :racc_error, + 3, 26, :_reduce_1, + 1, 26, :_reduce_2, + 2, 27, :_reduce_3, + 1, 27, :_reduce_4, + 2, 28, :_reduce_5, + 1, 28, :_reduce_none, + 1, 29, :_reduce_none, + 3, 29, :_reduce_8, + 3, 29, :_reduce_9, + 4, 29, :_reduce_10, + 1, 29, :_reduce_11, + 1, 29, :_reduce_12, + 1, 29, :_reduce_13, + 1, 29, :_reduce_14, + 3, 31, :_reduce_15, + 6, 31, :_reduce_16, + 5, 31, :_reduce_17, + 5, 31, :_reduce_18, + 1, 33, :_reduce_none, + 1, 33, :_reduce_none, + 1, 30, :_reduce_none, + 1, 30, :_reduce_none, + 1, 30, :_reduce_none, + 5, 30, :_reduce_24, + 3, 30, :_reduce_25, + 2, 32, :_reduce_26, + 2, 32, :_reduce_27, + 1, 32, :_reduce_none, + 1, 32, :_reduce_none, + 4, 34, :_reduce_30, + 4, 34, :_reduce_31, + 4, 34, :_reduce_32, + 3, 34, :_reduce_33, + 1, 35, :_reduce_34, + 1, 35, :_reduce_35, + 1, 35, :_reduce_36 ] + +racc_reduce_n = 37 + +racc_shift_n = 68 + +racc_token_table = { + false => 0, + :error => 1, + :BAR => 2, + :LBRACK => 3, + :CTYPE => 4, + :RBRACK => 5, + :NEGATE => 6, + :CCLASS => 7, + :DOT => 8, + :CHAR => 9, + :LPAREN => 10, + :RPAREN => 11, + :QMARK => 12, + :COLON => 13, + :NAME => 14, + :L_ANCHOR => 15, + :R_ANCHOR => 16, + :STAR => 17, + :PLUS => 18, + :LCURLY => 19, + :RCURLY => 20, + :MINUS => 21, + :MULTILINE => 22, + :IGNORECASE => 23, + :EXTENDED => 24 } + +racc_nt_base = 25 + +racc_use_result_var = true + +Racc_arg = [ + racc_action_table, + racc_action_check, + racc_action_default, + racc_action_pointer, + racc_goto_table, + racc_goto_check, + racc_goto_default, + racc_goto_pointer, + racc_nt_base, + racc_reduce_table, + racc_token_table, + racc_shift_n, + racc_reduce_n, + racc_use_result_var ] + +Racc_token_to_s_table = [ + "$end", + "error", + "BAR", + "LBRACK", + "CTYPE", + "RBRACK", + "NEGATE", + "CCLASS", + "DOT", + "CHAR", + "LPAREN", + "RPAREN", + "QMARK", + "COLON", + "NAME", + "L_ANCHOR", + "R_ANCHOR", + "STAR", + "PLUS", + "LCURLY", + "RCURLY", + "MINUS", + "MULTILINE", + "IGNORECASE", + "EXTENDED", + "$start", + "expression", + "subexpression", + "quantified_atom", + "atom", + "quantifier", + "group", + "bracket_expression", + "anchor", + "options", + "modifier" ] + +Racc_debug_parser = false + +##### State transition tables end ##### + +# reduce 0 omitted + +def _reduce_1(val, _values, result) + # TODO remove this conditional by breaking + # it into another production + if val[0][0].is_a?(Regin::Alternation) + alt = val[0][0] + [Expression.new(val[2])] + else + alt = Alternation.new(val[0], Expression.new(val[2])) + end + result = Expression.new(alt) + + result +end + +def _reduce_2(val, _values, result) + result = Expression.new(val[0]) + result +end + +def _reduce_3(val, _values, result) + result = val[0] + [val[1]] + result +end + +def _reduce_4(val, _values, result) + result = [val[0]] + result +end + +def _reduce_5(val, _values, result) + result = val[0].dup(:quantifier => val[1]) + result +end + +# reduce 6 omitted + +# reduce 7 omitted + +def _reduce_8(val, _values, result) + result = CharacterClass.new(val[1]) + result +end + +def _reduce_9(val, _values, result) + result = CharacterClass.new(val[1]) + result +end + +def _reduce_10(val, _values, result) + result = CharacterClass.new(val[2], :negate => true) + result +end + +def _reduce_11(val, _values, result) + result = CharacterClass.new(val[0]) + result +end + +def _reduce_12(val, _values, result) + result = CharacterClass.new('.') + result +end + +def _reduce_13(val, _values, result) + result = Anchor.new(val[0]) + result +end + +def _reduce_14(val, _values, result) + result = Character.new(val[0]) + result +end + +def _reduce_15(val, _values, result) + result = Group.new(val[1], :index => @capture_index_stack.pop) + + result +end + +def _reduce_16(val, _values, result) + result = Group.new(val[4], val[2].merge(:capture => false)) + @options_stack.pop + + result +end + +def _reduce_17(val, _values, result) + result = Group.new(val[3], :capture => false); + + result +end + +def _reduce_18(val, _values, result) + result = Group.new(val[3], :name => val[2], :index => @capture_index_stack.pop); + + result +end + +# reduce 19 omitted + +# reduce 20 omitted + +# reduce 21 omitted + +# reduce 22 omitted + +# reduce 23 omitted + +def _reduce_24(val, _values, result) + result = val.join + result +end + +def _reduce_25(val, _values, result) + result = val.join + result +end + +def _reduce_26(val, _values, result) + result = val.join + result +end + +def _reduce_27(val, _values, result) + result = val.join + result +end + +# reduce 28 omitted + +# reduce 29 omitted + +def _reduce_30(val, _values, result) + @options_stack << result = { val[1] => false, val[2] => false, val[3] => false } + + result +end + +def _reduce_31(val, _values, result) + @options_stack << result = { val[0] => true, val[2] => false, val[3] => false } + + result +end + +def _reduce_32(val, _values, result) + @options_stack << result = { val[0] => true, val[1] => true, val[3] => false } + + result +end + +def _reduce_33(val, _values, result) + @options_stack << result = { val[0] => true, val[1] => true, val[2] => true } + + result +end + +def _reduce_34(val, _values, result) + result = :multiline + result +end + +def _reduce_35(val, _values, result) + result = :ignorecase + result +end + +def _reduce_36(val, _values, result) + result = :extended + result +end + +def _reduce_none(val, _values, result) + val[0] +end + + end # class Parser +end # module Regin + +require 'regin/tokenizer' diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/tokenizer.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/tokenizer.rb new file mode 100644 index 0000000000..59e4ffb611 --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/tokenizer.rb @@ -0,0 +1,213 @@ +#-- +# DO NOT MODIFY!!!! +# This file is automatically generated by rex 1.0.5.beta1 +# from lexical definition file "lib/regin/tokenizer.rex". +#++ + +require 'racc/parser' +class Regin::Parser < Racc::Parser + require 'strscan' + + class ScanError < StandardError ; end + + attr_reader :lineno + attr_reader :filename + attr_accessor :state + + def scan_setup(str) + @ss = StringScanner.new(str) + @lineno = 1 + @state = nil + end + + def action + yield + end + + def scan_str(str) + scan_setup(str) + do_parse + end + alias :scan :scan_str + + def load_file( filename ) + @filename = filename + open(filename, "r") do |f| + scan_setup(f.read) + end + end + + def scan_file( filename ) + load_file(filename) + do_parse + end + + + def next_token + return if @ss.eos? + + text = @ss.peek(1) + @lineno += 1 if text == "\n" + token = case @state + when nil + case + when (text = @ss.scan(/\\[dDsSwW]/)) + action { [:CCLASS, text] } + + when (text = @ss.scan(/\^|\\A/)) + action { [:L_ANCHOR, text] } + + when (text = @ss.scan(/\$|\\Z/)) + action { [:R_ANCHOR, text] } + + when (text = @ss.scan(/<(\w+)>/)) + action { [:NAME, @ss[1]] } + + when (text = @ss.scan(/\(/)) + action { + @capture_index_stack << @capture_index + @capture_index += 1 + @state = :OPTIONS if @ss.peek(1) == '?'; + [:LPAREN, text] + } + + + when (text = @ss.scan(/\)/)) + action { [:RPAREN, text] } + + when (text = @ss.scan(/\[/)) + action { @state = :CCLASS; [:LBRACK, text] } + + when (text = @ss.scan(/\{/)) + action { [:LCURLY, text] } + + when (text = @ss.scan(/\}/)) + action { [:RCURLY, text] } + + when (text = @ss.scan(/\|/)) + action { [:BAR, text] } + + when (text = @ss.scan(/\./)) + action { [:DOT, text] } + + when (text = @ss.scan(/\?/)) + action { [:QMARK, text] } + + when (text = @ss.scan(/\+(?:\?)/)) + action { [:PLUS, text] } + + when (text = @ss.scan(/\*(?:\?)/)) + action { [:STAR, text] } + + when (text = @ss.scan(/\#/)) + action { + if @options_stack[-1][:extended] + @state = :COMMENT; + next_token + else + [:CHAR, text] + end + } + + + when (text = @ss.scan(/\s|\n/)) + action { + if @options_stack[-1][:extended] + next_token + else + [:CHAR, text] + end + } + + + when (text = @ss.scan(/\\(.)/)) + action { [:CHAR, @ss[1]] } + + when (text = @ss.scan(/./)) + action { [:CHAR, text] } + + else + text = @ss.string[@ss.pos .. -1] + raise ScanError, "can not match: '" + text + "'" + end # if + + when :CCLASS + case + when (text = @ss.scan(/\]/)) + action { @state = nil; [:RBRACK, text] } + + when (text = @ss.scan(/\^/)) + action { [:NEGATE, text] } + + when (text = @ss.scan(/:(alnum|alpha|ascii|blank|cntrl|digit|graph|lower|print|punct|space|upper|word|xdigit):/)) + action { [:CTYPE, text] } + + when (text = @ss.scan(/\\-/)) + action { [:CHAR, text] } + + when (text = @ss.scan(/\\(.)/)) + action { [:CHAR, @ss[1]] } + + when (text = @ss.scan(/./)) + action { [:CHAR, text] } + + else + text = @ss.string[@ss.pos .. -1] + raise ScanError, "can not match: '" + text + "'" + end # if + + when :OPTIONS + case + when (text = @ss.scan(/\?/)) + action { + @state = nil unless @ss.peek(1) =~ /-|m|i|x|:/ + [:QMARK, text] + } + + + when (text = @ss.scan(/\-/)) + action { [:MINUS, text] } + + when (text = @ss.scan(/m/)) + action { [:MULTILINE, text] } + + when (text = @ss.scan(/i/)) + action { [:IGNORECASE, text] } + + when (text = @ss.scan(/x/)) + action { [:EXTENDED, text] } + + when (text = @ss.scan(/\:/)) + action { + @capture_index_stack.pop + @capture_index -= 1 + @state = nil; + [:COLON, text] + } + + + else + text = @ss.string[@ss.pos .. -1] + raise ScanError, "can not match: '" + text + "'" + end # if + + when :COMMENT + case + when (text = @ss.scan(/\n/)) + action { @state = nil; next_token } + + when (text = @ss.scan(/./)) + action { next_token } + + else + text = @ss.string[@ss.pos .. -1] + raise ScanError, "can not match: '" + text + "'" + end # if + + else + raise ScanError, "undefined state: '" + state.to_s + "'" + end # case state + token + end # def next_token + +end # class diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/version.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/version.rb new file mode 100644 index 0000000000..7ad2a5a25e --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/version.rb @@ -0,0 +1,3 @@ +module Regin + Version = '0.3.3' +end diff --git a/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb new file mode 100644 index 0000000000..a3688b102e --- /dev/null +++ b/actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb @@ -0,0 +1,5 @@ +module Rack + module Mount + Version = '0.6.6.pre' + end +end |