strongman 1.0.0 → 1.0.5
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/lib/strongman/version.rb +1 -1
- data/lib/wimp.rb +180 -0
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 07d29577fed62c5f82058d27afc01a59454115031a4b421fd932f8dc03689655
|
4
|
+
data.tar.gz: c85cb8d1d63b3889096e856160a31082dd10a8342ae8c9c3a5d89252b8c1cf78
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5760f359bfa016ec0eb414264418fbefbaa49381f85e8dd35fc33df4d31f35bab751d454b341bfb6684ebd943a75100d826c02551e6fb53697c74cced425c4c1
|
7
|
+
data.tar.gz: 79fe0d462a995d9d58d7f152519730921d80c4a816aa5054149665a2b263c7361cee8523fc0db231819fe6f4eca8eb07e4ca9247860df57e5875544f86d92324
|
data/lib/strongman/version.rb
CHANGED
data/lib/wimp.rb
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
|
3
|
+
class DelayedResult
|
4
|
+
def initialize(&resolver)
|
5
|
+
@resolver = resolver
|
6
|
+
end
|
7
|
+
|
8
|
+
def then(&block)
|
9
|
+
DelayedResult.new do
|
10
|
+
block.(value!)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.zip(*results, &block)
|
15
|
+
DelayedResult.new do
|
16
|
+
results = results.map(&:value!)
|
17
|
+
block.(*results)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def value!
|
22
|
+
@value ||= @resolver.().yield_self do |val|
|
23
|
+
if val&.is_a?(DelayedResult)
|
24
|
+
val.value!
|
25
|
+
else
|
26
|
+
val
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def value
|
32
|
+
value!
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Wimp
|
37
|
+
class NoCache
|
38
|
+
def compute_if_absent(_key)
|
39
|
+
yield
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Batch
|
44
|
+
attr_accessor :name
|
45
|
+
attr_accessor :fulfilled
|
46
|
+
|
47
|
+
def initialize(loader_block, name: nil, max_batch_size: Float::INFINITY)
|
48
|
+
@name = name
|
49
|
+
@queue = Concurrent::Array.new
|
50
|
+
@lock = Concurrent::ReadWriteLock.new
|
51
|
+
@loader_block = loader_block
|
52
|
+
@max_batch_size = max_batch_size
|
53
|
+
@fulfilled = false
|
54
|
+
@results = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
def queue(key)
|
58
|
+
@queue << key
|
59
|
+
|
60
|
+
DelayedResult.new do
|
61
|
+
results = if @fulfilled
|
62
|
+
@lock.with_read_lock do
|
63
|
+
@results
|
64
|
+
end
|
65
|
+
else
|
66
|
+
@lock.with_write_lock do
|
67
|
+
if @fulfilled
|
68
|
+
@results
|
69
|
+
else
|
70
|
+
@fulfilled = true
|
71
|
+
r = @loader_block.(@queue)
|
72
|
+
@results = if r.is_a?(DelayedResult)
|
73
|
+
normalize_results(r.value!)
|
74
|
+
else
|
75
|
+
normalize_results(r)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
unless results.key?(key)
|
82
|
+
raise StandardError, "Batch loader didn't resolve a key: #{key}. Resolved keys: #{results.keys}"
|
83
|
+
end
|
84
|
+
|
85
|
+
results[key]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def fulfilled?
|
90
|
+
@fulfilled
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def normalize_results(results)
|
96
|
+
unless results.is_a?(Array) || results.is_a?(Hash)
|
97
|
+
raise TypeError, "Batch loader must return an Array or Hash, but returned: #{results.class.name}"
|
98
|
+
end
|
99
|
+
|
100
|
+
if @queue.size != results.size
|
101
|
+
raise StandardError, "Batch loader must be instantiated with function that returns Array or Hash " \
|
102
|
+
"of the same size as provided to it Array of keys" \
|
103
|
+
"\n\nProvided keys:\n#{@queue}" \
|
104
|
+
"\n\nReturned values:\n#{results}"
|
105
|
+
end
|
106
|
+
|
107
|
+
if results.is_a?(Array)
|
108
|
+
Hash[@queue.zip(results)]
|
109
|
+
elsif results.is_a?(Hash)
|
110
|
+
results
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
attr_accessor :cache
|
116
|
+
|
117
|
+
def initialize(**options, &block)
|
118
|
+
unless block_given?
|
119
|
+
raise TypeError, "Dataloader must be constructed with a block which accepts " \
|
120
|
+
"Array and returns either Array or Hash of the same size (or Promise)"
|
121
|
+
end
|
122
|
+
|
123
|
+
@name = options.delete(:name)
|
124
|
+
@cache = if options.has_key?(:cache)
|
125
|
+
options.delete(:cache) || NoCache.new
|
126
|
+
else
|
127
|
+
Concurrent::Map.new
|
128
|
+
end
|
129
|
+
@max_batch_size = options.fetch(:max_batch_size, Float::INFINITY)
|
130
|
+
|
131
|
+
@interceptor = options.delete(:interceptor) || -> (n) {
|
132
|
+
-> (ids) {
|
133
|
+
n.call(ids)
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
@loader_block = @interceptor.call(block)
|
138
|
+
end
|
139
|
+
|
140
|
+
def load(key)
|
141
|
+
if key.nil?
|
142
|
+
raise TypeError, "#load must be called with a key, but got: nil"
|
143
|
+
end
|
144
|
+
|
145
|
+
result = retrieve_from_cache(key) do
|
146
|
+
batch.queue(key)
|
147
|
+
end
|
148
|
+
|
149
|
+
if result.is_a?(DelayedResult)
|
150
|
+
result
|
151
|
+
else
|
152
|
+
DelayedResult.new { result }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def load_many(keys)
|
157
|
+
unless keys.is_a?(Array)
|
158
|
+
raise TypeError, "#load_many must be called with an Array, but got: #{keys.class.name}"
|
159
|
+
end
|
160
|
+
|
161
|
+
delayed_results = keys.map(&method(:load))
|
162
|
+
DelayedResult.new do
|
163
|
+
delayed_results.map(&:value!)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def batch
|
168
|
+
if @batch.nil? || @batch.fulfilled?
|
169
|
+
@batch = Batch.new(@loader_block, name: @name, max_batch_size: @max_batch_size)
|
170
|
+
else
|
171
|
+
@batch
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def retrieve_from_cache(key)
|
176
|
+
@cache.compute_if_absent(key) do
|
177
|
+
yield
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
metadata
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: strongman
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Caleb Land
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
|
-
name: concurrent-ruby
|
15
14
|
requirement: !ruby/object:Gem::Requirement
|
16
15
|
requirements:
|
17
16
|
- - "~>"
|
18
17
|
- !ruby/object:Gem::Version
|
19
18
|
version: '1.1'
|
19
|
+
name: concurrent-ruby
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -36,6 +36,7 @@ files:
|
|
36
36
|
- README.md
|
37
37
|
- lib/strongman.rb
|
38
38
|
- lib/strongman/version.rb
|
39
|
+
- lib/wimp.rb
|
39
40
|
homepage: https://github.com/caleb/strongman
|
40
41
|
licenses:
|
41
42
|
- MIT
|
@@ -55,7 +56,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
55
56
|
- !ruby/object:Gem::Version
|
56
57
|
version: '0'
|
57
58
|
requirements: []
|
58
|
-
rubygems_version: 3.
|
59
|
+
rubygems_version: 3.0.6
|
59
60
|
signing_key:
|
60
61
|
specification_version: 4
|
61
62
|
summary: Batch data loading, works great with graphql
|