journaled 3.1.0 → 4.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +102 -24
- data/Rakefile +10 -24
- data/app/controllers/concerns/journaled/actor.rb +8 -5
- data/app/jobs/journaled/delivery_job.rb +14 -5
- data/app/models/concerns/journaled/changes.rb +5 -5
- data/app/models/journaled/actor_uri_provider.rb +1 -2
- data/app/models/journaled/change.rb +14 -14
- data/app/models/journaled/change_writer.rb +8 -7
- data/app/models/journaled/event.rb +25 -3
- data/app/models/journaled/writer.rb +13 -11
- data/journaled_schemas/tagged_event.json +14 -0
- data/lib/journaled/current.rb +18 -0
- data/lib/journaled/relation_change_protection.rb +11 -10
- data/lib/journaled/version.rb +1 -1
- data/lib/journaled.rb +15 -3
- data/spec/dummy/config.ru +1 -1
- data/spec/jobs/journaled/delivery_job_spec.rb +72 -17
- data/spec/lib/journaled_spec.rb +4 -6
- data/spec/models/concerns/journaled/actor_spec.rb +8 -7
- data/spec/models/journaled/actor_uri_provider_spec.rb +6 -5
- data/spec/models/journaled/change_writer_spec.rb +10 -10
- data/spec/models/journaled/event_spec.rb +82 -6
- data/spec/models/journaled/json_schema_model/validator_spec.rb +6 -6
- data/spec/models/journaled/writer_spec.rb +37 -8
- data/spec/rails_helper.rb +1 -1
- data/spec/spec_helper.rb +4 -0
- metadata +26 -26
- data/app/models/journaled/delivery.rb +0 -88
- data/spec/models/journaled/delivery_spec.rb +0 -222
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bfdabb8b24bdfdf23c4f77ec819f03e89a4a0239ed997368eb1f07ac0bc2cd5b
|
4
|
+
data.tar.gz: 97eeec1dfa22f7b683f23e322eb663e6fae4b47e0c2786690c4f44166b8cca51
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 78c8b888f9c00a084e8d60c2f271d56a9a188f60287cb30edfbadc7a4f9fc1c3fc3210c763e757658a0040fb57f6f94f1a713fc1f95942db801e2e114dd50ec3
|
7
|
+
data.tar.gz: f0e56609680ecfc3e1f3f3a9d2ba801515762311307f0a208757a70a82ff7c37ba6af040f8ee5a64ebbf7a08a910579c27d65e940680b2406383b8eca022f323
|
data/README.md
CHANGED
@@ -18,22 +18,24 @@ add scoped ordering capability at a future date (and would gladly
|
|
18
18
|
entertain pull requests), but it is presently only designed to provide a
|
19
19
|
durable, eventually consistent record that discrete events happened.
|
20
20
|
|
21
|
+
**See [upgrades](#upgrades) below if you're upgrading from an older `journaled` version!**
|
22
|
+
|
21
23
|
## Installation
|
22
24
|
|
23
25
|
1. If you haven't already,
|
24
26
|
[configure ActiveJob](https://guides.rubyonrails.org/active_job_basics.html)
|
25
27
|
to use one of the following queue adapters:
|
26
28
|
|
27
|
-
- `:delayed_job` (via `delayed_job_active_record`)
|
28
|
-
- `:que`
|
29
|
-
- `:good_job`
|
30
|
-
- `:delayed`
|
29
|
+
- `:delayed_job` (via `delayed_job_active_record`)
|
30
|
+
- `:que`
|
31
|
+
- `:good_job`
|
32
|
+
- `:delayed`
|
31
33
|
|
32
|
-
Ensure that your queue adapter is not configured to delete jobs on failure.
|
34
|
+
Ensure that your queue adapter is not configured to delete jobs on failure.
|
33
35
|
|
34
|
-
**If you launch your application in production mode and the gem detects that
|
35
|
-
`ActiveJob::Base.queue_adapter` is not in the above list, it will raise an exception
|
36
|
-
and prevent your application from performing unsafe journaling.**
|
36
|
+
**If you launch your application in production mode and the gem detects that
|
37
|
+
`ActiveJob::Base.queue_adapter` is not in the above list, it will raise an exception
|
38
|
+
and prevent your application from performing unsafe journaling.**
|
37
39
|
|
38
40
|
2. To integrate Journaled into your application, simply include the gem in your
|
39
41
|
app's Gemfile.
|
@@ -51,20 +53,21 @@ app's Gemfile.
|
|
51
53
|
require 'journaled/rspec'
|
52
54
|
```
|
53
55
|
|
54
|
-
3. You will
|
56
|
+
3. You will need to set the following config in an initializer to allow Journaled to publish events to your AWS Kinesis event stream:
|
55
57
|
|
56
|
-
|
58
|
+
```ruby
|
59
|
+
Journaled.default_stream_name = "my_app_#{Rails.env}_events"
|
60
|
+
```
|
57
61
|
|
58
|
-
|
59
|
-
`#journaled_app_name` method to a non-nil value e.g. `my_app`, you will
|
60
|
-
instead need to provide a corresponding
|
61
|
-
`[upcased_app_name]_JOURNALED_STREAM_NAME` variable for each distinct
|
62
|
-
value, e.g. `MY_APP_JOURNALED_STREAM_NAME`. You can provide a default value
|
63
|
-
for all `Journaled::Event`s in an initializer like this:
|
62
|
+
You may also define a `#journaled_stream_name` method on `Journaled::Event` instances:
|
64
63
|
|
65
64
|
```ruby
|
66
|
-
|
67
|
-
|
65
|
+
def journaled_stream_name
|
66
|
+
"my_app_#{Rails.env}_alternate_events"
|
67
|
+
end
|
68
|
+
````
|
69
|
+
|
70
|
+
3. You may also need to define environment variables to allow Journaled to publish events to your AWS Kinesis event stream:
|
68
71
|
|
69
72
|
You may optionally define the following ENV vars to specify AWS
|
70
73
|
credentials outside of the locations that the AWS SDK normally looks:
|
@@ -88,11 +91,9 @@ app's Gemfile.
|
|
88
91
|
|
89
92
|
Journaling provides a number of different configuation options that can be set in Ruby using an initializer. Those values are:
|
90
93
|
|
91
|
-
#### `Journaled.
|
94
|
+
#### `Journaled.default_stream_name `
|
92
95
|
|
93
|
-
This is described in the
|
94
|
-
This is the default value for events that do NOT specify their own `#journaled_app_name`. For events that define their own `#journaled_app_name` method, that will take precedence over this default.
|
95
|
-
Ex: `Journaled.default_app_name = 'my_app'`
|
96
|
+
This is described in the "Installation" section above, and is used to specify which stream name to use.
|
96
97
|
|
97
98
|
#### `Journaled.job_priority` (default: 20)
|
98
99
|
|
@@ -149,8 +150,8 @@ class ApplicationController < ActionController::Base
|
|
149
150
|
end
|
150
151
|
```
|
151
152
|
|
152
|
-
Your authenticated entity must respond to `#to_global_id`, which
|
153
|
-
|
153
|
+
Your authenticated entity must respond to `#to_global_id`, which ActiveRecords do by default.
|
154
|
+
This feature relies on `ActiveSupport::CurrentAttributes` under the hood.
|
154
155
|
|
155
156
|
Every time any of the specified attributes is modified, or a `User`
|
156
157
|
record is created or destroyed, an event will be sent to Kinesis with the following attributes:
|
@@ -178,6 +179,40 @@ journaling. Note that the less-frequently-used methods `toggle`,
|
|
178
179
|
`increment*`, `decrement*`, and `update_counters` are not intercepted at
|
179
180
|
this time.
|
180
181
|
|
182
|
+
### Tagged Events
|
183
|
+
|
184
|
+
Events may be optionally marked as "tagged." This will add a `tags` field, intended for tracing and
|
185
|
+
auditing purposes.
|
186
|
+
|
187
|
+
```ruby
|
188
|
+
class MyEvent
|
189
|
+
include Journaled::Event
|
190
|
+
|
191
|
+
journal_attributes :attr_1, :attr_2, tagged: true
|
192
|
+
end
|
193
|
+
```
|
194
|
+
|
195
|
+
You may then use `Journaled.tag!` and `Journaled.tagged` inside of your
|
196
|
+
`ApplicationController` and `ApplicationJob` classes (or anywhere else!) to tag
|
197
|
+
all events with request and job metadata:
|
198
|
+
|
199
|
+
```ruby
|
200
|
+
class ApplicationController < ActionController::Base
|
201
|
+
before_action do
|
202
|
+
Journaled.tag!(request_id: request.request_id, current_user_id: current_user&.id)
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
class ApplicationJob < ActiveJob::Base
|
207
|
+
around_perform do |job, perform|
|
208
|
+
Journaled.tagged(job_id: job.id) { perform.call }
|
209
|
+
end
|
210
|
+
end
|
211
|
+
```
|
212
|
+
|
213
|
+
This feature relies on `ActiveSupport::CurrentAttributes` under the hood, so these tags are local to
|
214
|
+
the current thread, and will be cleared at the end of each request request/job.
|
215
|
+
|
181
216
|
#### Testing
|
182
217
|
|
183
218
|
If you use RSpec (and have required `journaled/rspec` in your
|
@@ -317,6 +352,49 @@ Returns one of the following in order of preference:
|
|
317
352
|
In order for this to be most useful, you must configure your controller
|
318
353
|
as described in [Change Journaling](#change-journaling) above.
|
319
354
|
|
355
|
+
## Upgrades
|
356
|
+
|
357
|
+
Since this gem relies on background jobs (which can remain in the queue across
|
358
|
+
code releases), this gem generally aims to support jobs enqueued by the prior
|
359
|
+
gem version.
|
360
|
+
|
361
|
+
As such, **we always recommend upgrading only one major version at a time.**
|
362
|
+
|
363
|
+
### Upgrading from 3.1.0
|
364
|
+
|
365
|
+
Versions of Journaled prior to 4.0 relied directly on environment variables for stream names, but now stream names are configured directly.
|
366
|
+
When upgrading, you can use the following configuration to maintain the previous behavior:
|
367
|
+
|
368
|
+
```ruby
|
369
|
+
Journaled.default_stream_name = ENV['JOURNALED_STREAM_NAME']
|
370
|
+
```
|
371
|
+
|
372
|
+
If you previously specified a `Journaled.default_app_name`, you would have required a more precise environment variable name (substitute `{{upcase_app_name}}`):
|
373
|
+
|
374
|
+
```ruby
|
375
|
+
Journaled.default_stream_name = ENV["{{upcase_app_name}}_JOURNALED_STREAM_NAME"]
|
376
|
+
```
|
377
|
+
|
378
|
+
And if you had defined any `journaled_app_name` methods on `Journaled::Event` instances, you can replace them with the following:
|
379
|
+
|
380
|
+
```ruby
|
381
|
+
def journaled_stream_name
|
382
|
+
ENV['{{upcase_app_name}}_JOURNALED_STREAM_NAME']
|
383
|
+
end
|
384
|
+
```
|
385
|
+
|
386
|
+
When upgrading from 3.1 or below, `Journaled::DeliveryJob` will handle any jobs that remain in the queue by accepting an `app_name` argument. **This behavior will be removed in version 5.0**, so it is recommended to upgrade one major version at a time.
|
387
|
+
|
388
|
+
### Upgrading from 2.5.0
|
389
|
+
|
390
|
+
Versions of Journaled prior to 3.0 relied direclty on `delayed_job` and a "performable" class called `Journaled::Delivery`.
|
391
|
+
In 3.0, this was superceded by an ActiveJob class called `Journaled::DeliveryJob`, but the `Journaled::Delivery` class was not removed until 4.0.
|
392
|
+
|
393
|
+
Therefore, when upgrading from 2.5.0 or below, it is recommended to first upgrade to 3.1.0 (to allow any `Journaled::Delivery` jobs to finish working off) before upgrading to 4.0+.
|
394
|
+
|
395
|
+
The upgrade to 3.1.0 will require a working ActiveJob config. ActiveJob can be configured globally by setting `ActiveJob::Base.queue_adapter`, or just for Journaled jobs by setting `Journaled::DeliveryJob.queue_adapter`.
|
396
|
+
The `:delayed_job` queue adapter will allow you to continue relying on `delayed_job`. You may also consider switching your app(s) to [`delayed`](https://github.com/Betterment/delayed) and using the `:delayed` queue adapter.
|
397
|
+
|
320
398
|
## Future improvements & issue tracking
|
321
399
|
Suggestions for enhancements to this engine are currently being tracked via Github Issues. Please feel free to open an
|
322
400
|
issue for a desired feature, as well as for any observed bugs.
|
data/Rakefile
CHANGED
@@ -4,37 +4,23 @@ rescue LoadError
|
|
4
4
|
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
5
|
end
|
6
6
|
|
7
|
-
require 'rdoc/task'
|
8
|
-
|
9
|
-
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
-
rdoc.rdoc_dir = 'rdoc'
|
11
|
-
rdoc.title = 'Journaled'
|
12
|
-
rdoc.options << '--line-numbers'
|
13
|
-
rdoc.rdoc_files.include('README.rdoc')
|
14
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
-
end
|
16
|
-
|
17
|
-
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
18
|
-
load 'rails/tasks/engine.rake'
|
19
|
-
|
20
7
|
Bundler::GemHelper.install_tasks
|
21
8
|
|
22
|
-
|
23
|
-
|
24
|
-
require 'rspec/core/rake_task'
|
25
|
-
RSpec::Core::RakeTask.new
|
9
|
+
require 'rubocop/rake_task'
|
10
|
+
RuboCop::RakeTask.new
|
26
11
|
|
27
|
-
|
28
|
-
|
12
|
+
require 'rspec/core'
|
13
|
+
require 'rspec/core/rake_task'
|
14
|
+
RSpec::Core::RakeTask.new(:spec)
|
29
15
|
|
30
|
-
|
16
|
+
def default_task
|
31
17
|
if ENV['APPRAISAL_INITIALIZED'] || ENV['CI']
|
32
|
-
|
18
|
+
%i(rubocop spec)
|
33
19
|
else
|
34
20
|
require 'appraisal'
|
35
21
|
Appraisal::Task.new
|
36
|
-
|
22
|
+
%i(appraisal)
|
37
23
|
end
|
38
|
-
|
39
|
-
task 'db:test:prepare' => 'db:setup'
|
40
24
|
end
|
25
|
+
|
26
|
+
task(:default).clear.enhance(default_task)
|
@@ -2,11 +2,14 @@ module Journaled::Actor
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
included do
|
5
|
-
class_attribute :_journaled_actor_method_name,
|
6
|
-
before_action
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
class_attribute :_journaled_actor_method_name, instance_writer: false
|
6
|
+
before_action :_set_journaled_actor_proc, if: :_journaled_actor_method_name?
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def _set_journaled_actor_proc
|
12
|
+
Journaled::Current.journaled_actor_proc = -> { send(self.class._journaled_actor_method_name) }
|
10
13
|
end
|
11
14
|
|
12
15
|
class_methods do
|
@@ -12,15 +12,24 @@ module Journaled
|
|
12
12
|
raise KinesisTemporaryFailure
|
13
13
|
end
|
14
14
|
|
15
|
-
|
15
|
+
UNSPECIFIED = Object.new
|
16
|
+
private_constant :UNSPECIFIED
|
17
|
+
|
18
|
+
def perform(serialized_event:, partition_key:, stream_name: UNSPECIFIED, app_name: UNSPECIFIED)
|
16
19
|
@serialized_event = serialized_event
|
17
20
|
@partition_key = partition_key
|
18
|
-
|
21
|
+
if app_name != UNSPECIFIED
|
22
|
+
@stream_name = self.class.legacy_computed_stream_name(app_name: app_name)
|
23
|
+
elsif stream_name != UNSPECIFIED && !stream_name.nil?
|
24
|
+
@stream_name = stream_name
|
25
|
+
else
|
26
|
+
raise(ArgumentError, 'missing keyword: stream_name')
|
27
|
+
end
|
19
28
|
|
20
29
|
journal!
|
21
30
|
end
|
22
31
|
|
23
|
-
def self.
|
32
|
+
def self.legacy_computed_stream_name(app_name:)
|
24
33
|
env_var_name = [app_name&.upcase, 'JOURNALED_STREAM_NAME'].compact.join('_')
|
25
34
|
ENV.fetch(env_var_name)
|
26
35
|
end
|
@@ -37,7 +46,7 @@ module Journaled
|
|
37
46
|
|
38
47
|
private
|
39
48
|
|
40
|
-
attr_reader :serialized_event, :partition_key, :
|
49
|
+
attr_reader :serialized_event, :partition_key, :stream_name
|
41
50
|
|
42
51
|
def journal!
|
43
52
|
kinesis_client.put_record record if Journaled.enabled?
|
@@ -45,7 +54,7 @@ module Journaled
|
|
45
54
|
|
46
55
|
def record
|
47
56
|
{
|
48
|
-
stream_name:
|
57
|
+
stream_name: stream_name,
|
49
58
|
data: serialized_event,
|
50
59
|
partition_key: partition_key,
|
51
60
|
}
|
@@ -39,8 +39,8 @@ module Journaled::Changes
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
-
def update_columns(attributes, force: false)
|
43
|
-
unless force || self.class.journaled_attribute_names.empty?
|
42
|
+
def update_columns(attributes, opts = { force: false })
|
43
|
+
unless opts[:force] || self.class.journaled_attribute_names.empty?
|
44
44
|
conflicting_journaled_attribute_names = self.class.journaled_attribute_names & attributes.keys.map(&:to_sym)
|
45
45
|
raise(<<~ERROR) if conflicting_journaled_attribute_names.present?
|
46
46
|
#update_columns aborted by Journaled::Changes due to journaled attributes:
|
@@ -56,7 +56,7 @@ module Journaled::Changes
|
|
56
56
|
end
|
57
57
|
|
58
58
|
class_methods do
|
59
|
-
def journal_changes_to(*attribute_names, as:, enqueue_with: {}) # rubocop:disable Naming/
|
59
|
+
def journal_changes_to(*attribute_names, as:, enqueue_with: {}) # rubocop:disable Naming/MethodParameterName
|
60
60
|
if attribute_names.empty? || attribute_names.any? { |n| !n.is_a?(Symbol) }
|
61
61
|
raise "one or more symbol attribute_name arguments is required"
|
62
62
|
end
|
@@ -69,8 +69,8 @@ module Journaled::Changes
|
|
69
69
|
end
|
70
70
|
|
71
71
|
if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR >= 2)
|
72
|
-
def delete(id_or_array, force: false)
|
73
|
-
if force || journaled_attribute_names.empty?
|
72
|
+
def delete(id_or_array, opts = { force: false })
|
73
|
+
if opts[:force] || journaled_attribute_names.empty?
|
74
74
|
where(primary_key => id_or_array).delete_all(force: true)
|
75
75
|
else
|
76
76
|
raise(<<~ERROR)
|
@@ -2,27 +2,27 @@ class Journaled::Change
|
|
2
2
|
include Journaled::Event
|
3
3
|
|
4
4
|
attr_reader :table_name,
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
:record_id,
|
6
|
+
:database_operation,
|
7
|
+
:logical_operation,
|
8
|
+
:changes,
|
9
|
+
:journaled_stream_name,
|
10
|
+
:journaled_enqueue_opts,
|
11
|
+
:actor
|
12
12
|
|
13
13
|
journal_attributes :table_name,
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
14
|
+
:record_id,
|
15
|
+
:database_operation,
|
16
|
+
:logical_operation,
|
17
|
+
:changes,
|
18
|
+
:actor
|
19
19
|
|
20
20
|
def initialize(table_name:,
|
21
21
|
record_id:,
|
22
22
|
database_operation:,
|
23
23
|
logical_operation:,
|
24
24
|
changes:,
|
25
|
-
|
25
|
+
journaled_stream_name:,
|
26
26
|
journaled_enqueue_opts:,
|
27
27
|
actor:)
|
28
28
|
@table_name = table_name
|
@@ -30,7 +30,7 @@ class Journaled::Change
|
|
30
30
|
@database_operation = database_operation
|
31
31
|
@logical_operation = logical_operation
|
32
32
|
@changes = changes
|
33
|
-
@
|
33
|
+
@journaled_stream_name = journaled_stream_name
|
34
34
|
@journaled_enqueue_opts = journaled_enqueue_opts
|
35
35
|
@actor = actor
|
36
36
|
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
class Journaled::ChangeWriter
|
2
2
|
attr_reader :model, :change_definition
|
3
|
+
|
3
4
|
delegate :attribute_names, :logical_operation, to: :change_definition
|
4
5
|
|
5
6
|
def initialize(model:, change_definition:)
|
@@ -27,7 +28,7 @@ class Journaled::ChangeWriter
|
|
27
28
|
database_operation: database_operation,
|
28
29
|
logical_operation: logical_operation,
|
29
30
|
changes: JSON.dump(changes),
|
30
|
-
|
31
|
+
journaled_stream_name: journaled_stream_name,
|
31
32
|
journaled_enqueue_opts: model.journaled_enqueue_opts,
|
32
33
|
actor: actor_uri,
|
33
34
|
)
|
@@ -52,16 +53,16 @@ class Journaled::ChangeWriter
|
|
52
53
|
private
|
53
54
|
|
54
55
|
def pluck_changed_values(change_hash, index:)
|
55
|
-
change_hash.
|
56
|
-
|
56
|
+
change_hash.transform_values do |v|
|
57
|
+
v[index]
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
60
|
-
def
|
61
|
-
if model.class.respond_to?(:
|
62
|
-
model.class.
|
61
|
+
def journaled_stream_name
|
62
|
+
if model.class.respond_to?(:journaled_stream_name)
|
63
|
+
model.class.journaled_stream_name
|
63
64
|
else
|
64
|
-
Journaled.
|
65
|
+
Journaled.default_stream_name
|
65
66
|
end
|
66
67
|
end
|
67
68
|
end
|
@@ -35,16 +35,22 @@ module Journaled::Event
|
|
35
35
|
event_type
|
36
36
|
end
|
37
37
|
|
38
|
-
def
|
39
|
-
Journaled.
|
38
|
+
def journaled_stream_name
|
39
|
+
Journaled.default_stream_name
|
40
|
+
end
|
41
|
+
|
42
|
+
def tagged?
|
43
|
+
false
|
40
44
|
end
|
41
45
|
|
42
46
|
private
|
43
47
|
|
44
48
|
class_methods do
|
45
|
-
def journal_attributes(*args, enqueue_with: {})
|
49
|
+
def journal_attributes(*args, enqueue_with: {}, tagged: false)
|
46
50
|
journaled_attributes.concat(args)
|
47
51
|
journaled_enqueue_opts.merge!(enqueue_with)
|
52
|
+
|
53
|
+
include Tagged if tagged
|
48
54
|
end
|
49
55
|
|
50
56
|
def journaled_attributes
|
@@ -61,4 +67,20 @@ module Journaled::Event
|
|
61
67
|
|
62
68
|
journal_attributes :id, :event_type, :created_at
|
63
69
|
end
|
70
|
+
|
71
|
+
module Tagged
|
72
|
+
extend ActiveSupport::Concern
|
73
|
+
|
74
|
+
included do
|
75
|
+
journaled_attributes << :tags
|
76
|
+
end
|
77
|
+
|
78
|
+
def tags
|
79
|
+
Journaled::Current.tags
|
80
|
+
end
|
81
|
+
|
82
|
+
def tagged?
|
83
|
+
true
|
84
|
+
end
|
85
|
+
end
|
64
86
|
end
|
@@ -3,7 +3,7 @@ class Journaled::Writer
|
|
3
3
|
journaled_schema_name
|
4
4
|
journaled_partition_key
|
5
5
|
journaled_attributes
|
6
|
-
|
6
|
+
journaled_stream_name
|
7
7
|
journaled_enqueue_opts
|
8
8
|
).freeze
|
9
9
|
|
@@ -25,23 +25,29 @@ class Journaled::Writer
|
|
25
25
|
end
|
26
26
|
|
27
27
|
def journal!
|
28
|
-
|
29
|
-
json_schema_validator.validate! serialized_event
|
28
|
+
validate!
|
30
29
|
Journaled::DeliveryJob
|
31
30
|
.set(journaled_enqueue_opts.reverse_merge(priority: Journaled.job_priority))
|
32
|
-
.perform_later(delivery_perform_args)
|
31
|
+
.perform_later(**delivery_perform_args)
|
33
32
|
end
|
34
33
|
|
35
34
|
private
|
36
35
|
|
37
36
|
attr_reader :journaled_event
|
37
|
+
|
38
38
|
delegate(*EVENT_METHOD_NAMES, to: :journaled_event)
|
39
39
|
|
40
|
+
def validate!
|
41
|
+
schema_validator('base_event').validate! serialized_event
|
42
|
+
schema_validator('tagged_event').validate! serialized_event if journaled_event.tagged?
|
43
|
+
schema_validator(journaled_schema_name).validate! serialized_event
|
44
|
+
end
|
45
|
+
|
40
46
|
def delivery_perform_args
|
41
47
|
{
|
42
48
|
serialized_event: serialized_event,
|
43
49
|
partition_key: journaled_partition_key,
|
44
|
-
|
50
|
+
stream_name: journaled_stream_name,
|
45
51
|
}
|
46
52
|
end
|
47
53
|
|
@@ -49,12 +55,8 @@ class Journaled::Writer
|
|
49
55
|
@serialized_event ||= journaled_attributes.to_json
|
50
56
|
end
|
51
57
|
|
52
|
-
def
|
53
|
-
|
54
|
-
end
|
55
|
-
|
56
|
-
def base_event_json_schema_validator
|
57
|
-
@base_event_json_schema_validator ||= Journaled::JsonSchemaModel::Validator.new('base_event')
|
58
|
+
def schema_validator(schema_name)
|
59
|
+
Journaled::JsonSchemaModel::Validator.new(schema_name)
|
58
60
|
end
|
59
61
|
|
60
62
|
def respond_to_all?(object, method_names)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Journaled
|
2
|
+
class Current < ActiveSupport::CurrentAttributes
|
3
|
+
attribute :tags
|
4
|
+
attribute :journaled_actor_proc
|
5
|
+
|
6
|
+
def tags=(value)
|
7
|
+
super(value.freeze)
|
8
|
+
end
|
9
|
+
|
10
|
+
def tags
|
11
|
+
attributes[:tags] ||= {}.freeze
|
12
|
+
end
|
13
|
+
|
14
|
+
def actor
|
15
|
+
journaled_actor_proc&.call
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -1,14 +1,15 @@
|
|
1
1
|
module Journaled::RelationChangeProtection
|
2
|
-
def update_all(updates, force: false) # rubocop:disable Metrics/AbcSize
|
3
|
-
unless force || !@klass.respond_to?(:journaled_attribute_names) || @klass.journaled_attribute_names.empty?
|
4
|
-
conflicting_journaled_attribute_names =
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
2
|
+
def update_all(updates, opts = { force: false }) # rubocop:disable Metrics/AbcSize
|
3
|
+
unless opts[:force] || !@klass.respond_to?(:journaled_attribute_names) || @klass.journaled_attribute_names.empty?
|
4
|
+
conflicting_journaled_attribute_names = case updates
|
5
|
+
when Hash
|
6
|
+
@klass.journaled_attribute_names & updates.keys.map(&:to_sym)
|
7
|
+
when String
|
8
|
+
@klass.journaled_attribute_names.select do |a|
|
9
|
+
updates.match?(/\b(?<!')#{a}(?!')\b/)
|
10
|
+
end
|
11
|
+
else
|
12
|
+
raise "unsupported type '#{updates.class}' for 'updates'"
|
12
13
|
end
|
13
14
|
raise(<<~ERROR) if conflicting_journaled_attribute_names.present?
|
14
15
|
.update_all aborted by Journaled::Changes due to journaled attributes:
|
data/lib/journaled/version.rb
CHANGED
data/lib/journaled.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
require "aws-sdk-kinesis"
|
2
2
|
require "active_job"
|
3
3
|
require "json-schema"
|
4
|
-
require "request_store"
|
5
4
|
|
6
5
|
require "journaled/engine"
|
6
|
+
require "journaled/current"
|
7
7
|
|
8
8
|
module Journaled
|
9
9
|
SUPPORTED_QUEUE_ADAPTERS = %w(delayed delayed_job good_job que).freeze
|
10
10
|
|
11
|
-
mattr_accessor :
|
11
|
+
mattr_accessor :default_stream_name
|
12
12
|
mattr_accessor(:job_priority) { 20 }
|
13
13
|
mattr_accessor(:http_idle_timeout) { 5 }
|
14
14
|
mattr_accessor(:http_open_timeout) { 2 }
|
@@ -20,7 +20,7 @@ module Journaled
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def enabled?
|
23
|
-
|
23
|
+
['0', 'false', false, 'f', ''].exclude?(ENV.fetch('JOURNALED_ENABLED', !development_or_test?))
|
24
24
|
end
|
25
25
|
|
26
26
|
def schema_providers
|
@@ -51,5 +51,17 @@ module Journaled
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
def self.tagged(**tags)
|
55
|
+
existing_tags = Current.tags
|
56
|
+
tag!(**tags)
|
57
|
+
yield
|
58
|
+
ensure
|
59
|
+
Current.tags = existing_tags
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.tag!(**tags)
|
63
|
+
Current.tags = Current.tags.merge(tags)
|
64
|
+
end
|
65
|
+
|
54
66
|
module_function :development_or_test?, :enabled?, :schema_providers, :commit_hash, :actor_uri, :detect_queue_adapter!
|
55
67
|
end
|
data/spec/dummy/config.ru
CHANGED