journaled 5.0.0 → 5.1.1

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: ac63d8988a5cbbd63340c019b82642a039a9dfee81f46d83b15e5a71d83e9cb7
4
- data.tar.gz: ae809e4b05901d6eb73eb0196119999be2b0fd414c3af3c4bb30d280216098cf
3
+ metadata.gz: 4d27e8931e64963620ecd1fd44c4a46c244470a6715dc19c56a7a1a29aaa97b6
4
+ data.tar.gz: f95df32775abd3f65cb8d526c60fae948fc69425957a378e4007765fdade14c0
5
5
  SHA512:
6
- metadata.gz: 72bcbe0ae43717280eb8a0b3383672cc87762e4c2b9b082778f1beac249004ef90a41e32e6fbabd693377a4d0af6cf6fc78df8a2b8ff44067276dd502cece63a
7
- data.tar.gz: b21401ba2cf6155a25f50f1c58c634e3036754ec69b8d7f287c86408f69819aed5cfac1a43d55636a2be461545096b1510318a6ce665bf47595f30c8b90bceb9
6
+ metadata.gz: 1cd4a144b2cc6dbb398b082dfe97b93829ab5d0e2ad7c22fb807ff63be9d9f5e1b24278db14eb947ce247c92f8fd09e135010ce9bc546e6e34589f98fdccaeb9
7
+ data.tar.gz: 10da7fde07cfe2e7d654978a99c0734976f7199efd1d1d5e6aa11684d30d537d625acf238e0f636f8dec75b298920a8bf3f64a8c41105ddab40f624b03f1e8d6
data/README.md CHANGED
@@ -122,25 +122,19 @@ Both model-level directives accept additional options to be passed into ActiveJo
122
122
  # For change journaling:
123
123
  journal_changes_to :email, as: :identity_change, enqueue_with: { priority: 10 }
124
124
 
125
+ # For audit logging:
126
+ has_audit_log enqueue_with: { priority: 30 }
127
+
125
128
  # Or for custom journaling:
126
129
  journal_attributes :email, enqueue_with: { priority: 20, queue: 'journaled' }
127
130
  ```
128
131
 
129
- ### Change Journaling
130
-
131
- Out of the box, `Journaled` provides an event type and ActiveRecord
132
- mix-in for durably journaling changes to your model, implemented via
133
- ActiveRecord hooks. Use it like so:
134
-
135
- ```ruby
136
- class User < ApplicationRecord
137
- include Journaled::Changes
138
-
139
- journal_changes_to :email, :first_name, :last_name, as: :identity_change
140
- end
141
- ```
132
+ ### Attribution
142
133
 
143
- Add the following to your controller base class for attribution:
134
+ Before using `Journaled::Changes` or `Journaled::AuditLog`, you will want to
135
+ set up automatic "actor" attribution (i.e. tracking the current user session).
136
+ To enable this feature, add the following to your controller base class for
137
+ attribution:
144
138
 
145
139
  ```ruby
146
140
  class ApplicationController < ActionController::Base
@@ -153,6 +147,20 @@ end
153
147
  Your authenticated entity must respond to `#to_global_id`, which ActiveRecords do by default.
154
148
  This feature relies on `ActiveSupport::CurrentAttributes` under the hood.
155
149
 
150
+ ### Change Journaling with `Journaled::Changes`
151
+
152
+ Out of the box, `Journaled` provides an event type and ActiveRecord
153
+ mix-in for durably journaling changes to your model, implemented via
154
+ ActiveRecord hooks. Use it like so:
155
+
156
+ ```ruby
157
+ class User < ApplicationRecord
158
+ include Journaled::Changes
159
+
160
+ journal_changes_to :email, :first_name, :last_name, as: :identity_change
161
+ end
162
+ ```
163
+
156
164
  Every time any of the specified attributes is modified, or a `User`
157
165
  record is created or destroyed, an event will be sent to Kinesis with the following attributes:
158
166
 
@@ -179,6 +187,213 @@ journaling. Note that the less-frequently-used methods `toggle`,
179
187
  `increment*`, `decrement*`, and `update_counters` are not intercepted at
180
188
  this time.
181
189
 
190
+
191
+ ### Audit Logging with `Journaled::AuditLog`
192
+
193
+ Journaled includes a feature for producing audit logs of changes to your model.
194
+ Unlike `Journaled::Changes`, which will emit individual sets of changes as
195
+ "logical" events, `Journaled::AuditLog` will log all changes in their entirety,
196
+ unless otherwise told to ignore changes to specific columns.
197
+
198
+ This behavior is similar to
199
+ [papertrail](https://github.com/paper-trail-gem/paper_trail),
200
+ [audited](https://github.com/collectiveidea/audited), and
201
+ [logidze](https://github.com/palkan/logidze), except instead of storing
202
+ changes/versions locally (in your application's database), it emits them to
203
+ Kinesis (as Journaled events).
204
+
205
+ #### Audit Log Configuration
206
+
207
+ To enable audit logging for a given record, use the `has_audit_log` directive:
208
+
209
+ ```ruby
210
+ class MyModel < ApplicationRecord
211
+ has_audit_log
212
+
213
+ # This class will now be audited,
214
+ # but will ignore changes to `created_at` and `updated_at`.
215
+ end
216
+ ```
217
+
218
+ To ignore changes to additional columns, use the `ignore` option:
219
+
220
+ ```ruby
221
+ class MyModel < ApplicationRecord
222
+ has_audit_log ignore: :last_synced_at
223
+
224
+ # This class will be audited,
225
+ # and will ignore changes to `created_at`, `updated_at`, and `last_synced_at`.
226
+ end
227
+ ```
228
+
229
+ By default, changes to `updated_at` and `created_at` will be ignored (since
230
+ these generally change on every update), but this behavior can be reconfigured:
231
+
232
+ ```ruby
233
+ # change the defaults:
234
+ Journaled::AuditLog.default_ignored_columns = %i(createdAt updatedAt)
235
+
236
+ # or append new defaults:
237
+ Journaled::AuditLog.default_ignored_columns += %i(modified_at)
238
+
239
+ # or disable defaults entirely:
240
+ Journaled::AuditLog.default_ignored_columns = []
241
+ ```
242
+
243
+ Subclasses will inherit audit log configs:
244
+
245
+ ```ruby
246
+ class MyModel < ApplicationRecord
247
+ has_audit_log ignore: :last_synced_at
248
+ end
249
+
250
+ class MySubclass < MyModel
251
+ # this class will be audited,
252
+ # and will ignore `created_at`, `updated_at`, and `last_synced_at`.
253
+ end
254
+ ```
255
+
256
+ To disable audit logs on subclasses, use `skip_audit_log`:
257
+
258
+ ```ruby
259
+ class MySubclass < MyModel
260
+ skip_audit_log
261
+ end
262
+ ```
263
+
264
+ Subclasses may specify additional columns to ignore (which will be merged into
265
+ the inherited list):
266
+
267
+ ```ruby
268
+ class MySubclass < MyModel
269
+ has_audit_log ignore: :another_field
270
+
271
+ # this class will ignore `another_field`, IN ADDITION TO `created_at`, `updated_at`,
272
+ # and any other fields specified by the parent class.
273
+ end
274
+ ```
275
+
276
+ To temporarily disable audit logging globally, use the `without_audit_logging` directive:
277
+
278
+ ```ruby
279
+ Journaled::AuditLog.without_audit_logging do
280
+ # Any operation in here will skip audit logging
281
+ end
282
+ ```
283
+
284
+ #### Audit Log Events
285
+
286
+ Whenever an audited record is created, updated, or destroyed, a
287
+ `journaled_audit_log` event is emitted. For example, calling
288
+ `user.update!(name: 'Bart')` would result in an event that looks something like
289
+ this:
290
+
291
+ ```json
292
+ {
293
+ "id": "bc7cb6a6-88cf-4849-a4f0-a31b0b199c47",
294
+ "event_type": "journaled_audit_log",
295
+ "created_at": "2022-01-28T11:06:54.928-05:00",
296
+ "class_name": "User",
297
+ "table_name": "users",
298
+ "record_id": "123",
299
+ "database_operation": "update",
300
+ "changes": { "name": ["Homer", "Bart"] },
301
+ "snapshot": null,
302
+ "actor": "gid://app_name/AdminUser/456",
303
+ "tags": {}
304
+ }
305
+ ```
306
+
307
+ The field breakdown is as follows:
308
+
309
+ - `id`: a randomly-generated ID for the event itself
310
+ - `event_type`: the type of event (always `journaled_audit_log`)
311
+ - `created_at`: the time that the action occurred (should match `updated_at` on
312
+ the ActiveRecord)
313
+ - `class_name`: the name of the ActiveRecord class
314
+ - `table_name`: the underlying table that the class interfaces with
315
+ - `record_id`: the primary key of the ActiveRecord
316
+ - `database_operation`: the type of operation (`insert`, `update`, or `delete`)
317
+ - `changes`: the changes to the record, in the form of `"field_name":
318
+ ["from_value", "to_value"]`
319
+ - `snapshot`: an (optional) snapshot of all of the record's columns and their
320
+ values (see below).
321
+ - `actor`: the current `Journaled.actor`
322
+ - `tags`: the current `Journaled.tags`
323
+
324
+ #### Snapshots
325
+
326
+ When records are created, updated, and deleted, the `changes` field is populated
327
+ with only the columns that changed. While this keeps event payload size down, it
328
+ may make it harder to reconstruct the state of the record at a given point in
329
+ time.
330
+
331
+ This is where the `snapshot` field comes in! To produce a full snapshot of a
332
+ record as part of an update, set use the virtual `_log_snapshot` attribute, like
333
+ so:
334
+
335
+ ```ruby
336
+ my_user.update!(name: 'Bart', _log_snapshot: true)
337
+ ```
338
+
339
+ Or to produce snapshots for all records that change for a given operation,
340
+ wrap it a `with_snapshots` block, like so:
341
+
342
+ ```ruby
343
+ Journaled::AuditLog.with_snapshots do
344
+ ComplicatedOperation.run!
345
+ end
346
+ ```
347
+
348
+ Events with snapshots will continue to populate the `changes` field, but will
349
+ additionally contain a snapshot with the full state of the user:
350
+
351
+ ```json
352
+ {
353
+ "...": "...",
354
+ "changes": { "name": ["Homer", "Bart"] },
355
+ "snapshot": { "name": "Bart", "email": "simpson@example.com", "favorite_food": "pizza" },
356
+ "...": "..."
357
+ }
358
+ ```
359
+
360
+ #### Handling Sensitive Data
361
+
362
+ Both `changes` and `snapshot` will filter out sensitive fields, as defined by
363
+ your `Rails.application.config.filter_parameters` list:
364
+
365
+ ```json
366
+ {
367
+ "...": "...",
368
+ "changes": { "ssn": ["[FILTERED]", "[FILTERED]"] },
369
+ "snapshot": { "ssn": "[FILTERED]" },
370
+ "...": "..."
371
+ }
372
+ ```
373
+
374
+ They will also filter out any fields whose name ends in `_crypt` or `_hmac`, as
375
+ well as fields that rely on Active Record Encryption / `encrypts` ([introduced
376
+ in Rails 7](https://edgeguides.rubyonrails.org/active_record_encryption.html)).
377
+
378
+ This is done to avoid emitting values to locations where it is difficult or
379
+ impossible to rotate encryption keys (or otherwise scrub values after the
380
+ fact), and currently there is no built-in configuration to bypass this
381
+ behavior. If you need to track changes to sensitive/encrypted fields, it is
382
+ recommended that you store the values in a local history table (still
383
+ encrypted, of course!).
384
+
385
+ #### Caveats
386
+
387
+ Because Journaled events are not guaranteed to arrive in order, events emitted
388
+ by `Journaled::AuditLog` must be sorted by their `created_at` value, which
389
+ should correspond roughly to the time that the SQL statement was issued.
390
+ **There is currently no other means of globally ordering audit log events**,
391
+ making them susceptible to clock drift and race conditions.
392
+
393
+ These issues may be mitigated on a per-model basis via
394
+ `ActiveRecord::Locking::Optimistic` (and its auto-incrementing `lock_version`
395
+ column), and/or by careful use of other locking mechanisms.
396
+
182
397
  ### Custom Journaling
183
398
 
184
399
  For every custom implementation of journaling in your application, define the JSON schema for the attributes in your event.
@@ -338,7 +553,7 @@ Returns one of the following in order of preference:
338
553
  * a string of the form `gid://[app_name]` as a fallback
339
554
 
340
555
  In order for this to be most useful, you must configure your controller
341
- as described in [Change Journaling](#change-journaling) above.
556
+ as described in [Attribution](#attribution) above.
342
557
 
343
558
  ### Testing
344
559
 
@@ -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/MethodParameterName
59
+ def journal_changes_to(*attribute_names, as:, enqueue_with: {})
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
@@ -0,0 +1,91 @@
1
+ # FIXME: This cannot be included in lib/ because Journaled::Event is autoloaded via app/models
2
+ # Autoloading Journaled::Event isn't strictly necessary, and for compatibility it would
3
+ # make sense to move it to lib/.
4
+ module Journaled
5
+ module AuditLog
6
+ Event = Struct.new(:record, :database_operation, :unfiltered_changes, :enqueue_opts) do
7
+ include Journaled::Event
8
+
9
+ journal_attributes :class_name, :table_name, :record_id,
10
+ :database_operation, :changes, :snapshot, :actor, tagged: true
11
+
12
+ def journaled_stream_name
13
+ AuditLog.default_stream_name || super
14
+ end
15
+
16
+ def journaled_enqueue_opts
17
+ record.class.audit_log_config.enqueue_opts
18
+ end
19
+
20
+ def created_at
21
+ case database_operation
22
+ when 'insert'
23
+ record_created_at
24
+ when 'update'
25
+ record_updated_at
26
+ when 'delete'
27
+ Time.zone.now
28
+ else
29
+ raise "Unhandled database operation type: #{database_operation}"
30
+ end
31
+ end
32
+
33
+ def record_created_at
34
+ record.try(:created_at) || Time.zone.now
35
+ end
36
+
37
+ def record_updated_at
38
+ record.try(:updated_at) || Time.zone.now
39
+ end
40
+
41
+ def class_name
42
+ record.class.name
43
+ end
44
+
45
+ def table_name
46
+ record.class.table_name
47
+ end
48
+
49
+ def record_id
50
+ record.id
51
+ end
52
+
53
+ def changes
54
+ filtered_changes = unfiltered_changes.deep_dup.deep_symbolize_keys
55
+ filtered_changes.each do |key, value|
56
+ filtered_changes[key] = value.map { |val| '[FILTERED]' if val } if filter_key?(key)
57
+ end
58
+ end
59
+
60
+ def snapshot
61
+ filtered_attributes if record._log_snapshot || AuditLog.snapshots_enabled
62
+ end
63
+
64
+ def actor
65
+ Journaled.actor_uri
66
+ end
67
+
68
+ private
69
+
70
+ def filter_key?(key)
71
+ filter_params.include?(key) || encrypted_column?(key)
72
+ end
73
+
74
+ def encrypted_column?(key)
75
+ key.to_s.end_with?('_crypt', '_hmac') ||
76
+ (Rails::VERSION::MAJOR >= 7 && record.encrypted_attribute?(key))
77
+ end
78
+
79
+ def filter_params
80
+ Rails.application.config.filter_parameters
81
+ end
82
+
83
+ def filtered_attributes
84
+ attrs = record.attributes.dup.symbolize_keys
85
+ attrs.each do |key, _value|
86
+ attrs[key] = '[FILTERED]' if filter_key?(key)
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,31 @@
1
+ {
2
+ "type": "object",
3
+ "title": "audit_log_event",
4
+ "additionalProperties": false,
5
+ "required": [
6
+ "id",
7
+ "event_type",
8
+ "created_at",
9
+ "class_name",
10
+ "table_name",
11
+ "record_id",
12
+ "database_operation",
13
+ "changes",
14
+ "snapshot",
15
+ "actor",
16
+ "tags"
17
+ ],
18
+ "properties": {
19
+ "id": { "type": "string" },
20
+ "event_type": { "type": "string" },
21
+ "created_at": { "type": "string" },
22
+ "class_name": { "type": "string" },
23
+ "table_name": { "type": "string" },
24
+ "record_id": { "type": ["string", "integer"] },
25
+ "database_operation": { "type": "string" },
26
+ "changes": { "type": "object", "additionalProperties": true },
27
+ "snapshot": { "type": ["object", "null"], "additionalProperties": true },
28
+ "actor": { "type": "string" },
29
+ "tags": { "type": "object", "additionalProperties": true }
30
+ }
31
+ }
@@ -0,0 +1,211 @@
1
+ require 'active_support/core_ext/module/attribute_accessors_per_thread'
2
+
3
+ module Journaled
4
+ module AuditLog
5
+ extend ActiveSupport::Concern
6
+
7
+ DEFAULT_EXCLUDED_CLASSES = %w(
8
+ Delayed::Job
9
+ PaperTrail::Version
10
+ ActiveStorage::Attachment
11
+ ActiveStorage::Blob
12
+ ActiveRecord::InternalMetadata
13
+ ActiveRecord::SchemaMigration
14
+ ).freeze
15
+
16
+ mattr_accessor(:default_ignored_columns) { %i(created_at updated_at) }
17
+ mattr_accessor(:default_stream_name) { Journaled.default_stream_name }
18
+ mattr_accessor(:default_enqueue_opts) { {} }
19
+ mattr_accessor(:excluded_classes) { DEFAULT_EXCLUDED_CLASSES.dup }
20
+ thread_mattr_accessor(:snapshots_enabled) { false }
21
+ thread_mattr_accessor(:_disabled) { false }
22
+ thread_mattr_accessor(:_force) { false }
23
+
24
+ class << self
25
+ def exclude_classes!
26
+ excluded_classes.each do |name|
27
+ if Rails::VERSION::MAJOR >= 6 && Rails.autoloaders.zeitwerk_enabled?
28
+ zeitwerk_exclude!(name)
29
+ else
30
+ classic_exclude!(name)
31
+ end
32
+ end
33
+ end
34
+
35
+ def with_snapshots
36
+ snapshots_enabled_was = snapshots_enabled
37
+ self.snapshots_enabled = true
38
+ yield
39
+ ensure
40
+ self.snapshots_enabled = snapshots_enabled_was
41
+ end
42
+
43
+ def without_audit_logging
44
+ disabled_was = _disabled
45
+ self._disabled = true
46
+ yield
47
+ ensure
48
+ self._disabled = disabled_was
49
+ end
50
+
51
+ private
52
+
53
+ def zeitwerk_exclude!(name)
54
+ if Object.const_defined?(name)
55
+ name.constantize.skip_audit_log
56
+ else
57
+ Rails.autoloaders.main.on_load(name) { |klass, _path| klass.skip_audit_log }
58
+ end
59
+ end
60
+
61
+ def classic_exclude!(name)
62
+ name.constantize.skip_audit_log
63
+ rescue NameError
64
+ nil
65
+ end
66
+ end
67
+
68
+ Config = Struct.new(:enabled, :ignored_columns, :enqueue_opts) do
69
+ def self.default
70
+ new(false, AuditLog.default_ignored_columns.dup, AuditLog.default_enqueue_opts.dup)
71
+ end
72
+
73
+ def initialize(*)
74
+ super
75
+ self.ignored_columns ||= []
76
+ self.enqueue_opts ||= {}
77
+ end
78
+
79
+ def enabled?
80
+ !AuditLog._disabled && self[:enabled].present?
81
+ end
82
+
83
+ def dup
84
+ super.tap do |config|
85
+ config.ignored_columns = ignored_columns.dup
86
+ config.enqueue_opts = enqueue_opts.dup
87
+ end
88
+ end
89
+
90
+ private :enabled
91
+ end
92
+
93
+ included do
94
+ prepend BlockedMethods
95
+ singleton_class.prepend BlockedClassMethods
96
+
97
+ class_attribute :audit_log_config, default: Config.default
98
+
99
+ attr_accessor :_log_snapshot
100
+
101
+ after_create { _emit_audit_log!('insert') }
102
+ after_update { _emit_audit_log!('update') if _audit_log_changes.any? }
103
+ after_destroy { _emit_audit_log!('delete') }
104
+ end
105
+
106
+ class_methods do
107
+ def has_audit_log(ignore: [], enqueue_with: {})
108
+ self.audit_log_config = audit_log_config.dup
109
+ audit_log_config.enabled = true
110
+ audit_log_config.ignored_columns |= [ignore].flatten(1)
111
+ audit_log_config.enqueue_opts.merge!(enqueue_with)
112
+ end
113
+
114
+ def skip_audit_log
115
+ self.audit_log_config = audit_log_config.dup
116
+ audit_log_config.enabled = false
117
+ end
118
+ end
119
+
120
+ module BlockedMethods
121
+ BLOCKED_METHODS = {
122
+ delete: '#destroy',
123
+ update_column: '#update!',
124
+ update_columns: '#update!',
125
+ }.freeze
126
+
127
+ def delete(**kwargs)
128
+ _journaled_audit_log_check!(:delete, **kwargs) do
129
+ super()
130
+ end
131
+ end
132
+
133
+ def update_column(name, value, **kwargs)
134
+ _journaled_audit_log_check!(:update_column, **kwargs.merge(name => value)) do
135
+ super(name, value)
136
+ end
137
+ end
138
+
139
+ def update_columns(args = {}, **kwargs)
140
+ _journaled_audit_log_check!(:update_columns, **args.merge(kwargs)) do
141
+ super(args.merge(kwargs).except(:_force))
142
+ end
143
+ end
144
+
145
+ def _journaled_audit_log_check!(method, **kwargs) # rubocop:disable Metrics/AbcSize
146
+ force_was = AuditLog._force
147
+ AuditLog._force = kwargs.delete(:_force) if kwargs.key?(:_force)
148
+ audited_columns = kwargs.keys - audit_log_config.ignored_columns
149
+
150
+ if method == :delete || audited_columns.any?
151
+ column_message = <<~MSG if kwargs.any?
152
+ You are attempting to change the following audited columns:
153
+ #{audited_columns.inspect}
154
+
155
+ MSG
156
+ raise <<~MSG if audit_log_config.enabled? && !AuditLog._force
157
+ #{column_message}Using `#{method}` is blocked because it skips audit logging (and other Rails callbacks)!
158
+ Consider using `#{BLOCKED_METHODS[method]}` instead, or pass `_force: true` as an argument.
159
+ MSG
160
+ end
161
+
162
+ yield
163
+ ensure
164
+ AuditLog._force = force_was
165
+ end
166
+ end
167
+
168
+ module BlockedClassMethods
169
+ BLOCKED_METHODS = {
170
+ delete_all: '.destroy_all',
171
+ insert: '.create!',
172
+ insert_all: '.each { create!(...) }',
173
+ update_all: '.find_each { update!(...) }',
174
+ upsert: '.create_or_find_by!',
175
+ upsert_all: '.each { create_or_find_by!(...) }',
176
+ }.freeze
177
+
178
+ BLOCKED_METHODS.each do |method, alternative|
179
+ define_method(method) do |*args, **kwargs, &block|
180
+ force_was = AuditLog._force
181
+ AuditLog._force = kwargs.delete(:_force) if kwargs.key?(:_force)
182
+
183
+ raise <<~MSG if audit_log_config.enabled? && !AuditLog._force
184
+ `#{method}` is blocked because it skips callbacks and audit logs!
185
+ Consider using `#{alternative}` instead, or pass `_force: true` as an argument.
186
+ MSG
187
+
188
+ super(*args, **kwargs, &block)
189
+ ensure
190
+ AuditLog._force = force_was
191
+ end
192
+ end
193
+ end
194
+
195
+ def _emit_audit_log!(database_operation)
196
+ if audit_log_config.enabled?
197
+ event = Journaled::AuditLog::Event.new(self, database_operation, _audit_log_changes, audit_log_config.enqueue_opts)
198
+ ActiveSupport::Notifications.instrument('journaled.audit_log.journal', event: event) do
199
+ event.journal!
200
+ end
201
+ end
202
+ end
203
+
204
+ def _audit_log_changes
205
+ previous_changes.except(*audit_log_config.ignored_columns)
206
+ end
207
+ end
208
+ end
209
+
210
+ ActiveSupport.on_load(:active_record) { include Journaled::AuditLog }
211
+ Journaled::Engine.config.after_initialize { Journaled::AuditLog.exclude_classes! }
@@ -1,3 +1,3 @@
1
1
  module Journaled
2
- VERSION = "5.0.0".freeze
2
+ VERSION = "5.1.1".freeze
3
3
  end
data/lib/journaled.rb CHANGED
@@ -69,3 +69,5 @@ module Journaled
69
69
  Current.tags = Current.tags.merge(tags)
70
70
  end
71
71
  end
72
+
73
+ require 'journaled/audit_log'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: journaled
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.0.0
4
+ version: 5.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jake Lipson
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2022-08-23 00:00:00.000000000 Z
14
+ date: 2022-09-16 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: activejob
@@ -41,6 +41,20 @@ dependencies:
41
41
  - - ">="
42
42
  - !ruby/object:Gem::Version
43
43
  version: '0'
44
+ - !ruby/object:Gem::Dependency
45
+ name: activesupport
46
+ requirement: !ruby/object:Gem::Requirement
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: '0'
51
+ type: :runtime
52
+ prerelease: false
53
+ version_requirements: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
44
58
  - !ruby/object:Gem::Dependency
45
59
  name: aws-sdk-kinesis
46
60
  requirement: !ruby/object:Gem::Requirement
@@ -242,6 +256,7 @@ files:
242
256
  - app/jobs/journaled/delivery_job.rb
243
257
  - app/models/concerns/journaled/changes.rb
244
258
  - app/models/journaled/actor_uri_provider.rb
259
+ - app/models/journaled/audit_log/event.rb
245
260
  - app/models/journaled/change.rb
246
261
  - app/models/journaled/change_definition.rb
247
262
  - app/models/journaled/change_writer.rb
@@ -252,9 +267,11 @@ files:
252
267
  - config/initializers/change_protection.rb
253
268
  - config/spring.rb
254
269
  - journaled_schemas/base_event.json
270
+ - journaled_schemas/journaled/audit_log/event.json
255
271
  - journaled_schemas/journaled/change.json
256
272
  - journaled_schemas/tagged_event.json
257
273
  - lib/journaled.rb
274
+ - lib/journaled/audit_log.rb
258
275
  - lib/journaled/connection.rb
259
276
  - lib/journaled/current.rb
260
277
  - lib/journaled/engine.rb