journaled 4.1.0 → 5.0.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +148 -46
  3. data/Rakefile +10 -24
  4. data/app/jobs/journaled/delivery_job.rb +17 -28
  5. data/app/models/concerns/journaled/changes.rb +5 -5
  6. data/app/models/journaled/change.rb +12 -12
  7. data/app/models/journaled/change_writer.rb +3 -2
  8. data/app/models/journaled/event.rb +1 -1
  9. data/app/models/journaled/writer.rb +32 -15
  10. data/lib/journaled/connection.rb +48 -0
  11. data/lib/journaled/engine.rb +5 -0
  12. data/lib/journaled/errors.rb +3 -0
  13. data/lib/journaled/relation_change_protection.rb +11 -10
  14. data/lib/journaled/rspec.rb +86 -0
  15. data/lib/journaled/transaction_ext.rb +31 -0
  16. data/lib/journaled/version.rb +1 -1
  17. data/lib/journaled.rb +17 -13
  18. metadata +54 -97
  19. data/spec/dummy/README.rdoc +0 -28
  20. data/spec/dummy/Rakefile +0 -6
  21. data/spec/dummy/bin/bundle +0 -3
  22. data/spec/dummy/bin/rails +0 -4
  23. data/spec/dummy/bin/rake +0 -4
  24. data/spec/dummy/config/application.rb +0 -25
  25. data/spec/dummy/config/boot.rb +0 -5
  26. data/spec/dummy/config/database.yml +0 -6
  27. data/spec/dummy/config/environment.rb +0 -5
  28. data/spec/dummy/config/environments/development.rb +0 -24
  29. data/spec/dummy/config/environments/test.rb +0 -37
  30. data/spec/dummy/config/initializers/backtrace_silencers.rb +0 -7
  31. data/spec/dummy/config/initializers/cookies_serializer.rb +0 -3
  32. data/spec/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  33. data/spec/dummy/config/initializers/inflections.rb +0 -16
  34. data/spec/dummy/config/initializers/mime_types.rb +0 -4
  35. data/spec/dummy/config/initializers/session_store.rb +0 -3
  36. data/spec/dummy/config/initializers/wrap_parameters.rb +0 -14
  37. data/spec/dummy/config/locales/en.yml +0 -23
  38. data/spec/dummy/config/routes.rb +0 -56
  39. data/spec/dummy/config/secrets.yml +0 -22
  40. data/spec/dummy/config.ru +0 -4
  41. data/spec/dummy/db/schema.rb +0 -18
  42. data/spec/dummy/public/404.html +0 -67
  43. data/spec/dummy/public/422.html +0 -67
  44. data/spec/dummy/public/500.html +0 -66
  45. data/spec/dummy/public/favicon.ico +0 -0
  46. data/spec/jobs/journaled/delivery_job_spec.rb +0 -276
  47. data/spec/lib/journaled_spec.rb +0 -91
  48. data/spec/models/concerns/journaled/actor_spec.rb +0 -47
  49. data/spec/models/concerns/journaled/changes_spec.rb +0 -106
  50. data/spec/models/database_change_protection_spec.rb +0 -109
  51. data/spec/models/journaled/actor_uri_provider_spec.rb +0 -42
  52. data/spec/models/journaled/change_writer_spec.rb +0 -281
  53. data/spec/models/journaled/event_spec.rb +0 -236
  54. data/spec/models/journaled/json_schema_model/validator_spec.rb +0 -133
  55. data/spec/models/journaled/writer_spec.rb +0 -174
  56. data/spec/rails_helper.rb +0 -19
  57. data/spec/spec_helper.rb +0 -20
  58. data/spec/support/environment_spec_helper.rb +0 -16
@@ -1,91 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Journaled do
4
- it "is enabled in production" do
5
- allow(Rails).to receive(:env).and_return("production")
6
- expect(described_class).to be_enabled
7
- end
8
-
9
- it "is disabled in development" do
10
- allow(Rails).to receive(:env).and_return("development")
11
- expect(described_class).not_to be_enabled
12
- end
13
-
14
- it "is disabled in test" do
15
- allow(Rails).to receive(:env).and_return("test")
16
- expect(described_class).not_to be_enabled
17
- end
18
-
19
- it "is enabled in whatevs" do
20
- allow(Rails).to receive(:env).and_return("whatevs")
21
- expect(described_class).to be_enabled
22
- end
23
-
24
- it "is enabled when explicitly enabled in development" do
25
- with_env(JOURNALED_ENABLED: true) do
26
- allow(Rails).to receive(:env).and_return("development")
27
- expect(described_class).to be_enabled
28
- end
29
- end
30
-
31
- it "is disabled when explicitly disabled in production" do
32
- with_env(JOURNALED_ENABLED: false) do
33
- allow(Rails).to receive(:env).and_return("production")
34
- expect(described_class).not_to be_enabled
35
- end
36
- end
37
-
38
- it "is disabled when explicitly disabled with empty string" do
39
- with_env(JOURNALED_ENABLED: '') do
40
- allow(Rails).to receive(:env).and_return("production")
41
- expect(described_class).not_to be_enabled
42
- end
43
- end
44
-
45
- describe "#actor_uri" do
46
- it "delegates to ActorUriProvider" do
47
- allow(Journaled::ActorUriProvider).to receive(:instance)
48
- .and_return(instance_double(Journaled::ActorUriProvider, actor_uri: "my actor uri"))
49
- expect(described_class.actor_uri).to eq "my actor uri"
50
- end
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
91
- end
@@ -1,47 +0,0 @@
1
- require 'rails_helper'
2
-
3
- # This is a controller mixin, but testing as a model spec!
4
- RSpec.describe Journaled::Actor do
5
- let(:user) { double("User") }
6
- let(:klass) do
7
- Class.new do
8
- cattr_accessor(:before_actions) { [] }
9
-
10
- def self.before_action(method_name, _opts)
11
- before_actions << method_name
12
- end
13
-
14
- include Journaled::Actor
15
-
16
- self.journaled_actor = :current_user
17
-
18
- def current_user
19
- nil
20
- end
21
-
22
- def trigger_before_actions
23
- before_actions.each { |method_name| send(method_name) }
24
- end
25
- end
26
- end
27
-
28
- subject { klass.new }
29
-
30
- it "Stores a thunk returning nil if current_user returns nil" do
31
- subject.trigger_before_actions
32
-
33
- allow(subject).to receive(:current_user).and_return(nil)
34
-
35
- expect(Journaled::Current.journaled_actor_proc.call).to eq nil
36
- expect(Journaled::Current.actor).to eq nil
37
- end
38
-
39
- it "Stores a thunk returning current_user if it is set when called" do
40
- subject.trigger_before_actions
41
-
42
- allow(subject).to receive(:current_user).and_return(user)
43
-
44
- expect(Journaled::Current.journaled_actor_proc.call).to eq user
45
- expect(Journaled::Current.actor).to eq user
46
- end
47
- end
@@ -1,106 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Journaled::Changes do
4
- let(:klass) do
5
- Class.new do
6
- cattr_accessor :after_create_hooks
7
- self.after_create_hooks = []
8
- cattr_accessor :after_save_hooks
9
- self.after_save_hooks = []
10
- cattr_accessor :after_destroy_hooks
11
- self.after_destroy_hooks = []
12
-
13
- def self.after_create(&hook)
14
- after_create_hooks << hook
15
- end
16
-
17
- def self.after_save(opts, &hook)
18
- # This is a back-door assertion to prevent regressions in the module's hook definition behavior
19
- raise "expected `unless: :saved_change_to_id?`" unless opts[:unless] == :saved_change_to_id?
20
-
21
- after_save_hooks << hook
22
- end
23
-
24
- def self.after_destroy(&hook)
25
- after_destroy_hooks << hook
26
- end
27
-
28
- include Journaled::Changes
29
- journal_changes_to :my_heart, as: :change_of_heart
30
-
31
- def trigger_after_create_hooks
32
- after_create_hooks.each { |proc| instance_eval(&proc) }
33
- end
34
-
35
- def trigger_after_save_hooks
36
- after_save_hooks.each { |proc| instance_eval(&proc) }
37
- end
38
-
39
- def trigger_after_destroy_hooks
40
- after_destroy_hooks.each { |proc| instance_eval(&proc) }
41
- end
42
- end
43
- end
44
-
45
- subject { klass.new }
46
-
47
- let(:change_writer) { double(Journaled::ChangeWriter, create: true, update: true, delete: true) }
48
-
49
- before do
50
- allow(Journaled::ChangeWriter).to receive(:new) do |opts|
51
- expect(opts[:model]).to eq(subject)
52
- expect(opts[:change_definition].logical_operation).to eq(:change_of_heart)
53
- change_writer
54
- end
55
- end
56
-
57
- it "can be asserted on with our matcher" do
58
- expect(klass).to journal_changes_to(:my_heart, as: :change_of_heart)
59
-
60
- expect(klass).not_to journal_changes_to(:foobaloo, as: :an_event_to_remember)
61
-
62
- expect {
63
- expect(klass).to journal_changes_to(:foobaloo, as: :an_event_to_remember)
64
- }.to raise_error(/> to journal changes to :foobaloo as :an_event_to_remember/)
65
-
66
- expect {
67
- expect(klass).not_to journal_changes_to(:my_heart, as: :change_of_heart)
68
- }.to raise_error(/> not to journal changes to :my_heart as :change_of_heart/)
69
- end
70
-
71
- it "has a single change definition" do
72
- expect(klass._journaled_change_definitions.length).to eq 1
73
- end
74
-
75
- it "journals create events on create" do
76
- subject.trigger_after_create_hooks
77
-
78
- expect(change_writer).to have_received(:create)
79
- expect(Journaled::ChangeWriter).to have_received(:new)
80
- end
81
-
82
- it "journals update events on save" do
83
- subject.trigger_after_save_hooks
84
-
85
- expect(change_writer).to have_received(:update)
86
- expect(Journaled::ChangeWriter).to have_received(:new)
87
- end
88
-
89
- it "journals delete events on destroy" do
90
- subject.trigger_after_destroy_hooks
91
-
92
- expect(change_writer).to have_received(:delete)
93
- expect(Journaled::ChangeWriter).to have_received(:new)
94
- end
95
-
96
- context 'when DJ opts are provided' do
97
- before do
98
- klass.journal_changes_to :thing, as: :other_thing, enqueue_with: { asdf: 1, foo: 'bar' }
99
- end
100
-
101
- it 'sets them on the model' do
102
- expect(klass.journaled_enqueue_opts).to eq(asdf: 1, foo: 'bar')
103
- expect(klass.new.journaled_enqueue_opts).to eq(asdf: 1, foo: 'bar')
104
- end
105
- end
106
- end
@@ -1,109 +0,0 @@
1
- require 'rails_helper'
2
-
3
- if Rails::VERSION::MAJOR > 5 || (Rails::VERSION::MAJOR == 5 && Rails::VERSION::MINOR >= 2)
4
- # rubocop:disable Rails/SkipsModelValidations
5
- RSpec.describe "Raw database change protection" do
6
- let(:journaled_class) do
7
- Class.new(ActiveRecord::Base) do
8
- include Journaled::Changes
9
-
10
- self.table_name = 'widgets'
11
-
12
- journal_changes_to :name, as: :attempt
13
- end
14
- end
15
-
16
- let(:journaled_class_with_no_journaled_columns) do
17
- Class.new(ActiveRecord::Base) do
18
- include Journaled::Changes
19
-
20
- self.table_name = 'widgets'
21
- end
22
- end
23
-
24
- describe "the relation" do
25
- describe "#update_all" do
26
- it "refuses on journaled columns passed as hash" do
27
- expect { journaled_class.update_all(name: nil) }.to raise_error(/aborted by Journaled/)
28
- end
29
-
30
- it "refuses on journaled columns passed as string" do
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
35
- end
36
-
37
- it "succeeds on unjournaled columns" do
38
- expect { journaled_class.update_all(other_column: "") }.not_to raise_error
39
- end
40
-
41
- it "succeeds when forced on journaled columns" do
42
- expect { journaled_class.update_all({ name: nil }, force: true) }.not_to raise_error
43
- end
44
- end
45
-
46
- describe "#delete" do
47
- it "refuses if journaled columns exist" do
48
- expect { journaled_class.delete(1) }.to raise_error(/aborted by Journaled/)
49
- end
50
-
51
- it "succeeds if no journaled columns exist" do
52
- expect { journaled_class_with_no_journaled_columns.delete(1) }.not_to raise_error
53
- end
54
-
55
- it "succeeds if journaled columns exist when forced" do
56
- expect { journaled_class.delete(1, force: true) }.not_to raise_error
57
- end
58
- end
59
-
60
- describe "#delete_all" do
61
- it "refuses if journaled columns exist" do
62
- expect { journaled_class.delete_all }.to raise_error(/aborted by Journaled/)
63
- end
64
-
65
- it "succeeds if no journaled columns exist" do
66
- expect { journaled_class_with_no_journaled_columns.delete_all }.not_to raise_error
67
- end
68
-
69
- it "succeeds if journaled columns exist when forced" do
70
- expect { journaled_class.delete_all(force: true) }.not_to raise_error
71
- end
72
- end
73
- end
74
-
75
- describe "an instance" do
76
- subject { journaled_class.create!(name: 'foo') }
77
-
78
- describe "#update_columns" do
79
- it "refuses on journaled columns" do
80
- expect { subject.update_columns(name: nil) }.to raise_error(/aborted by Journaled/)
81
- end
82
-
83
- it "succeeds on unjournaled columns" do
84
- expect { subject.update_columns(other_column: "") }.not_to raise_error
85
- end
86
-
87
- it "succeeds when forced on journaled columns" do
88
- expect { subject.update_columns({ name: nil }, force: true) }.not_to raise_error
89
- end
90
- end
91
-
92
- describe "#delete" do
93
- it "refuses if journaled columns exist" do
94
- expect { subject.delete }.to raise_error(/aborted by Journaled/)
95
- end
96
-
97
- it "succeeds if no journaled columns exist" do
98
- instance = journaled_class_with_no_journaled_columns.create!
99
- expect { instance.delete }.not_to raise_error
100
- end
101
-
102
- it "succeeds if journaled columns exist when forced" do
103
- expect { subject.delete(force: true) }.not_to raise_error
104
- end
105
- end
106
- end
107
- end
108
- # rubocop:enable Rails/SkipsModelValidations
109
- end
@@ -1,42 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Journaled::ActorUriProvider do
4
- describe "#actor_uri" do
5
- let(:current_attributes) { double(:[] => nil) }
6
- let(:actor) { double(to_global_id: actor_gid) }
7
- let(:actor_gid) { double(to_s: "my_fancy_gid") }
8
- let(:program_name) { "/usr/local/bin/puma_or_something" }
9
-
10
- subject { described_class.instance }
11
-
12
- around do |example|
13
- orig_program_name = $PROGRAM_NAME
14
- $PROGRAM_NAME = program_name
15
- example.run
16
- $PROGRAM_NAME = orig_program_name
17
- end
18
-
19
- before do
20
- allow(Journaled::Current.instance)
21
- .to receive(:attributes).and_return(current_attributes)
22
- end
23
-
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 })
26
- expect(subject.actor_uri).to eq("my_fancy_gid")
27
- expect(current_attributes).to have_received(:[]).with(:journaled_actor_proc)
28
- end
29
-
30
- context "when running in rake" do
31
- let(:program_name) { "rake" }
32
- it "slurps up command line username if available" do
33
- allow(Etc).to receive(:getlogin).and_return("my_unix_username")
34
- expect(subject.actor_uri).to eq("gid://local/my_unix_username")
35
- end
36
- end
37
-
38
- it "falls back to printing out a GID of bare app name" do
39
- expect(subject.actor_uri).to eq("gid://dummy")
40
- end
41
- end
42
- end
@@ -1,281 +0,0 @@
1
- require 'rails_helper'
2
-
3
- RSpec.describe Journaled::ChangeWriter do
4
- let(:model) do
5
- now = Time.zone.now
6
- double(
7
- "Soldier",
8
- id: 28_473,
9
- class: model_class,
10
- attributes: {
11
- "name" => "bob",
12
- "rank" => "first lieutenant",
13
- "serial_number" => "foobar",
14
- "last_sign_in_at" => now,
15
- },
16
- saved_changes: {
17
- "name" => %w(bill bob),
18
- "last_sign_in_at" => now,
19
- },
20
- journaled_enqueue_opts: {},
21
- )
22
- end
23
-
24
- let(:model_class) do
25
- double(
26
- "SoldierClass",
27
- table_name: "soldiers",
28
- attribute_names: %w(id name rank serial_number last_sign_in_at),
29
- )
30
- end
31
-
32
- let(:change_definition) do
33
- Journaled::ChangeDefinition.new(
34
- attribute_names: %i(name rank serial_number),
35
- logical_operation: "identity_change",
36
- )
37
- end
38
-
39
- let(:faulty_change_definition) do
40
- Journaled::ChangeDefinition.new(
41
- attribute_names: %i(name rank serial_number nonexistent_thingie),
42
- logical_operation: "identity_change",
43
- )
44
- end
45
-
46
- subject { described_class.new(model: model, change_definition: change_definition) }
47
-
48
- it "fails to instantiate with an undefined attribute_name" do
49
- expect { described_class.new(model: model, change_definition: faulty_change_definition) }.to raise_error(/\bnonexistent_thingie\n/)
50
- end
51
-
52
- describe "#relevant_attributes" do
53
- let(:model) do
54
- double(
55
- "Soldier",
56
- id: 28_473,
57
- class: model_class,
58
- attributes: {
59
- "name" => "bill",
60
- "rank" => "first lieutenant",
61
- "serial_number" => "foobar",
62
- "last_sign_in_at" => Time.zone.now,
63
- },
64
- saved_changes: {},
65
- journaled_enqueue_opts: {},
66
- )
67
- end
68
-
69
- it "returns all relevant attributes regardless of saved changes" do
70
- expect(subject.relevant_attributes).to eq(
71
- "name" => "bill",
72
- "rank" => "first lieutenant",
73
- "serial_number" => "foobar",
74
- )
75
- end
76
- end
77
-
78
- describe "#relevant_unperturbed_attributes" do
79
- let(:model) do
80
- double(
81
- "Soldier",
82
- id: 28_473,
83
- class: model_class,
84
- attributes: {
85
- "name" => "bill",
86
- "rank" => "first lieutenant",
87
- "serial_number" => "foobar",
88
- "last_sign_in_at" => Time.zone.now,
89
- },
90
- changes: {
91
- "name" => %w(bob bill),
92
- },
93
- journaled_enqueue_opts: {},
94
- )
95
- end
96
-
97
- it "returns the pre-change value of the attributes, regardless of whether they changed" do
98
- expect(subject.relevant_unperturbed_attributes).to eq(
99
- "name" => "bob",
100
- "rank" => "first lieutenant",
101
- "serial_number" => "foobar",
102
- )
103
- end
104
- end
105
-
106
- describe "#relevant_changed_attributes" do
107
- it "returns only relevant changes" do
108
- expect(subject.relevant_changed_attributes).to eq("name" => "bob")
109
- end
110
- end
111
-
112
- describe "#actor_uri" do
113
- it "delegates to ActorUriProvider" do
114
- allow(Journaled::ActorUriProvider).to receive(:instance).and_return(double(actor_uri: "my actor uri"))
115
- expect(Journaled.actor_uri).to eq "my actor uri"
116
- end
117
- end
118
-
119
- describe "#journaled_change_for" do
120
- it "stores passed changes serialized to json" do
121
- expect(subject.journaled_change_for("update", "name" => "bob").changes).to eq('{"name":"bob"}')
122
- end
123
-
124
- it "stores the model's table_name" do
125
- expect(subject.journaled_change_for("update", {}).table_name).to eq("soldiers")
126
- end
127
-
128
- it "converts the model's record_id to a string" do
129
- expect(subject.journaled_change_for("update", {}).record_id).to eq("28473")
130
- end
131
-
132
- it "stuffs the database operation directly" do
133
- expect(subject.journaled_change_for("update", {}).database_operation).to eq("update")
134
- expect(subject.journaled_change_for("delete", {}).database_operation).to eq("delete")
135
- end
136
-
137
- it "includes logical_operation" do
138
- expect(subject.journaled_change_for("update", {}).logical_operation).to eq("identity_change")
139
- end
140
-
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
- end
144
-
145
- context "with journaled default app name set" do
146
- around do |example|
147
- orig_app_name = Journaled.default_stream_name
148
- Journaled.default_stream_name = "foo"
149
- example.run
150
- Journaled.default_stream_name = orig_app_name
151
- end
152
-
153
- it "passes through default" do
154
- expect(subject.journaled_change_for("update", {}).journaled_stream_name).to eq("foo")
155
- end
156
- end
157
-
158
- context "when model class defines journaled_stream_name" do
159
- before do
160
- allow(model_class).to receive(:journaled_stream_name).and_return("my_app_events")
161
- end
162
-
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
- end
166
- end
167
- end
168
-
169
- context "with journaling stubbed" do
170
- let(:journaled_change) { instance_double(Journaled::Change, journal!: true) }
171
-
172
- before do
173
- allow(Journaled::Change).to receive(:new).and_return(nil) # must be restubbed to work in context
174
- end
175
-
176
- describe "#create" do
177
- let(:model) do
178
- double(
179
- "Soldier",
180
- id: 28_473,
181
- class: model_class,
182
- attributes: {
183
- "name" => "bill",
184
- "rank" => "first lieutenant",
185
- "serial_number" => "foobar",
186
- "last_sign_in_at" => Time.zone.now,
187
- },
188
- saved_changes: {},
189
- journaled_enqueue_opts: {},
190
- )
191
- end
192
-
193
- it "always journals all relevant attributes, even if unchanged" do
194
- allow(Journaled::Change).to receive(:new) do |opts|
195
- expect(opts[:changes]).to eq '{"name":"bill","rank":"first lieutenant","serial_number":"foobar"}'
196
- journaled_change
197
- end
198
-
199
- subject.create
200
-
201
- expect(Journaled::Change).to have_received(:new)
202
- expect(journaled_change).to have_received(:journal!)
203
- end
204
- end
205
-
206
- describe "#update" do
207
- it "journals only relevant changes" do
208
- allow(Journaled::Change).to receive(:new) do |opts|
209
- expect(opts[:changes]).to eq '{"name":"bob"}'
210
- journaled_change
211
- end
212
-
213
- subject.update
214
-
215
- expect(Journaled::Change).to have_received(:new)
216
- expect(journaled_change).to have_received(:journal!)
217
- end
218
-
219
- context "with no changes" do
220
- let(:model) do
221
- double(
222
- "Soldier",
223
- id: 28_473,
224
- class: model_class,
225
- attributes: {
226
- "name" => "bill",
227
- "rank" => "first lieutenant",
228
- "serial_number" => "foobar",
229
- "last_sign_in_at" => Time.zone.now,
230
- },
231
- saved_changes: {},
232
- )
233
- end
234
-
235
- it "doesn't journal" do
236
- subject.update
237
-
238
- expect(Journaled::Change).not_to have_received(:new)
239
- expect(journaled_change).not_to have_received(:journal!)
240
- end
241
- end
242
- end
243
-
244
- describe "#delete" do
245
- let(:model) do
246
- now = Time.zone.now
247
- double(
248
- "Soldier",
249
- id: 28_473,
250
- class: model_class,
251
- attributes: {
252
- "name" => "bob",
253
- "rank" => "first lieutenant",
254
- "serial_number" => "foobar",
255
- "last_sign_in_at" => now,
256
- },
257
- changes: {
258
- "name" => %w(bill bob),
259
- },
260
- journaled_enqueue_opts: {},
261
- )
262
- end
263
-
264
- it "journals the unperturbed values of all relevant attributes" do
265
- allow(Journaled::Change).to receive(:new) do |opts|
266
- expect(JSON.parse(opts[:changes])).to eq(
267
- "name" => "bill",
268
- "rank" => "first lieutenant",
269
- "serial_number" => "foobar",
270
- )
271
- journaled_change
272
- end
273
-
274
- subject.delete
275
-
276
- expect(Journaled::Change).to have_received(:new)
277
- expect(journaled_change).to have_received(:journal!)
278
- end
279
- end
280
- end
281
- end