delorean_lang 0.4.6 → 0.4.7
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 +5 -5
- data/README.md +49 -4
- data/lib/delorean/base.rb +3 -0
- data/lib/delorean/cache/adapters/base.rb +33 -0
- data/lib/delorean/cache/adapters/ruby_cache.rb +54 -0
- data/lib/delorean/cache/adapters.rb +8 -0
- data/lib/delorean/cache.rb +13 -0
- data/lib/delorean/model.rb +46 -0
- data/lib/delorean/version.rb +1 -1
- data/lib/delorean_lang.rb +1 -0
- data/spec/cache_spec.rb +58 -0
- data/spec/spec_helper.rb +5 -0
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: f606ebe7eea8de6ef41dcc2de653b09ed4177308
|
4
|
+
data.tar.gz: b545ce6a844aa31945cd590f73a456a23c68e613
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d934f9c0172103dc572132b42ca728749801d50a3f63e87574d4788c09c995f4a475aaa9c96ebfc6f430732190fc2dd059e285587a9b53f1a406bb65d09289d0
|
7
|
+
data.tar.gz: 87ce61ccb1bce7c5e89df4bb8d0b975adcc4297cfaa8b913c5f318a02cee2d9b658479b446e81c13b73490d26a7d1429387a9d0053cefd26f0beb3e6df19d974
|
data/README.md
CHANGED
@@ -26,9 +26,9 @@ Or add it to your `Gemfile`, etc.
|
|
26
26
|
NodeB: NodeA
|
27
27
|
attr3 = attr1 / NodeA.attr3
|
28
28
|
eom
|
29
|
-
|
29
|
+
|
30
30
|
engine.parse my_code
|
31
|
-
|
31
|
+
|
32
32
|
engine.evaluate("NodeB", %w{attr1 attr2 attr3})
|
33
33
|
|
34
34
|
## The Delorean Language
|
@@ -73,7 +73,7 @@ Therefore, in the above example, `NodeA.attr2` evaluates to `246`.
|
|
73
73
|
Delorean attribute definitions have the following form:
|
74
74
|
|
75
75
|
attr = expression
|
76
|
-
|
76
|
+
|
77
77
|
Where `attr` is an attribute name. Attribute names are required to
|
78
78
|
match the following regular expression: `[a-z][a-zA-Z0-9_]*`. An
|
79
79
|
attribute can only be specified once in a node. Also, any attributes
|
@@ -111,7 +111,7 @@ nodes. The following example shows the usage of inheritance:
|
|
111
111
|
|
112
112
|
IndiaInfo: USInfo
|
113
113
|
teen_min = 10
|
114
|
-
|
114
|
+
|
115
115
|
In this example, node `USInfo` provides a definition of a
|
116
116
|
`is_teenager` when provided with an `age` parameter. Node `IndiaInfo`
|
117
117
|
is derived from `USInfo` and so it shares all of its attribute
|
@@ -135,6 +135,51 @@ TODO: provide details on the following topics:
|
|
135
135
|
This implementation of Delorean "compiles" script code to
|
136
136
|
Ruby.
|
137
137
|
|
138
|
+
### Caching
|
139
|
+
|
140
|
+
Delorean provides `cached_delorean_function` method that will cache result based on arguments.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
cached_delorean_fn :returns_cached_openstruct, sig: 1 do |timestamp|
|
144
|
+
User.all
|
145
|
+
end
|
146
|
+
|
147
|
+
```
|
148
|
+
|
149
|
+
If `::Delorean::Cache.adapter.cache_item?(...)` returns `false` then caching will not be performed.
|
150
|
+
|
151
|
+
By default cache keeps the last 1000 of the results per class. You can override it:
|
152
|
+
|
153
|
+
```ruby
|
154
|
+
|
155
|
+
::Delorean::Cache.adapter = ::Delorean::Cache::Adapters::RubyCache.new(size_per_class: 10)
|
156
|
+
|
157
|
+
```
|
158
|
+
|
159
|
+
If you want use other caching method, you can use your own adapter:
|
160
|
+
|
161
|
+
```ruby
|
162
|
+
|
163
|
+
::Delorean::Cache.adapter = ::My::Custom::Cache::Adapter.new
|
164
|
+
|
165
|
+
```
|
166
|
+
|
167
|
+
Delorean expects it to have methods with following signatures:
|
168
|
+
|
169
|
+
```ruby
|
170
|
+
|
171
|
+
cache_item(klass:, cache_key:, item:)
|
172
|
+
fetch_item(klass:, cache_key:)
|
173
|
+
cache_key(klass:, method_name:, args:)
|
174
|
+
clear!(klass:)
|
175
|
+
clear_all!
|
176
|
+
cache_item?(klass:, method_name:, args:)
|
177
|
+
|
178
|
+
# See lib/delorean/cache/adapters/base.rb
|
179
|
+
|
180
|
+
```
|
181
|
+
|
182
|
+
|
138
183
|
TODO: provide details
|
139
184
|
|
140
185
|
## License
|
data/lib/delorean/base.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
require 'active_support/time'
|
2
2
|
require 'active_record'
|
3
3
|
require 'bigdecimal'
|
4
|
+
require 'delorean/cache'
|
4
5
|
|
5
6
|
module Delorean
|
6
7
|
|
8
|
+
::Delorean::Cache.adapter = ::Delorean::Cache::Adapters::RubyCache.new(size_per_class: 1000)
|
9
|
+
|
7
10
|
TI_TYPES = [Time, ActiveSupport::TimeWithZone]
|
8
11
|
DT_TYPES = [Date] + TI_TYPES
|
9
12
|
NUM_OR_STR = [Numeric, String]
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Delorean
|
2
|
+
module Cache
|
3
|
+
module Adapters
|
4
|
+
class Base
|
5
|
+
def cache_item(klass:, cache_key:, args:)
|
6
|
+
raise 'cache_item is not implemented'
|
7
|
+
end
|
8
|
+
|
9
|
+
def fetch_item(klass:, cache_key:, args:)
|
10
|
+
raise 'fetch_item is not implemented'
|
11
|
+
end
|
12
|
+
|
13
|
+
def cache_key(klass:, method_name:, args:)
|
14
|
+
raise 'cache_key is not implemented'
|
15
|
+
end
|
16
|
+
|
17
|
+
def clear!(klass:)
|
18
|
+
raise 'clear! is not implemented'
|
19
|
+
end
|
20
|
+
|
21
|
+
def clear_all!
|
22
|
+
raise 'clear_all! is not implemented'
|
23
|
+
end
|
24
|
+
|
25
|
+
# Redefine this method in descendants
|
26
|
+
# to avoid caching calls with certain arguments
|
27
|
+
def cache_item?(klass:, method_name:, args:)
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require_relative './base'
|
2
|
+
|
3
|
+
module Delorean
|
4
|
+
module Cache
|
5
|
+
module Adapters
|
6
|
+
class RubyCache < ::Delorean::Cache::Adapters::Base
|
7
|
+
attr_reader :lookup_cache, :size_per_class
|
8
|
+
|
9
|
+
def initialize(size_per_class: 1000)
|
10
|
+
@lookup_cache = {}
|
11
|
+
@size_per_class = size_per_class
|
12
|
+
end
|
13
|
+
|
14
|
+
def cache_item(klass:, cache_key:, item:)
|
15
|
+
lookup_cache[klass] ||= {}
|
16
|
+
clear_outdated_items(klass: klass)
|
17
|
+
lookup_cache[klass][cache_key] = item
|
18
|
+
end
|
19
|
+
|
20
|
+
def fetch_item(klass:, cache_key:)
|
21
|
+
lookup_cache.dig(klass, cache_key)
|
22
|
+
end
|
23
|
+
|
24
|
+
def cache_key(klass:, method_name:, args:)
|
25
|
+
[method_name] + args.map do |arg|
|
26
|
+
next arg.id if arg.respond_to?(:id)
|
27
|
+
arg
|
28
|
+
end.freeze
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear!(klass:)
|
32
|
+
lookup_cache[klass] = {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def clear_all!
|
36
|
+
@lookup_cache = {}
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def clear_outdated_items(klass:)
|
42
|
+
cache_object = lookup_cache[klass]
|
43
|
+
return unless cache_object
|
44
|
+
return if cache_object.count < size_per_class
|
45
|
+
|
46
|
+
max_items = (size_per_class / 5).floor
|
47
|
+
cache_object.keys[0..max_items].each do |key|
|
48
|
+
cache_object.delete(key)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
data/lib/delorean/model.rb
CHANGED
@@ -22,6 +22,52 @@ module Delorean
|
|
22
22
|
self.const_set(name.to_s.upcase+Delorean::SIG, sig)
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
# FIXME IDEA: we just make :cache an argument to delorean_fn.
|
27
|
+
# That way, we don't need the cached_ flavors. It'll make all
|
28
|
+
# this code a lot simpler. We should also just add the :private
|
29
|
+
# mechanism here.
|
30
|
+
|
31
|
+
# By default implements a VERY HACKY class-based (per process) caching
|
32
|
+
# mechanism for database lookup results. Issues include: cached
|
33
|
+
# values are ActiveRecord objects. Query results can be very
|
34
|
+
# large lists which we count as one item in the cache. Caching
|
35
|
+
# mechanism will result in large processes.
|
36
|
+
def cached_delorean_fn(name, options = {}, &block)
|
37
|
+
delorean_fn(name, options) do |args|
|
38
|
+
delorean_cache_adapter = ::Delorean::Cache.adapter
|
39
|
+
# Check if caching should be performed
|
40
|
+
next block.call(args) unless delorean_cache_adapter.cache_item?(
|
41
|
+
klass: self, method_name: name, args: args
|
42
|
+
)
|
43
|
+
|
44
|
+
cache_key = delorean_cache_adapter.cache_key(
|
45
|
+
klass: self, method_name: name, args: [args]
|
46
|
+
)
|
47
|
+
cached_item = delorean_cache_adapter.fetch_item(
|
48
|
+
klass: self, cache_key: cache_key
|
49
|
+
)
|
50
|
+
|
51
|
+
next cached_item if cached_item
|
52
|
+
|
53
|
+
res = block.call(args)
|
54
|
+
|
55
|
+
delorean_cache_adapter.cache_item(
|
56
|
+
klass: self, cache_key: cache_key, item: res
|
57
|
+
)
|
58
|
+
|
59
|
+
# Since we're caching this object and don't want anyone
|
60
|
+
# changing it. FIXME: ideally should freeze this object
|
61
|
+
# recursively.
|
62
|
+
res.freeze if res.respond_to?(:freeze)
|
63
|
+
|
64
|
+
res
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def clear_lookup_cache!
|
69
|
+
::Delorean::Cache.adapter.clear!(klass: self)
|
70
|
+
end
|
25
71
|
end
|
26
72
|
end
|
27
73
|
end
|
data/lib/delorean/version.rb
CHANGED
data/lib/delorean_lang.rb
CHANGED
data/spec/cache_spec.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Delorean cache" do
|
4
|
+
before do
|
5
|
+
Dummy.clear_lookup_cache!
|
6
|
+
end
|
7
|
+
|
8
|
+
it 'allows to set adapter' do
|
9
|
+
::Delorean::Cache.adapter = ::Delorean::Cache::Adapters::RubyCache.new(
|
10
|
+
size_per_class: 100
|
11
|
+
)
|
12
|
+
expect(::Delorean::Cache.adapter.size_per_class).to eq 100
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'uses cache' do
|
16
|
+
expect(OpenStruct).to receive(:new).once.and_call_original
|
17
|
+
|
18
|
+
res1 = Dummy.returns_cached_openstruct
|
19
|
+
res2 = Dummy.returns_cached_openstruct
|
20
|
+
|
21
|
+
expect(res1).to eq res2
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'clears cache' do
|
25
|
+
expect(OpenStruct).to receive(:new).twice.and_call_original
|
26
|
+
Dummy.returns_cached_openstruct
|
27
|
+
Dummy.clear_lookup_cache!
|
28
|
+
Dummy.returns_cached_openstruct
|
29
|
+
end
|
30
|
+
|
31
|
+
it "doesn't use cache with different keys" do
|
32
|
+
expect(OpenStruct).to receive(:new).twice.and_call_original
|
33
|
+
|
34
|
+
Dummy.returns_cached_openstruct(1)
|
35
|
+
Dummy.returns_cached_openstruct(2)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'removes outdated items from cache' do
|
39
|
+
::Delorean::Cache.adapter = ::Delorean::Cache::Adapters::RubyCache.new(
|
40
|
+
size_per_class: 10
|
41
|
+
)
|
42
|
+
|
43
|
+
12.times do |t|
|
44
|
+
Dummy.returns_cached_openstruct(t)
|
45
|
+
end
|
46
|
+
|
47
|
+
item_2 = ::Delorean::Cache.adapter.fetch_item(
|
48
|
+
klass: Dummy, cache_key: [:returns_cached_openstruct, 2]
|
49
|
+
)
|
50
|
+
|
51
|
+
item_10 = ::Delorean::Cache.adapter.fetch_item(
|
52
|
+
klass: Dummy, cache_key: [:returns_cached_openstruct, 10]
|
53
|
+
)
|
54
|
+
|
55
|
+
expect(item_2).to_not be_present
|
56
|
+
expect(item_10).to be_present
|
57
|
+
end
|
58
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -4,6 +4,7 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
|
|
4
4
|
require 'rspec'
|
5
5
|
require 'delorean_lang'
|
6
6
|
require 'active_record'
|
7
|
+
require 'pry'
|
7
8
|
|
8
9
|
# Requires supporting files with custom matchers and macros, etc,
|
9
10
|
# in ./support/ and its subdirectories.
|
@@ -86,6 +87,10 @@ class Dummy < ActiveRecord::Base
|
|
86
87
|
delorean_fn :returns_openstruct, sig: 0 do
|
87
88
|
OpenStruct.new({"abc"=>"def"})
|
88
89
|
end
|
90
|
+
|
91
|
+
cached_delorean_fn :returns_cached_openstruct, sig: 1 do |ts = nil|
|
92
|
+
OpenStruct.new({"abc"=>"def"})
|
93
|
+
end
|
89
94
|
end
|
90
95
|
|
91
96
|
class DummyChild < Dummy
|
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.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arman Bostani
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: treetop
|
@@ -96,6 +96,10 @@ files:
|
|
96
96
|
- Rakefile
|
97
97
|
- delorean.gemspec
|
98
98
|
- lib/delorean/base.rb
|
99
|
+
- lib/delorean/cache.rb
|
100
|
+
- lib/delorean/cache/adapters.rb
|
101
|
+
- lib/delorean/cache/adapters/base.rb
|
102
|
+
- lib/delorean/cache/adapters/ruby_cache.rb
|
99
103
|
- lib/delorean/const.rb
|
100
104
|
- lib/delorean/container.rb
|
101
105
|
- lib/delorean/debug.rb
|
@@ -107,6 +111,7 @@ files:
|
|
107
111
|
- lib/delorean/nodes.rb
|
108
112
|
- lib/delorean/version.rb
|
109
113
|
- lib/delorean_lang.rb
|
114
|
+
- spec/cache_spec.rb
|
110
115
|
- spec/dev_spec.rb
|
111
116
|
- spec/eval_spec.rb
|
112
117
|
- spec/func_spec.rb
|
@@ -132,11 +137,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
137
|
version: '0'
|
133
138
|
requirements: []
|
134
139
|
rubyforge_project:
|
135
|
-
rubygems_version: 2.
|
140
|
+
rubygems_version: 2.6.14
|
136
141
|
signing_key:
|
137
142
|
specification_version: 4
|
138
143
|
summary: Delorean compiler
|
139
144
|
test_files:
|
145
|
+
- spec/cache_spec.rb
|
140
146
|
- spec/dev_spec.rb
|
141
147
|
- spec/eval_spec.rb
|
142
148
|
- spec/func_spec.rb
|