materialist 3.6.0 → 3.8.2
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 +23 -5
- data/RELEASE_NOTES.md +7 -0
- data/lib/materialist/materializer/internals/dsl.rb +11 -2
- data/lib/materialist/materializer/internals/materializer.rb +11 -7
- data/lib/materialist/version.rb +1 -1
- data/spec/materialist/materializer_spec.rb +84 -39
- metadata +7 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ce1a473a4a182ebbf80364ac65046c146796067e5901d2bec1fac4f24fe9d39f
|
4
|
+
data.tar.gz: 61a6d7e77e9d044490b5a2fc92a9c69ce4ece93237344a6b0332fb0d376c9dd8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b46b98674ee9d5482a505bb6aea3d8683b31b74e25a72fb1b25515d13a8d8c8de4afd7389d20b89ea8bbe0480fcab5a1699347f50bd9f38d807465a88c6816b9
|
7
|
+
data.tar.gz: 72eae6ef4cbe277c1c45a9ad5dff11ada17c8392b3ec709048e6baf1f85e7b195e184d41341b9634513d0520a8c0cd67ddda8f7ba33287f20b3bd918f3f20595
|
data/README.md
CHANGED
@@ -91,7 +91,7 @@ Materialist.configure do |config|
|
|
91
91
|
end
|
92
92
|
```
|
93
93
|
|
94
|
-
- `topics` (only when using in `.subscribe`): A string array of topics to be used.
|
94
|
+
- `topics` (only when using in `.subscribe`): A string array of topics to be used.
|
95
95
|
If not provided nothing would be materialized.
|
96
96
|
- `sidekiq_options` (optional, default: `{ retry: 10 }`) -- See [Sidekiq docs](https://github.com/mperham/sidekiq/wiki/Advanced-Options#workers) for list of options
|
97
97
|
- `api_client` (optional) -- You can pass your `Routemaster::APIClient` instance
|
@@ -155,8 +155,8 @@ class ZoneMaterializer
|
|
155
155
|
|
156
156
|
persist_to :zone
|
157
157
|
|
158
|
-
source_key :source_id do |url|
|
159
|
-
/(\d+)\/?$/.match(url)[1]
|
158
|
+
source_key :source_id do |url, response|
|
159
|
+
/(\d+)\/?$/.match(url)[1] # or response.dig(:some_attr)
|
160
160
|
end
|
161
161
|
|
162
162
|
capture :id, as: :orderweb_id
|
@@ -189,10 +189,10 @@ describes the name of the active record model to be used.
|
|
189
189
|
If missing, materialist skips materialising the resource itself, but will continue
|
190
190
|
with any other functionality -- such as `materialize_link`.
|
191
191
|
|
192
|
-
#### `source_key <column> <
|
192
|
+
#### `source_key <column> <parser_block> (default: url, resource response body[create, update action only])`
|
193
193
|
describes the column used to persist the unique identifier parsed from the url_parser_block.
|
194
194
|
By default the column used is `:source_url` and the original `url` is used as the identifier.
|
195
|
-
Passing an optional block allows you to extract an identifier from the URL.
|
195
|
+
Passing an optional block allows you to extract an identifier from the URL and captured attributes.
|
196
196
|
|
197
197
|
#### `capture <key>, as: <column> (default: key)`
|
198
198
|
describes mapping a resource key to a database column.
|
@@ -243,6 +243,24 @@ class ZoneMaterializer
|
|
243
243
|
end
|
244
244
|
```
|
245
245
|
|
246
|
+
#### `before_upsert_with_payload <method> (, <method>(, ...))`
|
247
|
+
describes the name of the instance method(s) to be invoked before a record is
|
248
|
+
materialized, with the record as it exists in the database, or nil if it has
|
249
|
+
not been created yet. The function will get as a second argument the `payload`
|
250
|
+
of the HTTP response, this can be used to add additional information/persist
|
251
|
+
other objects.
|
252
|
+
|
253
|
+
|
254
|
+
```ruby
|
255
|
+
class ZoneMaterializer
|
256
|
+
include Materialist::Materializer
|
257
|
+
|
258
|
+
before_upsert_with_payload :my_method
|
259
|
+
|
260
|
+
def my_method(record, payload); end
|
261
|
+
end
|
262
|
+
```
|
263
|
+
|
246
264
|
|
247
265
|
#### `after_upsert <method> (, <method>(, ...))` -- also `after_destroy`
|
248
266
|
describes the name of the instance method(s) to be invoked after a record was materialized, with the updated record as a parameter. See above for a similar example implementation.
|
data/RELEASE_NOTES.md
CHANGED
@@ -38,9 +38,18 @@ module Materialist
|
|
38
38
|
__materialist_options[:sidekiq_options] = options
|
39
39
|
end
|
40
40
|
|
41
|
-
def source_key(key, &
|
41
|
+
def source_key(key, &source_key_parser)
|
42
42
|
__materialist_options[:source_key] = key
|
43
|
-
__materialist_options[:
|
43
|
+
__materialist_options[:source_key_parser] = source_key_parser
|
44
|
+
end
|
45
|
+
|
46
|
+
# This method is meant to be used for cases when the application needs
|
47
|
+
# to have access to the `payload` that is returned on the HTTP call.
|
48
|
+
# Such an example would be if the application logic requires all
|
49
|
+
# relationships to be present before the `resource` is saved in the
|
50
|
+
# database. Introduced in https://github.com/deliveroo/materialist/pull/47
|
51
|
+
def before_upsert_with_payload(*method_array)
|
52
|
+
__materialist_options[:before_upsert_with_payload] = method_array
|
44
53
|
end
|
45
54
|
|
46
55
|
def before_upsert(*method_array)
|
@@ -59,9 +59,10 @@ module Materialist
|
|
59
59
|
end
|
60
60
|
|
61
61
|
def upsert_record
|
62
|
-
model_class.find_or_initialize_by(source_lookup(url)).tap do |entity|
|
62
|
+
model_class.find_or_initialize_by(source_lookup(url, resource)).tap do |entity|
|
63
63
|
send_messages(before_upsert, entity) unless before_upsert.nil?
|
64
|
-
|
64
|
+
before_upsert_with_payload&.each { |m| instance.send(m, entity, resource) }
|
65
|
+
entity.update!(attributes)
|
65
66
|
end
|
66
67
|
end
|
67
68
|
|
@@ -74,7 +75,6 @@ module Materialist
|
|
74
75
|
return unless link = resource.dig(:_links, key)
|
75
76
|
return unless materializer_class = MaterializerFactory.class_from_topic(opts.fetch(:topic))
|
76
77
|
|
77
|
-
# TODO: perhaps consider doing this asynchronously some how?
|
78
78
|
materializer_class.perform(link[:href], :noop)
|
79
79
|
end
|
80
80
|
|
@@ -86,6 +86,10 @@ module Materialist
|
|
86
86
|
options[:before_upsert]
|
87
87
|
end
|
88
88
|
|
89
|
+
def before_upsert_with_payload
|
90
|
+
options[:before_upsert_with_payload]
|
91
|
+
end
|
92
|
+
|
89
93
|
def after_upsert
|
90
94
|
options[:after_upsert]
|
91
95
|
end
|
@@ -106,12 +110,12 @@ module Materialist
|
|
106
110
|
options.fetch(:source_key, :source_url)
|
107
111
|
end
|
108
112
|
|
109
|
-
def
|
110
|
-
options[:
|
113
|
+
def source_key_parser
|
114
|
+
options[:source_key_parser] || ->(url, data) { url }
|
111
115
|
end
|
112
116
|
|
113
|
-
def source_lookup(url)
|
114
|
-
@_source_lookup ||= { source_key =>
|
117
|
+
def source_lookup(url, resource={})
|
118
|
+
@_source_lookup ||= { source_key => source_key_parser.call(url, resource) }
|
115
119
|
end
|
116
120
|
|
117
121
|
def attributes
|
data/lib/materialist/version.rb
CHANGED
@@ -50,7 +50,7 @@ RSpec.describe Materialist::Materializer::Internals::Materializer do
|
|
50
50
|
let(:source_body) {{ _links: { city: { href: city_url }}, name: 'jack', age: 30 }}
|
51
51
|
let(:defined_source_id) { 65 }
|
52
52
|
let(:defined_source_url) { "https://service.dev/defined_sources/#{defined_source_id}" }
|
53
|
-
let(:defined_source_body) {{ name: 'ben' }}
|
53
|
+
let(:defined_source_body) {{ name: 'ben', id: defined_source_id }}
|
54
54
|
|
55
55
|
def stub_resource(url, body)
|
56
56
|
stub_request(:get, url).to_return(
|
@@ -199,8 +199,13 @@ RSpec.describe Materialist::Materializer::Internals::Materializer do
|
|
199
199
|
|
200
200
|
persist_to :foobar
|
201
201
|
before_upsert :before_hook
|
202
|
+
before_upsert_with_payload :before_hook_with_payload
|
202
203
|
after_upsert :after_hook
|
203
204
|
|
205
|
+
def before_hook_with_payload(entity, payload)
|
206
|
+
self.actions_called[:before_hook_with_payload] = true
|
207
|
+
end
|
208
|
+
|
204
209
|
def before_hook(entity); self.actions_called[:before_hook] = true; end
|
205
210
|
def after_hook(entity); self.actions_called[:after_hook] = true; end
|
206
211
|
end
|
@@ -209,6 +214,11 @@ RSpec.describe Materialist::Materializer::Internals::Materializer do
|
|
209
214
|
%i(create update noop).each do |action_name|
|
210
215
|
context "when action is :#{action_name}" do
|
211
216
|
let(:action) { action_name }
|
217
|
+
|
218
|
+
it "calls before_upsert_with_payload method" do
|
219
|
+
expect{ perform }.to change { actions_called[:before_hook_with_payload] }
|
220
|
+
end
|
221
|
+
|
212
222
|
it "calls before_upsert method" do
|
213
223
|
expect{ perform }.to change { actions_called[:before_hook] }
|
214
224
|
end
|
@@ -225,8 +235,16 @@ RSpec.describe Materialist::Materializer::Internals::Materializer do
|
|
225
235
|
|
226
236
|
persist_to :foobar
|
227
237
|
before_upsert :before_hook, :before_hook2
|
238
|
+
before_upsert_with_payload :before_hook_with_payload, :before_hook_with_payload2
|
228
239
|
after_upsert :after_hook, :after_hook2
|
229
240
|
|
241
|
+
def before_hook_with_payload(entity, payload)
|
242
|
+
self.actions_called[:before_hook_with_payload] = true
|
243
|
+
end
|
244
|
+
def before_hook_with_payload2(entity, payload)
|
245
|
+
self.actions_called[:before_hook_with_payload2] = true
|
246
|
+
end
|
247
|
+
|
230
248
|
def before_hook(entity); self.actions_called[:before_hook] = true; end
|
231
249
|
def before_hook2(entity); self.actions_called[:before_hook2] = true; end
|
232
250
|
def after_hook(entity); self.actions_called[:after_hook] = true; end
|
@@ -239,6 +257,8 @@ RSpec.describe Materialist::Materializer::Internals::Materializer do
|
|
239
257
|
.and change { actions_called[:before_hook2] }
|
240
258
|
.and change { actions_called[:after_hook] }
|
241
259
|
.and change { actions_called[:after_hook2] }
|
260
|
+
.and change { actions_called[:before_hook_with_payload] }
|
261
|
+
.and change { actions_called[:before_hook_with_payload2] }
|
242
262
|
end
|
243
263
|
end
|
244
264
|
end
|
@@ -360,62 +380,87 @@ RSpec.describe Materialist::Materializer::Internals::Materializer do
|
|
360
380
|
end
|
361
381
|
|
362
382
|
context "entity based on the source_key column" do
|
363
|
-
|
364
|
-
|
365
|
-
|
383
|
+
shared_examples 'an upsert materialization event' do
|
384
|
+
context "when creating" do
|
385
|
+
let(:perform) { subject.perform(defined_source_url, action) }
|
366
386
|
|
367
|
-
|
368
|
-
|
369
|
-
source_key :source_id do |url|
|
370
|
-
url.split('/').last.to_i
|
387
|
+
it "creates based on source_key" do
|
388
|
+
expect{perform}.to change{DefinedSource.count}.by 1
|
371
389
|
end
|
372
390
|
|
373
|
-
|
391
|
+
it "sets the correct source key" do
|
392
|
+
perform
|
393
|
+
inserted = DefinedSource.find_by(source_id: defined_source_id)
|
394
|
+
expect(inserted.source_id).to eq defined_source_id
|
395
|
+
expect(inserted.name).to eq defined_source_body[:name]
|
396
|
+
end
|
374
397
|
end
|
375
|
-
end
|
376
398
|
|
377
|
-
|
378
|
-
|
399
|
+
context "when updating" do
|
400
|
+
let(:action) { :update }
|
401
|
+
let!(:record) { DefinedSource.create!(source_id: defined_source_id, name: 'mo') }
|
402
|
+
let(:perform) { subject.perform(defined_source_url, action) }
|
379
403
|
|
380
|
-
|
381
|
-
|
382
|
-
|
404
|
+
it "updates based on source_key" do
|
405
|
+
perform
|
406
|
+
expect(DefinedSource.count).to eq 1
|
407
|
+
end
|
383
408
|
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
409
|
+
it "updates the existing record" do
|
410
|
+
perform
|
411
|
+
inserted = DefinedSource.find_by(source_id: defined_source_id)
|
412
|
+
expect(inserted.source_id).to eq defined_source_id
|
413
|
+
expect(inserted.name).to eq defined_source_body[:name]
|
414
|
+
end
|
389
415
|
end
|
390
416
|
end
|
391
417
|
|
392
|
-
context
|
393
|
-
|
394
|
-
|
395
|
-
|
418
|
+
context 'with url source key parser' do
|
419
|
+
subject do
|
420
|
+
Class.new do
|
421
|
+
include Materialist::Materializer
|
422
|
+
|
423
|
+
persist_to :defined_source
|
424
|
+
|
425
|
+
source_key :source_id do |url|
|
426
|
+
url.split('/').last.to_i
|
427
|
+
end
|
396
428
|
|
397
|
-
|
398
|
-
|
399
|
-
expect(DefinedSource.count).to eq 1
|
429
|
+
capture :name
|
430
|
+
end
|
400
431
|
end
|
401
432
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
433
|
+
context "when deleting" do
|
434
|
+
let(:action) { :delete }
|
435
|
+
let!(:record) { DefinedSource.create!(source_id: defined_source_id, name: 'mo') }
|
436
|
+
let(:perform) { subject.perform(defined_source_url, action) }
|
437
|
+
|
438
|
+
it "deletes based on source_key" do
|
439
|
+
perform
|
440
|
+
expect(DefinedSource.count).to eq 0
|
441
|
+
end
|
407
442
|
end
|
443
|
+
|
444
|
+
it_behaves_like 'an upsert materialization event'
|
408
445
|
end
|
409
446
|
|
410
|
-
context
|
411
|
-
|
412
|
-
|
413
|
-
|
447
|
+
context 'with resource source key parser' do
|
448
|
+
subject do
|
449
|
+
Class.new do
|
450
|
+
include Materialist::Materializer
|
414
451
|
|
415
|
-
|
416
|
-
|
417
|
-
|
452
|
+
persist_to :defined_source
|
453
|
+
|
454
|
+
source_key :source_id do |_, resource|
|
455
|
+
resource.dig(:id)
|
456
|
+
end
|
457
|
+
|
458
|
+
capture :name
|
459
|
+
capture :id
|
460
|
+
end
|
418
461
|
end
|
462
|
+
|
463
|
+
it_behaves_like 'an upsert materialization event'
|
419
464
|
end
|
420
465
|
end
|
421
466
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: materialist
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.8.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mo Valipour
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-11-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sidekiq
|
@@ -178,7 +178,7 @@ dependencies:
|
|
178
178
|
- - ">="
|
179
179
|
- !ruby/object:Gem::Version
|
180
180
|
version: '0'
|
181
|
-
description:
|
181
|
+
description:
|
182
182
|
email:
|
183
183
|
- valipour@gmail.com
|
184
184
|
executables: []
|
@@ -232,7 +232,7 @@ homepage: http://github.com/deliveroo/materialist
|
|
232
232
|
licenses:
|
233
233
|
- MIT
|
234
234
|
metadata: {}
|
235
|
-
post_install_message:
|
235
|
+
post_install_message:
|
236
236
|
rdoc_options: []
|
237
237
|
require_paths:
|
238
238
|
- lib
|
@@ -247,9 +247,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
247
247
|
- !ruby/object:Gem::Version
|
248
248
|
version: '0'
|
249
249
|
requirements: []
|
250
|
-
|
251
|
-
|
252
|
-
signing_key:
|
250
|
+
rubygems_version: 3.0.9
|
251
|
+
signing_key:
|
253
252
|
specification_version: 4
|
254
253
|
summary: Utilities to materialize routemaster topics
|
255
254
|
test_files:
|