paper_trail 10.3.1 → 13.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.
- checksums.yaml +4 -4
- data/LICENSE +20 -0
- data/lib/generators/paper_trail/install/install_generator.rb +25 -7
- data/lib/generators/paper_trail/install/templates/create_versions.rb.erb +4 -2
- data/lib/generators/paper_trail/migration_generator.rb +5 -4
- data/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb +4 -2
- data/lib/paper_trail/attribute_serializers/attribute_serializer_factory.rb +24 -10
- data/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +14 -46
- data/lib/paper_trail/compatibility.rb +3 -3
- data/lib/paper_trail/config.rb +0 -33
- data/lib/paper_trail/errors.rb +33 -0
- data/lib/paper_trail/events/base.rb +68 -68
- data/lib/paper_trail/events/destroy.rb +1 -1
- data/lib/paper_trail/events/update.rb +23 -4
- data/lib/paper_trail/frameworks/active_record.rb +9 -2
- data/lib/paper_trail/frameworks/rails/controller.rb +1 -9
- data/lib/paper_trail/frameworks/rails/railtie.rb +30 -0
- data/lib/paper_trail/frameworks/rails.rb +1 -2
- data/lib/paper_trail/has_paper_trail.rb +1 -1
- data/lib/paper_trail/model_config.rb +49 -46
- data/lib/paper_trail/queries/versions/where_attribute_changes.rb +50 -0
- data/lib/paper_trail/queries/versions/where_object.rb +1 -1
- data/lib/paper_trail/queries/versions/where_object_changes.rb +8 -13
- data/lib/paper_trail/queries/versions/where_object_changes_from.rb +57 -0
- data/lib/paper_trail/queries/versions/where_object_changes_to.rb +57 -0
- data/lib/paper_trail/record_trail.rb +7 -7
- data/lib/paper_trail/reifier.rb +41 -26
- data/lib/paper_trail/request.rb +0 -3
- data/lib/paper_trail/serializers/json.rb +0 -10
- data/lib/paper_trail/serializers/yaml.rb +19 -13
- data/lib/paper_trail/type_serializers/postgres_array_serializer.rb +1 -14
- data/lib/paper_trail/version_concern.rb +86 -41
- data/lib/paper_trail/version_number.rb +3 -3
- data/lib/paper_trail.rb +17 -40
- metadata +104 -43
- data/lib/paper_trail/frameworks/rails/engine.rb +0 -45
@@ -25,9 +25,7 @@ module PaperTrail
|
|
25
25
|
# @api public
|
26
26
|
def user_for_paper_trail
|
27
27
|
return unless defined?(current_user)
|
28
|
-
|
29
|
-
rescue NoMethodError
|
30
|
-
current_user
|
28
|
+
current_user.try(:id) || current_user
|
31
29
|
end
|
32
30
|
|
33
31
|
# Returns any information about the controller or request that you
|
@@ -103,9 +101,3 @@ module PaperTrail
|
|
103
101
|
end
|
104
102
|
end
|
105
103
|
end
|
106
|
-
|
107
|
-
if defined?(::ActionController)
|
108
|
-
::ActiveSupport.on_load(:action_controller) do
|
109
|
-
include ::PaperTrail::Rails::Controller
|
110
|
-
end
|
111
|
-
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PaperTrail
|
4
|
+
# Represents code to load within Rails framework. See documentation in
|
5
|
+
# `railties/lib/rails/railtie.rb`.
|
6
|
+
# @api private
|
7
|
+
class Railtie < ::Rails::Railtie
|
8
|
+
# PaperTrail only has one initializer.
|
9
|
+
#
|
10
|
+
# We specify `before: "load_config_initializers"` to ensure that the PT
|
11
|
+
# initializer happens before "app initializers" (those defined in
|
12
|
+
# the app's `config/initalizers`).
|
13
|
+
initializer "paper_trail", before: "load_config_initializers" do
|
14
|
+
# `on_load` is a "lazy load hook". It "declares a block that will be
|
15
|
+
# executed when a Rails component is fully loaded". (See
|
16
|
+
# `active_support/lazy_load_hooks.rb`)
|
17
|
+
ActiveSupport.on_load(:action_controller) do
|
18
|
+
require "paper_trail/frameworks/rails/controller"
|
19
|
+
|
20
|
+
# Mix our extensions into `ActionController::Base`, which is `self`
|
21
|
+
# because of the `class_eval` in `lazy_load_hooks.rb`.
|
22
|
+
include PaperTrail::Rails::Controller
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveSupport.on_load(:active_record) do
|
26
|
+
require "paper_trail/frameworks/active_record"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -18,11 +18,6 @@ module PaperTrail
|
|
18
18
|
`abstract_class`. This is fine, but all application models must be
|
19
19
|
configured to use concrete (not abstract) version models.
|
20
20
|
STR
|
21
|
-
E_MODEL_LIMIT_REQUIRES_ITEM_SUBTYPE = <<~STR.squish.freeze
|
22
|
-
To use PaperTrail's per-model limit in your %s model, you must have an
|
23
|
-
item_subtype column in your versions table. See documentation sections
|
24
|
-
2.e.1 Per-model limit, and 4.b.1 The optional item_subtype column.
|
25
|
-
STR
|
26
21
|
DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION = <<~STR.squish
|
27
22
|
Passing versions association name as `has_paper_trail versions: %{versions_name}`
|
28
23
|
is deprecated. Use `has_paper_trail versions: {name: %{versions_name}}` instead.
|
@@ -45,22 +40,14 @@ module PaperTrail
|
|
45
40
|
@model_class.after_create { |r|
|
46
41
|
r.paper_trail.record_create if r.paper_trail.save_version?
|
47
42
|
}
|
48
|
-
|
49
|
-
@model_class.paper_trail_options[:on] << :create
|
43
|
+
append_option_uniquely(:on, :create)
|
50
44
|
end
|
51
45
|
|
52
46
|
# Adds a callback that records a version before or after a "destroy" event.
|
53
47
|
#
|
54
48
|
# @api public
|
55
49
|
def on_destroy(recording_order = "before")
|
56
|
-
|
57
|
-
raise ArgumentError, 'recording order can only be "after" or "before"'
|
58
|
-
end
|
59
|
-
|
60
|
-
if recording_order.to_s == "after" && cannot_record_after_destroy?
|
61
|
-
raise E_CANNOT_RECORD_AFTER_DESTROY
|
62
|
-
end
|
63
|
-
|
50
|
+
assert_valid_recording_order_for_on_destroy(recording_order)
|
64
51
|
@model_class.send(
|
65
52
|
"#{recording_order}_destroy",
|
66
53
|
lambda do |r|
|
@@ -68,9 +55,7 @@ module PaperTrail
|
|
68
55
|
r.paper_trail.record_destroy(recording_order)
|
69
56
|
end
|
70
57
|
)
|
71
|
-
|
72
|
-
return if @model_class.paper_trail_options[:on].include?(:destroy)
|
73
|
-
@model_class.paper_trail_options[:on] << :destroy
|
58
|
+
append_option_uniquely(:on, :destroy)
|
74
59
|
end
|
75
60
|
|
76
61
|
# Adds a callback that records a version after an "update" event.
|
@@ -92,19 +77,27 @@ module PaperTrail
|
|
92
77
|
@model_class.after_update { |r|
|
93
78
|
r.paper_trail.clear_version_instance
|
94
79
|
}
|
95
|
-
|
96
|
-
@model_class.paper_trail_options[:on] << :update
|
80
|
+
append_option_uniquely(:on, :update)
|
97
81
|
end
|
98
82
|
|
99
83
|
# Adds a callback that records a version after a "touch" event.
|
84
|
+
#
|
85
|
+
# Rails < 6.0 has a bug where dirty-tracking does not occur during
|
86
|
+
# a `touch`. (https://github.com/rails/rails/issues/33429) See also:
|
87
|
+
# https://github.com/paper-trail-gem/paper_trail/issues/1121
|
88
|
+
# https://github.com/paper-trail-gem/paper_trail/issues/1161
|
89
|
+
# https://github.com/paper-trail-gem/paper_trail/pull/1285
|
90
|
+
#
|
100
91
|
# @api public
|
101
92
|
def on_touch
|
102
93
|
@model_class.after_touch { |r|
|
103
|
-
r.paper_trail.
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
94
|
+
if r.paper_trail.save_version?
|
95
|
+
r.paper_trail.record_update(
|
96
|
+
force: RAILS_LT_6_0,
|
97
|
+
in_after_callback: true,
|
98
|
+
is_touch: true
|
99
|
+
)
|
100
|
+
end
|
108
101
|
}
|
109
102
|
end
|
110
103
|
|
@@ -117,42 +110,48 @@ module PaperTrail
|
|
117
110
|
@model_class.send :include, ::PaperTrail::Model::InstanceMethods
|
118
111
|
setup_options(options)
|
119
112
|
setup_associations(options)
|
120
|
-
check_presence_of_item_subtype_column(options)
|
121
113
|
@model_class.after_rollback { paper_trail.clear_rolled_back_versions }
|
122
114
|
setup_callbacks_from_options options[:on]
|
123
115
|
end
|
124
116
|
|
117
|
+
# @api private
|
125
118
|
def version_class
|
126
|
-
@
|
119
|
+
@version_class ||= @model_class.version_class_name.constantize
|
127
120
|
end
|
128
121
|
|
129
122
|
private
|
130
123
|
|
131
|
-
|
132
|
-
|
124
|
+
RAILS_LT_6_0 = ::ActiveRecord.gem_version < ::Gem::Version.new("6.0.0")
|
125
|
+
private_constant :RAILS_LT_6_0
|
126
|
+
|
127
|
+
# @api private
|
128
|
+
def append_option_uniquely(option, value)
|
129
|
+
collection = @model_class.paper_trail_options.fetch(option)
|
130
|
+
return if collection.include?(value)
|
131
|
+
collection << value
|
133
132
|
end
|
134
133
|
|
135
134
|
# Raises an error if the provided class is an `abstract_class`.
|
136
135
|
# @api private
|
137
136
|
def assert_concrete_activerecord_class(class_name)
|
138
137
|
if class_name.constantize.abstract_class?
|
139
|
-
raise format(E_HPT_ABSTRACT_CLASS, @model_class, class_name)
|
138
|
+
raise Error, format(E_HPT_ABSTRACT_CLASS, @model_class, class_name)
|
140
139
|
end
|
141
140
|
end
|
142
141
|
|
143
|
-
|
144
|
-
|
145
|
-
|
142
|
+
# @api private
|
143
|
+
def assert_valid_recording_order_for_on_destroy(recording_order)
|
144
|
+
unless %w[after before].include?(recording_order.to_s)
|
145
|
+
raise ArgumentError, 'recording order can only be "after" or "before"'
|
146
|
+
end
|
147
|
+
|
148
|
+
if recording_order.to_s == "after" && cannot_record_after_destroy?
|
149
|
+
raise Error, E_CANNOT_RECORD_AFTER_DESTROY
|
150
|
+
end
|
146
151
|
end
|
147
152
|
|
148
|
-
|
149
|
-
|
150
|
-
#
|
151
|
-
# @api private
|
152
|
-
def check_presence_of_item_subtype_column(options)
|
153
|
-
return unless options.key?(:limit)
|
154
|
-
return if version_class.item_subtype_column_present?
|
155
|
-
raise format(E_MODEL_LIMIT_REQUIRES_ITEM_SUBTYPE, @model_class.name)
|
153
|
+
def cannot_record_after_destroy?
|
154
|
+
::ActiveRecord::Base.belongs_to_required_by_default
|
156
155
|
end
|
157
156
|
|
158
157
|
def check_version_class_name(options)
|
@@ -210,6 +209,14 @@ module PaperTrail
|
|
210
209
|
options
|
211
210
|
end
|
212
211
|
|
212
|
+
# Process an `ignore`, `skip`, or `only` option.
|
213
|
+
def event_attribute_option(option_name)
|
214
|
+
[@model_class.paper_trail_options[option_name]].
|
215
|
+
flatten.
|
216
|
+
compact.
|
217
|
+
map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
|
218
|
+
end
|
219
|
+
|
213
220
|
def get_versions_scope(options)
|
214
221
|
options[:versions][:scope] || -> { order(model.timestamp_sort_order) }
|
215
222
|
end
|
@@ -244,12 +251,8 @@ module PaperTrail
|
|
244
251
|
@model_class.paper_trail_options = options.dup
|
245
252
|
|
246
253
|
%i[ignore skip only].each do |k|
|
247
|
-
@model_class.paper_trail_options[k] =
|
248
|
-
flatten.
|
249
|
-
compact.
|
250
|
-
map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
|
254
|
+
@model_class.paper_trail_options[k] = event_attribute_option(k)
|
251
255
|
end
|
252
|
-
|
253
256
|
@model_class.paper_trail_options[:meta] ||= {}
|
254
257
|
end
|
255
258
|
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PaperTrail
|
4
|
+
module Queries
|
5
|
+
module Versions
|
6
|
+
# For public API documentation, see `where_attribute_changes` in
|
7
|
+
# `paper_trail/version_concern.rb`.
|
8
|
+
# @api private
|
9
|
+
class WhereAttributeChanges
|
10
|
+
# - version_model_class - The class that VersionConcern was mixed into.
|
11
|
+
# - attribute - An attribute that changed. See the public API
|
12
|
+
# documentation for details.
|
13
|
+
# @api private
|
14
|
+
def initialize(version_model_class, attribute)
|
15
|
+
@version_model_class = version_model_class
|
16
|
+
@attribute = attribute
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
def execute
|
21
|
+
if PaperTrail.config.object_changes_adapter.respond_to?(:where_attribute_changes)
|
22
|
+
return PaperTrail.config.object_changes_adapter.where_attribute_changes(
|
23
|
+
@version_model_class, @attribute
|
24
|
+
)
|
25
|
+
end
|
26
|
+
column_type = @version_model_class.columns_hash["object_changes"].type
|
27
|
+
case column_type
|
28
|
+
when :jsonb, :json
|
29
|
+
json
|
30
|
+
else
|
31
|
+
raise UnsupportedColumnType.new(
|
32
|
+
method: "where_attribute_changes",
|
33
|
+
expected: "json or jsonb",
|
34
|
+
actual: column_type
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @api private
|
42
|
+
def json
|
43
|
+
sql = "object_changes -> ? IS NOT NULL"
|
44
|
+
|
45
|
+
@version_model_class.where(sql, @attribute)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -19,7 +19,7 @@ module PaperTrail
|
|
19
19
|
# @api private
|
20
20
|
def execute
|
21
21
|
column = @version_model_class.columns_hash["object"]
|
22
|
-
raise "where_object
|
22
|
+
raise Error, "where_object requires an object column" unless column
|
23
23
|
|
24
24
|
case column.type
|
25
25
|
when :jsonb
|
@@ -23,18 +23,23 @@ module PaperTrail
|
|
23
23
|
|
24
24
|
# @api private
|
25
25
|
def execute
|
26
|
-
if PaperTrail.config.object_changes_adapter
|
26
|
+
if PaperTrail.config.object_changes_adapter.respond_to?(:where_object_changes)
|
27
27
|
return PaperTrail.config.object_changes_adapter.where_object_changes(
|
28
28
|
@version_model_class, @attributes
|
29
29
|
)
|
30
30
|
end
|
31
|
-
|
31
|
+
column_type = @version_model_class.columns_hash["object_changes"].type
|
32
|
+
case column_type
|
32
33
|
when :jsonb
|
33
34
|
jsonb
|
34
35
|
when :json
|
35
36
|
json
|
36
37
|
else
|
37
|
-
|
38
|
+
raise UnsupportedColumnType.new(
|
39
|
+
method: "where_object_changes",
|
40
|
+
expected: "json or jsonb",
|
41
|
+
actual: column_type
|
42
|
+
)
|
38
43
|
end
|
39
44
|
end
|
40
45
|
|
@@ -59,16 +64,6 @@ module PaperTrail
|
|
59
64
|
@attributes.each { |field, value| @attributes[field] = [value] }
|
60
65
|
@version_model_class.where("object_changes @> ?", @attributes.to_json)
|
61
66
|
end
|
62
|
-
|
63
|
-
# @api private
|
64
|
-
def text
|
65
|
-
arel_field = @version_model_class.arel_table[:object_changes]
|
66
|
-
where_conditions = @attributes.map { |field, value|
|
67
|
-
::PaperTrail.serializer.where_object_changes_condition(arel_field, field, value)
|
68
|
-
}
|
69
|
-
where_conditions = where_conditions.reduce { |a, e| a.and(e) }
|
70
|
-
@version_model_class.where(where_conditions)
|
71
|
-
end
|
72
67
|
end
|
73
68
|
end
|
74
69
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PaperTrail
|
4
|
+
module Queries
|
5
|
+
module Versions
|
6
|
+
# For public API documentation, see `where_object_changes_from` in
|
7
|
+
# `paper_trail/version_concern.rb`.
|
8
|
+
# @api private
|
9
|
+
class WhereObjectChangesFrom
|
10
|
+
# - version_model_class - The class that VersionConcern was mixed into.
|
11
|
+
# - attributes - A `Hash` of attributes and values. See the public API
|
12
|
+
# documentation for details.
|
13
|
+
# @api private
|
14
|
+
def initialize(version_model_class, attributes)
|
15
|
+
@version_model_class = version_model_class
|
16
|
+
@attributes = attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
def execute
|
21
|
+
if PaperTrail.config.object_changes_adapter.respond_to?(:where_object_changes_from)
|
22
|
+
return PaperTrail.config.object_changes_adapter.where_object_changes_from(
|
23
|
+
@version_model_class, @attributes
|
24
|
+
)
|
25
|
+
end
|
26
|
+
column_type = @version_model_class.columns_hash["object_changes"].type
|
27
|
+
case column_type
|
28
|
+
when :jsonb, :json
|
29
|
+
json
|
30
|
+
else
|
31
|
+
raise UnsupportedColumnType.new(
|
32
|
+
method: "where_object_changes_from",
|
33
|
+
expected: "json or jsonb",
|
34
|
+
actual: column_type
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @api private
|
42
|
+
def json
|
43
|
+
predicates = []
|
44
|
+
values = []
|
45
|
+
@attributes.each do |field, value|
|
46
|
+
predicates.push(
|
47
|
+
"(object_changes->>? ILIKE ?)"
|
48
|
+
)
|
49
|
+
values.concat([field, "[#{value.to_json},%"])
|
50
|
+
end
|
51
|
+
sql = predicates.join(" and ")
|
52
|
+
@version_model_class.where(sql, *values)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PaperTrail
|
4
|
+
module Queries
|
5
|
+
module Versions
|
6
|
+
# For public API documentation, see `where_object_changes_to` in
|
7
|
+
# `paper_trail/version_concern.rb`.
|
8
|
+
# @api private
|
9
|
+
class WhereObjectChangesTo
|
10
|
+
# - version_model_class - The class that VersionConcern was mixed into.
|
11
|
+
# - attributes - A `Hash` of attributes and values. See the public API
|
12
|
+
# documentation for details.
|
13
|
+
# @api private
|
14
|
+
def initialize(version_model_class, attributes)
|
15
|
+
@version_model_class = version_model_class
|
16
|
+
@attributes = attributes
|
17
|
+
end
|
18
|
+
|
19
|
+
# @api private
|
20
|
+
def execute
|
21
|
+
if PaperTrail.config.object_changes_adapter.respond_to?(:where_object_changes_to)
|
22
|
+
return PaperTrail.config.object_changes_adapter.where_object_changes_to(
|
23
|
+
@version_model_class, @attributes
|
24
|
+
)
|
25
|
+
end
|
26
|
+
column_type = @version_model_class.columns_hash["object_changes"].type
|
27
|
+
case column_type
|
28
|
+
when :jsonb, :json
|
29
|
+
json
|
30
|
+
else
|
31
|
+
raise UnsupportedColumnType.new(
|
32
|
+
method: "where_object_changes_to",
|
33
|
+
expected: "json or jsonb",
|
34
|
+
actual: column_type
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# @api private
|
42
|
+
def json
|
43
|
+
predicates = []
|
44
|
+
values = []
|
45
|
+
@attributes.each do |field, value|
|
46
|
+
predicates.push(
|
47
|
+
"(object_changes->>? ILIKE ?)"
|
48
|
+
)
|
49
|
+
values.concat([field, "[%#{value.to_json}]"])
|
50
|
+
end
|
51
|
+
sql = predicates.join(" and ")
|
52
|
+
@version_model_class.where(sql, *values)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -7,8 +7,6 @@ require "paper_trail/events/update"
|
|
7
7
|
module PaperTrail
|
8
8
|
# Represents the "paper trail" for a single record.
|
9
9
|
class RecordTrail
|
10
|
-
RAILS_GTE_5_1 = ::ActiveRecord.gem_version >= ::Gem::Version.new("5.1.0.beta1")
|
11
|
-
|
12
10
|
def initialize(record)
|
13
11
|
@record = record
|
14
12
|
end
|
@@ -196,15 +194,17 @@ module PaperTrail
|
|
196
194
|
# Save, and create a version record regardless of options such as `:on`,
|
197
195
|
# `:if`, or `:unless`.
|
198
196
|
#
|
199
|
-
#
|
197
|
+
# `in_after_callback`: Indicates if this method is being called within an
|
198
|
+
# `after` callback. Defaults to `false`.
|
199
|
+
# `options`: Optional arguments passed to `save`.
|
200
200
|
#
|
201
201
|
# This is an "update" event. That is, we record the same data we would in
|
202
202
|
# the case of a normal AR `update`.
|
203
|
-
def save_with_version(
|
203
|
+
def save_with_version(in_after_callback: false, **options)
|
204
204
|
::PaperTrail.request(enabled: false) do
|
205
|
-
@record.save(
|
205
|
+
@record.save(**options)
|
206
206
|
end
|
207
|
-
record_update(force: true, in_after_callback:
|
207
|
+
record_update(force: true, in_after_callback: in_after_callback, is_touch: false)
|
208
208
|
end
|
209
209
|
|
210
210
|
# Like the `update_column` method from `ActiveRecord::Persistence`, but also
|
@@ -285,7 +285,7 @@ module PaperTrail
|
|
285
285
|
def log_version_errors(version, action)
|
286
286
|
version.logger&.warn(
|
287
287
|
"Unable to create version for #{action} of #{@record.class.name}" \
|
288
|
-
|
288
|
+
"##{@record.id}: " + version.errors.full_messages.join(", ")
|
289
289
|
)
|
290
290
|
end
|
291
291
|
|
data/lib/paper_trail/reifier.rb
CHANGED
@@ -52,26 +52,29 @@ module PaperTrail
|
|
52
52
|
# not the actual subclass. If `type` is present but empty, the class is
|
53
53
|
# the base class.
|
54
54
|
def init_model(attrs, options, version)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
model = item_found
|
69
|
-
init_unversioned_attrs(attrs, model)
|
70
|
-
end
|
55
|
+
klass = version_reification_class(version, attrs)
|
56
|
+
|
57
|
+
# The `dup` option and destroyed version always returns a new object,
|
58
|
+
# otherwise we should attempt to load item or to look for the item
|
59
|
+
# outside of default scope(s).
|
60
|
+
model = if options[:dup] == true || version.event == "destroy"
|
61
|
+
klass.new
|
62
|
+
else
|
63
|
+
version.item || init_model_by_finding_item_id(klass, version) || klass.new
|
64
|
+
end
|
65
|
+
|
66
|
+
if options[:unversioned_attributes] == :nil && !model.new_record?
|
67
|
+
init_unversioned_attrs(attrs, model)
|
71
68
|
end
|
69
|
+
|
72
70
|
model
|
73
71
|
end
|
74
72
|
|
73
|
+
# @api private
|
74
|
+
def init_model_by_finding_item_id(klass, version)
|
75
|
+
klass.unscoped.where(klass.primary_key => version.item_id).first
|
76
|
+
end
|
77
|
+
|
75
78
|
# Look for attributes that exist in `model` and not in this version.
|
76
79
|
# These attributes should be set to nil. Modifies `attrs`.
|
77
80
|
# @api private
|
@@ -88,9 +91,7 @@ module PaperTrail
|
|
88
91
|
#
|
89
92
|
# @api private
|
90
93
|
def reify_attribute(k, v, model, version)
|
91
|
-
|
92
|
-
is_enum_without_type_caster = ::ActiveRecord::VERSION::MAJOR < 5 && enums.key?(k)
|
93
|
-
if model.has_attribute?(k) && !is_enum_without_type_caster
|
94
|
+
if model.has_attribute?(k)
|
94
95
|
model[k.to_sym] = v
|
95
96
|
elsif model.respond_to?("#{k}=")
|
96
97
|
model.send("#{k}=", v)
|
@@ -111,21 +112,35 @@ module PaperTrail
|
|
111
112
|
end
|
112
113
|
|
113
114
|
# Given a `version`, return the class to reify. This method supports
|
114
|
-
# Single Table Inheritance (STI) with custom inheritance columns
|
115
|
+
# Single Table Inheritance (STI) with custom inheritance columns and
|
116
|
+
# custom inheritance column values.
|
115
117
|
#
|
116
118
|
# For example, imagine a `version` whose `item_type` is "Animal". The
|
117
119
|
# `animals` table is an STI table (it has cats and dogs) and it has a
|
118
120
|
# custom inheritance column, `species`. If `attrs["species"]` is "Dog",
|
119
121
|
# this method returns the constant `Dog`. If `attrs["species"]` is blank,
|
120
|
-
# this method returns the constant `Animal`.
|
121
|
-
#
|
122
|
+
# this method returns the constant `Animal`.
|
123
|
+
#
|
124
|
+
# The values contained in the inheritance columns may be non-camelized
|
125
|
+
# strings (e.g. 'dog' instead of 'Dog'). To reify classes in this case
|
126
|
+
# we need to call the parents class `sti_class_for` method to retrieve
|
127
|
+
# the correct record class.
|
122
128
|
#
|
123
|
-
#
|
129
|
+
# You can see these particular examples in action in
|
130
|
+
# `spec/models/animal_spec.rb` and `spec/models/plant_spec.rb`
|
124
131
|
def version_reification_class(version, attrs)
|
125
|
-
|
132
|
+
clazz = version.item_type.constantize
|
133
|
+
inheritance_column_name = clazz.inheritance_column
|
126
134
|
inher_col_value = attrs[inheritance_column_name]
|
127
|
-
|
128
|
-
|
135
|
+
return clazz if inher_col_value.blank?
|
136
|
+
|
137
|
+
# Rails 6.1 adds a public method for clients to use to customize STI classes. If that
|
138
|
+
# method is not available, fall back to using the private one
|
139
|
+
if clazz.public_methods.include?(:sti_class_for)
|
140
|
+
return clazz.sti_class_for(inher_col_value)
|
141
|
+
end
|
142
|
+
|
143
|
+
clazz.send(:find_sti_class, inher_col_value)
|
129
144
|
end
|
130
145
|
end
|
131
146
|
end
|
data/lib/paper_trail/request.rb
CHANGED
@@ -12,9 +12,6 @@ module PaperTrail
|
|
12
12
|
#
|
13
13
|
# @api private
|
14
14
|
module Request
|
15
|
-
class InvalidOption < RuntimeError
|
16
|
-
end
|
17
|
-
|
18
15
|
class << self
|
19
16
|
# Sets any data from the controller that you want PaperTrail to store.
|
20
17
|
# See also `PaperTrail::Rails::Controller#info_for_paper_trail`.
|
@@ -31,16 +31,6 @@ module PaperTrail
|
|
31
31
|
arel_field.matches("%\"#{field}\":#{json_value}%")
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
35
|
-
def where_object_changes_condition(*)
|
36
|
-
raise <<-STR.squish.freeze
|
37
|
-
where_object_changes no longer supports reading JSON from a text
|
38
|
-
column. The old implementation was inaccurate, returning more records
|
39
|
-
than you wanted. This feature was deprecated in 7.1.0 and removed in
|
40
|
-
8.0.0. The json and jsonb datatypes are still supported. See the
|
41
|
-
discussion at https://github.com/paper-trail-gem/paper_trail/issues/803
|
42
|
-
STR
|
43
|
-
end
|
44
34
|
end
|
45
35
|
end
|
46
36
|
end
|