delorean_lang 0.5.4 → 0.6.0
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.
- 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
|