strongman 1.0.0 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/strongman/version.rb +1 -1
  3. data/lib/wimp.rb +180 -0
  4. metadata +5 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 329a7cf887ef3a484d31e6c1f943d86857d045cf33daa6862e8c4720366d2273
4
- data.tar.gz: 1f4f60aebb814692098f07d5f1b20d520ebd7d6ad897dfec1b541f60b6f4efbc
3
+ metadata.gz: 07d29577fed62c5f82058d27afc01a59454115031a4b421fd932f8dc03689655
4
+ data.tar.gz: c85cb8d1d63b3889096e856160a31082dd10a8342ae8c9c3a5d89252b8c1cf78
5
5
  SHA512:
6
- metadata.gz: 1ca56b74b4bec3f25cfcbd51b5aea8d7eb75725d03ee51adb3e43dd00c01925eeb219b4071c204a61fc7ecad08882410bc466fc80a26bc8de90d70445f8993f2
7
- data.tar.gz: 2a986b767a76db4e1a5a087a0e05d9d9f8b632de1c6fd0288e622d2ebd25d926aa897d55ac510e45bf918649e9214611a38cbe895641eea270b1d5dcfcc49372
6
+ metadata.gz: 5760f359bfa016ec0eb414264418fbefbaa49381f85e8dd35fc33df4d31f35bab751d454b341bfb6684ebd943a75100d826c02551e6fb53697c74cced425c4c1
7
+ data.tar.gz: 79fe0d462a995d9d58d7f152519730921d80c4a816aa5054149665a2b263c7361cee8523fc0db231819fe6f4eca8eb07e4ca9247860df57e5875544f86d92324
@@ -1,4 +1,4 @@
1
1
  class Strongman
2
2
  # @!visibility private
3
- VERSION = "1.0.0".freeze
3
+ VERSION = "1.0.5".freeze
4
4
  end
@@ -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.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-07-19 00:00:00.000000000 Z
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.1.2
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