h8 0.4.5 → 0.4.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/ext/h8/h8.h +1 -2
- data/ext/h8/main.cpp +2 -2
- data/ext/h8/ruby_gate.cpp +1 -1
- data/hybrid8.gemspec +1 -0
- data/lib/h8.rb +3 -0
- data/lib/h8/coffee.rb +3 -4
- data/lib/h8/context.rb +51 -11
- data/lib/h8/value.rb +33 -0
- data/lib/h8/version.rb +1 -1
- data/lib/scripts/globals.coffee +2 -1
- data/spec/coffee_spec.rb +39 -5
- data/spec/ruby_gate_spec.rb +47 -21
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ae98780cc495a9c2f15db1b3de81f81cc014978b
|
4
|
+
data.tar.gz: ee00bd70c38b2f4b62ecf8beddb2e15fb6aacb61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4a2be9806423de66eacb312891d1abcff2af0f24679e740af339689cae1ae50fd8dbb07d15d138fc13976bbb410b3acf92951d020bcb96df93c16ab9a5fd3b20
|
7
|
+
data.tar.gz: 85f4398e82fc5a818654936cdb2b0fe5b51ee64c8e08284d131657d4f89c90b55585fbb8fc052dfe673a8371ce9ad7f69ab4a440c3d8301ec5a23d5777981250
|
data/ext/h8/h8.h
CHANGED
@@ -19,8 +19,7 @@ extern VALUE value_class;
|
|
19
19
|
extern VALUE ruby_gate_class;
|
20
20
|
extern VALUE Rundefined;
|
21
21
|
|
22
|
-
extern ID id_is_a;
|
23
|
-
extern ID id_safe_call;
|
22
|
+
extern ID id_is_a, id_safe_call, id_safe_proc_call;
|
24
23
|
|
25
24
|
VALUE protect_ruby(const std::function<VALUE()> &block);
|
26
25
|
|
data/ext/h8/main.cpp
CHANGED
@@ -14,8 +14,7 @@ VALUE ruby_gate_class;
|
|
14
14
|
VALUE value_class;
|
15
15
|
VALUE Rundefined;
|
16
16
|
|
17
|
-
ID id_is_a;
|
18
|
-
ID id_safe_call;
|
17
|
+
ID id_is_a, id_safe_call, id_safe_proc_call;
|
19
18
|
|
20
19
|
VALUE protect_ruby(const std::function<VALUE()> &block) {
|
21
20
|
try {
|
@@ -189,6 +188,7 @@ void Init_h8(void) {
|
|
189
188
|
|
190
189
|
id_is_a = rb_intern("is_a?");
|
191
190
|
id_safe_call = rb_intern("secure_call");
|
191
|
+
id_safe_proc_call = rb_intern("safe_proc_call");
|
192
192
|
|
193
193
|
VALUE h8 = rb_define_module("H8");
|
194
194
|
|
data/ext/h8/ruby_gate.cpp
CHANGED
@@ -124,7 +124,7 @@ VALUE h8::RubyGate::rescue_callback(VALUE me, VALUE exception_object) {
|
|
124
124
|
VALUE RubyGate::call(VALUE args) {
|
125
125
|
VALUE callable = rb_ary_pop(args);
|
126
126
|
VALUE context = rb_ary_pop(args);
|
127
|
-
VALUE res = rb_funcall(context,
|
127
|
+
VALUE res = rb_funcall(context, id_safe_proc_call, 2, callable, args);
|
128
128
|
return res;
|
129
129
|
}
|
130
130
|
|
data/hybrid8.gemspec
CHANGED
@@ -33,6 +33,7 @@ spec = Gem::Specification.new do |spec|
|
|
33
33
|
spec.add_development_dependency "rake"
|
34
34
|
spec.add_development_dependency "rake-compiler"
|
35
35
|
spec.add_development_dependency "rspec", '~> 3.1'
|
36
|
+
spec.add_development_dependency 'hashie', '>= 0.1.2'
|
36
37
|
|
37
38
|
spec.add_dependency 'pargser', '>= 0.1.2'
|
38
39
|
end
|
data/lib/h8.rb
CHANGED
data/lib/h8/coffee.rb
CHANGED
@@ -18,7 +18,7 @@ module H8
|
|
18
18
|
# compiler instance across all threads with a mutex.
|
19
19
|
def self.eval src, ** kwargs
|
20
20
|
@@mutex.synchronize {
|
21
|
-
(@@compiler ||= Coffee.new).eval src, **
|
21
|
+
(@@compiler ||= Coffee.new).eval src, **kwargs
|
22
22
|
}
|
23
23
|
end
|
24
24
|
|
@@ -29,13 +29,13 @@ module H8
|
|
29
29
|
# compiler instance across all threads with a mutex.
|
30
30
|
def self.compile src, ** kwargs
|
31
31
|
@@mutex.synchronize {
|
32
|
-
(@@compiler ||= Coffee.new).compile src, **
|
32
|
+
(@@compiler ||= Coffee.new).compile src, **kwargs
|
33
33
|
}
|
34
34
|
end
|
35
35
|
|
36
36
|
# Create compiler instance.
|
37
37
|
def initialize
|
38
|
-
@context = H8::Context.new
|
38
|
+
@context = H8::Context.new noglobals: true
|
39
39
|
@context.eval read_script 'coffee-script.js'
|
40
40
|
eval read_script('globals.coffee')
|
41
41
|
end
|
@@ -70,5 +70,4 @@ module H8
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
|
73
|
-
|
74
73
|
end
|
data/lib/h8/context.rb
CHANGED
@@ -1,14 +1,17 @@
|
|
1
1
|
require 'thread'
|
2
|
+
require 'h8'
|
2
3
|
|
3
4
|
module H8
|
5
|
+
|
4
6
|
class Context
|
5
7
|
# Create new context optionally providing variables hash
|
6
|
-
def initialize **kwargs
|
8
|
+
def initialize noglobals: false, **kwargs
|
7
9
|
@idcount = 0
|
8
10
|
set_all **kwargs
|
9
11
|
_set_var '___create_ruby_class', -> (cls, args) {
|
10
12
|
_do_create_ruby_class cls, args
|
11
13
|
}
|
14
|
+
# noglobals or execute_script 'globals.coffee'
|
12
15
|
end
|
13
16
|
|
14
17
|
# set variables from keyword arguments to this context
|
@@ -48,13 +51,13 @@ module H8
|
|
48
51
|
# If you need to execute same script more than once consider first H8::Coffee.compile
|
49
52
|
# and cache compiled script.
|
50
53
|
def coffee script, ** kwargs
|
51
|
-
eval Coffee.compile script, **kwargs
|
54
|
+
eval Coffee.compile script, ** kwargs
|
52
55
|
end
|
53
56
|
|
54
57
|
|
55
58
|
# Execute script in a new context with optionally set vars. @see H8#set_all
|
56
59
|
# @return [Value] wrapped object returned by the script
|
57
|
-
def self.eval script, file_name: nil, **kwargs
|
60
|
+
def self.eval script, file_name: nil, ** kwargs
|
58
61
|
Context.new(** kwargs).eval script, file_name: file_name
|
59
62
|
end
|
60
63
|
|
@@ -67,12 +70,12 @@ module H8
|
|
67
70
|
def self.secure_call instance, method, args=nil
|
68
71
|
method = method.to_sym
|
69
72
|
begin
|
70
|
-
m
|
73
|
+
m = instance.public_method(method)
|
71
74
|
owner = m.owner
|
72
75
|
if can_access?(owner)
|
73
76
|
return m.call(*args) if method[0] == '[' || method[-1] == '='
|
74
77
|
if m.arity != 0
|
75
|
-
return -> (*args) { m.call *args }
|
78
|
+
return ProcGate.new( -> (*args) { m.call *args } )
|
76
79
|
else
|
77
80
|
return m.call
|
78
81
|
end
|
@@ -80,7 +83,7 @@ module H8
|
|
80
83
|
rescue NameError
|
81
84
|
# No exact method, calling []/[]= if any
|
82
85
|
method, args = if method[-1] == '='
|
83
|
-
[:[]=, [method[0..-2].to_s, args[0]]
|
86
|
+
[:[]=, [method[0..-2].to_s, args[0]]]
|
84
87
|
else
|
85
88
|
[:[], [method.to_s]]
|
86
89
|
end
|
@@ -88,7 +91,11 @@ module H8
|
|
88
91
|
m = instance.public_method(method)
|
89
92
|
if can_access?(owner)
|
90
93
|
if method == :[]
|
91
|
-
|
94
|
+
if instance.is_a?(Hash)
|
95
|
+
return m.call(*args) || m.call(args[0].to_sym)
|
96
|
+
else
|
97
|
+
return m.call(*args)
|
98
|
+
end
|
92
99
|
else
|
93
100
|
return m.call(*args)
|
94
101
|
end
|
@@ -115,10 +122,13 @@ module H8
|
|
115
122
|
# Set var that could be either a callable, class instance, simple value or a Class class
|
116
123
|
# in which case constructor function will be created
|
117
124
|
def set_var name, value
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
125
|
+
case value
|
126
|
+
when Class
|
127
|
+
_gate_class name.to_s, -> (*args) { value.new *args }
|
128
|
+
when Proc
|
129
|
+
_set_var name, ProcGate.new(value)
|
130
|
+
else
|
131
|
+
_set_var name, value
|
122
132
|
end
|
123
133
|
end
|
124
134
|
|
@@ -127,6 +137,36 @@ module H8
|
|
127
137
|
def _do_create_ruby_class(klass, arguments)
|
128
138
|
klass.new *H8::arguments_to_a(arguments.to_ruby.values)
|
129
139
|
end
|
140
|
+
|
141
|
+
|
142
|
+
@@base = File.expand_path File.join(File.dirname(__FILE__), '../scripts')
|
143
|
+
@@cache = {}
|
144
|
+
|
145
|
+
def execute_script name
|
146
|
+
p [:exs, name]
|
147
|
+
script = @@cache[name] ||= begin
|
148
|
+
p 'cache miss'
|
149
|
+
script = open(File.join(@@base, name), 'r').read
|
150
|
+
name.downcase.end_with?('.coffee') ? H8::Coffee.compile(script) : script
|
151
|
+
end
|
152
|
+
eval script
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
# The gate for Ruby's callable to support javascript's 'apply' functionality
|
158
|
+
class ProcGate
|
159
|
+
def initialize callable
|
160
|
+
@callable = callable
|
161
|
+
end
|
162
|
+
|
163
|
+
def apply this, args
|
164
|
+
@callable.call *args
|
165
|
+
end
|
166
|
+
|
167
|
+
def call *args
|
168
|
+
@callable.call *args
|
169
|
+
end
|
130
170
|
end
|
131
171
|
|
132
172
|
end
|
data/lib/h8/value.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
1
3
|
module H8
|
2
4
|
|
3
5
|
# Wrapper for javascript objects.
|
@@ -179,6 +181,37 @@ module H8
|
|
179
181
|
|
180
182
|
end
|
181
183
|
|
184
|
+
class OpenStruct
|
185
|
+
# OpenStruct converts to plain ruby hash in depth. Primary usage
|
186
|
+
# is when it was used bu javascript and could contain gated objects.
|
187
|
+
def to_ruby depth=0
|
188
|
+
to_h.to_ruby depth+1
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
class Hash
|
193
|
+
# Hash copies in depth converting its data. Primary usage
|
194
|
+
# is when it was used bu javascript and could contain gated objects.
|
195
|
+
#
|
196
|
+
# Important! that converted keys are turned to string even of were
|
197
|
+
# pure ruby symbols. This is done to remove ambiguity: work the same
|
198
|
+
# with ruby hashes, javasctipt objects, OpenStruct and Hashie::Mash instances
|
199
|
+
def to_ruby depth=0
|
200
|
+
res = {}
|
201
|
+
depth += 1
|
202
|
+
each { |k,v| res[k.to_ruby(depth).to_s] = v.to_ruby depth }
|
203
|
+
res
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
class Array
|
208
|
+
# @return new array with all components converted to_ruby
|
209
|
+
def to_ruby depth
|
210
|
+
depth += 1
|
211
|
+
map { |x| x.to_ruby depth }
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
182
215
|
# Ruby object's t_ruby does nothing (tree conversion optimization)
|
183
216
|
class Object
|
184
217
|
# It is already a ruby object. Gate objects should override
|
data/lib/h8/version.rb
CHANGED
data/lib/scripts/globals.coffee
CHANGED
data/spec/coffee_spec.rb
CHANGED
@@ -35,7 +35,7 @@ describe 'coffeescript' do
|
|
35
35
|
# above is bad
|
36
36
|
END
|
37
37
|
# pending
|
38
|
-
expect(->{
|
38
|
+
expect(-> {
|
39
39
|
H8::Coffee.compile script, file_name: 'test.coffee'
|
40
40
|
}).to raise_error(H8::JsError) { |e| e.to_s.should =~ /test.coffee\:4/ }
|
41
41
|
end
|
@@ -63,15 +63,49 @@ describe 'coffeescript' do
|
|
63
63
|
// CoffeeScript.sourceMaps['inner'] = res.sourceMap
|
64
64
|
eval(res.js);
|
65
65
|
END
|
66
|
-
cxt
|
66
|
+
cxt = H8::Coffee.new.context
|
67
67
|
cxt[:puts] = -> (*args) { puts args.join(' ') }
|
68
|
-
cxt[:src]
|
68
|
+
cxt[:src] = src
|
69
69
|
# cxt[:src] = 'return "hello"'
|
70
70
|
begin
|
71
|
-
|
72
|
-
rescue Exception=>e
|
71
|
+
res = cxt.eval script, file_name: 'extest.coffee'
|
72
|
+
rescue Exception => e
|
73
73
|
puts e
|
74
74
|
end
|
75
75
|
end
|
76
76
|
|
77
|
+
context 'realword' do
|
78
|
+
class Room
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should process varargs' do
|
82
|
+
c = H8::Context.new
|
83
|
+
c[:puts] = ->(*args) { puts "> "+args.join('') }
|
84
|
+
c.coffee <<-End
|
85
|
+
@.fn1 = (args...) ->
|
86
|
+
args.join(',')
|
87
|
+
|
88
|
+
@.test = (args...) ->
|
89
|
+
fn1 'first', args...
|
90
|
+
End
|
91
|
+
c.coffee('return test(1,2,3);').should == 'first,1,2,3'
|
92
|
+
|
93
|
+
# Real world example
|
94
|
+
c[:r3] = -> (*args) {
|
95
|
+
@last_args = args
|
96
|
+
}
|
97
|
+
c[:r4] = -> (first, second=0) {
|
98
|
+
[first+100, second]
|
99
|
+
}
|
100
|
+
script = <<-End
|
101
|
+
@r1 = (args...) ->
|
102
|
+
r3 'done', args[0..-2]...
|
103
|
+
@r2 = (args...) ->
|
104
|
+
r4 args...
|
105
|
+
End
|
106
|
+
c.coffee script
|
107
|
+
c.eval('r1( "now", 1, 2, 4);').should == ['done', 'now', 1, 2]
|
108
|
+
c.eval('r2( 100, 200);').should == [200, 200]
|
109
|
+
end
|
110
|
+
end
|
77
111
|
end
|
data/spec/ruby_gate_spec.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
require 'h8'
|
3
|
+
require 'ostruct'
|
4
|
+
require 'hashie'
|
3
5
|
|
4
6
|
describe 'ruby gate' do
|
5
7
|
|
@@ -173,7 +175,7 @@ describe 'ruby gate' do
|
|
173
175
|
end
|
174
176
|
|
175
177
|
class Test < Base
|
176
|
-
attr :ro
|
178
|
+
attr :ro, :val
|
177
179
|
attr_accessor :rw
|
178
180
|
|
179
181
|
def initialize
|
@@ -216,7 +218,7 @@ describe 'ruby gate' do
|
|
216
218
|
end
|
217
219
|
|
218
220
|
it 'should access object properties and methods' do
|
219
|
-
cxt
|
221
|
+
cxt = H8::Context.new
|
220
222
|
cxt.eval('RubyGate.prototype.test2 = function() { return "ttt"; }; null;');
|
221
223
|
cxt[:foo] = Test.new
|
222
224
|
cxt.eval('foo.ro').should == 'readonly'
|
@@ -231,19 +233,11 @@ describe 'ruby gate' do
|
|
231
233
|
cxt.eval('foo.object_id').should == H8::Undefined
|
232
234
|
cxt.eval('foo.prot_method').should == H8::Undefined
|
233
235
|
cxt.eval('foo.priv_method').should == H8::Undefined
|
234
|
-
cxt.eval('foo.test_args').should be_kind_of(
|
236
|
+
cxt.eval('foo.test_args').should be_kind_of(H8::ProcGate)
|
235
237
|
cxt.eval('foo.test_args("hi", "you")').should == 'hi-you'
|
236
238
|
cxt.eval('foo instanceof RubyGate').should == true
|
237
239
|
end
|
238
240
|
|
239
|
-
it 'should set ruby properties' do
|
240
|
-
cxt = H8::Context.new
|
241
|
-
cxt[:foo] = t = Test.new
|
242
|
-
cxt.eval('foo.rw="hello";')
|
243
|
-
t.rw.should == 'hello'
|
244
|
-
cxt.eval('foo.rw').should == 'hello'
|
245
|
-
end
|
246
|
-
|
247
241
|
context 'do interceptors' do
|
248
242
|
class Test2 < Base
|
249
243
|
attr :ro
|
@@ -280,7 +274,7 @@ describe 'ruby gate' do
|
|
280
274
|
cxt.eval('foo.send').should == H8::Undefined
|
281
275
|
cxt.eval('foo.prot_method').should == H8::Undefined
|
282
276
|
cxt.eval('foo.priv_method').should == H8::Undefined
|
283
|
-
cxt.eval('foo.test_args').should be_kind_of(
|
277
|
+
cxt.eval('foo.test_args').should be_kind_of(H8::ProcGate)
|
284
278
|
cxt.eval('foo.test_args("hi", "you")').should == 'hi-you'
|
285
279
|
end
|
286
280
|
|
@@ -294,15 +288,37 @@ describe 'ruby gate' do
|
|
294
288
|
end
|
295
289
|
|
296
290
|
it 'should add and intercept property access' do
|
297
|
-
# pending
|
298
291
|
t = Test.new
|
299
292
|
t.do_throw = true
|
300
293
|
cxt = H8::Context.new t: t
|
294
|
+
|
295
|
+
# see Test class implementation: this is a valid test
|
301
296
|
cxt.eval("t['foo'];").should == 'init[]'
|
297
|
+
cxt.eval("t.foo").should == 'init[]'
|
302
298
|
expect(-> { cxt.eval("t['foo1'];") }).to raise_error(RuntimeError)
|
303
|
-
cxt.eval("t
|
304
|
-
|
305
|
-
|
299
|
+
cxt.eval("t.foo='bar'");
|
300
|
+
cxt.eval("t.foo;").should == 'bar'
|
301
|
+
t.val.should == 'bar'
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'should allow adding data to ruby hash' do
|
305
|
+
[OpenStruct.new, Hashie::Mash.new].each { |s|
|
306
|
+
s.test = 'foo'
|
307
|
+
c = H8::Context.new
|
308
|
+
c[:data] = s
|
309
|
+
c[:assert] = -> (cond) { !cond and raise "assertion failed" }
|
310
|
+
c.coffee 'assert data.test == "foo"'
|
311
|
+
c.coffee 'data.test = "bar"; assert data.test == "bar"'
|
312
|
+
s.test.should == 'bar'
|
313
|
+
c.coffee 'data.foo = "baz"'
|
314
|
+
s.foo.should == 'baz'
|
315
|
+
c.coffee 'data.h = { foo: "bar", bar: { baz: 1 } }'
|
316
|
+
s.h.foo.should == 'bar'
|
317
|
+
c.coffee 'data.h.bar.baz == 1'
|
318
|
+
s.h.bar.baz.should == 1
|
319
|
+
c.coffee 'data.h.bar.arr = ["hello", { one: 2 }]'
|
320
|
+
s.to_ruby.should == { 'test' => "bar", 'foo' => "baz", 'h' => { "foo" => "bar", "bar" => { "baz" => 1, "arr" => ["hello", { "one" => 2 }] } } }
|
321
|
+
}
|
306
322
|
end
|
307
323
|
|
308
324
|
it 'should access plain arrays (provide numeric indexes)' do
|
@@ -341,9 +357,9 @@ describe 'ruby gate' do
|
|
341
357
|
end
|
342
358
|
|
343
359
|
it 'should gate classes through API' do
|
344
|
-
c
|
360
|
+
c = H8::Context.new
|
345
361
|
la = -> (*args) {
|
346
|
-
{ 'hello' => 'world'}
|
362
|
+
{ 'hello' => 'world' }
|
347
363
|
}
|
348
364
|
c._gate_class 'Tec', la
|
349
365
|
c.eval("var res = new Tec()")
|
@@ -372,6 +388,10 @@ describe 'ruby gate' do
|
|
372
388
|
self
|
373
389
|
end
|
374
390
|
|
391
|
+
def testm a1, a2='???'
|
392
|
+
"#{a1} - #{a2}"
|
393
|
+
end
|
394
|
+
|
375
395
|
def to_str
|
376
396
|
inspect
|
377
397
|
end
|
@@ -391,18 +411,24 @@ describe 'ruby gate' do
|
|
391
411
|
cxt.eval('rc.init_args').should == ['hello', 'world']
|
392
412
|
end
|
393
413
|
|
414
|
+
it 'should provide apply to gated class and instance' do
|
415
|
+
c = H8::Context.new RClass: Gated
|
416
|
+
c.eval('new RClass().testm(1,"d");').should == '1 - d'
|
417
|
+
c.eval('new RClass().testm.apply( null, [1,"n"]);').should == '1 - n'
|
418
|
+
end
|
419
|
+
|
394
420
|
it 'should not die on calling wrong arity' do
|
395
421
|
cxt = H8::Context.new RClass: Gated
|
396
|
-
g1
|
422
|
+
g1 = cxt.eval 'var g1 = new RClass(1,2.3); g1'
|
397
423
|
|
398
424
|
# We call gated ruby object with wrong number of args
|
399
425
|
# which in turn causes attempt to call not callable result:
|
400
|
-
expect(-> {cxt.eval('g1.checkself(12)')}).to raise_error(NoMethodError)
|
426
|
+
expect(-> { cxt.eval('g1.checkself(12)') }).to raise_error(NoMethodError)
|
401
427
|
end
|
402
428
|
|
403
429
|
it 'should return self from gated class' do
|
404
430
|
cxt = H8::Context.new RClass: Gated
|
405
|
-
g1
|
431
|
+
g1 = cxt.eval 'var g1 = new RClass(1,2.3); g1'
|
406
432
|
g1.should be_a(Gated)
|
407
433
|
g2 = cxt.eval 'g1.checkself'
|
408
434
|
g2.should be_a(Gated)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: h8
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sergeych
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: hashie
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 0.1.2
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.1.2
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: pargser
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|