journaled 5.0.0 → 5.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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