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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d758f3a24c9170a8b7585c330c59a80483ac763640ad847e229f16123d6f9f1b
4
- data.tar.gz: adb70f763fd7daffcdc89a02898846a55132461f6329a67d787ce7fd10f73257
3
+ metadata.gz: bfdabb8b24bdfdf23c4f77ec819f03e89a4a0239ed997368eb1f07ac0bc2cd5b
4
+ data.tar.gz: 97eeec1dfa22f7b683f23e322eb663e6fae4b47e0c2786690c4f44166b8cca51
5
5
  SHA512:
6
- metadata.gz: 346e26235e8de20fc34e0a888b2d3bf98189d73aaa9968db4af9addcf04b8c8577bb0f8e31d5443d85549f0da6c6defae8f554d328fc328732ae029a90704e2f
7
- data.tar.gz: 1be21eed56a1ac95e27e13712831bd45fe0c4e8cf3b896b19e7ad9037b924a9d6543828ddb6d2112cbd25aa77aa464e7e99fd4fd8a4288a89c7d11ad2836915b
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 also need to define the following environment variables to allow Journaled to publish events to your AWS Kinesis event stream:
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
- * `JOURNALED_STREAM_NAME`
58
+ ```ruby
59
+ Journaled.default_stream_name = "my_app_#{Rails.env}_events"
60
+ ```
57
61
 
58
- Special case: if your `Journaled::Event` objects override the
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
- Journaled.default_app_name = 'my_app'
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.default_app_name`
94
+ #### `Journaled.default_stream_name `
92
95
 
93
- This is described in the proceeding paragraph and is used to specify which app name to use, which corresponds to which Journaled Stream to send events too.
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
- ActiveRecords do by default.
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
- if %w(development test).include? Rails.env
23
- require 'rspec/core'
24
- require 'rspec/core/rake_task'
25
- RSpec::Core::RakeTask.new
9
+ require 'rubocop/rake_task'
10
+ RuboCop::RakeTask.new
26
11
 
27
- require 'rubocop/rake_task'
28
- RuboCop::RakeTask.new
12
+ require 'rspec/core'
13
+ require 'rspec/core/rake_task'
14
+ RSpec::Core::RakeTask.new(:spec)
29
15
 
30
- task(:default).clear
16
+ def default_task
31
17
  if ENV['APPRAISAL_INITIALIZED'] || ENV['CI']
32
- task default: %i(rubocop spec)
18
+ %i(rubocop spec)
33
19
  else
34
20
  require 'appraisal'
35
21
  Appraisal::Task.new
36
- task default: :appraisal
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, instance_accessor: false, instance_predicate: false
6
- before_action do
7
- RequestStore.store[:journaled_actor_proc] = self.class._journaled_actor_method_name &&
8
- -> { send(self.class._journaled_actor_method_name) }
9
- end
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
- def perform(serialized_event:, partition_key:, app_name:)
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
- @app_name = app_name
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.stream_name(app_name:)
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, :app_name
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: self.class.stream_name(app_name: app_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/UncommunicativeMethodParamName
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)
@@ -8,8 +8,7 @@ class Journaled::ActorUriProvider
8
8
  private
9
9
 
10
10
  def actor_global_id_uri
11
- actor = RequestStore.store[:journaled_actor_proc]&.call
12
- actor.to_global_id.to_s if actor
11
+ Journaled::Current.actor&.to_global_id&.to_s
13
12
  end
14
13
 
15
14
  def fallback_global_id_uri
@@ -2,27 +2,27 @@ class Journaled::Change
2
2
  include Journaled::Event
3
3
 
4
4
  attr_reader :table_name,
5
- :record_id,
6
- :database_operation,
7
- :logical_operation,
8
- :changes,
9
- :journaled_app_name,
10
- :journaled_enqueue_opts,
11
- :actor
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
- :record_id,
15
- :database_operation,
16
- :logical_operation,
17
- :changes,
18
- :actor
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
- journaled_app_name:,
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
- @journaled_app_name = journaled_app_name
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
- journaled_app_name: journaled_app_name,
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.each_with_object({}) do |(k, v), result|
56
- result[k] = v[index]
56
+ change_hash.transform_values do |v|
57
+ v[index]
57
58
  end
58
59
  end
59
60
 
60
- def journaled_app_name
61
- if model.class.respond_to?(:journaled_app_name)
62
- model.class.journaled_app_name
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.default_app_name
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 journaled_app_name
39
- Journaled.default_app_name
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
- journaled_app_name
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
- base_event_json_schema_validator.validate! serialized_event
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
- app_name: journaled_app_name,
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 json_schema_validator
53
- @json_schema_validator ||= Journaled::JsonSchemaModel::Validator.new(journaled_schema_name)
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,14 @@
1
+ {
2
+ "type": "object",
3
+ "title": "tagged_event",
4
+ "additionalProperties": true,
5
+ "required": [
6
+ "tags"
7
+ ],
8
+ "properties": {
9
+ "tags": {
10
+ "type": "object",
11
+ "additionalProperties": true
12
+ }
13
+ }
14
+ }
@@ -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, Metrics/PerceivedComplexity
3
- unless force || !@klass.respond_to?(:journaled_attribute_names) || @klass.journaled_attribute_names.empty?
4
- conflicting_journaled_attribute_names = if updates.is_a?(Hash)
5
- @klass.journaled_attribute_names & updates.keys.map(&:to_sym)
6
- elsif updates.is_a?(String)
7
- @klass.journaled_attribute_names.select do |a|
8
- updates.match?(/\b(?<!')#{a}(?!')\b/)
9
- end
10
- else
11
- raise "unsupported type '#{updates.class}' for 'updates'"
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:
@@ -1,3 +1,3 @@
1
1
  module Journaled
2
- VERSION = "3.1.0".freeze
2
+ VERSION = "4.2.0".freeze
3
3
  end
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 :default_app_name
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
- !['0', 'false', false, 'f', ''].include?(ENV.fetch('JOURNALED_ENABLED', !development_or_test?))
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
@@ -1,4 +1,4 @@
1
1
  # This file is used by Rack-based servers to start the application.
2
2
 
3
- require ::File.expand_path('../config/environment', __FILE__)
3
+ require ::File.expand_path('config/environment', __dir__)
4
4
  run Rails.application