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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f606ebe7eea8de6ef41dcc2de653b09ed4177308
4
- data.tar.gz: b545ce6a844aa31945cd590f73a456a23c68e613
3
+ metadata.gz: 3a6eb15c510767db6fe200846f73c99332074552
4
+ data.tar.gz: a088e3928a4646f276f23f144e05fb1ed5d2717b
5
5
  SHA512:
6
- metadata.gz: d934f9c0172103dc572132b42ca728749801d50a3f63e87574d4788c09c995f4a475aaa9c96ebfc6f430732190fc2dd059e285587a9b53f1a406bb65d09289d0
7
- data.tar.gz: 87ce61ccb1bce7c5e89df4bb8d0b975adcc4297cfaa8b913c5f318a02cee2d9b658479b446e81c13b73490d26a7d1429387a9d0053cefd26f0beb3e6df19d974
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.
@@ -0,0 +1,9 @@
1
+ require 'delorean/base'
2
+
3
+ module Delorean
4
+ class AbstractContainer
5
+ def get_engine(name)
6
+ raise "get_engine needs to be overriden"
7
+ end
8
+ end
9
+ end
@@ -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::Cache.adapter = ::Delorean::Cache::Adapters::RubyCache.new(size_per_class: 1000)
10
+ ::Delorean::Ruby.whitelist = ::Delorean::Ruby::Whitelists::Default.new
9
11
 
10
- TI_TYPES = [Time, ActiveSupport::TimeWithZone]
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
- # RUBY_WHITELIST and SIG mechanisms.
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
- # if sig is a string, then method mapped to another name
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 "too many args to #{method}" if args.length>(sig.length-1)
169
+ raise "no such method #{method}" unless matcher
251
170
 
252
- arglist = [obj] + args
171
+ return(
172
+ _instance_call(obj, matcher.match_to, args, _e)
173
+ ) if matcher.match_to?
253
174
 
254
- sig.each_with_index do |s, i|
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
@@ -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 |args|
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: [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,13 @@
1
+ require 'delorean/ruby/whitelists'
2
+
3
+ module Delorean
4
+ module Ruby
5
+ def self.whitelist=(new_whitelist)
6
+ @whitelist = new_whitelist
7
+ end
8
+
9
+ def self.whitelist
10
+ @whitelist
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,8 @@
1
+ require 'delorean/ruby/whitelists/default'
2
+
3
+ module Delorean
4
+ module Ruby
5
+ module Whitelists
6
+ end
7
+ end
8
+ end
@@ -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,12 @@
1
+ require 'delorean/ruby/whitelists/base'
2
+
3
+ module Delorean
4
+ module Ruby
5
+ module Whitelists
6
+ class Empty < ::Delorean::Ruby::Whitelists::Base
7
+ def initialize_hook
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ require 'delorean/ruby/whitelists/matchers/method'
2
+
3
+ module Delorean
4
+ module Ruby
5
+ module Whitelists
6
+ module Matchers
7
+ end
8
+ end
9
+ end
10
+ 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
@@ -0,0 +1,8 @@
1
+ module Delorean
2
+ module Ruby
3
+ module Whitelists
4
+ class WhitelistError < StandardError
5
+ end
6
+ end
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module Delorean
2
- VERSION = "0.4.7"
2
+ VERSION = "0.4.8"
3
3
  end
@@ -1,12 +1,13 @@
1
1
  require "delorean/version"
2
2
 
3
3
  require 'treetop'
4
- require 'delorean/cache'
4
+ require 'delorean/abstract_container'
5
+ require 'delorean/base'
6
+ require 'delorean/debug'
5
7
  require 'delorean/delorean'
6
- require 'delorean/nodes'
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/debug'
12
+ require 'delorean/nodes'
13
+ require 'delorean/ruby'
@@ -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
@@ -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
- " b = [a.between(10,20), a.between(1,3)]",
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", "b")).to eq([false, true])
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
@@ -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: 1 do |ts = nil|
92
- OpenStruct.new({"abc"=>"def"})
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::RUBY_WHITELIST.
119
- merge!(
120
- name2: [Dummy],
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.7
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-08 00:00:00.000000000 Z
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
@@ -1,7 +0,0 @@
1
- require 'delorean/base'
2
-
3
- class Delorean::AbstractContainer
4
- def get_engine(name)
5
- raise "get_engine needs to be overriden"
6
- end
7
- end