http_router 0.8.9 → 0.8.10
Sign up to get free protection for your applications and to get access to all the features.
- data/benchmarks/gen2.rb +6 -6
- data/lib/http_router.rb +0 -4
- data/lib/http_router/node.rb +1 -6
- data/lib/http_router/node/lookup.rb +1 -3
- data/lib/http_router/node/regex.rb +16 -5
- data/lib/http_router/node/request.rb +1 -0
- data/lib/http_router/node/spanning_regex.rb +2 -4
- data/lib/http_router/regex_route.rb +7 -3
- data/lib/http_router/route.rb +105 -135
- data/lib/http_router/version.rb +1 -1
- data/test/test_recognize.rb +1 -0
- data/test/test_request.rb +6 -1
- data/test/test_variable.rb +4 -0
- metadata +4 -4
data/benchmarks/gen2.rb
CHANGED
@@ -3,14 +3,14 @@ require 'rbench'
|
|
3
3
|
#require 'lib/usher'
|
4
4
|
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
5
5
|
|
6
|
-
require '
|
6
|
+
require 'http_router'
|
7
7
|
|
8
8
|
u = HttpRouter.new
|
9
|
-
u.add('/simple') .name(:simple).
|
10
|
-
u.add('/simple/:variable') .name(:one_variable).
|
11
|
-
u.add('/simple/:var1/:var2/:var3') .name(:three_variables).
|
12
|
-
u.add('/simple/:v1/:v2/:v3/:v4/:v5/:v6/:v7/:v8') .name(:eight_variables).
|
13
|
-
u.add('/with_condition/:cond1/:cond2').matching(:cond1 => /^\d+$/, :cond2 => /^[a-z]+$/) .name(:two_conditions).
|
9
|
+
u.add('/simple') .name(:simple).to{}
|
10
|
+
u.add('/simple/:variable') .name(:one_variable).to{}
|
11
|
+
u.add('/simple/:var1/:var2/:var3') .name(:three_variables).to{}
|
12
|
+
u.add('/simple/:v1/:v2/:v3/:v4/:v5/:v6/:v7/:v8') .name(:eight_variables).to{}
|
13
|
+
u.add('/with_condition/:cond1/:cond2').matching(:cond1 => /^\d+$/, :cond2 => /^[a-z]+$/) .name(:two_conditions).to{}
|
14
14
|
|
15
15
|
TIMES = 50_000
|
16
16
|
|
data/lib/http_router.rb
CHANGED
data/lib/http_router/node.rb
CHANGED
@@ -65,18 +65,13 @@ class HttpRouter
|
|
65
65
|
|
66
66
|
private
|
67
67
|
def inject_root_methods(code = nil, &blk)
|
68
|
-
|
69
|
-
root.methods_module.module_eval(code)
|
70
|
-
else
|
71
|
-
root.methods_module.module_eval(&blk)
|
72
|
-
end
|
68
|
+
code ? root.methods_module.module_eval(code) : root.methods_module.module_eval(&blk)
|
73
69
|
end
|
74
70
|
|
75
71
|
def inject_root_ivar(name, val)
|
76
72
|
root.instance_variable_set(name, val)
|
77
73
|
end
|
78
74
|
|
79
|
-
|
80
75
|
def add(matcher)
|
81
76
|
@matchers << matcher unless matcher.usable?(@matchers.last)
|
82
77
|
@matchers.last
|
@@ -25,9 +25,7 @@ class HttpRouter
|
|
25
25
|
#{@map[k].map{|n| n.to_code} * "\n"}
|
26
26
|
request.path.unshift part
|
27
27
|
end"}.join("\n")
|
28
|
-
|
29
|
-
send(\"#{method_prefix}\#{request.path.first}\", request) if !request.path_finished? && #{lookup_ivar}.key?(request.path.first)
|
30
|
-
"
|
28
|
+
"send(\"#{method_prefix}\#{request.path.first}\", request) if !request.path_finished? && #{lookup_ivar}.key?(request.path.first)"
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
@@ -2,10 +2,14 @@ class HttpRouter
|
|
2
2
|
class Node
|
3
3
|
class Regex < Node
|
4
4
|
alias_method :node_to_code, :to_code
|
5
|
-
attr_reader :matcher, :splitting_indicies, :capturing_indicies
|
5
|
+
attr_reader :matcher, :splitting_indicies, :capturing_indicies, :ordered_indicies
|
6
6
|
|
7
7
|
def initialize(router, parent, matcher, capturing_indicies, splitting_indicies = nil)
|
8
8
|
@matcher, @capturing_indicies, @splitting_indicies = matcher, capturing_indicies, splitting_indicies
|
9
|
+
@ordered_indicies = []
|
10
|
+
@ordered_indicies.concat(capturing_indicies.map{|i| [i, :capture]}) if capturing_indicies
|
11
|
+
@ordered_indicies.concat(splitting_indicies.map{|i| [i, :split]}) if splitting_indicies
|
12
|
+
@ordered_indicies.sort!
|
9
13
|
super(router, parent)
|
10
14
|
end
|
11
15
|
|
@@ -16,14 +20,21 @@ class HttpRouter
|
|
16
20
|
def to_code
|
17
21
|
params_size = @splitting_indicies.size + @capturing_indicies.size
|
18
22
|
"if match = #{@matcher.inspect}.match(request.path.first) and match.begin(0).zero?
|
19
|
-
part = request.path.shift\n" <<
|
20
|
-
|
21
|
-
@capturing_indicies.map { |c| "request.params << match[#{c}]\n" }.join << "
|
22
|
-
#{super}
|
23
|
+
part = request.path.shift\n" << param_capturing_code <<
|
24
|
+
"#{super}
|
23
25
|
request.path.unshift part
|
24
26
|
#{params_size == 1 ? "request.params.pop" : "request.params.slice!(#{-params_size}, #{params_size})"}
|
25
27
|
end"
|
26
28
|
end
|
29
|
+
|
30
|
+
def param_capturing_code
|
31
|
+
@ordered_indicies.map{|(i, type)|
|
32
|
+
case type
|
33
|
+
when :capture then "request.params << match[#{i}]\n"
|
34
|
+
when :split then "request.params << match[#{i}].split(/\\//)\n"
|
35
|
+
end
|
36
|
+
}.join("")
|
37
|
+
end
|
27
38
|
end
|
28
39
|
end
|
29
40
|
end
|
@@ -2,13 +2,11 @@ class HttpRouter
|
|
2
2
|
class Node
|
3
3
|
class SpanningRegex < Regex
|
4
4
|
def to_code
|
5
|
-
params_count =
|
5
|
+
params_count = @ordered_indicies.size
|
6
6
|
"whole_path#{depth} = request.joined_path
|
7
7
|
if match = #{@matcher.inspect}.match(whole_path#{depth}) and match.begin(0).zero?
|
8
8
|
original_path#{depth} = request.path.dup
|
9
|
-
" <<
|
10
|
-
(@splitting_indicies || []).map { |s| "request.params << match[#{s}].split(/\\//)\n" }.join <<
|
11
|
-
@capturing_indicies.map { |c| "request.params << match[#{c}]\n" }.join << "
|
9
|
+
" << param_capturing_code << "
|
12
10
|
remaining_path = whole_path#{depth}[match[0].size + (whole_path#{depth}[match[0].size] == ?/ ? 1 : 0), whole_path#{depth}.size]
|
13
11
|
request.path = remaining_path.split('/')
|
14
12
|
#{node_to_code}
|
@@ -2,13 +2,17 @@ class HttpRouter
|
|
2
2
|
class RegexRoute < Route
|
3
3
|
def initialize(router, path, opts = {})
|
4
4
|
@router, @original_path, @opts = router, path, opts
|
5
|
+
@param_names = @original_path.respond_to?(:names) ? @original_path.names.map(&:to_sym) : []
|
5
6
|
process_opts
|
6
7
|
end
|
7
8
|
|
8
|
-
def
|
9
|
-
@
|
9
|
+
def add_path_to_tree
|
10
|
+
@paths = [@original_path]
|
10
11
|
add_non_path_to_tree(@router.root.add_free_match(@original_path), path, @param_names)
|
11
|
-
|
12
|
+
end
|
13
|
+
|
14
|
+
def significant_variable_names
|
15
|
+
@param_names
|
12
16
|
end
|
13
17
|
|
14
18
|
def match_partially?
|
data/lib/http_router/route.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
class HttpRouter
|
2
2
|
class Route
|
3
|
-
|
3
|
+
DoubleCompileError = Class.new(RuntimeError)
|
4
|
+
|
5
|
+
attr_reader :default_values, :router, :path, :conditions, :original_path, :match_partially, :dest, :regex, :named, :matches_with
|
6
|
+
alias_method :match_partially?, :match_partially
|
7
|
+
alias_method :regex?, :regex
|
4
8
|
|
5
9
|
def initialize(router, path, opts = {})
|
6
|
-
@router = router
|
7
|
-
@original_path = path
|
8
|
-
@opts = opts
|
9
|
-
@matches_with = {}
|
10
|
-
@default_values = opts[:default_values] || {}
|
10
|
+
@router, @original_path, @opts = router, path, opts
|
11
11
|
if @original_path[-1] == ?*
|
12
12
|
@match_partially = true
|
13
13
|
path.slice!(-1)
|
@@ -16,15 +16,24 @@ class HttpRouter
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def process_opts
|
19
|
-
@
|
20
|
-
@
|
21
|
-
@
|
22
|
-
|
23
|
-
|
19
|
+
@default_values = @opts[:__default_values__] || @opts[:default_values] || {}
|
20
|
+
@arbitrary = @opts[:__arbitrary__] || @opts[:arbitrary]
|
21
|
+
@matches_with = significant_variable_names.include?(:matching) ? @opts : @opts[:__matching__] || @opts[:matching] || {}
|
22
|
+
significant_variable_names.each do |name|
|
23
|
+
@matches_with[name] = @opts[name] if @opts.key?(name) && !@matches_with.key?(name)
|
24
|
+
end
|
25
|
+
@conditions = @opts[:__conditions__] || @opts[:conditions] || {}
|
26
|
+
@match_partially = @opts[:__partial__] if @match_partially.nil? && !@opts[:__partial__].nil?
|
27
|
+
@match_partially = @opts[:partial] if @match_partially.nil? && !@opts[:partial].nil?
|
28
|
+
name(@opts[:__name__] || @opts[:name]) if @opts.key?(:__name__) || @opts.key?(:name)
|
24
29
|
end
|
25
30
|
|
26
31
|
def as_options
|
27
|
-
{:
|
32
|
+
{:__matching__ => @matches_with, :__conditions__ => @conditions, :__default_values__ => @default_values, :__name__ => @named, :__partial__ => @partially_match, :__arbitrary__ => @arbitrary}
|
33
|
+
end
|
34
|
+
|
35
|
+
def compiled?
|
36
|
+
!@paths.nil?
|
28
37
|
end
|
29
38
|
|
30
39
|
def partial(match_partially = true)
|
@@ -32,30 +41,14 @@ class HttpRouter
|
|
32
41
|
self
|
33
42
|
end
|
34
43
|
|
35
|
-
def match_partially?
|
36
|
-
@match_partially
|
37
|
-
end
|
38
|
-
|
39
|
-
def dest
|
40
|
-
@app
|
41
|
-
end
|
42
|
-
|
43
|
-
def regex?
|
44
|
-
false
|
45
|
-
end
|
46
|
-
|
47
44
|
def to(dest = nil, &dest2)
|
48
|
-
@
|
49
|
-
|
45
|
+
@dest = dest || dest2
|
46
|
+
add_path_to_tree
|
50
47
|
self
|
51
48
|
end
|
52
49
|
|
53
|
-
def compiled?
|
54
|
-
@compiled
|
55
|
-
end
|
56
|
-
|
57
50
|
def name(n)
|
58
|
-
@
|
51
|
+
@named = n
|
59
52
|
@router.named_routes[n] = self
|
60
53
|
self
|
61
54
|
end
|
@@ -77,8 +70,7 @@ class HttpRouter
|
|
77
70
|
end
|
78
71
|
|
79
72
|
def matching(matchers)
|
80
|
-
matchers
|
81
|
-
@opts.merge!(matchers)
|
73
|
+
@matches_with.merge!(matchers.is_a?(Array) ? Hash[*matchers] : matchers)
|
82
74
|
self
|
83
75
|
end
|
84
76
|
|
@@ -141,11 +133,7 @@ class HttpRouter
|
|
141
133
|
options = args.last.is_a?(Hash) ? args.pop : nil
|
142
134
|
options = options.nil? ? default_values.dup : default_values.merge(options) if default_values
|
143
135
|
options.delete_if{ |k,v| v.nil? } if options
|
144
|
-
path =
|
145
|
-
matching_path(options)
|
146
|
-
else
|
147
|
-
matching_path(args, options)
|
148
|
-
end
|
136
|
+
path = args.empty? ? matching_path(options) : matching_path(args, options)
|
149
137
|
raise InvalidRouteException unless path
|
150
138
|
result, params = path.url(args, options)
|
151
139
|
mount_point = router.url_mount && router.url_mount.url(options)
|
@@ -157,118 +145,100 @@ class HttpRouter
|
|
157
145
|
end
|
158
146
|
|
159
147
|
def matching_path(params, other_hash = nil)
|
160
|
-
if @paths.size == 1
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
path.param_names.size == var_count
|
168
|
-
}
|
169
|
-
else
|
170
|
-
@paths.each do |path|
|
171
|
-
if params && !params.empty?
|
172
|
-
return path if (path.param_names & params.keys).size == path.param_names.size
|
173
|
-
elsif path.param_names.empty?
|
174
|
-
return path
|
175
|
-
end
|
176
|
-
end
|
177
|
-
nil
|
178
|
-
end
|
148
|
+
return @paths.first if @paths.size == 1
|
149
|
+
case params
|
150
|
+
when Array
|
151
|
+
significant_keys = other_hash && significant_variable_names & other_hash.keys
|
152
|
+
@paths.find { |path| path.param_names.size == (significant_keys ? params.size + significant_keys.size : params.size) }
|
153
|
+
when Hash
|
154
|
+
@paths.find { |path| (params && !params.empty? && (path.param_names & params.keys).size == path.param_names.size) || path.param_names.empty? }
|
179
155
|
end
|
180
156
|
end
|
181
157
|
|
182
|
-
def named
|
183
|
-
@name
|
184
|
-
end
|
185
|
-
|
186
158
|
def to_s
|
187
159
|
"#<HttpRouter:Route #{object_id} @original_path=#{@original_path.inspect} @conditions=#{@conditions.inspect} @arbitrary=#{@arbitrary.inspect}>"
|
188
160
|
end
|
189
161
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
162
|
+
private
|
163
|
+
def raw_paths
|
164
|
+
@raw_paths ||= begin
|
165
|
+
start_index, end_index = 0, 1
|
166
|
+
@raw_paths, chars = [""], @original_path.split('')
|
167
|
+
until chars.empty?
|
168
|
+
case fc = chars.first[0]
|
169
|
+
when ?(
|
170
|
+
chars.shift
|
171
|
+
(start_index...end_index).each { |path_index| raw_paths << raw_paths[path_index].dup }
|
172
|
+
start_index = end_index
|
173
|
+
end_index = raw_paths.size
|
174
|
+
when ?)
|
175
|
+
chars.shift
|
176
|
+
start_index -= end_index - start_index
|
177
|
+
else
|
178
|
+
c = if chars[0][0] == ?\\ && (chars[1][0] == ?( || chars[1][0] == ?)); chars.shift; chars.shift; else; chars.shift; end
|
179
|
+
(start_index...end_index).each { |path_index| raw_paths[path_index] << c }
|
180
|
+
end
|
207
181
|
end
|
182
|
+
@raw_paths.reverse!
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
def add_normal_part(node, part, param_names)
|
187
|
+
name = part[1, part.size]
|
188
|
+
node = case part[0]
|
189
|
+
when ?\\
|
190
|
+
node.add_lookup(part[1].chr)
|
191
|
+
when ?:
|
192
|
+
param_names << name.to_sym
|
193
|
+
@matches_with[name.to_sym] ? node.add_spanning_match(@matches_with[name.to_sym]) : node.add_variable
|
194
|
+
when ?*
|
195
|
+
param_names << name.to_sym
|
196
|
+
@matches_with[name.to_sym] ? node.add_glob_regexp(@matches_with[name.to_sym]) : node.add_glob
|
197
|
+
else
|
198
|
+
node.add_lookup(part)
|
208
199
|
end
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
@opts[name.to_sym] ? node.add_spanning_match(@opts.delete(name.to_sym)) : node.add_variable
|
225
|
-
when ?*
|
226
|
-
param_names << name.to_sym
|
227
|
-
matches_with[name.to_sym] = @opts[name.to_sym]
|
228
|
-
@opts[name.to_sym] ? node.add_glob_regexp(@opts.delete(name.to_sym)) : node.add_glob
|
229
|
-
else
|
230
|
-
node.add_lookup(parts[0])
|
231
|
-
end
|
200
|
+
end
|
201
|
+
|
202
|
+
def add_complex_part(node, parts, param_names)
|
203
|
+
capturing_indicies, splitting_indicies, captures, spans = [], [], 0, false
|
204
|
+
regex = parts.inject('') do |reg, part|
|
205
|
+
reg << case part[0]
|
206
|
+
when ?\\ then Regexp.quote(part[1].chr)
|
207
|
+
when ?:, ?*
|
208
|
+
spans = true if part[0] == ?*
|
209
|
+
captures += 1
|
210
|
+
(part[0] == ?* ? splitting_indicies : capturing_indicies) << captures
|
211
|
+
name = part[1, part.size].to_sym
|
212
|
+
param_names << name
|
213
|
+
if spans
|
214
|
+
@matches_with[name] ? "((?:#{@matches_with[name]}\\/?)+)" : '(.*?)'
|
232
215
|
else
|
233
|
-
|
234
|
-
splitting_indicies = []
|
235
|
-
captures = 0
|
236
|
-
spans = false
|
237
|
-
regex = parts.inject('') do |reg, part|
|
238
|
-
reg << case part[0]
|
239
|
-
when ?\\
|
240
|
-
Regexp.quote(part[1].chr)
|
241
|
-
when ?:
|
242
|
-
captures += 1
|
243
|
-
capturing_indicies << captures
|
244
|
-
name = part[1, part.size].to_sym
|
245
|
-
param_names << name
|
246
|
-
matches_with[name] = @opts[name]
|
247
|
-
"(#{(@opts[name] || '[^/]*?')})"
|
248
|
-
when ?*
|
249
|
-
spans = true
|
250
|
-
captures += 1
|
251
|
-
splitting_indicies << captures
|
252
|
-
name = part[1, part.size].to_sym
|
253
|
-
param_names << name
|
254
|
-
matches_with[name] = @opts[name]
|
255
|
-
@opts[name] ?
|
256
|
-
"((?:#{@opts[name]}\\/?)+)" : '(.*?)'
|
257
|
-
else
|
258
|
-
Regexp.quote(part)
|
259
|
-
end
|
260
|
-
end
|
261
|
-
node = spans ? node.add_spanning_match(Regexp.new("#{regex}$"), capturing_indicies, splitting_indicies) :
|
262
|
-
node.add_match(Regexp.new("#{regex}$"), capturing_indicies, splitting_indicies)
|
216
|
+
"(#{(@matches_with[name] || '[^/]*?')})"
|
263
217
|
end
|
218
|
+
else
|
219
|
+
Regexp.quote(part)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
spans ? node.add_spanning_match(Regexp.new("#{regex}$"), capturing_indicies, splitting_indicies) :
|
223
|
+
node.add_match(Regexp.new("#{regex}$"), capturing_indicies, splitting_indicies)
|
224
|
+
end
|
225
|
+
|
226
|
+
def add_path_to_tree
|
227
|
+
raise DoubleCompileError if compiled?
|
228
|
+
@paths ||= begin
|
229
|
+
raw_paths.map do |path|
|
230
|
+
param_names = []
|
231
|
+
node = @router.root
|
232
|
+
path.split(/\//).each do |part|
|
233
|
+
next if part == ''
|
234
|
+
parts = part.scan(/\\.|[:*][a-z0-9_]+|[^:*\\]+/)
|
235
|
+
node = parts.size == 1 ? add_normal_part(node, part, param_names) : add_complex_part(node, parts, param_names)
|
236
|
+
end
|
237
|
+
add_non_path_to_tree(node, path, param_names)
|
264
238
|
end
|
265
|
-
add_non_path_to_tree(node, path, param_names)
|
266
239
|
end
|
267
|
-
@compiled = true
|
268
|
-
self
|
269
240
|
end
|
270
241
|
|
271
|
-
private
|
272
242
|
def add_non_path_to_tree(node, path, names)
|
273
243
|
path_obj = Path.new(self, path, names)
|
274
244
|
node = node.add_request(@conditions) unless @conditions.empty?
|
data/lib/http_router/version.rb
CHANGED
data/test/test_recognize.rb
CHANGED
@@ -53,6 +53,7 @@ class TestRecognition < MiniTest::Unit::TestCase
|
|
53
53
|
assert_route route, '/one/two/three'
|
54
54
|
assert_route route, '/one/two/three/four'
|
55
55
|
assert_route route, '/one/two/three/five'
|
56
|
+
assert_route route, '/one/two/three/four/five'
|
56
57
|
end
|
57
58
|
|
58
59
|
def test_escape_paren
|
data/test/test_request.rb
CHANGED
@@ -7,11 +7,16 @@ class TestRequest < MiniTest::Unit::TestCase
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def test_simple_case_with_array
|
10
|
-
r = router { add('test', :
|
10
|
+
r = router { add('test', :conditions => {:request_method => ['POST', 'GET']}) }
|
11
11
|
assert_route r, Rack::MockRequest.env_for('/test', :method => 'POST')
|
12
12
|
assert_route r, Rack::MockRequest.env_for('/test', :method => 'GET')
|
13
13
|
end
|
14
14
|
|
15
|
+
def test_simple_case_with_non_array
|
16
|
+
r = router { add('test', :conditions => {:request_method => 'GET'}) }
|
17
|
+
assert_route r, Rack::MockRequest.env_for('/test', :method => 'GET')
|
18
|
+
end
|
19
|
+
|
15
20
|
def test_single_app_with_404
|
16
21
|
r = router { add('test').post.to{|env| [404, {}, []]} }
|
17
22
|
assert_route nil, Rack::MockRequest.env_for('/test', :method => 'POST')
|
data/test/test_variable.rb
CHANGED
@@ -94,6 +94,10 @@ class TestVariable < MiniTest::Unit::TestCase
|
|
94
94
|
assert_route '/test/*variable/test/*variable2', '/test/one/two/three/test/four/five/six', {:variable => ['one', 'two', 'three'], :variable2 => ['four', 'five', 'six']}
|
95
95
|
end
|
96
96
|
|
97
|
+
def test_var_glob_var
|
98
|
+
assert_route '/test/:test-*variable.:format', '/test/one-two/three/four/five.six', {:test=> 'one', :variable => ['two', 'three', 'four', 'five'], :format => 'six'}
|
99
|
+
end
|
100
|
+
|
97
101
|
def test_glob_with_regexp
|
98
102
|
r = router { add('/test/*variable', :variable => /[a-z]+/) }
|
99
103
|
assert_route nil, '/test/asd/123'
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: http_router
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 43
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 8
|
9
|
-
-
|
10
|
-
version: 0.8.
|
9
|
+
- 10
|
10
|
+
version: 0.8.10
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Joshua Hull
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-06-
|
18
|
+
date: 2011-06-23 00:00:00 -07:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|