lgierth-rack-mount 0.6.13
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +36 -0
- data/lib/rack/mount.rb +32 -0
- data/lib/rack/mount/analysis/frequency.rb +60 -0
- data/lib/rack/mount/analysis/histogram.rb +74 -0
- data/lib/rack/mount/analysis/splitting.rb +159 -0
- data/lib/rack/mount/code_generation.rb +117 -0
- data/lib/rack/mount/generatable_regexp.rb +210 -0
- data/lib/rack/mount/multimap.rb +53 -0
- data/lib/rack/mount/prefix.rb +36 -0
- data/lib/rack/mount/regexp_with_named_groups.rb +69 -0
- data/lib/rack/mount/route.rb +130 -0
- data/lib/rack/mount/route_set.rb +420 -0
- data/lib/rack/mount/strexp.rb +68 -0
- data/lib/rack/mount/strexp/parser.rb +160 -0
- data/lib/rack/mount/strexp/parser.y +34 -0
- data/lib/rack/mount/strexp/tokenizer.rb +83 -0
- data/lib/rack/mount/strexp/tokenizer.rex +12 -0
- data/lib/rack/mount/utils.rb +162 -0
- data/lib/rack/mount/vendor/multimap/multimap.rb +569 -0
- data/lib/rack/mount/vendor/multimap/multiset.rb +185 -0
- data/lib/rack/mount/vendor/multimap/nested_multimap.rb +158 -0
- data/lib/rack/mount/vendor/regin/regin.rb +75 -0
- data/lib/rack/mount/vendor/regin/regin/alternation.rb +40 -0
- data/lib/rack/mount/vendor/regin/regin/anchor.rb +4 -0
- data/lib/rack/mount/vendor/regin/regin/atom.rb +54 -0
- data/lib/rack/mount/vendor/regin/regin/character.rb +51 -0
- data/lib/rack/mount/vendor/regin/regin/character_class.rb +50 -0
- data/lib/rack/mount/vendor/regin/regin/collection.rb +77 -0
- data/lib/rack/mount/vendor/regin/regin/expression.rb +126 -0
- data/lib/rack/mount/vendor/regin/regin/group.rb +85 -0
- data/lib/rack/mount/vendor/regin/regin/options.rb +55 -0
- data/lib/rack/mount/vendor/regin/regin/parser.rb +520 -0
- data/lib/rack/mount/vendor/regin/regin/tokenizer.rb +246 -0
- data/lib/rack/mount/vendor/regin/regin/version.rb +3 -0
- data/lib/rack/mount/version.rb +3 -0
- metadata +140 -0
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
# Multiset implements a collection of unordered values and
|
4
|
+
# allows duplicates.
|
5
|
+
#
|
6
|
+
# == Example
|
7
|
+
#
|
8
|
+
# require 'multiset'
|
9
|
+
# s1 = Multiset.new [1, 2] # -> #<Multiset: {1, 2}>
|
10
|
+
# s1.add(2) # -> #<Multiset: {1, 2, 2}>
|
11
|
+
# s1.merge([2, 6]) # -> #<Multiset: {1, 2, 2, 2, 3}>
|
12
|
+
# s1.multiplicity(2) # -> 3
|
13
|
+
# s1.multiplicity(3) # -> 1
|
14
|
+
class Multiset < Set
|
15
|
+
def initialize(*args, &block) #:nodoc:
|
16
|
+
@hash = Hash.new(0)
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns the number of times an element belongs to the multiset.
|
21
|
+
def multiplicity(e)
|
22
|
+
@hash[e]
|
23
|
+
end
|
24
|
+
|
25
|
+
# Returns the total number of elements in a multiset, including
|
26
|
+
# repeated memberships
|
27
|
+
def cardinality
|
28
|
+
@hash.inject(0) { |s, (e, m)| s += m }
|
29
|
+
end
|
30
|
+
alias_method :size, :cardinality
|
31
|
+
alias_method :length, :cardinality
|
32
|
+
|
33
|
+
# Converts the set to an array. The order of elements is uncertain.
|
34
|
+
def to_a
|
35
|
+
inject([]) { |ary, (key, _)| ary << key }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Returns true if the set is a superset of the given set.
|
39
|
+
def superset?(set)
|
40
|
+
set.is_a?(self.class) or raise ArgumentError, "value must be a set"
|
41
|
+
return false if cardinality < set.cardinality
|
42
|
+
set.all? { |o| set.multiplicity(o) <= multiplicity(o) }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns true if the set is a proper superset of the given set.
|
46
|
+
def proper_superset?(set)
|
47
|
+
set.is_a?(self.class) or raise ArgumentError, "value must be a set"
|
48
|
+
return false if cardinality <= set.cardinality
|
49
|
+
set.all? { |o| set.multiplicity(o) <= multiplicity(o) }
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns true if the set is a subset of the given set.
|
53
|
+
def subset?(set)
|
54
|
+
set.is_a?(self.class) or raise ArgumentError, "value must be a set"
|
55
|
+
return false if set.cardinality < cardinality
|
56
|
+
all? { |o| multiplicity(o) <= set.multiplicity(o) }
|
57
|
+
end
|
58
|
+
|
59
|
+
# Returns true if the set is a proper subset of the given set.
|
60
|
+
def proper_subset?(set)
|
61
|
+
set.is_a?(self.class) or raise ArgumentError, "value must be a set"
|
62
|
+
return false if set.cardinality <= cardinality
|
63
|
+
all? { |o| multiplicity(o) <= set.multiplicity(o) }
|
64
|
+
end
|
65
|
+
|
66
|
+
# Calls the given block once for each element in the set, passing
|
67
|
+
# the element as parameter. Returns an enumerator if no block is
|
68
|
+
# given.
|
69
|
+
def each
|
70
|
+
@hash.each_pair do |key, multiplicity|
|
71
|
+
multiplicity.times do
|
72
|
+
yield(key)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
# Adds the given object to the set and returns self. Use +merge+ to
|
79
|
+
# add many elements at once.
|
80
|
+
def add(o)
|
81
|
+
@hash[o] ||= 0
|
82
|
+
@hash[o] += 1
|
83
|
+
self
|
84
|
+
end
|
85
|
+
alias << add
|
86
|
+
|
87
|
+
undef :add?
|
88
|
+
|
89
|
+
# Deletes all the identical object from the set and returns self.
|
90
|
+
# If +n+ is given, it will remove that amount of identical objects
|
91
|
+
# from the set. Use +subtract+ to delete many different items at
|
92
|
+
# once.
|
93
|
+
def delete(o, n = nil)
|
94
|
+
if n
|
95
|
+
@hash[o] ||= 0
|
96
|
+
@hash[o] -= n if @hash[o] > 0
|
97
|
+
@hash.delete(o) if @hash[o] == 0
|
98
|
+
else
|
99
|
+
@hash.delete(o)
|
100
|
+
end
|
101
|
+
self
|
102
|
+
end
|
103
|
+
|
104
|
+
undef :delete?
|
105
|
+
|
106
|
+
# Deletes every element of the set for which block evaluates to
|
107
|
+
# true, and returns self.
|
108
|
+
def delete_if
|
109
|
+
each { |o| delete(o) if yield(o) }
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
# Merges the elements of the given enumerable object to the set and
|
114
|
+
# returns self.
|
115
|
+
def merge(enum)
|
116
|
+
enum.each { |o| add(o) }
|
117
|
+
self
|
118
|
+
end
|
119
|
+
|
120
|
+
# Deletes every element that appears in the given enumerable object
|
121
|
+
# and returns self.
|
122
|
+
def subtract(enum)
|
123
|
+
enum.each { |o| delete(o, 1) }
|
124
|
+
self
|
125
|
+
end
|
126
|
+
|
127
|
+
# Returns a new set containing elements common to the set and the
|
128
|
+
# given enumerable object.
|
129
|
+
def &(enum)
|
130
|
+
s = dup
|
131
|
+
n = self.class.new
|
132
|
+
enum.each { |o|
|
133
|
+
if s.include?(o)
|
134
|
+
s.delete(o, 1)
|
135
|
+
n.add(o)
|
136
|
+
end
|
137
|
+
}
|
138
|
+
n
|
139
|
+
end
|
140
|
+
alias intersection &
|
141
|
+
|
142
|
+
# Returns a new set containing elements exclusive between the set
|
143
|
+
# and the given enumerable object. (set ^ enum) is equivalent to
|
144
|
+
# ((set | enum) - (set & enum)).
|
145
|
+
def ^(enum)
|
146
|
+
n = self.class.new(enum)
|
147
|
+
each { |o| n.include?(o) ? n.delete(o, 1) : n.add(o) }
|
148
|
+
n
|
149
|
+
end
|
150
|
+
|
151
|
+
# Returns true if two sets are equal. Two multisets are equal if
|
152
|
+
# they have the same cardinalities and each element has the same
|
153
|
+
# multiplicity in both sets. The equality of each element inside
|
154
|
+
# the multiset is defined according to Object#eql?.
|
155
|
+
def eql?(set)
|
156
|
+
return true if equal?(set)
|
157
|
+
set = self.class.new(set) unless set.is_a?(self.class)
|
158
|
+
return false unless cardinality == set.cardinality
|
159
|
+
superset?(set) && subset?(set)
|
160
|
+
end
|
161
|
+
alias_method :==, :eql?
|
162
|
+
|
163
|
+
def marshal_dump #:nodoc:
|
164
|
+
@hash
|
165
|
+
end
|
166
|
+
|
167
|
+
def marshal_load(hash) #:nodoc:
|
168
|
+
@hash = hash
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_yaml(opts = {}) #:nodoc:
|
172
|
+
YAML::quick_emit(self, opts) do |out|
|
173
|
+
out.map(taguri, to_yaml_style) do |map|
|
174
|
+
@hash.each do |k, v|
|
175
|
+
map.add(k, v)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def yaml_initialize(tag, val) #:nodoc:
|
182
|
+
@hash = val
|
183
|
+
self
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'multimap'
|
2
|
+
|
3
|
+
# NestedMultimap allows values to be assoicated with a nested
|
4
|
+
# set of keys.
|
5
|
+
class NestedMultimap < Multimap
|
6
|
+
# call-seq:
|
7
|
+
# multimap[*keys] = value => value
|
8
|
+
# multimap.store(*keys, value) => value
|
9
|
+
#
|
10
|
+
# Associates the value given by <i>value</i> with multiple key
|
11
|
+
# given by <i>keys</i>.
|
12
|
+
#
|
13
|
+
# map = NestedMultimap.new
|
14
|
+
# map["a"] = 100
|
15
|
+
# map["a", "b"] = 101
|
16
|
+
# map["a"] = 102
|
17
|
+
# map #=> {"a"=>{"b"=>[100, 101, 102], default => [100, 102]}}
|
18
|
+
def store(*args)
|
19
|
+
keys = args
|
20
|
+
value = args.pop
|
21
|
+
|
22
|
+
raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value
|
23
|
+
|
24
|
+
if keys.length > 1
|
25
|
+
update_container(keys.shift) do |container|
|
26
|
+
container = self.class.new(container) unless container.is_a?(self.class)
|
27
|
+
container[*keys] = value
|
28
|
+
container
|
29
|
+
end
|
30
|
+
elsif keys.length == 1
|
31
|
+
super(keys.first, value)
|
32
|
+
else
|
33
|
+
self << value
|
34
|
+
end
|
35
|
+
end
|
36
|
+
alias_method :[]=, :store
|
37
|
+
|
38
|
+
# call-seq:
|
39
|
+
# multimap << obj => multimap
|
40
|
+
#
|
41
|
+
# Pushes the given object on to the end of all the containers.
|
42
|
+
#
|
43
|
+
# map = NestedMultimap["a" => [100], "b" => [200, 300]]
|
44
|
+
# map << 300
|
45
|
+
# map["a"] #=> [100, 300]
|
46
|
+
# map["c"] #=> [300]
|
47
|
+
def <<(value)
|
48
|
+
@hash.each_value { |container| container << value }
|
49
|
+
self.default << value
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
# call-seq:
|
54
|
+
# multimap[*keys] => value
|
55
|
+
# multimap[key1, key2, key3] => value
|
56
|
+
#
|
57
|
+
# Retrieves the <i>value</i> object corresponding to the
|
58
|
+
# <i>*keys</i> object.
|
59
|
+
def [](*keys)
|
60
|
+
i, l, r, k = 0, keys.length, self, self.class
|
61
|
+
while r.is_a?(k)
|
62
|
+
r = i < l ? r._internal_hash[keys[i]] : r.default
|
63
|
+
i += 1
|
64
|
+
end
|
65
|
+
r
|
66
|
+
end
|
67
|
+
|
68
|
+
# call-seq:
|
69
|
+
# multimap.each_association { |key, container| block } => multimap
|
70
|
+
#
|
71
|
+
# Calls <i>block</i> once for each key/container in <i>map</i>, passing
|
72
|
+
# the key and container to the block as parameters.
|
73
|
+
#
|
74
|
+
# map = NestedMultimap.new
|
75
|
+
# map["a"] = 100
|
76
|
+
# map["a", "b"] = 101
|
77
|
+
# map["a"] = 102
|
78
|
+
# map["c"] = 200
|
79
|
+
# map.each_association { |key, container| puts "#{key} is #{container}" }
|
80
|
+
#
|
81
|
+
# <em>produces:</em>
|
82
|
+
#
|
83
|
+
# ["a", "b"] is [100, 101, 102]
|
84
|
+
# "c" is [200]
|
85
|
+
def each_association
|
86
|
+
super() do |key, container|
|
87
|
+
if container.respond_to?(:each_association)
|
88
|
+
container.each_association do |nested_key, value|
|
89
|
+
yield [key, nested_key].flatten, value
|
90
|
+
end
|
91
|
+
else
|
92
|
+
yield key, container
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# call-seq:
|
98
|
+
# multimap.each_container_with_default { |container| block } => map
|
99
|
+
#
|
100
|
+
# Calls <i>block</i> for every container in <i>map</i> including
|
101
|
+
# the default, passing the container as a parameter.
|
102
|
+
#
|
103
|
+
# map = NestedMultimap.new
|
104
|
+
# map["a"] = 100
|
105
|
+
# map["a", "b"] = 101
|
106
|
+
# map["a"] = 102
|
107
|
+
# map.each_container_with_default { |container| puts container }
|
108
|
+
#
|
109
|
+
# <em>produces:</em>
|
110
|
+
#
|
111
|
+
# [100, 101, 102]
|
112
|
+
# [100, 102]
|
113
|
+
# []
|
114
|
+
def each_container_with_default(&block)
|
115
|
+
@hash.each_value do |container|
|
116
|
+
iterate_over_container(container, &block)
|
117
|
+
end
|
118
|
+
iterate_over_container(default, &block)
|
119
|
+
self
|
120
|
+
end
|
121
|
+
|
122
|
+
# call-seq:
|
123
|
+
# multimap.containers_with_default => array
|
124
|
+
#
|
125
|
+
# Returns a new array populated with all the containers from
|
126
|
+
# <i>map</i> including the default.
|
127
|
+
#
|
128
|
+
# map = NestedMultimap.new
|
129
|
+
# map["a"] = 100
|
130
|
+
# map["a", "b"] = 101
|
131
|
+
# map["a"] = 102
|
132
|
+
# map.containers_with_default #=> [[100, 101, 102], [100, 102], []]
|
133
|
+
def containers_with_default
|
134
|
+
containers = []
|
135
|
+
each_container_with_default { |container| containers << container }
|
136
|
+
containers
|
137
|
+
end
|
138
|
+
|
139
|
+
def inspect #:nodoc:
|
140
|
+
super.gsub(/\}$/, ", default => #{default.inspect}}")
|
141
|
+
end
|
142
|
+
|
143
|
+
private
|
144
|
+
def iterate_over_container(container)
|
145
|
+
if container.respond_to?(:each_container_with_default)
|
146
|
+
container.each_container_with_default do |value|
|
147
|
+
yield value
|
148
|
+
end
|
149
|
+
else
|
150
|
+
yield container
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
begin
|
156
|
+
require 'nested_multimap_ext'
|
157
|
+
rescue LoadError
|
158
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Regin
|
2
|
+
autoload :Alternation, 'regin/alternation'
|
3
|
+
autoload :Anchor, 'regin/anchor'
|
4
|
+
autoload :Atom, 'regin/atom'
|
5
|
+
autoload :Character, 'regin/character'
|
6
|
+
autoload :CharacterClass, 'regin/character_class'
|
7
|
+
autoload :Collection, 'regin/collection'
|
8
|
+
autoload :Expression, 'regin/expression'
|
9
|
+
autoload :Group, 'regin/group'
|
10
|
+
autoload :Options, 'regin/options'
|
11
|
+
autoload :Parser, 'regin/parser'
|
12
|
+
|
13
|
+
# Detect named capture support
|
14
|
+
begin
|
15
|
+
old_debug, $DEBUG = $DEBUG, nil
|
16
|
+
eval('foo = /(?<foo>.*)/').named_captures
|
17
|
+
|
18
|
+
# Returns true if the interpreter is using the Oniguruma Regexp lib
|
19
|
+
# and supports named captures.
|
20
|
+
#
|
21
|
+
# /(?<foo>bar)/
|
22
|
+
def self.regexp_supports_named_captures?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
rescue SyntaxError, NoMethodError
|
26
|
+
def self.regexp_supports_named_captures? #:nodoc:
|
27
|
+
false
|
28
|
+
end
|
29
|
+
ensure
|
30
|
+
$DEBUG = old_debug
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
POSIX_BRACKET_TYPES = %w(
|
35
|
+
alnum alpha ascii blank cntrl digit graph
|
36
|
+
lower print punct space upper word xdigit
|
37
|
+
foo
|
38
|
+
)
|
39
|
+
|
40
|
+
# Returns array of supported POSX bracket types
|
41
|
+
def self.supported_posix_bracket_types
|
42
|
+
@supported_posix_bracket_types ||= []
|
43
|
+
end
|
44
|
+
|
45
|
+
# Detect supported posix bracket types
|
46
|
+
begin
|
47
|
+
old_debug, $DEBUG = $DEBUG, nil
|
48
|
+
|
49
|
+
POSIX_BRACKET_TYPES.each do |type|
|
50
|
+
begin
|
51
|
+
eval("foo = /[[:#{type}:]]/")
|
52
|
+
supported_posix_bracket_types << type
|
53
|
+
rescue SyntaxError, RegexpError
|
54
|
+
end
|
55
|
+
end
|
56
|
+
ensure
|
57
|
+
$DEBUG = old_debug
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# Parses Regexp and returns a Expression data structure.
|
62
|
+
def self.parse(regexp)
|
63
|
+
Parser.parse_regexp(regexp)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Recompiles Regexp by parsing it and turning it back into a Regexp.
|
67
|
+
#
|
68
|
+
# (In the future Regin will perform some Regexp optimizations
|
69
|
+
# such as removing unnecessary captures and options)
|
70
|
+
def self.compile(source)
|
71
|
+
regexp = Regexp.compile(source)
|
72
|
+
expression = parse(regexp)
|
73
|
+
Regexp.compile(expression.to_s(true), expression.flags)
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Regin
|
2
|
+
class Alternation < Collection
|
3
|
+
def initialize(*args)
|
4
|
+
args, options = extract_options(args)
|
5
|
+
|
6
|
+
if args.length == 1 && args.first.instance_of?(Array)
|
7
|
+
super(args.first)
|
8
|
+
else
|
9
|
+
super(args)
|
10
|
+
end
|
11
|
+
|
12
|
+
if options.key?(:ignorecase)
|
13
|
+
@array.map! { |e| e.dup(:ignorecase => options[:ignorecase]) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Returns true if expression could be treated as a literal string.
|
18
|
+
#
|
19
|
+
# Alternation groups are never literal.
|
20
|
+
def literal?
|
21
|
+
false
|
22
|
+
end
|
23
|
+
|
24
|
+
def flags
|
25
|
+
0
|
26
|
+
end
|
27
|
+
|
28
|
+
def dup(options = {})
|
29
|
+
self.class.new(to_a, options)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s(parent = false)
|
33
|
+
map { |e| e.to_s(parent) }.join('|')
|
34
|
+
end
|
35
|
+
|
36
|
+
def inspect #:nodoc:
|
37
|
+
to_s.inspect
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|