journaled 2.5.0 → 4.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +115 -24
  3. data/Rakefile +7 -1
  4. data/app/controllers/concerns/journaled/actor.rb +8 -5
  5. data/app/jobs/journaled/application_job.rb +4 -0
  6. data/app/jobs/journaled/delivery_job.rb +105 -0
  7. data/app/models/journaled/actor_uri_provider.rb +1 -2
  8. data/app/models/journaled/change.rb +3 -3
  9. data/app/models/journaled/change_writer.rb +5 -5
  10. data/app/models/journaled/event.rb +25 -3
  11. data/app/models/journaled/writer.rb +17 -14
  12. data/journaled_schemas/tagged_event.json +14 -0
  13. data/lib/journaled/current.rb +18 -0
  14. data/lib/journaled/engine.rb +5 -0
  15. data/lib/journaled/version.rb +1 -1
  16. data/lib/journaled.rb +35 -5
  17. data/spec/dummy/config/application.rb +1 -2
  18. data/spec/dummy/config/database.yml +4 -19
  19. data/spec/dummy/config/environments/development.rb +0 -13
  20. data/spec/dummy/config/environments/test.rb +3 -5
  21. data/spec/dummy/db/schema.rb +3 -16
  22. data/spec/{models/journaled/delivery_spec.rb → jobs/journaled/delivery_job_spec.rb} +79 -25
  23. data/spec/lib/journaled_spec.rb +39 -0
  24. data/spec/models/concerns/journaled/actor_spec.rb +8 -7
  25. data/spec/models/database_change_protection_spec.rb +19 -25
  26. data/spec/models/journaled/actor_uri_provider_spec.rb +6 -5
  27. data/spec/models/journaled/change_writer_spec.rb +10 -10
  28. data/spec/models/journaled/event_spec.rb +82 -6
  29. data/spec/models/journaled/writer_spec.rb +47 -15
  30. data/spec/rails_helper.rb +1 -2
  31. data/spec/spec_helper.rb +1 -3
  32. metadata +29 -84
  33. data/app/models/journaled/delivery.rb +0 -88
  34. data/config/routes.rb +0 -2
  35. data/lib/journaled/enqueue.rb +0 -13
  36. data/spec/dummy/config/environments/production.rb +0 -78
  37. data/spec/dummy/config/initializers/assets.rb +0 -8
  38. data/spec/dummy/db/migrate/20180606205114_create_delayed_jobs.rb +0 -18
  39. data/spec/support/delayed_job_spec_helper.rb +0 -11
@@ -1,21 +1,6 @@
1
- default: &default
2
- pool: 5
3
- encoding: unicode
4
-
5
- postgresql:
6
- default: &postgres_default
7
- adapter: postgresql
8
- url: <%= ENV['DATABASE_URL'] %>
9
- test: &postgres_test
10
- <<: *postgres_default
11
- url: <%= ENV['DATABASE_URL'] || "postgresql://localhost:#{ENV.fetch('PGPORT', 5432)}/journaled_test" %>
12
- database: journaled_test
13
- development: &postgres_development
14
- <<: *postgres_default
15
- url: <%= ENV['DATABASE_URL'] || "postgresql://localhost:#{ENV.fetch('PGPORT', 5432)}/journaled_development" %>
16
- database: journaled_development
17
-
18
1
  development:
19
- <<: *postgres_development
2
+ adapter: sqlite3
3
+ database: ":memory:"
20
4
  test:
21
- <<: *postgres_test
5
+ adapter: sqlite3
6
+ database: ":memory:"
@@ -13,25 +13,12 @@ Rails.application.configure do
13
13
  config.consider_all_requests_local = true
14
14
  config.action_controller.perform_caching = false
15
15
 
16
- # Don't care if the mailer can't send.
17
- config.action_mailer.raise_delivery_errors = false
18
-
19
16
  # Print deprecation notices to the Rails logger.
20
17
  config.active_support.deprecation = :log
21
18
 
22
19
  # Raise an error on page load if there are pending migrations.
23
20
  config.active_record.migration_error = :page_load
24
21
 
25
- # Debug mode disables concatenation and preprocessing of assets.
26
- # This option may cause significant delays in view rendering with a large
27
- # number of complex assets.
28
- config.assets.debug = true
29
-
30
- # Adds additional error checking when serving assets at runtime.
31
- # Checks for improperly declared sprockets dependencies.
32
- # Raises helpful error messages.
33
- config.assets.raise_runtime_errors = true
34
-
35
22
  # Raises error for missing translations
36
23
  # config.action_view.raise_on_missing_translations = true
37
24
  end
@@ -26,14 +26,12 @@ Rails.application.configure do
26
26
  # Disable request forgery protection in test environment.
27
27
  config.action_controller.allow_forgery_protection = false
28
28
 
29
- # Tell Action Mailer not to deliver emails to the real world.
30
- # The :test delivery method accumulates sent emails in the
31
- # ActionMailer::Base.deliveries array.
32
- config.action_mailer.delivery_method = :test
33
-
34
29
  # Print deprecation notices to the stderr.
35
30
  config.active_support.deprecation = :stderr
36
31
 
37
32
  # Raises error for missing translations
38
33
  # config.action_view.raise_on_missing_translations = true
34
+
35
+ # Use ActiveJob test adapter
36
+ config.active_job.queue_adapter = :test
39
37
  end
@@ -11,21 +11,8 @@
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
13
  ActiveRecord::Schema.define(version: 20180606205114) do
14
- # These are extensions that must be enabled in order to support this database
15
- enable_extension "plpgsql"
16
-
17
- create_table "delayed_jobs", force: :cascade do |t|
18
- t.integer "priority", default: 0, null: false
19
- t.integer "attempts", default: 0, null: false
20
- t.text "handler", null: false
21
- t.text "last_error"
22
- t.datetime "run_at"
23
- t.datetime "locked_at"
24
- t.datetime "failed_at"
25
- t.string "locked_by"
26
- t.string "queue"
27
- t.datetime "created_at"
28
- t.datetime "updated_at"
29
- t.index ["priority", "run_at"], name: "delayed_jobs_priority"
14
+ create_table "widgets", force: :cascade do |t|
15
+ t.string "name"
16
+ t.string "other_column"
30
17
  end
31
18
  end
@@ -1,16 +1,11 @@
1
1
  require 'rails_helper'
2
2
 
3
- RSpec.describe Journaled::Delivery do
3
+ RSpec.describe Journaled::DeliveryJob do
4
4
  let(:stream_name) { 'test_events' }
5
5
  let(:partition_key) { 'fake_partition_key' }
6
6
  let(:serialized_event) { '{"foo":"bar"}' }
7
7
  let(:kinesis_client) { Aws::Kinesis::Client.new(stub_responses: true) }
8
-
9
- around do |example|
10
- with_env(JOURNALED_STREAM_NAME: stream_name) { example.run }
11
- end
12
-
13
- subject { described_class.new serialized_event: serialized_event, partition_key: partition_key, app_name: nil }
8
+ let(:args) { { serialized_event: serialized_event, partition_key: partition_key, stream_name: stream_name } }
14
9
 
15
10
  describe '#perform' do
16
11
  let(:return_status_body) { { shard_id: '101', sequence_number: '101123' } }
@@ -20,15 +15,21 @@ RSpec.describe Journaled::Delivery do
20
15
  allow(Aws::AssumeRoleCredentials).to receive(:new).and_call_original
21
16
  allow(Aws::Kinesis::Client).to receive(:new).and_return kinesis_client
22
17
  kinesis_client.stub_responses(:put_record, return_status_body)
18
+ allow(kinesis_client).to receive(:put_record).and_call_original
23
19
 
24
20
  allow(Journaled).to receive(:enabled?).and_return(true)
25
21
  end
26
22
 
27
23
  it 'makes requests to AWS to put the event on the Kinesis with the correct body' do
28
- event = subject.perform
24
+ event = described_class.perform_now(args)
29
25
 
30
26
  expect(event.shard_id).to eq '101'
31
27
  expect(event.sequence_number).to eq '101123'
28
+ expect(kinesis_client).to have_received(:put_record).with(
29
+ stream_name: 'test_events',
30
+ data: '{"foo":"bar"}',
31
+ partition_key: 'fake_partition_key',
32
+ )
32
33
  end
33
34
 
34
35
  context 'when JOURNALED_IAM_ROLE_ARN is defined' do
@@ -59,7 +60,7 @@ RSpec.describe Journaled::Delivery do
59
60
  end
60
61
 
61
62
  it 'initializes a Kinesis client with assume role credentials' do
62
- subject.perform
63
+ described_class.perform_now(args)
63
64
 
64
65
  expect(Aws::AssumeRoleCredentials).to have_received(:new).with(
65
66
  client: aws_sts_client,
@@ -69,11 +70,64 @@ RSpec.describe Journaled::Delivery do
69
70
  end
70
71
  end
71
72
 
72
- context 'when the stream name env var is NOT set' do
73
+ context 'when the stream name is not set' do
73
74
  let(:stream_name) { nil }
74
75
 
75
76
  it 'raises an KeyError error' do
76
- expect { subject.perform }.to raise_error KeyError
77
+ expect { described_class.perform_now(args) }.to raise_error ArgumentError, 'missing keyword: stream_name'
78
+ end
79
+ end
80
+
81
+ unless Gem::Version.new(Journaled::VERSION) < Gem::Version.new('5.0.0')
82
+ raise <<~MSG
83
+ Hey! I see that you're bumping the version to 5.0!
84
+
85
+ This is a reminder to:
86
+ - remove the `app_name` argument (and related logic) from `Journaled::DeliveryJob`,
87
+ - remove the following app_name test contexts, and
88
+ - make `stream_name` a required kwarg
89
+
90
+ Thanks!
91
+ MSG
92
+ end
93
+
94
+ context 'when the legacy app_name argument is present but nil' do
95
+ let(:args) { { serialized_event: serialized_event, partition_key: partition_key, app_name: nil } }
96
+
97
+ around do |example|
98
+ with_env(JOURNALED_STREAM_NAME: 'legacy_stream_name') { example.run }
99
+ end
100
+
101
+ it 'makes requests to AWS to put the event on the Kinesis with the correct body' do
102
+ event = described_class.perform_now(args)
103
+
104
+ expect(event.shard_id).to eq '101'
105
+ expect(event.sequence_number).to eq '101123'
106
+ expect(kinesis_client).to have_received(:put_record).with(
107
+ stream_name: 'legacy_stream_name',
108
+ data: '{"foo":"bar"}',
109
+ partition_key: 'fake_partition_key',
110
+ )
111
+ end
112
+ end
113
+
114
+ context 'when the legacy app_name argument is present and has a value' do
115
+ let(:args) { { serialized_event: serialized_event, partition_key: partition_key, app_name: 'pied_piper' } }
116
+
117
+ around do |example|
118
+ with_env(PIED_PIPER_JOURNALED_STREAM_NAME: 'pied_piper_events') { example.run }
119
+ end
120
+
121
+ it 'makes requests to AWS to put the event on the Kinesis with the correct body' do
122
+ event = described_class.perform_now(args)
123
+
124
+ expect(event.shard_id).to eq '101'
125
+ expect(event.sequence_number).to eq '101123'
126
+ expect(kinesis_client).to have_received(:put_record).with(
127
+ stream_name: 'pied_piper_events',
128
+ data: '{"foo":"bar"}',
129
+ partition_key: 'fake_partition_key',
130
+ )
77
131
  end
78
132
  end
79
133
 
@@ -83,8 +137,11 @@ RSpec.describe Journaled::Delivery do
83
137
  end
84
138
 
85
139
  it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
86
- expect(Rails.logger).to receive(:error).with("Kinesis Error - Server Error occurred - Aws::Kinesis::Errors::InternalFailure").once
87
- expect { subject.perform }.to raise_error described_class::KinesisTemporaryFailure
140
+ allow(Rails.logger).to receive(:error)
141
+ expect { described_class.perform_now(args) }.to raise_error described_class::KinesisTemporaryFailure
142
+ expect(Rails.logger).to have_received(:error).with(
143
+ "Kinesis Error - Server Error occurred - Aws::Kinesis::Errors::InternalFailure",
144
+ ).once
88
145
  end
89
146
  end
90
147
 
@@ -95,7 +152,7 @@ RSpec.describe Journaled::Delivery do
95
152
 
96
153
  it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
97
154
  allow(Rails.logger).to receive(:error)
98
- expect { subject.perform }.to raise_error described_class::KinesisTemporaryFailure
155
+ expect { described_class.perform_now(args) }.to raise_error described_class::KinesisTemporaryFailure
99
156
  expect(Rails.logger).to have_received(:error).with(/\AKinesis Error/).once
100
157
  end
101
158
  end
@@ -106,7 +163,7 @@ RSpec.describe Journaled::Delivery do
106
163
  end
107
164
 
108
165
  it 'raises an error that subclasses Aws::Kinesis::Errors::ServiceError' do
109
- expect { subject.perform }.to raise_error Aws::Kinesis::Errors::ServiceError
166
+ expect { described_class.perform_now(args) }.to raise_error Aws::Kinesis::Errors::ServiceError
110
167
  end
111
168
  end
112
169
 
@@ -116,7 +173,7 @@ RSpec.describe Journaled::Delivery do
116
173
  end
117
174
 
118
175
  it 'raises an AccessDeniedException error' do
119
- expect { subject.perform }.to raise_error Aws::Kinesis::Errors::AccessDeniedException
176
+ expect { described_class.perform_now(args) }.to raise_error Aws::Kinesis::Errors::AccessDeniedException
120
177
  end
121
178
  end
122
179
 
@@ -126,31 +183,28 @@ RSpec.describe Journaled::Delivery do
126
183
  end
127
184
 
128
185
  it 'catches the error and re-raises a subclass of NotTrulyExceptionalError and logs about the failure' do
129
- expect(Rails.logger).to receive(:error).with(
186
+ allow(Rails.logger).to receive(:error)
187
+ expect { described_class.perform_now(args) }.to raise_error described_class::KinesisTemporaryFailure
188
+ expect(Rails.logger).to have_received(:error).with(
130
189
  "Kinesis Error - Networking Error occurred - Seahorse::Client::NetworkingError",
131
190
  ).once
132
- expect { subject.perform }.to raise_error described_class::KinesisTemporaryFailure
133
191
  end
134
192
  end
135
193
  end
136
194
 
137
- describe "#stream_name" do
195
+ describe ".legacy_computed_stream_name" do
138
196
  context "when app_name is unspecified" do
139
- subject { described_class.new serialized_event: serialized_event, partition_key: partition_key, app_name: nil }
140
-
141
197
  it "is fetched from a prefixed ENV var if specified" do
142
198
  allow(ENV).to receive(:fetch).and_return("expected_stream_name")
143
- expect(subject.stream_name).to eq("expected_stream_name")
199
+ expect(described_class.legacy_computed_stream_name(app_name: nil)).to eq("expected_stream_name")
144
200
  expect(ENV).to have_received(:fetch).with("JOURNALED_STREAM_NAME")
145
201
  end
146
202
  end
147
203
 
148
204
  context "when app_name is specified" do
149
- subject { described_class.new serialized_event: serialized_event, partition_key: partition_key, app_name: "my_funky_app_name" }
150
-
151
205
  it "is fetched from a prefixed ENV var if specified" do
152
206
  allow(ENV).to receive(:fetch).and_return("expected_stream_name")
153
- expect(subject.stream_name).to eq("expected_stream_name")
207
+ expect(described_class.legacy_computed_stream_name(app_name: "my_funky_app_name")).to eq("expected_stream_name")
154
208
  expect(ENV).to have_received(:fetch).with("MY_FUNKY_APP_NAME_JOURNALED_STREAM_NAME")
155
209
  end
156
210
  end
@@ -49,4 +49,43 @@ RSpec.describe Journaled do
49
49
  expect(described_class.actor_uri).to eq "my actor uri"
50
50
  end
51
51
  end
52
+
53
+ describe '.detect_queue_adapter!' do
54
+ it 'raises an error unless the queue adapter is DB-backed' do
55
+ expect { described_class.detect_queue_adapter! }.to raise_error <<~MSG
56
+ Journaled has detected an unsupported ActiveJob queue adapter: `:test`
57
+
58
+ Journaled jobs must be enqueued transactionally to your primary database.
59
+
60
+ Please install the appropriate gems and set `queue_adapter` to one of the following:
61
+ - `:delayed`
62
+ - `:delayed_job`
63
+ - `:good_job`
64
+ - `:que`
65
+
66
+ Read more at https://github.com/Betterment/journaled
67
+ MSG
68
+ end
69
+
70
+ context 'when the queue adapter is supported' do
71
+ before do
72
+ stub_const("ActiveJob::QueueAdapters::DelayedAdapter", Class.new)
73
+ ActiveJob::Base.disable_test_adapter
74
+ ActiveJob::Base.queue_adapter = :delayed
75
+ end
76
+
77
+ around do |example|
78
+ begin
79
+ example.run
80
+ ensure
81
+ ActiveJob::Base.queue_adapter = :test
82
+ ActiveJob::Base.enable_test_adapter(ActiveJob::QueueAdapters::TestAdapter.new)
83
+ end
84
+ end
85
+
86
+ it 'does not raise an error' do
87
+ expect { described_class.detect_queue_adapter! }.not_to raise_error
88
+ end
89
+ end
90
+ end
52
91
  end
@@ -5,11 +5,10 @@ RSpec.describe Journaled::Actor do
5
5
  let(:user) { double("User") }
6
6
  let(:klass) do
7
7
  Class.new do
8
- cattr_accessor :before_actions
9
- self.before_actions = []
8
+ cattr_accessor(:before_actions) { [] }
10
9
 
11
- def self.before_action(&hook)
12
- before_actions << hook
10
+ def self.before_action(method_name, _opts)
11
+ before_actions << method_name
13
12
  end
14
13
 
15
14
  include Journaled::Actor
@@ -21,7 +20,7 @@ RSpec.describe Journaled::Actor do
21
20
  end
22
21
 
23
22
  def trigger_before_actions
24
- before_actions.each { |proc| instance_eval(&proc) }
23
+ before_actions.each { |method_name| send(method_name) }
25
24
  end
26
25
  end
27
26
  end
@@ -33,7 +32,8 @@ RSpec.describe Journaled::Actor do
33
32
 
34
33
  allow(subject).to receive(:current_user).and_return(nil)
35
34
 
36
- expect(RequestStore.store[:journaled_actor_proc].call).to eq nil
35
+ expect(Journaled::Current.journaled_actor_proc.call).to eq nil
36
+ expect(Journaled::Current.actor).to eq nil
37
37
  end
38
38
 
39
39
  it "Stores a thunk returning current_user if it is set when called" do
@@ -41,6 +41,7 @@ RSpec.describe Journaled::Actor do
41
41
 
42
42
  allow(subject).to receive(:current_user).and_return(user)
43
43
 
44
- expect(RequestStore.store[:journaled_actor_proc].call).to eq user
44
+ expect(Journaled::Current.journaled_actor_proc.call).to eq user
45
+ expect(Journaled::Current.actor).to eq user
45
46
  end
46
47
  end
@@ -4,38 +4,42 @@ if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::M
4
4
  # rubocop:disable Rails/SkipsModelValidations
5
5
  RSpec.describe "Raw database change protection" do
6
6
  let(:journaled_class) do
7
- Class.new(Delayed::Job) do
7
+ Class.new(ActiveRecord::Base) do
8
8
  include Journaled::Changes
9
9
 
10
- journal_changes_to :locked_at, as: :attempt
10
+ self.table_name = 'widgets'
11
+
12
+ journal_changes_to :name, as: :attempt
11
13
  end
12
14
  end
13
15
 
14
16
  let(:journaled_class_with_no_journaled_columns) do
15
- Class.new(Delayed::Job) do
17
+ Class.new(ActiveRecord::Base) do
16
18
  include Journaled::Changes
19
+
20
+ self.table_name = 'widgets'
17
21
  end
18
22
  end
19
23
 
20
24
  describe "the relation" do
21
25
  describe "#update_all" do
22
26
  it "refuses on journaled columns passed as hash" do
23
- expect { journaled_class.update_all(locked_at: nil) }.to raise_error(/aborted by Journaled/)
27
+ expect { journaled_class.update_all(name: nil) }.to raise_error(/aborted by Journaled/)
24
28
  end
25
29
 
26
30
  it "refuses on journaled columns passed as string" do
27
- expect { journaled_class.update_all("\"locked_at\" = NULL") }.to raise_error(/aborted by Journaled/)
28
- expect { journaled_class.update_all("locked_at = null") }.to raise_error(/aborted by Journaled/)
29
- expect { journaled_class.update_all("delayed_jobs.locked_at = null") }.to raise_error(/aborted by Journaled/)
30
- expect { journaled_class.update_all("last_error = 'locked_at'") }.not_to raise_error
31
+ expect { journaled_class.update_all("\"name\" = NULL") }.to raise_error(/aborted by Journaled/)
32
+ expect { journaled_class.update_all("name = null") }.to raise_error(/aborted by Journaled/)
33
+ expect { journaled_class.update_all("widgets.name = null") }.to raise_error(/aborted by Journaled/)
34
+ expect { journaled_class.update_all("other_column = 'name'") }.not_to raise_error
31
35
  end
32
36
 
33
37
  it "succeeds on unjournaled columns" do
34
- expect { journaled_class.update_all(handler: "") }.not_to raise_error
38
+ expect { journaled_class.update_all(other_column: "") }.not_to raise_error
35
39
  end
36
40
 
37
41
  it "succeeds when forced on journaled columns" do
38
- expect { journaled_class.update_all({ locked_at: nil }, force: true) }.not_to raise_error
42
+ expect { journaled_class.update_all({ name: nil }, force: true) }.not_to raise_error
39
43
  end
40
44
  end
41
45
 
@@ -69,29 +73,19 @@ if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::M
69
73
  end
70
74
 
71
75
  describe "an instance" do
72
- let(:job) do
73
- module TestJob
74
- def perform
75
- "foo"
76
- end
77
-
78
- module_function :perform
79
- end
80
- end
81
-
82
- subject { journaled_class.enqueue(job) }
76
+ subject { journaled_class.create!(name: 'foo') }
83
77
 
84
78
  describe "#update_columns" do
85
79
  it "refuses on journaled columns" do
86
- expect { subject.update_columns(locked_at: nil) }.to raise_error(/aborted by Journaled/)
80
+ expect { subject.update_columns(name: nil) }.to raise_error(/aborted by Journaled/)
87
81
  end
88
82
 
89
83
  it "succeeds on unjournaled columns" do
90
- expect { subject.update_columns(handler: "") }.not_to raise_error
84
+ expect { subject.update_columns(other_column: "") }.not_to raise_error
91
85
  end
92
86
 
93
87
  it "succeeds when forced on journaled columns" do
94
- expect { subject.update_columns({ locked_at: nil }, force: true) }.not_to raise_error
88
+ expect { subject.update_columns({ name: nil }, force: true) }.not_to raise_error
95
89
  end
96
90
  end
97
91
 
@@ -101,7 +95,7 @@ if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::M
101
95
  end
102
96
 
103
97
  it "succeeds if no journaled columns exist" do
104
- instance = journaled_class_with_no_journaled_columns.enqueue(job)
98
+ instance = journaled_class_with_no_journaled_columns.create!
105
99
  expect { instance.delete }.not_to raise_error
106
100
  end
107
101
 
@@ -2,7 +2,7 @@ require 'rails_helper'
2
2
 
3
3
  RSpec.describe Journaled::ActorUriProvider do
4
4
  describe "#actor_uri" do
5
- let(:request_store) { double(:[] => nil) }
5
+ let(:current_attributes) { double(:[] => nil) }
6
6
  let(:actor) { double(to_global_id: actor_gid) }
7
7
  let(:actor_gid) { double(to_s: "my_fancy_gid") }
8
8
  let(:program_name) { "/usr/local/bin/puma_or_something" }
@@ -17,13 +17,14 @@ RSpec.describe Journaled::ActorUriProvider do
17
17
  end
18
18
 
19
19
  before do
20
- allow(RequestStore).to receive(:store).and_return(request_store)
20
+ allow(Journaled::Current.instance)
21
+ .to receive(:attributes).and_return(current_attributes)
21
22
  end
22
23
 
23
- it "returns the global ID of the entity returned by RequestStore.store[:journaled_actor_proc].call if set" do
24
- allow(request_store).to receive(:[]).and_return(-> { actor })
24
+ it "returns the global ID of the entity returned by Current.journaled_actor_proc.call if set" do
25
+ allow(current_attributes).to receive(:[]).and_return(-> { actor })
25
26
  expect(subject.actor_uri).to eq("my_fancy_gid")
26
- expect(request_store).to have_received(:[]).with(:journaled_actor_proc)
27
+ expect(current_attributes).to have_received(:[]).with(:journaled_actor_proc)
27
28
  end
28
29
 
29
30
  context "when running in rake" do
@@ -138,30 +138,30 @@ RSpec.describe Journaled::ChangeWriter do
138
138
  expect(subject.journaled_change_for("update", {}).logical_operation).to eq("identity_change")
139
139
  end
140
140
 
141
- it "doesn't set journaled_app_name if model class doesn't respond to it" do
142
- expect(subject.journaled_change_for("update", {}).journaled_app_name).to eq(nil)
141
+ it "doesn't set journaled_stream_name if model class doesn't respond to it" do
142
+ expect(subject.journaled_change_for("update", {}).journaled_stream_name).to eq(nil)
143
143
  end
144
144
 
145
145
  context "with journaled default app name set" do
146
146
  around do |example|
147
- orig_app_name = Journaled.default_app_name
148
- Journaled.default_app_name = "foo"
147
+ orig_app_name = Journaled.default_stream_name
148
+ Journaled.default_stream_name = "foo"
149
149
  example.run
150
- Journaled.default_app_name = orig_app_name
150
+ Journaled.default_stream_name = orig_app_name
151
151
  end
152
152
 
153
153
  it "passes through default" do
154
- expect(subject.journaled_change_for("update", {}).journaled_app_name).to eq("foo")
154
+ expect(subject.journaled_change_for("update", {}).journaled_stream_name).to eq("foo")
155
155
  end
156
156
  end
157
157
 
158
- context "when model class defines journaled_app_name" do
158
+ context "when model class defines journaled_stream_name" do
159
159
  before do
160
- allow(model_class).to receive(:journaled_app_name).and_return("my_app")
160
+ allow(model_class).to receive(:journaled_stream_name).and_return("my_app_events")
161
161
  end
162
162
 
163
- it "sets journaled_app_name if model_class responds to it" do
164
- expect(subject.journaled_change_for("update", {}).journaled_app_name).to eq("my_app")
163
+ it "sets journaled_stream_name if model_class responds to it" do
164
+ expect(subject.journaled_change_for("update", {}).journaled_stream_name).to eq("my_app_events")
165
165
  end
166
166
  end
167
167
  end
@@ -65,14 +65,14 @@ RSpec.describe Journaled::Event do
65
65
  end
66
66
  end
67
67
 
68
- describe '#journaled_app_name' do
68
+ describe '#journaled_stream_name' do
69
69
  it 'returns nil in the base class so it can be set explicitly in apps spanning multiple app domains' do
70
- expect(sample_journaled_event.journaled_app_name).to be_nil
70
+ expect(sample_journaled_event.journaled_stream_name).to be_nil
71
71
  end
72
72
 
73
73
  it 'returns the journaled default if set' do
74
- allow(Journaled).to receive(:default_app_name).and_return("my_app")
75
- expect(sample_journaled_event.journaled_app_name).to eq("my_app")
74
+ allow(Journaled).to receive(:default_stream_name).and_return("my_app_events")
75
+ expect(sample_journaled_event.journaled_stream_name).to eq("my_app_events")
76
76
  end
77
77
  end
78
78
 
@@ -88,8 +88,10 @@ RSpec.describe Journaled::Event do
88
88
 
89
89
  context 'when no additional attributes have been defined' do
90
90
  it 'returns the base attributes, and memoizes them after the first call' do
91
- expect(sample_journaled_event.journaled_attributes).to eq id: fake_uuid, created_at: frozen_time, event_type: 'some_class_name'
92
- expect(sample_journaled_event.journaled_attributes).to eq id: fake_uuid, created_at: frozen_time, event_type: 'some_class_name'
91
+ expect(sample_journaled_event.journaled_attributes)
92
+ .to eq id: fake_uuid, created_at: frozen_time, event_type: 'some_class_name'
93
+ expect(sample_journaled_event.journaled_attributes)
94
+ .to eq id: fake_uuid, created_at: frozen_time, event_type: 'some_class_name'
93
95
  end
94
96
  end
95
97
 
@@ -134,6 +136,80 @@ RSpec.describe Journaled::Event do
134
136
  )
135
137
  end
136
138
  end
139
+
140
+ context 'tagged: true' do
141
+ before do
142
+ sample_journaled_event_class.journal_attributes tagged: true
143
+ end
144
+
145
+ it 'adds a "tags" attribute' do
146
+ expect(sample_journaled_event.journaled_attributes).to include(tags: {})
147
+ end
148
+
149
+ context 'when tags are specified' do
150
+ around do |example|
151
+ Journaled.tag!(foo: 'bar')
152
+ Journaled.tagged(baz: 'bat') { example.run }
153
+ end
154
+
155
+ it 'adds them to the journaled attributes' do
156
+ expect(sample_journaled_event.journaled_attributes).to include(
157
+ tags: { foo: 'bar', baz: 'bat' },
158
+ )
159
+ end
160
+
161
+ context 'when even more tags are nested' do
162
+ it 'merges them in and then resets them' do
163
+ Journaled.tagged(oh_no: 'even more tags') do
164
+ expect(sample_journaled_event.journaled_attributes).to include(
165
+ tags: { foo: 'bar', baz: 'bat', oh_no: 'even more tags' },
166
+ )
167
+ end
168
+
169
+ allow(SecureRandom).to receive(:uuid).and_return(fake_uuid).once
170
+ expect(sample_journaled_event_class.new.journaled_attributes).to include(
171
+ tags: { foo: 'bar', baz: 'bat' },
172
+ )
173
+ end
174
+ end
175
+
176
+ context 'when custom event tags are also specified and merged' do
177
+ let(:sample_journaled_event_class) do
178
+ Class.new do
179
+ include Journaled::Event
180
+
181
+ def tags
182
+ super.merge(abc: '123')
183
+ end
184
+ end
185
+ end
186
+
187
+ it 'combines all tags' do
188
+ expect(sample_journaled_event.journaled_attributes).to include(
189
+ tags: { foo: 'bar', baz: 'bat', abc: '123' },
190
+ )
191
+ end
192
+ end
193
+
194
+ context 'when custom event tags are also specified but not merged' do
195
+ let(:sample_journaled_event_class) do
196
+ Class.new do
197
+ include Journaled::Event
198
+
199
+ def tags
200
+ { bananas: 'are great', but_not_actually: 'the best source of potassium' } # it's true
201
+ end
202
+ end
203
+ end
204
+
205
+ it 'adds them to the journaled attributes' do
206
+ expect(sample_journaled_event.journaled_attributes).to include(
207
+ tags: { bananas: 'are great', but_not_actually: 'the best source of potassium' },
208
+ )
209
+ end
210
+ end
211
+ end
212
+ end
137
213
  end
138
214
 
139
215
  describe '#journaled_enqueue_opts, .journaled_enqueue_opts' do