delorean_lang 0.4.7 → 0.4.8

Sign up to get free protection for your applications and to get access to all the features.
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