materialist 3.6.0 → 3.8.2
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|