delorean_lang 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +3 -0
- data/README.md +30 -1
- data/delorean.gemspec +1 -0
- data/lib/delorean/base.rb +1 -1
- data/lib/delorean/engine.rb +2 -3
- data/lib/delorean/functions.rb +66 -0
- data/lib/delorean/model.rb +2 -66
- data/lib/delorean/version.rb +1 -1
- data/lib/delorean_lang.rb +2 -0
- data/spec/eval_spec.rb +14 -0
- data/spec/spec_helper.rb +12 -0
- metadata +17 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: afc7142d7683c1fe4d606a86dd7c46eff3ae672b1eb58db13cb587c18b9ea741
|
4
|
+
data.tar.gz: a522e77939cbcaa4b8592980ced9281179a07e171fe3e34b69adba13661a8215
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 03b53dcf912b0311f12151e6fa189e797f767205cd36d6aac6547d58ab6fad6cfbe6905766caae199c15d4898360829a2249379d61634fec98062fc33753b8ae
|
7
|
+
data.tar.gz: d21c189ed7f03b98cc5c21525a1c031b4a6d43477291744a7016e75f411ca9aad5cafe4c2b87451059036438d5c60d6c14fc9a94fd703be7f902b802dfbe2fe2
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -159,7 +159,7 @@ Ruby.
|
|
159
159
|
|
160
160
|
### Calling ruby methods from Delorean
|
161
161
|
|
162
|
-
|
162
|
+
There are two ways of calling ruby code from delorean. First one is to whitelist methods:
|
163
163
|
|
164
164
|
```ruby
|
165
165
|
|
@@ -184,6 +184,35 @@ By default Delorean has some methods whitelisted, such as `length`, `min`, `max`
|
|
184
184
|
|
185
185
|
```
|
186
186
|
|
187
|
+
Another way is to define methods using `delorean_fn` and `cached_delorean_fn`.
|
188
|
+
Use `extend Delorean::Functions` or `include Delorean::Model` in your module or class.
|
189
|
+
|
190
|
+
```ruby
|
191
|
+
class Dummy < ActiveRecord::Base
|
192
|
+
include Delorean::Model
|
193
|
+
|
194
|
+
delorean_fn(:heres_my_number, sig: [0, Float::INFINITY]) do |*a|
|
195
|
+
a.inject(0, :+)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
module DummyModule
|
200
|
+
extend Delorean::Functions
|
201
|
+
|
202
|
+
delorean_fn(:heres_my_number, sig: [0, Float::INFINITY]) do |*a|
|
203
|
+
a.inject(0, :+)
|
204
|
+
end
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
`heres_my_number` method will be accessible from Delorean code.
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
ExampleScript:
|
212
|
+
a = Dummy.heres_my_number(867, 5309)'
|
213
|
+
b = DummyModule.heres_my_number(867, 5309)'
|
214
|
+
```
|
215
|
+
|
187
216
|
### Caching
|
188
217
|
|
189
218
|
Delorean provides `cached_delorean_function` method that will cache result based on arguments.
|
data/delorean.gemspec
CHANGED
@@ -22,5 +22,6 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.add_development_dependency 'pry'
|
23
23
|
gem.add_development_dependency 'rspec', '~> 2.1'
|
24
24
|
gem.add_development_dependency 'rubocop'
|
25
|
+
gem.add_development_dependency 'rubocop-performance'
|
25
26
|
gem.add_development_dependency 'sqlite3', '~> 1.3.10'
|
26
27
|
end
|
data/lib/delorean/base.rb
CHANGED
@@ -162,7 +162,7 @@ module Delorean
|
|
162
162
|
|
163
163
|
# FIXME: this is pretty hacky -- should probably merge
|
164
164
|
# whitelist and SIG mechanisms.
|
165
|
-
if obj.is_a?(Class)
|
165
|
+
if obj.is_a?(Class) || obj.is_a?(Module)
|
166
166
|
_e[:_engine].parse_check_call_fn(method, args.count, obj)
|
167
167
|
return obj.send(msg, *args)
|
168
168
|
end
|
data/lib/delorean/engine.rb
CHANGED
@@ -193,10 +193,9 @@ module Delorean
|
|
193
193
|
err(UndefinedError, "Can't find class: #{class_name}")
|
194
194
|
end
|
195
195
|
|
196
|
-
|
197
|
-
klass.instance_of?(Class)
|
196
|
+
return klass if klass.instance_of?(Class) || klass.instance_of?(Module)
|
198
197
|
|
199
|
-
|
198
|
+
err(UndefinedError, "Access to non-class/module: #{class_name}")
|
200
199
|
end
|
201
200
|
|
202
201
|
def err(exc, msg)
|
@@ -0,0 +1,66 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Delorean
|
4
|
+
module Functions
|
5
|
+
def delorean_fn(name, options = {})
|
6
|
+
define_singleton_method(name) do |*args|
|
7
|
+
yield(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
sig = options[:sig]
|
11
|
+
|
12
|
+
raise 'no signature' unless sig
|
13
|
+
|
14
|
+
sig = [sig, sig] if sig.is_a? Integer
|
15
|
+
raise 'Bad signature' unless sig.is_a?(Array) && (sig.length == 2)
|
16
|
+
|
17
|
+
const_set(name.to_s.upcase + Delorean::SIG, sig)
|
18
|
+
end
|
19
|
+
|
20
|
+
# FIXME: IDEA: we just make :cache an argument to delorean_fn.
|
21
|
+
# That way, we don't need the cached_ flavors. It'll make all
|
22
|
+
# this code a lot simpler. We should also just add the :private
|
23
|
+
# mechanism here.
|
24
|
+
|
25
|
+
# By default implements a VERY HACKY class-based (per process) caching
|
26
|
+
# mechanism for database lookup results. Issues include: cached
|
27
|
+
# values are ActiveRecord objects. Query results can be very
|
28
|
+
# large lists which we count as one item in the cache. Caching
|
29
|
+
# mechanism will result in large processes.
|
30
|
+
def cached_delorean_fn(name, options = {})
|
31
|
+
delorean_fn(name, options) do |*args|
|
32
|
+
delorean_cache_adapter = ::Delorean::Cache.adapter
|
33
|
+
# Check if caching should be performed
|
34
|
+
next yield(*args) unless delorean_cache_adapter.cache_item?(
|
35
|
+
klass: self, method_name: name, args: args
|
36
|
+
)
|
37
|
+
|
38
|
+
cache_key = delorean_cache_adapter.cache_key(
|
39
|
+
klass: self, method_name: name, args: args
|
40
|
+
)
|
41
|
+
cached_item = delorean_cache_adapter.fetch_item(
|
42
|
+
klass: self, cache_key: cache_key, default: :NF
|
43
|
+
)
|
44
|
+
|
45
|
+
next cached_item if cached_item != :NF
|
46
|
+
|
47
|
+
res = yield(*args)
|
48
|
+
|
49
|
+
delorean_cache_adapter.cache_item(
|
50
|
+
klass: self, cache_key: cache_key, item: res
|
51
|
+
)
|
52
|
+
|
53
|
+
# Since we're caching this object and don't want anyone
|
54
|
+
# changing it. FIXME: ideally should freeze this object
|
55
|
+
# recursively.
|
56
|
+
res.freeze if res.respond_to?(:freeze)
|
57
|
+
|
58
|
+
res
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def clear_lookup_cache!
|
63
|
+
::Delorean::Cache.adapter.clear!(klass: self)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/delorean/model.rb
CHANGED
@@ -1,76 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'delorean/const'
|
4
|
+
require 'delorean/functions'
|
4
5
|
|
5
6
|
module Delorean
|
6
7
|
module Model
|
7
8
|
def self.included(base)
|
8
|
-
base.send :extend,
|
9
|
-
end
|
10
|
-
|
11
|
-
module ClassMethods
|
12
|
-
def delorean_fn(name, options = {})
|
13
|
-
define_singleton_method(name) do |*args|
|
14
|
-
yield(*args)
|
15
|
-
end
|
16
|
-
|
17
|
-
sig = options[:sig]
|
18
|
-
|
19
|
-
raise 'no signature' unless sig
|
20
|
-
|
21
|
-
if sig
|
22
|
-
sig = [sig, sig] if sig.is_a? Integer
|
23
|
-
raise 'Bad signature' unless sig.is_a?(Array) && (sig.length == 2)
|
24
|
-
|
25
|
-
const_set(name.to_s.upcase + Delorean::SIG, sig)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# FIXME: IDEA: we just make :cache an argument to delorean_fn.
|
30
|
-
# That way, we don't need the cached_ flavors. It'll make all
|
31
|
-
# this code a lot simpler. We should also just add the :private
|
32
|
-
# mechanism here.
|
33
|
-
|
34
|
-
# By default implements a VERY HACKY class-based (per process) caching
|
35
|
-
# mechanism for database lookup results. Issues include: cached
|
36
|
-
# values are ActiveRecord objects. Query results can be very
|
37
|
-
# large lists which we count as one item in the cache. Caching
|
38
|
-
# mechanism will result in large processes.
|
39
|
-
def cached_delorean_fn(name, options = {})
|
40
|
-
delorean_fn(name, options) do |*args|
|
41
|
-
delorean_cache_adapter = ::Delorean::Cache.adapter
|
42
|
-
# Check if caching should be performed
|
43
|
-
next yield(*args) unless delorean_cache_adapter.cache_item?(
|
44
|
-
klass: self, method_name: name, args: args
|
45
|
-
)
|
46
|
-
|
47
|
-
cache_key = delorean_cache_adapter.cache_key(
|
48
|
-
klass: self, method_name: name, args: args
|
49
|
-
)
|
50
|
-
cached_item = delorean_cache_adapter.fetch_item(
|
51
|
-
klass: self, cache_key: cache_key, default: :NF
|
52
|
-
)
|
53
|
-
|
54
|
-
next cached_item if cached_item != :NF
|
55
|
-
|
56
|
-
res = yield(*args)
|
57
|
-
|
58
|
-
delorean_cache_adapter.cache_item(
|
59
|
-
klass: self, cache_key: cache_key, item: res
|
60
|
-
)
|
61
|
-
|
62
|
-
# Since we're caching this object and don't want anyone
|
63
|
-
# changing it. FIXME: ideally should freeze this object
|
64
|
-
# recursively.
|
65
|
-
res.freeze if res.respond_to?(:freeze)
|
66
|
-
|
67
|
-
res
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def clear_lookup_cache!
|
72
|
-
::Delorean::Cache.adapter.clear!(klass: self)
|
73
|
-
end
|
9
|
+
base.send :extend, ::Delorean::Functions
|
74
10
|
end
|
75
11
|
end
|
76
12
|
end
|
data/lib/delorean/version.rb
CHANGED
data/lib/delorean_lang.rb
CHANGED
@@ -8,8 +8,10 @@ require 'delorean/base'
|
|
8
8
|
require 'delorean/debug'
|
9
9
|
require 'delorean/delorean'
|
10
10
|
require 'delorean/cache'
|
11
|
+
require 'delorean/const'
|
11
12
|
require 'delorean/engine'
|
12
13
|
require 'delorean/error'
|
14
|
+
require 'delorean/functions'
|
13
15
|
require 'delorean/model'
|
14
16
|
require 'delorean/nodes'
|
15
17
|
require 'delorean/ruby'
|
data/spec/eval_spec.rb
CHANGED
@@ -357,6 +357,20 @@ eoc
|
|
357
357
|
r.should == 867 + 5309
|
358
358
|
end
|
359
359
|
|
360
|
+
it 'should be able to use ruby modules as values and call their methods' do
|
361
|
+
engine.parse defn('A:',
|
362
|
+
' a = DummyModule',
|
363
|
+
' b = a.heres_my_number(867, 5309)',
|
364
|
+
' c = M::DummyModule.heres_my_number(867, 5309)',
|
365
|
+
)
|
366
|
+
# binding.pry
|
367
|
+
r = engine.evaluate('A', 'b')
|
368
|
+
r.should == 867 + 5309
|
369
|
+
|
370
|
+
r = engine.evaluate('A', 'c')
|
371
|
+
r.should == 867 + 5309
|
372
|
+
end
|
373
|
+
|
360
374
|
it 'should ignore undeclared params sent to eval which match attr names' do
|
361
375
|
engine.parse defn('A:',
|
362
376
|
' d = 12',
|
data/spec/spec_helper.rb
CHANGED
@@ -122,6 +122,18 @@ Delorean::Ruby.whitelist.add_method :name2 do |method|
|
|
122
122
|
method.called_on Dummy
|
123
123
|
end
|
124
124
|
|
125
|
+
module DummyModule
|
126
|
+
extend Delorean::Functions
|
127
|
+
|
128
|
+
delorean_fn(:heres_my_number, sig: [0, Float::INFINITY]) do |*a|
|
129
|
+
a.inject(0, :+)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
module M
|
134
|
+
DummyModule = ::DummyModule
|
135
|
+
end
|
136
|
+
|
125
137
|
######################################################################
|
126
138
|
|
127
139
|
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.5.
|
4
|
+
version: 0.5.4
|
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-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -80,6 +80,20 @@ dependencies:
|
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rubocop-performance
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
83
97
|
- !ruby/object:Gem::Dependency
|
84
98
|
name: sqlite3
|
85
99
|
requirement: !ruby/object:Gem::Requirement
|
@@ -123,6 +137,7 @@ files:
|
|
123
137
|
- lib/delorean/delorean.treetop
|
124
138
|
- lib/delorean/engine.rb
|
125
139
|
- lib/delorean/error.rb
|
140
|
+
- lib/delorean/functions.rb
|
126
141
|
- lib/delorean/model.rb
|
127
142
|
- lib/delorean/nodes.rb
|
128
143
|
- lib/delorean/ruby.rb
|