http_router 0.8.9 → 0.8.10
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/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
|