delorean_lang 0.4.7 → 0.4.8
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/README.md +27 -0
- data/lib/delorean/abstract_container.rb +9 -0
- data/lib/delorean/base.rb +11 -98
- data/lib/delorean/model.rb +4 -4
- data/lib/delorean/ruby.rb +13 -0
- data/lib/delorean/ruby/whitelists.rb +8 -0
- data/lib/delorean/ruby/whitelists/base.rb +72 -0
- data/lib/delorean/ruby/whitelists/default.rb +304 -0
- data/lib/delorean/ruby/whitelists/empty.rb +12 -0
- data/lib/delorean/ruby/whitelists/matchers.rb +10 -0
- data/lib/delorean/ruby/whitelists/matchers/arguments.rb +47 -0
- data/lib/delorean/ruby/whitelists/matchers/method.rb +46 -0
- data/lib/delorean/ruby/whitelists/whitelist_error.rb +8 -0
- data/lib/delorean/version.rb +1 -1
- data/lib/delorean_lang.rb +6 -5
- data/spec/cache_spec.rb +7 -7
- data/spec/func_spec.rb +14 -2
- data/spec/ruby/whitelist_spec.rb +76 -0
- data/spec/spec_helper.rb +5 -6
- metadata +14 -3
- data/lib/delorean/container.rb +0 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3a6eb15c510767db6fe200846f73c99332074552
|
4
|
+
data.tar.gz: a088e3928a4646f276f23f144e05fb1ed5d2717b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 542634f338cb3af160f4a333548cbb65bd98337d6ea5cce30176c27dd7c574bdb4ce84bb0e5e5f0a4160e55a5ba01fa52533e4b41c4cb87d6bcec5d7949413d9
|
7
|
+
data.tar.gz: 6f13545b0fe049fd9d8ee461eab5047c8f1b0d446207751f0c10f3a10dcdcc89778f18045953e4394bce159a23122470de269b80682fea0fa50814f31d6f6478
|
data/README.md
CHANGED
@@ -135,6 +135,33 @@ TODO: provide details on the following topics:
|
|
135
135
|
This implementation of Delorean "compiles" script code to
|
136
136
|
Ruby.
|
137
137
|
|
138
|
+
### Calling ruby methods from Delorean
|
139
|
+
|
140
|
+
Ruby methods that are called from Delorean should be whitelisted.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
|
144
|
+
::Delorean::Ruby.whitelist.add_method :length do |method|
|
145
|
+
method.called_on String
|
146
|
+
method.called_on Enumerable
|
147
|
+
end
|
148
|
+
|
149
|
+
::Delorean::Ruby.whitelist.add_method :first do |method|
|
150
|
+
method.called_on Enumerable, with: [Integer]
|
151
|
+
end
|
152
|
+
|
153
|
+
```
|
154
|
+
|
155
|
+
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.
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
|
159
|
+
require 'delorean/ruby/whitelists/empty'
|
160
|
+
|
161
|
+
::Delorean::Ruby.whitelist = ::Delorean::Ruby::Whitelists::Empty.new
|
162
|
+
|
163
|
+
```
|
164
|
+
|
138
165
|
### Caching
|
139
166
|
|
140
167
|
Delorean provides `cached_delorean_function` method that will cache result based on arguments.
|
data/lib/delorean/base.rb
CHANGED
@@ -1,92 +1,15 @@
|
|
1
1
|
require 'active_support/time'
|
2
2
|
require 'active_record'
|
3
3
|
require 'bigdecimal'
|
4
|
+
require 'delorean/ruby'
|
5
|
+
require 'delorean/ruby/whitelists/default'
|
4
6
|
require 'delorean/cache'
|
5
7
|
|
6
8
|
module Delorean
|
7
9
|
|
8
|
-
::Delorean::
|
10
|
+
::Delorean::Ruby.whitelist = ::Delorean::Ruby::Whitelists::Default.new
|
9
11
|
|
10
|
-
|
11
|
-
DT_TYPES = [Date] + TI_TYPES
|
12
|
-
NUM_OR_STR = [Numeric, String]
|
13
|
-
NUM_OR_NIL = [nil, Integer]
|
14
|
-
|
15
|
-
# FIXME: the whitelist is quite hacky. It's currently difficult to
|
16
|
-
# override it. A user will likely want to directly modify this
|
17
|
-
# hash. The whole whitelist mechanism should be eventually
|
18
|
-
# rethought.
|
19
|
-
RUBY_WHITELIST = {
|
20
|
-
# FIXME: hack -- Relation.attributes currently implemented in marty
|
21
|
-
attributes: [[ActiveRecord::Base, ActiveRecord::Relation]],
|
22
|
-
between?: [NUM_OR_STR, NUM_OR_STR, NUM_OR_STR],
|
23
|
-
between: "between?",
|
24
|
-
compact: [[Array, Hash]],
|
25
|
-
to_set: [Array],
|
26
|
-
flatten: [Array, NUM_OR_NIL],
|
27
|
-
length: [[String, Enumerable]],
|
28
|
-
max: [Array],
|
29
|
-
member: "member?",
|
30
|
-
member?: [Enumerable, [Object]],
|
31
|
-
empty: "empty?",
|
32
|
-
empty?: [Enumerable],
|
33
|
-
except: [Hash, String] + [[nil, String]]*9,
|
34
|
-
reverse: [Array],
|
35
|
-
slice: [Array, Integer, Integer],
|
36
|
-
each_slice: [Array, Integer],
|
37
|
-
sort: [Array],
|
38
|
-
split: [String, String],
|
39
|
-
uniq: [Array],
|
40
|
-
sum: [Array],
|
41
|
-
transpose: [Array],
|
42
|
-
join: [Array, String],
|
43
|
-
zip: [Array, Array, [Array, nil], [Array, nil]],
|
44
|
-
index: [Array, [Object]],
|
45
|
-
product: [Array, Array],
|
46
|
-
first: [[ActiveRecord::Relation, Enumerable], NUM_OR_NIL],
|
47
|
-
last: [[ActiveRecord::Relation, Enumerable], NUM_OR_NIL],
|
48
|
-
intersection: [Set, Enumerable],
|
49
|
-
union: [Set, Enumerable],
|
50
|
-
|
51
|
-
keys: [Hash],
|
52
|
-
values: [Hash],
|
53
|
-
fetch: [Hash, Object, [Object]],
|
54
|
-
upcase: [String],
|
55
|
-
downcase: [String],
|
56
|
-
match: [String, [String], NUM_OR_NIL],
|
57
|
-
|
58
|
-
iso8601: [DT_TYPES],
|
59
|
-
hour: [DT_TYPES],
|
60
|
-
min: [DT_TYPES+[Array]],
|
61
|
-
sec: [DT_TYPES],
|
62
|
-
to_date: [DT_TYPES+[String]],
|
63
|
-
to_time: [DT_TYPES+[String]],
|
64
|
-
|
65
|
-
month: [DT_TYPES],
|
66
|
-
day: [DT_TYPES],
|
67
|
-
year: [DT_TYPES],
|
68
|
-
|
69
|
-
next_month: [DT_TYPES, NUM_OR_NIL],
|
70
|
-
prev_month: [DT_TYPES, NUM_OR_NIL],
|
71
|
-
|
72
|
-
beginning_of_month: [DT_TYPES],
|
73
|
-
end_of_month: [DT_TYPES],
|
74
|
-
|
75
|
-
next_day: [DT_TYPES, NUM_OR_NIL],
|
76
|
-
prev_day: [DT_TYPES, NUM_OR_NIL],
|
77
|
-
|
78
|
-
to_i: [NUM_OR_STR + TI_TYPES],
|
79
|
-
to_f: [NUM_OR_STR + TI_TYPES],
|
80
|
-
to_d: [NUM_OR_STR],
|
81
|
-
to_s: [Object],
|
82
|
-
to_a: [Object],
|
83
|
-
to_json: [Object],
|
84
|
-
abs: [Numeric],
|
85
|
-
round: [Numeric, [nil, Integer]],
|
86
|
-
ceil: [Numeric],
|
87
|
-
floor: [Numeric],
|
88
|
-
truncate: [Numeric, [nil, Integer]],
|
89
|
-
}
|
12
|
+
::Delorean::Cache.adapter = ::Delorean::Cache::Adapters::RubyCache.new(size_per_class: 1000)
|
90
13
|
|
91
14
|
module BaseModule
|
92
15
|
# _e is used by Marty promise_jobs to pass promise-related
|
@@ -233,33 +156,23 @@ module Delorean
|
|
233
156
|
end
|
234
157
|
|
235
158
|
# FIXME: this is pretty hacky -- should probably merge
|
236
|
-
#
|
159
|
+
# whitelist and SIG mechanisms.
|
237
160
|
if obj.is_a?(Class)
|
238
161
|
_e[:_engine].parse_check_call_fn(method, args.count, obj)
|
239
162
|
return obj.send(msg, *args)
|
240
163
|
end
|
241
164
|
|
242
165
|
cls = obj.class
|
243
|
-
sig = RUBY_WHITELIST[msg]
|
244
|
-
|
245
|
-
raise "no such method #{method}" unless sig
|
246
166
|
|
247
|
-
|
248
|
-
return _instance_call(obj, sig, args, _e) if sig.is_a? String
|
167
|
+
matcher = ::Delorean::Ruby.whitelist.matcher(method_name: msg)
|
249
168
|
|
250
|
-
raise "
|
169
|
+
raise "no such method #{method}" unless matcher
|
251
170
|
|
252
|
-
|
171
|
+
return(
|
172
|
+
_instance_call(obj, matcher.match_to, args, _e)
|
173
|
+
) if matcher.match_to?
|
253
174
|
|
254
|
-
|
255
|
-
s = [s] unless s.is_a?(Array)
|
256
|
-
|
257
|
-
ai = arglist[i]
|
258
|
-
|
259
|
-
raise "bad arg #{i}, method #{method}: #{ai}/#{ai.class} #{s}" unless
|
260
|
-
(s.member?(nil) && i>=arglist.length) ||
|
261
|
-
s.detect {|sc| sc && ai.class <= sc}
|
262
|
-
end
|
175
|
+
matcher.match!(klass: obj.class, args: args)
|
263
176
|
|
264
177
|
obj.send(msg, *args)
|
265
178
|
end
|
data/lib/delorean/model.rb
CHANGED
@@ -34,15 +34,15 @@ module Delorean
|
|
34
34
|
# large lists which we count as one item in the cache. Caching
|
35
35
|
# mechanism will result in large processes.
|
36
36
|
def cached_delorean_fn(name, options = {}, &block)
|
37
|
-
delorean_fn(name, options) do
|
37
|
+
delorean_fn(name, options) do |*args|
|
38
38
|
delorean_cache_adapter = ::Delorean::Cache.adapter
|
39
39
|
# Check if caching should be performed
|
40
|
-
next block.call(args) unless delorean_cache_adapter.cache_item?(
|
40
|
+
next block.call(*args) unless delorean_cache_adapter.cache_item?(
|
41
41
|
klass: self, method_name: name, args: args
|
42
42
|
)
|
43
43
|
|
44
44
|
cache_key = delorean_cache_adapter.cache_key(
|
45
|
-
klass: self, method_name: name, args:
|
45
|
+
klass: self, method_name: name, args: args
|
46
46
|
)
|
47
47
|
cached_item = delorean_cache_adapter.fetch_item(
|
48
48
|
klass: self, cache_key: cache_key
|
@@ -50,7 +50,7 @@ module Delorean
|
|
50
50
|
|
51
51
|
next cached_item if cached_item
|
52
52
|
|
53
|
-
res = block.call(args)
|
53
|
+
res = block.call(*args)
|
54
54
|
|
55
55
|
delorean_cache_adapter.cache_item(
|
56
56
|
klass: self, cache_key: cache_key, item: res
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'delorean/ruby/whitelists/whitelist_error'
|
2
|
+
require 'delorean/ruby/whitelists/matchers'
|
3
|
+
|
4
|
+
module Delorean
|
5
|
+
module Ruby
|
6
|
+
module Whitelists
|
7
|
+
class Base
|
8
|
+
attr_reader :matchers
|
9
|
+
|
10
|
+
def add_method(method_name, match_to: nil, &block)
|
11
|
+
return method_name_error unless method_name.is_a?(Symbol)
|
12
|
+
return block_and_match_error if !match_to.nil? && block_given?
|
13
|
+
|
14
|
+
return add_matched_method(
|
15
|
+
method_name: method_name, match_to: match_to
|
16
|
+
) unless match_to.nil?
|
17
|
+
|
18
|
+
matchers[method_name.to_sym] = method_matcher_class.new(
|
19
|
+
method_name: method_name, &block
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
def matcher(method_name:)
|
24
|
+
matchers[method_name.to_sym]
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
@matchers = {}
|
31
|
+
initialize_hook
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_matched_method(method_name:, match_to:)
|
35
|
+
correct_match_to = match_to.is_a?(String) || match_to.is_a?(Symbol)
|
36
|
+
return wrong_match_to_error unless correct_match_to
|
37
|
+
|
38
|
+
matcher = ::Delorean::Ruby::Whitelists::Matchers::Method.new(
|
39
|
+
method_name: method_name, match_to: match_to.to_sym
|
40
|
+
)
|
41
|
+
|
42
|
+
matchers[method_name.to_sym] = matcher
|
43
|
+
end
|
44
|
+
|
45
|
+
def block_and_match_error
|
46
|
+
raise(
|
47
|
+
::Delorean::Ruby::Whitelists::WhitelistError,
|
48
|
+
'Method can not receive match_to and a block the same time'
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def wrong_match_to_error
|
53
|
+
raise(
|
54
|
+
::Delorean::Ruby::Whitelists::WhitelistError,
|
55
|
+
'match_to should either be a string or a symbol'
|
56
|
+
)
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_name_error
|
60
|
+
raise(
|
61
|
+
::Delorean::Ruby::Whitelists::WhitelistError,
|
62
|
+
'First attribute of add_method should be a symbol'
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def method_matcher_class
|
67
|
+
::Delorean::Ruby::Whitelists::Matchers::Method
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
require 'delorean/ruby/whitelists/base'
|
2
|
+
|
3
|
+
module Delorean
|
4
|
+
module Ruby
|
5
|
+
module Whitelists
|
6
|
+
class Default < ::Delorean::Ruby::Whitelists::Base
|
7
|
+
TI_TYPES = [Time, ActiveSupport::TimeWithZone]
|
8
|
+
DT_TYPES = [Date] + TI_TYPES
|
9
|
+
NUM_OR_STR = [Numeric, String]
|
10
|
+
NUM_OR_NIL = [nil, Integer]
|
11
|
+
|
12
|
+
def initialize_hook
|
13
|
+
_add_default_methods
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def _add_default_methods
|
19
|
+
add_method :attributes do |method|
|
20
|
+
method.called_on ActiveRecord::Base
|
21
|
+
method.called_on ActiveRecord::Relation
|
22
|
+
end
|
23
|
+
|
24
|
+
add_method :between? do |method|
|
25
|
+
method.called_on String, with: [String, String]
|
26
|
+
method.called_on Numeric, with: [Numeric, Numeric]
|
27
|
+
end
|
28
|
+
|
29
|
+
add_method :between, match_to: :between?
|
30
|
+
|
31
|
+
add_method :compact do |method|
|
32
|
+
method.called_on Array
|
33
|
+
method.called_on Hash
|
34
|
+
end
|
35
|
+
|
36
|
+
add_method :to_set do |method|
|
37
|
+
method.called_on Array
|
38
|
+
end
|
39
|
+
|
40
|
+
add_method :flatten do |method|
|
41
|
+
method.called_on Array, with: [NUM_OR_NIL]
|
42
|
+
end
|
43
|
+
|
44
|
+
add_method :length do |method|
|
45
|
+
method.called_on String
|
46
|
+
method.called_on Enumerable
|
47
|
+
end
|
48
|
+
|
49
|
+
add_method :max do |method|
|
50
|
+
method.called_on Array
|
51
|
+
end
|
52
|
+
|
53
|
+
add_method :member, match_to: :member?
|
54
|
+
|
55
|
+
add_method :member? do |method|
|
56
|
+
method.called_on Enumerable, with: [Object]
|
57
|
+
end
|
58
|
+
|
59
|
+
add_method :empty, match_to: :empty?
|
60
|
+
|
61
|
+
add_method :empty? do |method|
|
62
|
+
method.called_on Enumerable
|
63
|
+
end
|
64
|
+
|
65
|
+
add_method :except do |method|
|
66
|
+
method.called_on Hash, with: [String] + [[nil, String]]*9
|
67
|
+
end
|
68
|
+
|
69
|
+
add_method :reverse do |method|
|
70
|
+
method.called_on Array
|
71
|
+
end
|
72
|
+
|
73
|
+
add_method :slice do |method|
|
74
|
+
method.called_on Array, with: [Integer, Integer]
|
75
|
+
end
|
76
|
+
|
77
|
+
add_method :each_slice do |method|
|
78
|
+
method.called_on Array, with: [Integer]
|
79
|
+
end
|
80
|
+
|
81
|
+
add_method :sort do |method|
|
82
|
+
method.called_on Array
|
83
|
+
end
|
84
|
+
|
85
|
+
add_method :split do |method|
|
86
|
+
method.called_on String, with: [String]
|
87
|
+
end
|
88
|
+
|
89
|
+
add_method :uniq do |method|
|
90
|
+
method.called_on Array
|
91
|
+
end
|
92
|
+
|
93
|
+
add_method :sum do |method|
|
94
|
+
method.called_on Array
|
95
|
+
end
|
96
|
+
|
97
|
+
add_method :transpose do |method|
|
98
|
+
method.called_on Array
|
99
|
+
end
|
100
|
+
|
101
|
+
add_method :join do |method|
|
102
|
+
method.called_on Array, with: [String]
|
103
|
+
end
|
104
|
+
|
105
|
+
add_method :zip do |method|
|
106
|
+
method.called_on Array, with: [Array, [Array, nil], [Array, nil]]
|
107
|
+
end
|
108
|
+
|
109
|
+
add_method :index do |method|
|
110
|
+
method.called_on Array, with: [[Object]]
|
111
|
+
end
|
112
|
+
|
113
|
+
add_method :product do |method|
|
114
|
+
method.called_on Array, with: [Array]
|
115
|
+
end
|
116
|
+
|
117
|
+
add_method :first do |method|
|
118
|
+
method.called_on ActiveRecord::Relation, with: [NUM_OR_NIL]
|
119
|
+
method.called_on Enumerable, with: [NUM_OR_NIL]
|
120
|
+
end
|
121
|
+
|
122
|
+
add_method :last do |method|
|
123
|
+
method.called_on ActiveRecord::Relation, with: [NUM_OR_NIL]
|
124
|
+
method.called_on Enumerable, with: [NUM_OR_NIL]
|
125
|
+
end
|
126
|
+
|
127
|
+
add_method :intersection do |method|
|
128
|
+
method.called_on Set, with: [Enumerable]
|
129
|
+
end
|
130
|
+
|
131
|
+
add_method :union do |method|
|
132
|
+
method.called_on Set, with: [Enumerable]
|
133
|
+
end
|
134
|
+
|
135
|
+
|
136
|
+
|
137
|
+
add_method :keys do |method|
|
138
|
+
method.called_on Hash
|
139
|
+
end
|
140
|
+
|
141
|
+
add_method :values do |method|
|
142
|
+
method.called_on Hash
|
143
|
+
end
|
144
|
+
|
145
|
+
add_method :fetch do |method|
|
146
|
+
method.called_on Hash, with: [Object, [Object]]
|
147
|
+
end
|
148
|
+
|
149
|
+
add_method :upcase do |method|
|
150
|
+
method.called_on String
|
151
|
+
end
|
152
|
+
|
153
|
+
add_method :downcase do |method|
|
154
|
+
method.called_on String
|
155
|
+
end
|
156
|
+
|
157
|
+
add_method :match do |method|
|
158
|
+
method.called_on String, with: [[String], NUM_OR_NIL]
|
159
|
+
end
|
160
|
+
|
161
|
+
add_method :iso8601 do |method|
|
162
|
+
DT_TYPES.each do |type|
|
163
|
+
method.called_on type
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
add_method :hour do |method|
|
168
|
+
DT_TYPES.each do |type|
|
169
|
+
method.called_on type
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
add_method :min do |method|
|
174
|
+
(DT_TYPES + [Array]).each do |type|
|
175
|
+
method.called_on type
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
add_method :sec do |method|
|
180
|
+
DT_TYPES.each do |type|
|
181
|
+
method.called_on type
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
add_method :to_date do |method|
|
186
|
+
(DT_TYPES + [String]).each do |type|
|
187
|
+
method.called_on type
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
add_method :to_time do |method|
|
192
|
+
(DT_TYPES + [String]).each do |type|
|
193
|
+
method.called_on type
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
add_method :month do |method|
|
198
|
+
DT_TYPES.each do |type|
|
199
|
+
method.called_on type
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
add_method :day do |method|
|
204
|
+
DT_TYPES.each do |type|
|
205
|
+
method.called_on type
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
add_method :year do |method|
|
210
|
+
DT_TYPES.each do |type|
|
211
|
+
method.called_on type
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
add_method :next_month do |method|
|
216
|
+
DT_TYPES.each do |type|
|
217
|
+
method.called_on type, with: [NUM_OR_NIL]
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
add_method :prev_month do |method|
|
222
|
+
DT_TYPES.each do |type|
|
223
|
+
method.called_on type, with: [NUM_OR_NIL]
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
add_method :beginning_of_month do |method|
|
228
|
+
DT_TYPES.each do |type|
|
229
|
+
method.called_on type
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
add_method :end_of_month do |method|
|
234
|
+
DT_TYPES.each do |type|
|
235
|
+
method.called_on type
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
add_method :next_day do |method|
|
240
|
+
DT_TYPES.each do |type|
|
241
|
+
method.called_on type, with: [NUM_OR_NIL]
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
add_method :prev_day do |method|
|
246
|
+
DT_TYPES.each do |type|
|
247
|
+
method.called_on type, with: [NUM_OR_NIL]
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
add_method :to_i do |method|
|
252
|
+
(NUM_OR_STR + TI_TYPES).each do |type|
|
253
|
+
method.called_on type
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
add_method :to_f do |method|
|
258
|
+
(NUM_OR_STR + TI_TYPES).each do |type|
|
259
|
+
method.called_on type
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
add_method :to_d do |method|
|
264
|
+
NUM_OR_STR.each do |type|
|
265
|
+
method.called_on type
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
add_method :to_s do |method|
|
270
|
+
method.called_on Object
|
271
|
+
end
|
272
|
+
|
273
|
+
add_method :to_a do |method|
|
274
|
+
method.called_on Object
|
275
|
+
end
|
276
|
+
|
277
|
+
add_method :to_json do |method|
|
278
|
+
method.called_on Object
|
279
|
+
end
|
280
|
+
|
281
|
+
add_method :abs do |method|
|
282
|
+
method.called_on Numeric
|
283
|
+
end
|
284
|
+
|
285
|
+
add_method :round do |method|
|
286
|
+
method.called_on Numeric, with: [[nil, Integer]]
|
287
|
+
end
|
288
|
+
|
289
|
+
add_method :ceil do |method|
|
290
|
+
method.called_on Numeric
|
291
|
+
end
|
292
|
+
|
293
|
+
add_method :floor do |method|
|
294
|
+
method.called_on Numeric
|
295
|
+
end
|
296
|
+
|
297
|
+
add_method :truncate do |method|
|
298
|
+
method.called_on Numeric, with: [[nil, Integer]]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Delorean
|
2
|
+
module Ruby
|
3
|
+
module Whitelists
|
4
|
+
module Matchers
|
5
|
+
class Arguments
|
6
|
+
attr_reader :called_on, :method_name, :with
|
7
|
+
|
8
|
+
def initialize(called_on:, method_name:, with: [])
|
9
|
+
@called_on = called_on
|
10
|
+
@method_name = method_name
|
11
|
+
@with = with
|
12
|
+
end
|
13
|
+
|
14
|
+
def match!(args:)
|
15
|
+
raise "too many args to #{method_name}" if args.size > with.size
|
16
|
+
|
17
|
+
with.each_with_index do |s, i|
|
18
|
+
arg_signature = Array(s)
|
19
|
+
|
20
|
+
arg = args[i]
|
21
|
+
|
22
|
+
# Sometimes signature contains extra elements that can be nil.
|
23
|
+
# In that case we allow it to not be passed.
|
24
|
+
# For example .first and .first(4)
|
25
|
+
next if arg_signature.member?(nil) && i >= args.size
|
26
|
+
|
27
|
+
next if valid_argument?(arg_signature: arg_signature, arg: arg)
|
28
|
+
|
29
|
+
bad_arg_error(arg_signature: arg_signature, index: i, arg: arg)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def valid_argument?(arg_signature:, arg:)
|
36
|
+
arg_signature.any? { |sc| sc && arg.class <= sc }
|
37
|
+
end
|
38
|
+
|
39
|
+
def bad_arg_error(arg_signature:, index:, arg:)
|
40
|
+
arg_error = "#{arg}/#{arg.class} #{arg_signature}"
|
41
|
+
raise "bad arg #{index}, method #{method_name}: #{arg_error}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'delorean/ruby/whitelists/matchers/arguments'
|
2
|
+
|
3
|
+
module Delorean
|
4
|
+
module Ruby
|
5
|
+
module Whitelists
|
6
|
+
module Matchers
|
7
|
+
class Method
|
8
|
+
attr_reader :method_name, :match_to, :arguments_matchers
|
9
|
+
|
10
|
+
def initialize(method_name:, match_to: nil)
|
11
|
+
@method_name = method_name
|
12
|
+
@match_to = match_to
|
13
|
+
@arguments_matchers = []
|
14
|
+
|
15
|
+
yield self if block_given?
|
16
|
+
end
|
17
|
+
|
18
|
+
def called_on(klass, with: [])
|
19
|
+
matcher = Ruby::Whitelists::Matchers::Arguments.new(
|
20
|
+
called_on: klass, method_name: method_name, with: with
|
21
|
+
)
|
22
|
+
|
23
|
+
arguments_matchers << matcher
|
24
|
+
end
|
25
|
+
|
26
|
+
def matcher(klass:)
|
27
|
+
matcher = arguments_matchers.find do
|
28
|
+
|matcher_object| klass <= matcher_object.called_on
|
29
|
+
end
|
30
|
+
|
31
|
+
raise "no such method #{method_name} for #{klass}" if matcher.nil?
|
32
|
+
matcher
|
33
|
+
end
|
34
|
+
|
35
|
+
def match!(klass:, args:)
|
36
|
+
matcher(klass: klass).match!(args: args)
|
37
|
+
end
|
38
|
+
|
39
|
+
def match_to?
|
40
|
+
!match_to.nil?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/delorean/version.rb
CHANGED
data/lib/delorean_lang.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require "delorean/version"
|
2
2
|
|
3
3
|
require 'treetop'
|
4
|
-
require 'delorean/
|
4
|
+
require 'delorean/abstract_container'
|
5
|
+
require 'delorean/base'
|
6
|
+
require 'delorean/debug'
|
5
7
|
require 'delorean/delorean'
|
6
|
-
require 'delorean/
|
8
|
+
require 'delorean/cache'
|
7
9
|
require 'delorean/engine'
|
8
|
-
require 'delorean/base'
|
9
10
|
require 'delorean/error'
|
10
|
-
require 'delorean/container'
|
11
11
|
require 'delorean/model'
|
12
|
-
require 'delorean/
|
12
|
+
require 'delorean/nodes'
|
13
|
+
require 'delorean/ruby'
|
data/spec/cache_spec.rb
CHANGED
@@ -15,8 +15,8 @@ describe "Delorean cache" do
|
|
15
15
|
it 'uses cache' do
|
16
16
|
expect(OpenStruct).to receive(:new).once.and_call_original
|
17
17
|
|
18
|
-
res1 = Dummy.returns_cached_openstruct
|
19
|
-
res2 = Dummy.returns_cached_openstruct
|
18
|
+
res1 = Dummy.returns_cached_openstruct(1, 2)
|
19
|
+
res2 = Dummy.returns_cached_openstruct(1, 2)
|
20
20
|
|
21
21
|
expect(res1).to eq res2
|
22
22
|
end
|
@@ -31,8 +31,8 @@ describe "Delorean cache" do
|
|
31
31
|
it "doesn't use cache with different keys" do
|
32
32
|
expect(OpenStruct).to receive(:new).twice.and_call_original
|
33
33
|
|
34
|
-
Dummy.returns_cached_openstruct(1)
|
35
|
-
Dummy.returns_cached_openstruct(2)
|
34
|
+
Dummy.returns_cached_openstruct(1, 1)
|
35
|
+
Dummy.returns_cached_openstruct(1, 2)
|
36
36
|
end
|
37
37
|
|
38
38
|
it 'removes outdated items from cache' do
|
@@ -41,15 +41,15 @@ describe "Delorean cache" do
|
|
41
41
|
)
|
42
42
|
|
43
43
|
12.times do |t|
|
44
|
-
Dummy.returns_cached_openstruct(t)
|
44
|
+
Dummy.returns_cached_openstruct(t, t)
|
45
45
|
end
|
46
46
|
|
47
47
|
item_2 = ::Delorean::Cache.adapter.fetch_item(
|
48
|
-
klass: Dummy, cache_key: [:returns_cached_openstruct, 2]
|
48
|
+
klass: Dummy, cache_key: [:returns_cached_openstruct, 2, 2]
|
49
49
|
)
|
50
50
|
|
51
51
|
item_10 = ::Delorean::Cache.adapter.fetch_item(
|
52
|
-
klass: Dummy, cache_key: [:returns_cached_openstruct, 10]
|
52
|
+
klass: Dummy, cache_key: [:returns_cached_openstruct, 10, 10]
|
53
53
|
)
|
54
54
|
|
55
55
|
expect(item_2).to_not be_present
|
data/spec/func_spec.rb
CHANGED
@@ -268,12 +268,23 @@ describe "Delorean" do
|
|
268
268
|
it "should handle BETWEEN" do
|
269
269
|
engine.parse defn("A:",
|
270
270
|
" a = 1.23",
|
271
|
-
"
|
271
|
+
" number_between = [a.between(10,20), a.between(1,3)]",
|
272
|
+
|
273
|
+
" c = 'c'",
|
274
|
+
" string_between = [c.between('a', 'd'), c.between('d', 'e')]",
|
275
|
+
|
276
|
+
" types_mismatch1 = [a.between('a', 'd')]",
|
277
|
+
" types_mismatch2 = [c.between(1, 3)]"
|
272
278
|
)
|
273
279
|
|
274
|
-
expect(engine.evaluate("A", "
|
280
|
+
expect(engine.evaluate("A", "number_between")).to eq([false, true])
|
281
|
+
expect(engine.evaluate("A", "string_between")).to eq([true, false])
|
282
|
+
|
283
|
+
expect { engine.evaluate("A", "types_mismatch1") }.to raise_error(/bad arg/)
|
284
|
+
expect { engine.evaluate("A", "types_mismatch2") }.to raise_error(/bad arg/)
|
275
285
|
end
|
276
286
|
|
287
|
+
|
277
288
|
it "should handle MATCH" do
|
278
289
|
engine.parse defn("A:",
|
279
290
|
" a = 'this is a test'.match('(.*)( is )(.*)')",
|
@@ -283,4 +294,5 @@ describe "Delorean" do
|
|
283
294
|
expect(engine.evaluate("A", "b")).
|
284
295
|
to eq(["this is a test", "this", " is ", "a test", nil])
|
285
296
|
end
|
297
|
+
|
286
298
|
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require_relative '../spec_helper'
|
2
|
+
require 'delorean/ruby/whitelists/empty'
|
3
|
+
|
4
|
+
describe "Delorean Ruby whitelisting" do
|
5
|
+
it 'allows to override whitelist with an empty one' do
|
6
|
+
::Delorean::Ruby.whitelist = whitelist
|
7
|
+
expect(whitelist.matchers).to be_empty
|
8
|
+
|
9
|
+
::Delorean::Ruby.whitelist = ::Delorean::Ruby::Whitelists::Default.new
|
10
|
+
expect(::Delorean::Ruby.whitelist.matchers).to_not be_empty
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:whitelist) { ::Delorean::Ruby::Whitelists::Empty.new }
|
14
|
+
|
15
|
+
describe "methods" do
|
16
|
+
before do
|
17
|
+
whitelist.add_method :testmethod do |method|
|
18
|
+
method.called_on Dummy
|
19
|
+
end
|
20
|
+
|
21
|
+
whitelist.add_method :testmethod_with_args do |method|
|
22
|
+
method.called_on Dummy, with: [Numeric, [String, nil], [String, nil]]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
let(:matcher) { whitelist.matcher(method_name: :testmethod) }
|
27
|
+
let(:matcher_with_args) do
|
28
|
+
whitelist.matcher(method_name: :testmethod_with_args)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'matches method' do
|
32
|
+
matcher = whitelist.matcher(method_name: :testmethod)
|
33
|
+
expect(matcher).to_not be_nil
|
34
|
+
expect { matcher.match!(klass: Dummy, args: []) }.to_not raise_error
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'allows missing nillable arguments' do
|
38
|
+
expect do
|
39
|
+
matcher_with_args.match!(klass: Dummy, args: [1])
|
40
|
+
end.to_not raise_error
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'raises error if method not allowed for a class' do
|
44
|
+
expect do
|
45
|
+
matcher.match!(klass: Date, args: [])
|
46
|
+
end.to raise_error('no such method testmethod for Date')
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'raises error if arguments list is too long' do
|
50
|
+
expect do matcher.match!(klass: Dummy, args: [1])
|
51
|
+
end.to raise_error('too many args to testmethod')
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'raises error if arguments list is too short' do
|
55
|
+
expect do
|
56
|
+
matcher_with_args.match!(klass: Dummy, args: [])
|
57
|
+
end.to raise_error(
|
58
|
+
'bad arg 0, method testmethod_with_args: /NilClass [Numeric]'
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'raises error if argument type is wrong' do
|
63
|
+
expect do
|
64
|
+
matcher_with_args.match!(klass: Dummy, args: [1, 2])
|
65
|
+
end.to raise_error(
|
66
|
+
'bad arg 1, method testmethod_with_args: 2/Fixnum [String, nil]'
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'allows match one method to another' do
|
71
|
+
whitelist.add_method :testmethod_matched, match_to: :testmethod_with_args
|
72
|
+
matcher = whitelist.matcher(method_name: :testmethod_matched)
|
73
|
+
expect(matcher.match_to?).to be true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -88,8 +88,8 @@ class Dummy < ActiveRecord::Base
|
|
88
88
|
OpenStruct.new({"abc"=>"def"})
|
89
89
|
end
|
90
90
|
|
91
|
-
cached_delorean_fn :returns_cached_openstruct, sig:
|
92
|
-
OpenStruct.new({
|
91
|
+
cached_delorean_fn :returns_cached_openstruct, sig: 2 do |first, last|
|
92
|
+
OpenStruct.new({ first.to_s => last })
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
@@ -115,10 +115,9 @@ module M
|
|
115
115
|
end
|
116
116
|
end
|
117
117
|
|
118
|
-
Delorean::
|
119
|
-
|
120
|
-
|
121
|
-
)
|
118
|
+
Delorean::Ruby.whitelist.add_method :name2 do |method|
|
119
|
+
method.called_on Dummy
|
120
|
+
end
|
122
121
|
|
123
122
|
######################################################################
|
124
123
|
|
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.
|
4
|
+
version: 0.4.8
|
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-01-
|
11
|
+
date: 2019-01-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: treetop
|
@@ -95,13 +95,13 @@ files:
|
|
95
95
|
- README.md
|
96
96
|
- Rakefile
|
97
97
|
- delorean.gemspec
|
98
|
+
- lib/delorean/abstract_container.rb
|
98
99
|
- lib/delorean/base.rb
|
99
100
|
- lib/delorean/cache.rb
|
100
101
|
- lib/delorean/cache/adapters.rb
|
101
102
|
- lib/delorean/cache/adapters/base.rb
|
102
103
|
- lib/delorean/cache/adapters/ruby_cache.rb
|
103
104
|
- lib/delorean/const.rb
|
104
|
-
- lib/delorean/container.rb
|
105
105
|
- lib/delorean/debug.rb
|
106
106
|
- lib/delorean/delorean.rb
|
107
107
|
- lib/delorean/delorean.treetop
|
@@ -109,6 +109,15 @@ files:
|
|
109
109
|
- lib/delorean/error.rb
|
110
110
|
- lib/delorean/model.rb
|
111
111
|
- lib/delorean/nodes.rb
|
112
|
+
- lib/delorean/ruby.rb
|
113
|
+
- lib/delorean/ruby/whitelists.rb
|
114
|
+
- lib/delorean/ruby/whitelists/base.rb
|
115
|
+
- lib/delorean/ruby/whitelists/default.rb
|
116
|
+
- lib/delorean/ruby/whitelists/empty.rb
|
117
|
+
- lib/delorean/ruby/whitelists/matchers.rb
|
118
|
+
- lib/delorean/ruby/whitelists/matchers/arguments.rb
|
119
|
+
- lib/delorean/ruby/whitelists/matchers/method.rb
|
120
|
+
- lib/delorean/ruby/whitelists/whitelist_error.rb
|
112
121
|
- lib/delorean/version.rb
|
113
122
|
- lib/delorean_lang.rb
|
114
123
|
- spec/cache_spec.rb
|
@@ -116,6 +125,7 @@ files:
|
|
116
125
|
- spec/eval_spec.rb
|
117
126
|
- spec/func_spec.rb
|
118
127
|
- spec/parse_spec.rb
|
128
|
+
- spec/ruby/whitelist_spec.rb
|
119
129
|
- spec/spec_helper.rb
|
120
130
|
homepage: https://github.com/arman000/delorean_lang
|
121
131
|
licenses:
|
@@ -147,4 +157,5 @@ test_files:
|
|
147
157
|
- spec/eval_spec.rb
|
148
158
|
- spec/func_spec.rb
|
149
159
|
- spec/parse_spec.rb
|
160
|
+
- spec/ruby/whitelist_spec.rb
|
150
161
|
- spec/spec_helper.rb
|