Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Vendor unreleased rack-mount 0.6.6.pre dependency

  • Loading branch information...
commit 654929190170c174c8b844d0adcd968c3049d515 1 parent d15256a
@jeremy jeremy authored
Showing with 4,016 additions and 3 deletions.
  1. +1 −1  Gemfile
  2. +1 −1  actionpack/actionpack.gemspec
  3. +3 −1 actionpack/lib/action_dispatch/routing/route_set.rb
  4. +32 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount.rb
  5. +60 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/frequency.rb
  6. +74 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/histogram.rb
  7. +159 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/analysis/splitting.rb
  8. +113 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/code_generation.rb
  9. +210 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/generatable_regexp.rb
  10. +53 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/multimap.rb
  11. +36 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/prefix.rb
  12. +69 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/regexp_with_named_groups.rb
  13. +130 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route.rb
  14. +409 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/route_set.rb
  15. +68 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp.rb
  16. +160 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.rb
  17. +34 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/parser.y
  18. +83 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rb
  19. +12 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/strexp/tokenizer.rex
  20. +148 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/utils.rb
  21. +569 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multimap.rb
  22. +185 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/multiset.rb
  23. +158 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/multimap/nested_multimap.rb
  24. +45 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin.rb
  25. +40 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/alternation.rb
  26. +4 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/anchor.rb
  27. +59 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/atom.rb
  28. +56 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character.rb
  29. +55 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/character_class.rb
  30. +83 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/collection.rb
  31. +126 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/expression.rb
  32. +90 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/group.rb
  33. +55 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/options.rb
  34. +415 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/parser.rb
  35. +213 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/tokenizer.rb
  36. +3 −0  actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/vendor/regin/regin/version.rb
  37. +5 −0 actionpack/lib/action_dispatch/vendor/rack-mount-0.6.6.pre/rack/mount/version.rb
View
2  Gemfile
@@ -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"
View
2  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
View
4 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:
View
32 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
View
60 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
View
74 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
View
159 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
View
113 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
View
210 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
View
53 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
View
36 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
View
69 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
View
130 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
View
409 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
View
68 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
View
160 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
View
34 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
View
83 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
View
12 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
View
148 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
View
569 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
View
185 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 t