rack-mount 0.6.13 → 0.8.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/lib/rack/mount/analysis/splitting.rb +43 -43
- data/lib/rack/mount/code_generation.rb +5 -2
- data/lib/rack/mount/multimap.rb +95 -11
- data/lib/rack/mount/prefix.rb +1 -1
- data/lib/rack/mount/route.rb +7 -11
- data/lib/rack/mount/route_set.rb +19 -41
- data/lib/rack/mount/strexp/parser.rb +0 -0
- data/lib/rack/mount/utils.rb +1 -1
- data/lib/rack/mount/vendor/regin/regin/group.rb +8 -3
- data/lib/rack/mount/vendor/regin/regin/parser.rb +210 -185
- data/lib/rack/mount/vendor/regin/regin/tokenizer.rb +0 -0
- data/lib/rack/mount/version.rb +1 -1
- data/lib/rack/mount.rb +0 -1
- metadata +31 -22
- data/lib/rack/mount/analysis/frequency.rb +0 -60
- data/lib/rack/mount/strexp/parser.y +0 -34
- data/lib/rack/mount/strexp/tokenizer.rex +0 -12
- data/lib/rack/mount/vendor/multimap/multimap.rb +0 -569
- data/lib/rack/mount/vendor/multimap/multiset.rb +0 -185
- data/lib/rack/mount/vendor/multimap/nested_multimap.rb +0 -158
|
@@ -2,8 +2,8 @@ require 'rack/mount/utils'
|
|
|
2
2
|
|
|
3
3
|
module Rack::Mount
|
|
4
4
|
module Analysis
|
|
5
|
-
class Splitting
|
|
6
|
-
NULL = "\0"
|
|
5
|
+
class Splitting
|
|
6
|
+
NULL = "\0"
|
|
7
7
|
|
|
8
8
|
class Key < Struct.new(:method, :index, :separators)
|
|
9
9
|
def self.split(value, separator_pattern)
|
|
@@ -26,26 +26,44 @@ module Rack::Mount
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
|
|
29
|
+
def initialize(*keys)
|
|
30
|
+
clear
|
|
31
|
+
keys.each { |key| self << key }
|
|
32
|
+
end
|
|
33
|
+
|
|
29
34
|
def clear
|
|
30
|
-
@
|
|
31
|
-
|
|
35
|
+
@raw_keys = []
|
|
36
|
+
@key_frequency = Analysis::Histogram.new
|
|
37
|
+
self
|
|
32
38
|
end
|
|
33
39
|
|
|
34
40
|
def <<(key)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
41
|
+
raise ArgumentError unless key.is_a?(Hash)
|
|
42
|
+
@raw_keys << key
|
|
43
|
+
nil
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def possible_keys
|
|
47
|
+
@possible_keys ||= begin
|
|
48
|
+
@raw_keys.map do |key|
|
|
49
|
+
key.inject({}) { |requirements, (method, requirement)|
|
|
50
|
+
process_key(requirements, method, requirement)
|
|
51
|
+
requirements
|
|
52
|
+
}
|
|
53
|
+
end
|
|
38
54
|
end
|
|
39
55
|
end
|
|
40
56
|
|
|
41
|
-
def
|
|
42
|
-
@
|
|
43
|
-
|
|
57
|
+
def report
|
|
58
|
+
@report ||= begin
|
|
59
|
+
possible_keys.each { |keys| keys.each_pair { |key, _| @key_frequency << key } }
|
|
60
|
+
return [] if @key_frequency.count <= 1
|
|
61
|
+
@key_frequency.keys_in_upper_quartile
|
|
62
|
+
end
|
|
44
63
|
end
|
|
45
|
-
attr_writer :separators
|
|
46
64
|
|
|
47
|
-
def
|
|
48
|
-
@
|
|
65
|
+
def expire!
|
|
66
|
+
@possible_keys = @report = nil
|
|
49
67
|
end
|
|
50
68
|
|
|
51
69
|
def process_key(requirements, method, requirement)
|
|
@@ -55,40 +73,22 @@ module Rack::Mount
|
|
|
55
73
|
requirements[Key.new(method, index, Regexp.union(*separators))] = value
|
|
56
74
|
end
|
|
57
75
|
else
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
private
|
|
63
|
-
def analyze_capture_boundaries(regexp, boundaries) #:nodoc:
|
|
64
|
-
return boundaries unless regexp.is_a?(Regexp)
|
|
65
|
-
|
|
66
|
-
parts = Utils.parse_regexp(regexp)
|
|
67
|
-
parts.each_with_index do |part, index|
|
|
68
|
-
if part.is_a?(Regin::Group)
|
|
69
|
-
if index > 0
|
|
70
|
-
previous = parts[index-1]
|
|
71
|
-
if previous.is_a?(Regin::Character) && previous.literal?
|
|
72
|
-
boundaries << previous.to_s
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
if inside = part.expression[0]
|
|
77
|
-
if inside.is_a?(Regin::Character) && inside.literal?
|
|
78
|
-
boundaries << inside.to_s
|
|
79
|
-
end
|
|
80
|
-
end
|
|
76
|
+
if requirement.is_a?(Regexp)
|
|
77
|
+
expression = Utils.parse_regexp(requirement)
|
|
81
78
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
boundaries << following.to_s
|
|
86
|
-
end
|
|
87
|
-
end
|
|
79
|
+
if expression.is_a?(Regin::Expression) && expression.anchored_to_line?
|
|
80
|
+
expression = Regin::Expression.new(expression.reject { |e| e.is_a?(Regin::Anchor) })
|
|
81
|
+
return requirements[method] = expression.to_s if expression.literal?
|
|
88
82
|
end
|
|
89
83
|
end
|
|
90
84
|
|
|
91
|
-
|
|
85
|
+
requirements[method] = requirement
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
private
|
|
90
|
+
def separators(key)
|
|
91
|
+
key == :path_info ? ["/", "."] : []
|
|
92
92
|
end
|
|
93
93
|
|
|
94
94
|
def generate_split_keys(regexp, separators) #:nodoc:
|
|
@@ -71,8 +71,11 @@ module Rack::Mount
|
|
|
71
71
|
def optimize_recognize!
|
|
72
72
|
Utils.debug "optimizing recognize"
|
|
73
73
|
|
|
74
|
+
uses_cache = false
|
|
75
|
+
|
|
74
76
|
keys = @recognition_keys.map { |key|
|
|
75
77
|
if key.respond_to?(:call_source)
|
|
78
|
+
uses_cache = true
|
|
76
79
|
key.call_source(:cache, :obj)
|
|
77
80
|
else
|
|
78
81
|
"obj.#{key}"
|
|
@@ -85,7 +88,7 @@ module Rack::Mount
|
|
|
85
88
|
|
|
86
89
|
instance_eval(<<-RUBY, __FILE__, __LINE__)
|
|
87
90
|
def recognize(obj)
|
|
88
|
-
cache = {}
|
|
91
|
+
#{"cache = {}" if uses_cache}
|
|
89
92
|
container = @recognition_graph[#{keys}]
|
|
90
93
|
optimize_container_iterator(container) unless container.respond_to?(:optimized_each)
|
|
91
94
|
|
|
@@ -110,7 +113,7 @@ module Rack::Mount
|
|
|
110
113
|
def remove_metaclass_method(symbol)
|
|
111
114
|
metaclass = class << self; self; end
|
|
112
115
|
Utils.silence_debug { metaclass.send(:remove_method, symbol) }
|
|
113
|
-
rescue NameError
|
|
116
|
+
rescue NameError
|
|
114
117
|
nil
|
|
115
118
|
end
|
|
116
119
|
end
|
data/lib/rack/mount/multimap.rb
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
|
-
begin
|
|
2
|
-
require 'nested_multimap'
|
|
3
|
-
rescue LoadError
|
|
4
|
-
$: << File.expand_path(File.join(File.dirname(__FILE__), 'vendor/multimap'))
|
|
5
|
-
require 'nested_multimap'
|
|
6
|
-
end
|
|
7
|
-
|
|
8
1
|
module Rack::Mount
|
|
9
|
-
class Multimap
|
|
2
|
+
class Multimap #:nodoc:
|
|
3
|
+
def initialize(default = [])
|
|
4
|
+
@hash = Hash.new(default)
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def initialize_copy(original)
|
|
8
|
+
@hash = Hash.new(original.default.dup)
|
|
9
|
+
original.hash.each_pair do |key, container|
|
|
10
|
+
@hash[key] = container.dup
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
10
14
|
def store(*args)
|
|
11
15
|
keys = args.dup
|
|
12
16
|
value = keys.pop
|
|
@@ -26,7 +30,7 @@ module Rack::Mount
|
|
|
26
30
|
@hash.each_pair { |k, _|
|
|
27
31
|
if k =~ key
|
|
28
32
|
args[0] = k
|
|
29
|
-
|
|
33
|
+
_store(*args)
|
|
30
34
|
end
|
|
31
35
|
}
|
|
32
36
|
|
|
@@ -34,12 +38,19 @@ module Rack::Mount
|
|
|
34
38
|
default[*keys.dup] = value
|
|
35
39
|
end
|
|
36
40
|
else
|
|
37
|
-
|
|
41
|
+
_store(*args)
|
|
38
42
|
end
|
|
39
43
|
end
|
|
40
44
|
alias_method :[]=, :store
|
|
41
45
|
|
|
42
|
-
|
|
46
|
+
def [](*keys)
|
|
47
|
+
i, l, r, k = 0, keys.length, self, self.class
|
|
48
|
+
while r.is_a?(k)
|
|
49
|
+
r = i < l ? r.hash[keys[i]] : r.default
|
|
50
|
+
i += 1
|
|
51
|
+
end
|
|
52
|
+
r
|
|
53
|
+
end
|
|
43
54
|
|
|
44
55
|
def height
|
|
45
56
|
containers_with_default.max { |a, b| a.length <=> b.length }.length
|
|
@@ -49,5 +60,78 @@ module Rack::Mount
|
|
|
49
60
|
lengths = containers_with_default.map { |e| e.length }
|
|
50
61
|
lengths.inject(0) { |sum, len| sum += len }.to_f / lengths.size
|
|
51
62
|
end
|
|
63
|
+
|
|
64
|
+
def containers_with_default
|
|
65
|
+
containers = []
|
|
66
|
+
each_container_with_default { |container| containers << container }
|
|
67
|
+
containers
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
protected
|
|
71
|
+
def _store(*args)
|
|
72
|
+
keys = args
|
|
73
|
+
value = args.pop
|
|
74
|
+
|
|
75
|
+
raise ArgumentError, 'wrong number of arguments (1 for 2)' unless value
|
|
76
|
+
|
|
77
|
+
if keys.length > 1
|
|
78
|
+
update_container(keys.shift) do |container|
|
|
79
|
+
container = self.class.new(container) unless container.is_a?(self.class)
|
|
80
|
+
container[*keys] = value
|
|
81
|
+
container
|
|
82
|
+
end
|
|
83
|
+
elsif keys.length == 1
|
|
84
|
+
update_container(keys.first) do |container|
|
|
85
|
+
container << value
|
|
86
|
+
container
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
self << value
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def <<(value)
|
|
94
|
+
@hash.each_value { |container| container << value }
|
|
95
|
+
self.default << value
|
|
96
|
+
self
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def each_container_with_default(&block)
|
|
100
|
+
@hash.each_value do |container|
|
|
101
|
+
iterate_over_container(container, &block)
|
|
102
|
+
end
|
|
103
|
+
iterate_over_container(default, &block)
|
|
104
|
+
self
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def default
|
|
108
|
+
@hash.default
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def default=(value)
|
|
112
|
+
@hash.default = value
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def hash
|
|
116
|
+
@hash
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
def update_container(key)
|
|
121
|
+
container = @hash[key]
|
|
122
|
+
container = container.dup if container.equal?(default)
|
|
123
|
+
container = yield(container)
|
|
124
|
+
@hash[key] = container
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def iterate_over_container(container)
|
|
128
|
+
if container.respond_to?(:each_container_with_default)
|
|
129
|
+
container.each_container_with_default do |value|
|
|
130
|
+
yield value
|
|
131
|
+
end
|
|
132
|
+
else
|
|
133
|
+
yield container
|
|
134
|
+
end
|
|
135
|
+
end
|
|
52
136
|
end
|
|
53
137
|
end
|
data/lib/rack/mount/prefix.rb
CHANGED
data/lib/rack/mount/route.rb
CHANGED
|
@@ -54,10 +54,9 @@ module Rack::Mount
|
|
|
54
54
|
@named_captures = {}
|
|
55
55
|
@conditions.map { |method, condition|
|
|
56
56
|
next unless condition.respond_to?(:named_captures)
|
|
57
|
-
@named_captures[method] = condition.named_captures.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}.freeze
|
|
57
|
+
@named_captures[method] = Hash[condition.named_captures.map { |k, v|
|
|
58
|
+
[k.to_sym, v.last - 1]
|
|
59
|
+
}].freeze
|
|
61
60
|
}
|
|
62
61
|
@named_captures.freeze
|
|
63
62
|
|
|
@@ -83,7 +82,7 @@ module Rack::Mount
|
|
|
83
82
|
|
|
84
83
|
|
|
85
84
|
def generation_keys
|
|
86
|
-
@conditions.inject({}) { |keys, (
|
|
85
|
+
@conditions.inject({}) { |keys, (_, condition)|
|
|
87
86
|
if condition.respond_to?(:required_defaults)
|
|
88
87
|
keys.merge!(condition.required_defaults)
|
|
89
88
|
else
|
|
@@ -98,12 +97,9 @@ module Rack::Mount
|
|
|
98
97
|
|
|
99
98
|
def generate(method, params = {}, recall = {}, options = {})
|
|
100
99
|
if method.nil?
|
|
101
|
-
result = @conditions.
|
|
102
|
-
if condition.respond_to?(:generate)
|
|
103
|
-
|
|
104
|
-
end
|
|
105
|
-
h
|
|
106
|
-
}
|
|
100
|
+
result = Hash[@conditions.map { |m, condition|
|
|
101
|
+
[m, condition.generate(params, recall, options)] if condition.respond_to?(:generate)
|
|
102
|
+
}]
|
|
107
103
|
return nil if result.values.compact.empty?
|
|
108
104
|
else
|
|
109
105
|
if condition = @conditions[method]
|
data/lib/rack/mount/route_set.rb
CHANGED
|
@@ -25,7 +25,9 @@ module Rack::Mount
|
|
|
25
25
|
@named_routes = {}
|
|
26
26
|
|
|
27
27
|
@recognition_key_analyzer = Analysis::Splitting.new
|
|
28
|
-
|
|
28
|
+
|
|
29
|
+
@generation_keys = [:controller, :action]
|
|
30
|
+
@generation_route_keys = []
|
|
29
31
|
|
|
30
32
|
@request_class = options.delete(:request_class) || Rack::Request
|
|
31
33
|
@valid_conditions = @request_class.public_instance_methods.map! { |m| m.to_sym }
|
|
@@ -69,7 +71,7 @@ module Rack::Mount
|
|
|
69
71
|
@recognition_key_analyzer << route.conditions
|
|
70
72
|
|
|
71
73
|
@named_routes[route.name] = route if route.name
|
|
72
|
-
@
|
|
74
|
+
@generation_route_keys << route.generation_keys
|
|
73
75
|
|
|
74
76
|
expire!
|
|
75
77
|
route
|
|
@@ -125,7 +127,7 @@ module Rack::Mount
|
|
|
125
127
|
|
|
126
128
|
# Rack compatible recognition and dispatching method. Routes are
|
|
127
129
|
# tried until one returns a non-catch status code. If no routes
|
|
128
|
-
# match,
|
|
130
|
+
# match, then catch status code is returned.
|
|
129
131
|
#
|
|
130
132
|
# This method can only be invoked after the RouteSet has been
|
|
131
133
|
# finalized.
|
|
@@ -144,9 +146,16 @@ module Rack::Mount
|
|
|
144
146
|
env[Prefix::KEY] = matches[:path_info].to_s
|
|
145
147
|
end
|
|
146
148
|
|
|
147
|
-
env[@parameters_key]
|
|
149
|
+
old_params = env[@parameters_key]
|
|
150
|
+
env[@parameters_key] = (old_params || {}).merge(params)
|
|
151
|
+
|
|
148
152
|
result = route.app.call(env)
|
|
149
|
-
|
|
153
|
+
|
|
154
|
+
if result[1][X_CASCADE] == PASS
|
|
155
|
+
env[@parameters_key] = old_params
|
|
156
|
+
else
|
|
157
|
+
return result
|
|
158
|
+
end
|
|
150
159
|
end
|
|
151
160
|
|
|
152
161
|
request || [404, {'Content-Type' => 'text/html', 'X-Cascade' => 'pass'}, ['Not Found']]
|
|
@@ -160,7 +169,7 @@ module Rack::Mount
|
|
|
160
169
|
# Additional parameters can be passed in as a hash
|
|
161
170
|
# url(env, :people, :id => "1") # => "/people/1"
|
|
162
171
|
#
|
|
163
|
-
# If no
|
|
172
|
+
# If no named route is given, it will fall back to a slower
|
|
164
173
|
# generation search.
|
|
165
174
|
# url(env, :controller => "people", :action => "show", :id => "1")
|
|
166
175
|
# # => "/people/1"
|
|
@@ -249,7 +258,6 @@ module Rack::Mount
|
|
|
249
258
|
|
|
250
259
|
@recognition_keys = build_recognition_keys
|
|
251
260
|
@recognition_graph = build_recognition_graph
|
|
252
|
-
@generation_keys = build_generation_keys
|
|
253
261
|
@generation_graph = build_generation_graph
|
|
254
262
|
|
|
255
263
|
self
|
|
@@ -261,9 +269,10 @@ module Rack::Mount
|
|
|
261
269
|
def freeze
|
|
262
270
|
unless frozen?
|
|
263
271
|
rehash
|
|
272
|
+
stubbed_request_class
|
|
264
273
|
|
|
265
274
|
@recognition_key_analyzer = nil
|
|
266
|
-
@
|
|
275
|
+
@generation_route_keys = nil
|
|
267
276
|
@valid_conditions = nil
|
|
268
277
|
|
|
269
278
|
@routes.each { |route| route.freeze }
|
|
@@ -273,26 +282,6 @@ module Rack::Mount
|
|
|
273
282
|
super
|
|
274
283
|
end
|
|
275
284
|
|
|
276
|
-
def marshal_dump #:nodoc:
|
|
277
|
-
hash = {}
|
|
278
|
-
|
|
279
|
-
instance_variables_to_serialize.each do |ivar|
|
|
280
|
-
hash[ivar] = instance_variable_get(ivar)
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
if graph = hash[:@recognition_graph]
|
|
284
|
-
hash[:@recognition_graph] = graph.dup
|
|
285
|
-
end
|
|
286
|
-
|
|
287
|
-
hash
|
|
288
|
-
end
|
|
289
|
-
|
|
290
|
-
def marshal_load(hash) #:nodoc:
|
|
291
|
-
hash.each do |ivar, value|
|
|
292
|
-
instance_variable_set(ivar, value)
|
|
293
|
-
end
|
|
294
|
-
end
|
|
295
|
-
|
|
296
285
|
protected
|
|
297
286
|
def recognition_stats
|
|
298
287
|
{ :keys => @recognition_keys,
|
|
@@ -307,12 +296,7 @@ module Rack::Mount
|
|
|
307
296
|
@recognition_keys = @recognition_graph = nil
|
|
308
297
|
@recognition_key_analyzer.expire!
|
|
309
298
|
|
|
310
|
-
@
|
|
311
|
-
@generation_key_analyzer.expire!
|
|
312
|
-
end
|
|
313
|
-
|
|
314
|
-
def instance_variables_to_serialize
|
|
315
|
-
instance_variables.map { |ivar| ivar.to_sym } - [:@stubbed_request_class, :@optimized_recognize_defined]
|
|
299
|
+
@generation_graph = nil
|
|
316
300
|
end
|
|
317
301
|
|
|
318
302
|
# An internal helper method for constructing a nested set from
|
|
@@ -350,7 +334,7 @@ module Rack::Mount
|
|
|
350
334
|
build_nested_route_set(@generation_keys) { |k, i|
|
|
351
335
|
throw :skip unless @routes[i].significant_params?
|
|
352
336
|
|
|
353
|
-
if k = @
|
|
337
|
+
if k = @generation_route_keys[i][k]
|
|
354
338
|
k.to_s
|
|
355
339
|
else
|
|
356
340
|
nil
|
|
@@ -358,12 +342,6 @@ module Rack::Mount
|
|
|
358
342
|
}
|
|
359
343
|
end
|
|
360
344
|
|
|
361
|
-
def build_generation_keys
|
|
362
|
-
keys = @generation_key_analyzer.report
|
|
363
|
-
Utils.debug "generation keys - #{keys.inspect}"
|
|
364
|
-
keys
|
|
365
|
-
end
|
|
366
|
-
|
|
367
345
|
def extract_params!(*args)
|
|
368
346
|
case args.length
|
|
369
347
|
when 4
|
|
Binary file
|
data/lib/rack/mount/utils.rb
CHANGED
|
@@ -116,7 +116,7 @@ module Rack::Mount
|
|
|
116
116
|
module_function :regexp_anchored?
|
|
117
117
|
|
|
118
118
|
def normalize_extended_expression(regexp)
|
|
119
|
-
return regexp
|
|
119
|
+
return regexp if (regexp.options & Regexp::EXTENDED) == 0
|
|
120
120
|
source = regexp.source
|
|
121
121
|
source.gsub!(/#.+$/, '')
|
|
122
122
|
source.gsub!(/\s+/, '')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module Regin
|
|
2
2
|
class Group
|
|
3
|
-
attr_reader :expression, :quantifier, :capture, :index, :name
|
|
3
|
+
attr_reader :expression, :quantifier, :lookahead, :capture, :index, :name
|
|
4
4
|
|
|
5
5
|
def initialize(expression, options = {})
|
|
6
6
|
@quantifier = @index = @name = nil
|
|
@@ -8,6 +8,7 @@ module Regin
|
|
|
8
8
|
@expression = expression.dup(options)
|
|
9
9
|
|
|
10
10
|
@quantifier = options[:quantifier] if options.key?(:quantifier)
|
|
11
|
+
@lookahead = options[:lookahead] if options.key?(:lookahead)
|
|
11
12
|
@capture = options[:capture] if options.key?(:capture)
|
|
12
13
|
@index = options[:index] if options.key?(:index)
|
|
13
14
|
@name = options[:name] if options.key?(:name)
|
|
@@ -21,11 +22,15 @@ module Regin
|
|
|
21
22
|
#
|
|
22
23
|
# A Group is literal if its expression is literal and it has no quantifier.
|
|
23
24
|
def literal?
|
|
24
|
-
quantifier.nil? && expression.literal?
|
|
25
|
+
quantifier.nil? && lookahead.nil? && expression.literal?
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
def to_s(parent = false)
|
|
28
|
-
if
|
|
29
|
+
if lookahead == :postive
|
|
30
|
+
"(?=#{expression.to_s(parent)})#{quantifier}"
|
|
31
|
+
elsif lookahead == :negative
|
|
32
|
+
"(?!#{expression.to_s(parent)})#{quantifier}"
|
|
33
|
+
elsif !expression.options?
|
|
29
34
|
"(#{capture ? '' : '?:'}#{expression.to_s(parent)})#{quantifier}"
|
|
30
35
|
elsif capture == false
|
|
31
36
|
"#{expression.to_s}#{quantifier}"
|