delorean_lang 0.5.4 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -2
- data/Gemfile +0 -5
- data/README.md +4 -0
- data/delorean.gemspec +1 -0
- data/lib/delorean/base.rb +8 -7
- data/lib/delorean/const.rb +2 -3
- data/lib/delorean/engine.rb +1 -15
- data/lib/delorean/functions.rb +6 -11
- data/lib/delorean/nodes.rb +3 -3
- data/lib/delorean/ruby/whitelists/base.rb +42 -1
- data/lib/delorean/ruby/whitelists/matchers/arguments.rb +4 -0
- data/lib/delorean/ruby/whitelists/matchers/method.rb +10 -0
- data/lib/delorean/version.rb +1 -1
- data/spec/eval_spec.rb +77 -0
- data/spec/parse_spec.rb +15 -15
- data/spec/ruby/whitelist_spec.rb +107 -0
- data/spec/spec_helper.rb +79 -14
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2c6e040929f0803bd19bee9c0cd459a1a1bdcbdc20e99da3998a2e8c7f0ef1d3
|
4
|
+
data.tar.gz: 175ad860916a349b6a7bfe493e17a4e6c18a7eaaf3ce5625e7a71117f49581fa
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 51d8f5e885171313517eab09ea2536bfb807aae2fde1e450ccce5ffabf4b3250dfebd84384f32a67856d6eaa00fc8b8c56cd15ec2c9d7b07f7a48e84495e994d
|
7
|
+
data.tar.gz: 9896fef50b9852c7347048e5db5d4bb4412a78beb218d46117b0c805e8a9eccc3cb63aada6b9a04abf042d0357ea2e05977b9ab25e8f46acd662ef283c24b44e
|
data/.rubocop.yml
CHANGED
@@ -17,8 +17,7 @@ Style/StringLiterals:
|
|
17
17
|
Metrics/LineLength:
|
18
18
|
Max: 80
|
19
19
|
Exclude:
|
20
|
-
- 'spec
|
21
|
-
- 'spec/parse_spec.rb'
|
20
|
+
- 'spec/**/*'
|
22
21
|
|
23
22
|
Metrics/ModuleLength:
|
24
23
|
Max: 100
|
@@ -78,3 +77,7 @@ Naming/HeredocDelimiterCase:
|
|
78
77
|
|
79
78
|
Naming/RescuedExceptionsVariableName:
|
80
79
|
Enabled: false
|
80
|
+
|
81
|
+
Metrics/ParameterLists:
|
82
|
+
Exclude:
|
83
|
+
- 'spec/spec_helper.rb'
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -172,6 +172,10 @@ There are two ways of calling ruby code from delorean. First one is to whitelist
|
|
172
172
|
method.called_on Enumerable, with: [Integer]
|
173
173
|
end
|
174
174
|
|
175
|
+
::Delorean::Ruby.whitelist.add_class_method :last do |method|
|
176
|
+
method.called_on ActiveRecord::Base, with: [Integer]
|
177
|
+
end
|
178
|
+
|
175
179
|
```
|
176
180
|
|
177
181
|
By default Delorean has some methods whitelisted, such as `length`, `min`, `max`, etc. Those can be found in `/lib/delorean/ruby/whitelists/default`. If you don't want to use defaults, you can override whitelist with and empty one.
|
data/delorean.gemspec
CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.add_dependency 'treetop', '~> 1.5'
|
22
22
|
gem.add_development_dependency 'pry'
|
23
23
|
gem.add_development_dependency 'rspec', '~> 2.1'
|
24
|
+
gem.add_development_dependency 'rspec-instafail'
|
24
25
|
gem.add_development_dependency 'rubocop'
|
25
26
|
gem.add_development_dependency 'rubocop-performance'
|
26
27
|
gem.add_development_dependency 'sqlite3', '~> 1.3.10'
|
data/lib/delorean/base.rb
CHANGED
@@ -160,15 +160,16 @@ module Delorean
|
|
160
160
|
raise "bad method #{method}"
|
161
161
|
end
|
162
162
|
|
163
|
-
# FIXME: this is pretty hacky -- should probably merge
|
164
|
-
# whitelist and SIG mechanisms.
|
165
163
|
if obj.is_a?(Class) || obj.is_a?(Module)
|
166
|
-
|
167
|
-
|
164
|
+
matcher = ::Delorean::Ruby.whitelist.class_method_matcher(
|
165
|
+
method_name: msg
|
166
|
+
)
|
167
|
+
klass = obj
|
168
|
+
else
|
169
|
+
matcher = ::Delorean::Ruby.whitelist.matcher(method_name: msg)
|
170
|
+
klass = obj.class
|
168
171
|
end
|
169
172
|
|
170
|
-
matcher = ::Delorean::Ruby.whitelist.matcher(method_name: msg)
|
171
|
-
|
172
173
|
raise "no such method #{method}" unless matcher
|
173
174
|
|
174
175
|
if matcher.match_to?
|
@@ -177,7 +178,7 @@ module Delorean
|
|
177
178
|
)
|
178
179
|
end
|
179
180
|
|
180
|
-
matcher.match!(klass:
|
181
|
+
matcher.match!(klass: klass, args: args)
|
181
182
|
|
182
183
|
obj.send(msg, *args)
|
183
184
|
end
|
data/lib/delorean/const.rb
CHANGED
data/lib/delorean/engine.rb
CHANGED
@@ -202,7 +202,7 @@ module Delorean
|
|
202
202
|
raise exc.new(msg, @module_name, curr_line)
|
203
203
|
end
|
204
204
|
|
205
|
-
def parse_check_call_fn(fn,
|
205
|
+
def parse_check_call_fn(fn, _argcount, class_name = nil)
|
206
206
|
klass = case class_name
|
207
207
|
when nil
|
208
208
|
@m::BaseClass
|
@@ -214,20 +214,6 @@ module Delorean
|
|
214
214
|
|
215
215
|
err(UndefinedFunctionError, "Function #{fn} not found") unless
|
216
216
|
klass.methods.member? fn.to_sym
|
217
|
-
|
218
|
-
# signature methods must be named FUNCTION_NAME_SIG
|
219
|
-
sig = "#{fn}#{SIG}".upcase.to_sym
|
220
|
-
|
221
|
-
err(UndefinedFunctionError, "Signature #{sig} not found") unless
|
222
|
-
klass.constants.member? sig
|
223
|
-
|
224
|
-
min, max = klass.const_get(sig)
|
225
|
-
|
226
|
-
err(BadCallError, "Too many args to #{fn} (#{argcount} > #{max})") if
|
227
|
-
argcount > max
|
228
|
-
|
229
|
-
err(BadCallError, "Too few args to #{fn} (#{argcount} < #{min})") if
|
230
|
-
argcount < min
|
231
217
|
end
|
232
218
|
|
233
219
|
def parser
|
data/lib/delorean/functions.rb
CHANGED
@@ -2,19 +2,14 @@
|
|
2
2
|
|
3
3
|
module Delorean
|
4
4
|
module Functions
|
5
|
-
def delorean_fn(name,
|
6
|
-
|
7
|
-
yield(*args)
|
8
|
-
end
|
9
|
-
|
10
|
-
sig = options[:sig]
|
5
|
+
def delorean_fn(name, _options = {}, &block)
|
6
|
+
any_args = Delorean::Ruby::Whitelists::Matchers::Arguments::ANYTHING
|
11
7
|
|
12
|
-
|
8
|
+
define_singleton_method(name, block)
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
const_set(name.to_s.upcase + Delorean::SIG, sig)
|
10
|
+
::Delorean::Ruby.whitelist.add_class_method name do |method|
|
11
|
+
method.called_on self, with: any_args
|
12
|
+
end
|
18
13
|
end
|
19
14
|
|
20
15
|
# FIXME: IDEA: we just make :cache an argument to delorean_fn.
|
data/lib/delorean/nodes.rb
CHANGED
@@ -400,13 +400,13 @@ eos
|
|
400
400
|
end
|
401
401
|
|
402
402
|
if vcode.is_a?(ClassText)
|
403
|
+
# FIXME: Do we really need this check here?
|
403
404
|
# ruby class call
|
404
405
|
class_name = vcode.text
|
405
406
|
context.parse_check_call_fn(i.text_value, arg_count, class_name)
|
406
|
-
"#{class_name}.#{i.text_value}(#{args_str})"
|
407
|
-
else
|
408
|
-
"_instance_call(#{vcode}, '#{i.text_value}', [#{args_str}], _e)"
|
409
407
|
end
|
408
|
+
|
409
|
+
"_instance_call(#{vcode}, '#{i.text_value}', [#{args_str}], _e)"
|
410
410
|
end
|
411
411
|
end
|
412
412
|
|
@@ -8,6 +8,7 @@ module Delorean
|
|
8
8
|
module Whitelists
|
9
9
|
class Base
|
10
10
|
attr_reader :matchers
|
11
|
+
attr_reader :class_method_matchers
|
11
12
|
|
12
13
|
def add_method(method_name, match_to: nil, &block)
|
13
14
|
return method_name_error unless method_name.is_a?(Symbol)
|
@@ -19,19 +20,48 @@ module Delorean
|
|
19
20
|
)
|
20
21
|
end
|
21
22
|
|
23
|
+
matcher = matchers[method_name.to_sym]
|
24
|
+
|
25
|
+
return matcher.extend_matcher(&block) if matcher
|
26
|
+
|
22
27
|
matchers[method_name.to_sym] = method_matcher_class.new(
|
23
28
|
method_name: method_name, &block
|
24
29
|
)
|
25
30
|
end
|
26
31
|
|
32
|
+
def add_class_method(method_name, match_to: nil, &block)
|
33
|
+
return method_name_error unless method_name.is_a?(Symbol)
|
34
|
+
return block_and_match_error if !match_to.nil? && block_given?
|
35
|
+
|
36
|
+
unless match_to.nil?
|
37
|
+
return add_class_matched_method(
|
38
|
+
method_name: method_name, match_to: match_to
|
39
|
+
)
|
40
|
+
end
|
41
|
+
|
42
|
+
matcher = class_method_matchers[method_name.to_sym]
|
43
|
+
|
44
|
+
return matcher.extend_matcher(&block) if matcher
|
45
|
+
|
46
|
+
class_method_matchers[method_name.to_sym] = method_matcher_class.new(
|
47
|
+
method_name: method_name, &block
|
48
|
+
)
|
49
|
+
end
|
50
|
+
|
27
51
|
def matcher(method_name:)
|
28
52
|
matchers[method_name.to_sym]
|
29
53
|
end
|
30
54
|
|
55
|
+
def class_method_matcher(method_name:)
|
56
|
+
class_method_matchers[method_name.to_sym]
|
57
|
+
end
|
58
|
+
|
31
59
|
private
|
32
60
|
|
33
61
|
def initialize
|
34
62
|
@matchers = {}
|
63
|
+
@class_method_matchers = {}
|
64
|
+
|
35
65
|
initialize_hook
|
36
66
|
end
|
37
67
|
|
@@ -39,13 +69,24 @@ module Delorean
|
|
39
69
|
correct_match_to = match_to.is_a?(String) || match_to.is_a?(Symbol)
|
40
70
|
return wrong_match_to_error unless correct_match_to
|
41
71
|
|
42
|
-
matcher =
|
72
|
+
matcher = method_matcher_class.new(
|
43
73
|
method_name: method_name, match_to: match_to.to_sym
|
44
74
|
)
|
45
75
|
|
46
76
|
matchers[method_name.to_sym] = matcher
|
47
77
|
end
|
48
78
|
|
79
|
+
def add_class_matched_method(method_name:, match_to:)
|
80
|
+
correct_match_to = match_to.is_a?(String) || match_to.is_a?(Symbol)
|
81
|
+
return wrong_match_to_error unless correct_match_to
|
82
|
+
|
83
|
+
matcher = method_matcher_class.new(
|
84
|
+
method_name: method_name, match_to: match_to.to_sym
|
85
|
+
)
|
86
|
+
|
87
|
+
class_method_matchers[method_name.to_sym] = matcher
|
88
|
+
end
|
89
|
+
|
49
90
|
def block_and_match_error
|
50
91
|
raise(
|
51
92
|
::Delorean::Ruby::Whitelists::WhitelistError,
|
@@ -5,6 +5,8 @@ module Delorean
|
|
5
5
|
module Whitelists
|
6
6
|
module Matchers
|
7
7
|
class Arguments
|
8
|
+
ANYTHING = :_delorean_anything
|
9
|
+
|
8
10
|
attr_reader :called_on, :method_name, :with
|
9
11
|
|
10
12
|
def initialize(called_on:, method_name:, with: [])
|
@@ -14,6 +16,8 @@ module Delorean
|
|
14
16
|
end
|
15
17
|
|
16
18
|
def match!(args:)
|
19
|
+
return if with == ANYTHING
|
20
|
+
|
17
21
|
raise "too many args to #{method_name}" if args.size > with.size
|
18
22
|
|
19
23
|
with.each_with_index do |s, i|
|
@@ -23,6 +23,12 @@ module Delorean
|
|
23
23
|
)
|
24
24
|
|
25
25
|
arguments_matchers << matcher
|
26
|
+
|
27
|
+
# Sort matchers by reversed ancestors chain length, so
|
28
|
+
# matcher method would find the closest ancestor in hierarchy
|
29
|
+
arguments_matchers.sort_by! do |obj|
|
30
|
+
-obj.called_on.ancestors.size
|
31
|
+
end
|
26
32
|
end
|
27
33
|
|
28
34
|
def matcher(klass:)
|
@@ -42,6 +48,10 @@ module Delorean
|
|
42
48
|
def match_to?
|
43
49
|
!match_to.nil?
|
44
50
|
end
|
51
|
+
|
52
|
+
def extend_matcher
|
53
|
+
yield self if block_given?
|
54
|
+
end
|
45
55
|
end
|
46
56
|
end
|
47
57
|
end
|
data/lib/delorean/version.rb
CHANGED
data/spec/eval_spec.rb
CHANGED
@@ -371,6 +371,83 @@ eoc
|
|
371
371
|
r.should == 867 + 5309
|
372
372
|
end
|
373
373
|
|
374
|
+
it 'should be able call method defined in a parent or matched to' do
|
375
|
+
engine.parse defn(
|
376
|
+
'A:',
|
377
|
+
' b = DeloreanFunctionsChildClass.test_fn',
|
378
|
+
' c = DeloreanFunctionsChildClass.test_fn2',
|
379
|
+
' d = DifferentClassSameMethod.test_fn2',
|
380
|
+
' e = DifferentClassSameMethod.match_to_test_fn2',
|
381
|
+
)
|
382
|
+
|
383
|
+
r = engine.evaluate('A', 'b')
|
384
|
+
r.should == :test_fn_result
|
385
|
+
|
386
|
+
r = engine.evaluate('A', 'c')
|
387
|
+
r.should == :test_fn2_result
|
388
|
+
|
389
|
+
r = engine.evaluate('A', 'd')
|
390
|
+
r.should == :test_fn2_result_different
|
391
|
+
|
392
|
+
r = engine.evaluate('A', 'e')
|
393
|
+
r.should == :test_fn2_result_different
|
394
|
+
end
|
395
|
+
|
396
|
+
it 'should raise exception if method is not whitelisted' do
|
397
|
+
engine.parse defn(
|
398
|
+
'A:',
|
399
|
+
' a = DeloreanFunctionsChildClass.test_fn4',
|
400
|
+
' b = DeloreanFunctionsChildClass.test_fn4()',
|
401
|
+
' c = Dummy.this_is_crazy()',
|
402
|
+
)
|
403
|
+
|
404
|
+
lambda {
|
405
|
+
engine.evaluate('A', 'a')
|
406
|
+
}.should raise_error(
|
407
|
+
Delorean::InvalidGetAttribute,
|
408
|
+
"attr lookup failed: 'test_fn4' on <Class> DeloreanFunctionsChildClass - no such method test_fn4"
|
409
|
+
)
|
410
|
+
|
411
|
+
lambda {
|
412
|
+
engine.evaluate('A', 'b')
|
413
|
+
}.should raise_error(RuntimeError, 'no such method test_fn4')
|
414
|
+
|
415
|
+
lambda {
|
416
|
+
engine.evaluate('A', 'c')
|
417
|
+
}.should raise_error(RuntimeError, 'no such method this_is_crazy')
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'should raise exception if required arguments are missing' do
|
421
|
+
engine.parse defn(
|
422
|
+
'A:',
|
423
|
+
' a = DifferentClassSameMethod.test_fn3(1, 2, 3, 4, 5,
|
424
|
+
6, 7, 8, 9, 10)',
|
425
|
+
' b = DifferentClassSameMethod.test_fn3(1, 2, 3, 4) ',
|
426
|
+
' c = DifferentClassSameMethod.test_fn3(1, 2) ',
|
427
|
+
' d = DifferentClassSameMethod.test_fn3() ',
|
428
|
+
)
|
429
|
+
|
430
|
+
r = engine.evaluate('A', 'a')
|
431
|
+
r.should == { a: 1, b: 2, c: 3, d: 4, e: 5, rest: [6, 7, 8, 9, 10] }
|
432
|
+
|
433
|
+
r = engine.evaluate('A', 'b')
|
434
|
+
r.should == { a: 1, b: 2, c: 3, d: 4, e: nil, rest: [] }
|
435
|
+
|
436
|
+
lambda {
|
437
|
+
r = engine.evaluate('A', 'c')
|
438
|
+
}.should raise_error(
|
439
|
+
ArgumentError,
|
440
|
+
'wrong number of arguments (given 2, expected 3+)'
|
441
|
+
)
|
442
|
+
|
443
|
+
lambda {
|
444
|
+
r = engine.evaluate('A', 'd')
|
445
|
+
}.should raise_error(
|
446
|
+
ArgumentError,
|
447
|
+
'wrong number of arguments (given 0, expected 3+)'
|
448
|
+
)
|
449
|
+
end
|
450
|
+
|
374
451
|
it 'should ignore undeclared params sent to eval which match attr names' do
|
375
452
|
engine.parse defn('A:',
|
376
453
|
' d = 12',
|
data/spec/parse_spec.rb
CHANGED
@@ -393,21 +393,21 @@ describe 'Delorean' do
|
|
393
393
|
)
|
394
394
|
end
|
395
395
|
|
396
|
-
it 'should get exception on arg count to class method call' do
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
end
|
403
|
-
|
404
|
-
it "shouldn't be able to call ActiveRecord methods without signature" do
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
end
|
396
|
+
# it 'should get exception on arg count to class method call' do
|
397
|
+
# lambda {
|
398
|
+
# engine.parse defn('A:',
|
399
|
+
# ' b = Dummy.i_just_met_you(1, 2, 3)',
|
400
|
+
# )
|
401
|
+
# }.should raise_error(Delorean::BadCallError)
|
402
|
+
# end
|
403
|
+
#
|
404
|
+
# it "shouldn't be able to call ActiveRecord methods without signature" do
|
405
|
+
# lambda {
|
406
|
+
# engine.parse defn('A:',
|
407
|
+
# ' b = Dummy.this_is_crazy()',
|
408
|
+
# )
|
409
|
+
# }.should raise_error(Delorean::UndefinedFunctionError)
|
410
|
+
# end
|
411
411
|
|
412
412
|
it 'should be able to call class methods on ActiveRecord classes in modules' do
|
413
413
|
engine.parse defn('A:',
|
data/spec/ruby/whitelist_spec.rb
CHANGED
@@ -5,11 +5,14 @@ require 'delorean/ruby/whitelists/empty'
|
|
5
5
|
|
6
6
|
describe 'Delorean Ruby whitelisting' do
|
7
7
|
it 'allows to override whitelist with an empty one' do
|
8
|
+
old_whitelist = ::Delorean::Ruby.whitelist
|
8
9
|
::Delorean::Ruby.whitelist = whitelist
|
9
10
|
expect(whitelist.matchers).to be_empty
|
10
11
|
|
11
12
|
::Delorean::Ruby.whitelist = ::Delorean::Ruby::Whitelists::Default.new
|
12
13
|
expect(::Delorean::Ruby.whitelist.matchers).to_not be_empty
|
14
|
+
|
15
|
+
::Delorean::Ruby.whitelist = old_whitelist
|
13
16
|
end
|
14
17
|
|
15
18
|
let(:whitelist) { ::Delorean::Ruby::Whitelists::Empty.new }
|
@@ -76,4 +79,108 @@ describe 'Delorean Ruby whitelisting' do
|
|
76
79
|
expect(matcher.match_to?).to be true
|
77
80
|
end
|
78
81
|
end
|
82
|
+
|
83
|
+
describe 'class_methods' do
|
84
|
+
let(:method_matcher) do
|
85
|
+
Delorean::Ruby.whitelist.class_method_matcher(method_name: :test_method)
|
86
|
+
end
|
87
|
+
|
88
|
+
let(:engine) do
|
89
|
+
engine = Delorean::Engine.new 'XXX'
|
90
|
+
|
91
|
+
engine.parse defn(
|
92
|
+
'A:',
|
93
|
+
' a = RootClass.test_method(1)',
|
94
|
+
' b = RootClass.test_method(true)',
|
95
|
+
' c = RootClassChild.test_method("test")',
|
96
|
+
' d = RootClassChild.test_method(1)',
|
97
|
+
' e = RootClassChildsChild.test_method(true)',
|
98
|
+
' f = RootClassChildsChild.test_method(1)',
|
99
|
+
' g = RootClassChildsChildsChild.test_method(true)',
|
100
|
+
' h = RootClassChildsChildsChild.test_method(1)',
|
101
|
+
' i = RootClassChildsChildsChild.test_method()',
|
102
|
+
' j = RootClassChildsChildsChild.test_method',
|
103
|
+
' k = RootClassChildsChildsChild.test_method(true, true)',
|
104
|
+
' l = RootClassChildsChildsChild.test_method2(true)'
|
105
|
+
)
|
106
|
+
|
107
|
+
engine
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'fetches the closest method matcher in class hierarchy' do
|
111
|
+
arg_matcher = method_matcher.matcher(klass: RootClass)
|
112
|
+
expect(arg_matcher.with).to eq [Integer]
|
113
|
+
|
114
|
+
arg_matcher = method_matcher.matcher(klass: RootClassChild)
|
115
|
+
expect(arg_matcher.with).to eq [String]
|
116
|
+
|
117
|
+
arg_matcher = method_matcher.matcher(klass: RootClassChildsChild)
|
118
|
+
expect(arg_matcher.with).to eq [TrueClass]
|
119
|
+
|
120
|
+
arg_matcher = method_matcher.matcher(klass: RootClassChildsChildsChild)
|
121
|
+
expect(arg_matcher.with).to eq [TrueClass]
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'allows to call methods correctly' do
|
125
|
+
r = engine.evaluate('A', 'a')
|
126
|
+
expect(r).to eq :test_method_with_int_arg
|
127
|
+
|
128
|
+
r = engine.evaluate('A', 'c')
|
129
|
+
expect(r).to eq :test_method_with_str_arg
|
130
|
+
|
131
|
+
r = engine.evaluate('A', 'e')
|
132
|
+
expect(r).to eq :test_method_with_true_arg
|
133
|
+
|
134
|
+
r = engine.evaluate('A', 'g')
|
135
|
+
expect(r).to eq :test_method_with_true_arg
|
136
|
+
end
|
137
|
+
|
138
|
+
it 'raises exception if argument type mismatched' do
|
139
|
+
expect { engine.evaluate('A', 'b') }.to raise_error(
|
140
|
+
RuntimeError,
|
141
|
+
'bad arg 0, method test_method: true/TrueClass [Integer]'
|
142
|
+
)
|
143
|
+
|
144
|
+
expect { engine.evaluate('A', 'd') }.to raise_error(
|
145
|
+
RuntimeError,
|
146
|
+
'bad arg 0, method test_method: 1/Integer [String]'
|
147
|
+
)
|
148
|
+
|
149
|
+
expect { engine.evaluate('A', 'f') }.to raise_error(
|
150
|
+
RuntimeError,
|
151
|
+
'bad arg 0, method test_method: 1/Integer [TrueClass]'
|
152
|
+
)
|
153
|
+
|
154
|
+
expect { engine.evaluate('A', 'h') }.to raise_error(
|
155
|
+
RuntimeError,
|
156
|
+
'bad arg 0, method test_method: 1/Integer [TrueClass]'
|
157
|
+
)
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'raises exception if argument is not present' do
|
161
|
+
expect { engine.evaluate('A', 'i') }.to raise_error(
|
162
|
+
RuntimeError,
|
163
|
+
'bad arg 0, method test_method: /NilClass [TrueClass]'
|
164
|
+
)
|
165
|
+
|
166
|
+
expect { engine.evaluate('A', 'j') }.to raise_error(
|
167
|
+
Delorean::InvalidGetAttribute,
|
168
|
+
"attr lookup failed: 'test_method' on <Class> RootClassChildsChildsChild - bad arg 0, method test_method: /NilClass [TrueClass]"
|
169
|
+
)
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'raises exception if too many arguments' do
|
173
|
+
expect { engine.evaluate('A', 'k') }.to raise_error(
|
174
|
+
RuntimeError,
|
175
|
+
'too many args to test_method'
|
176
|
+
)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'raises exception method is not whitelisted' do
|
180
|
+
expect { engine.evaluate('A', 'l') }.to raise_error(
|
181
|
+
RuntimeError,
|
182
|
+
'no such method test_method2'
|
183
|
+
)
|
184
|
+
end
|
185
|
+
end
|
79
186
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,10 +3,10 @@
|
|
3
3
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
4
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
5
|
|
6
|
+
require 'pry'
|
6
7
|
require 'rspec'
|
7
8
|
require 'delorean_lang'
|
8
9
|
require 'active_record'
|
9
|
-
require 'pry'
|
10
10
|
|
11
11
|
# Requires supporting files with custom matchers and macros, etc,
|
12
12
|
# in ./support/ and its subdirectories.
|
@@ -36,18 +36,14 @@ class Dummy < ActiveRecord::Base
|
|
36
36
|
|
37
37
|
belongs_to :dummy
|
38
38
|
|
39
|
-
|
39
|
+
delorean_fn :i_just_met_you do |name, number|
|
40
40
|
Dummy.new(name: name, number: number)
|
41
41
|
end
|
42
42
|
|
43
|
-
|
44
|
-
|
45
|
-
def self.call_me_maybe(*a)
|
43
|
+
delorean_fn :call_me_maybe do |*a|
|
46
44
|
a.inject(0, :+)
|
47
45
|
end
|
48
46
|
|
49
|
-
CALL_ME_MAYBE_SIG = [0, Float::INFINITY].freeze
|
50
|
-
|
51
47
|
def self.this_is_crazy; end
|
52
48
|
|
53
49
|
def self.miss_you_so_bad
|
@@ -55,18 +51,14 @@ class Dummy < ActiveRecord::Base
|
|
55
51
|
Dummy.new(name: 'jello', number: 456, dummy: d)
|
56
52
|
end
|
57
53
|
|
58
|
-
MISS_YOU_SO_BAD_SIG = [0, 0].freeze
|
59
|
-
|
60
54
|
delorean_fn :all_of_me, sig: 0 do
|
61
55
|
[{ 'name' => 'hello', 'foo' => 'bar' }]
|
62
56
|
end
|
63
57
|
|
64
|
-
|
58
|
+
delorean_fn :i_threw_a_hash_in_the_well do
|
65
59
|
{ a: 123, 'a' => 456, b: 789 }
|
66
60
|
end
|
67
61
|
|
68
|
-
I_THREW_A_HASH_IN_THE_WELL_SIG = [0, 0].freeze
|
69
|
-
|
70
62
|
def name2
|
71
63
|
"#{name}-#{number.round(4)}"
|
72
64
|
end
|
@@ -96,7 +88,6 @@ class DummyChild < Dummy
|
|
96
88
|
def self.hello
|
97
89
|
DummyChild.new(name: 'child', number: 99_999)
|
98
90
|
end
|
99
|
-
HELLO_SIG = [0, 0].freeze
|
100
91
|
end
|
101
92
|
|
102
93
|
module M
|
@@ -106,10 +97,10 @@ module M
|
|
106
97
|
delorean_fn(:heres_my_number, sig: [0, Float::INFINITY]) do |*a|
|
107
98
|
a.inject(0, :+)
|
108
99
|
end
|
100
|
+
|
109
101
|
def self.sup
|
110
102
|
LittleDummy.new
|
111
103
|
end
|
112
|
-
SUP_SIG = [0, 0].freeze
|
113
104
|
end
|
114
105
|
|
115
106
|
module N
|
@@ -134,6 +125,80 @@ module M
|
|
134
125
|
DummyModule = ::DummyModule
|
135
126
|
end
|
136
127
|
|
128
|
+
class DeloreanFunctionsClass
|
129
|
+
extend Delorean::Functions
|
130
|
+
|
131
|
+
delorean_fn :test_fn, sig: 0 do
|
132
|
+
:test_fn_result
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class DeloreanFunctionsChildClass < DeloreanFunctionsClass
|
137
|
+
delorean_fn :test_fn2, sig: 0 do
|
138
|
+
:test_fn2_result
|
139
|
+
end
|
140
|
+
|
141
|
+
def self.test_fn4; end
|
142
|
+
end
|
143
|
+
|
144
|
+
class DifferentClassSameMethod
|
145
|
+
extend Delorean::Functions
|
146
|
+
|
147
|
+
delorean_fn :test_fn2, sig: 0 do
|
148
|
+
:test_fn2_result_different
|
149
|
+
end
|
150
|
+
|
151
|
+
delorean_fn :test_fn3, sig: 0 do |a, b, c, d = :default, e = nil, *args|
|
152
|
+
{
|
153
|
+
a: a,
|
154
|
+
b: b,
|
155
|
+
c: c,
|
156
|
+
d: d,
|
157
|
+
e: e,
|
158
|
+
rest: args
|
159
|
+
}
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
class RootClass
|
164
|
+
def self.test_method(_int_arg)
|
165
|
+
:test_method_with_int_arg
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
Delorean::Ruby.whitelist.add_class_method :test_method do |method|
|
170
|
+
method.called_on RootClass, with: [Integer]
|
171
|
+
end
|
172
|
+
|
173
|
+
class RootClassChild < RootClass
|
174
|
+
def self.test_method(_str_arg)
|
175
|
+
:test_method_with_str_arg
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
Delorean::Ruby.whitelist.add_class_method :test_method do |method|
|
180
|
+
method.called_on RootClassChild, with: [String]
|
181
|
+
end
|
182
|
+
|
183
|
+
class RootClassChildsChild < RootClassChild
|
184
|
+
def self.test_method(_true_arg)
|
185
|
+
:test_method_with_true_arg
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
Delorean::Ruby.whitelist.add_class_method :test_method do |method|
|
190
|
+
method.called_on RootClassChildsChild, with: [TrueClass]
|
191
|
+
end
|
192
|
+
|
193
|
+
class RootClassChildsChildsChild < RootClassChildsChild
|
194
|
+
def self.test_method2(bool_arg); end
|
195
|
+
end
|
196
|
+
|
197
|
+
Delorean::Ruby.whitelist.add_class_method(
|
198
|
+
:match_to_test_fn2,
|
199
|
+
match_to: :test_fn2
|
200
|
+
)
|
201
|
+
|
137
202
|
######################################################################
|
138
203
|
|
139
204
|
class TestContainer < Delorean::AbstractContainer
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: delorean_lang
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arman Bostani
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-05-
|
11
|
+
date: 2019-05-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '2.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-instafail
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: rubocop
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|