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

Bump up to rack-mount 0.6.9 and rack-mount-0.6.6.pre removed from action_dispatch vendor

This commit is contained in:
Santiago Pastorino 2010-07-15 16:18:25 -03:00 committed by Jeremy Kemper
parent 0c2c893434
commit 3952268929
38 changed files with 3 additions and 4017 deletions

View file

@ -1,7 +1,6 @@
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 "rails", :path => File.dirname(__FILE__)
gem "rake", ">= 0.8.7"

View file

@ -25,7 +25,7 @@ Gem::Specification.new do |s|
s.add_dependency('i18n', '~> 0.4.1')
s.add_dependency('rack', '~> 1.2.1')
s.add_dependency('rack-test', '~> 0.5.4')
#s.add_dependency('rack-mount', '~> 0.6.6')
s.add_dependency('rack-mount', '~> 0.6.9')
s.add_dependency('tzinfo', '~> 0.3.16')
s.add_dependency('erubis', '~> 2.6.6')
end

View file

@ -1,10 +1,8 @@
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 file

@ -1,32 +0,0 @@
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 file

@ -1,60 +0,0 @@
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 file

@ -1,74 +0,0 @@
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 file

@ -1,159 +0,0 @@
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 file

@ -1,113 +0,0 @@
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 file

@ -1,210 +0,0 @@
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 file

@ -1,53 +0,0 @@
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 file

@ -1,36 +0,0 @@
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 file

@ -1,69 +0,0 @@
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 file

@ -1,130 +0,0 @@
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 file

@ -1,409 +0,0 @@
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 file

@ -1,68 +0,0 @@
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 file

@ -1,160 +0,0 @@
#
# 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 file

@ -1,34 +0,0 @@
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 file

@ -1,83 +0,0 @@
#--
# 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 file

@ -1,12 +0,0 @@
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 file

@ -1,148 +0,0 @@
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 file

@ -1,569 +0,0 @@
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 file

@ -1,185 +0,0 @@
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

View file

@ -1,158 +0,0 @@
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

View file

@ -1,45 +0,0 @@
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

View file

@ -1,40 +0,0 @@
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

View file

@ -1,4 +0,0 @@
module Regin
class Anchor < Atom
end
end

View file

@ -1,59 +0,0 @@
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

View file

@ -1,56 +0,0 @@
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

View file

@ -1,55 +0,0 @@
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

View file

@ -1,83 +0,0 @@
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

View file

@ -1,126 +0,0 @@
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

View file

@ -1,90 +0,0 @@
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

View file

@ -1,55 +0,0 @@
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

View file

@ -1,415 +0,0 @@
#
# 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'

View file

@ -1,213 +0,0 @@
#--
# 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

View file

@ -1,3 +0,0 @@
module Regin
Version = '0.3.3'
end

View file

@ -1,5 +0,0 @@
module Rack
module Mount
Version = '0.6.6.pre'
end
end

View file

@ -149,7 +149,7 @@ Here the only two gems we need are +rails+ and +sqlite3-ruby+, so it seems. This
* mime-types-1.16.gem
* polyglot-0.3.1.gem
* rack-1.1.0.gem
* rack-mount-0.6.5.gem
* rack-mount-0.6.9.gem
* rack-test-0.5.4.gem
* rails-3.0.0.beta4.gem
* railties-3.0.0.beta4.gem