journaled 3.1.0 → 4.2.0
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/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