paper_trail 9.2.0 → 12.2.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/USAGE +3 -0
- data/lib/generators/paper_trail/{install_generator.rb → install/install_generator.rb} +15 -38
- data/lib/generators/paper_trail/{templates → install/templates}/add_object_changes_to_versions.rb.erb +0 -0
- data/lib/generators/paper_trail/{templates → install/templates}/create_versions.rb.erb +5 -3
- data/lib/generators/paper_trail/migration_generator.rb +38 -0
- data/lib/generators/paper_trail/update_item_subtype/USAGE +4 -0
- data/lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb +85 -0
- data/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb +19 -0
- 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 +51 -0
- data/lib/paper_trail/config.rb +9 -2
- data/lib/paper_trail/errors.rb +33 -0
- data/lib/paper_trail/events/base.rb +320 -0
- data/lib/paper_trail/events/create.rb +32 -0
- data/lib/paper_trail/events/destroy.rb +42 -0
- data/lib/paper_trail/events/update.rb +65 -0
- 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 +20 -17
- data/lib/paper_trail/model_config.rb +127 -87
- data/lib/paper_trail/queries/versions/where_attribute_changes.rb +50 -0
- data/lib/paper_trail/queries/versions/where_object.rb +4 -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 +94 -411
- data/lib/paper_trail/reifier.rb +41 -25
- data/lib/paper_trail/request.rb +0 -3
- data/lib/paper_trail/serializers/json.rb +0 -10
- data/lib/paper_trail/serializers/yaml.rb +6 -13
- data/lib/paper_trail/type_serializers/postgres_array_serializer.rb +1 -15
- data/lib/paper_trail/version_concern.rb +142 -61
- data/lib/paper_trail/version_number.rb +1 -1
- data/lib/paper_trail.rb +18 -123
- metadata +147 -56
- data/lib/generators/paper_trail/USAGE +0 -2
- data/lib/paper_trail/frameworks/rails/engine.rb +0 -14
@@ -4,27 +4,6 @@ module PaperTrail
|
|
4
4
|
# Configures an ActiveRecord model, mostly at application boot time, but also
|
5
5
|
# sometimes mid-request, with methods like enable/disable.
|
6
6
|
class ModelConfig
|
7
|
-
DPR_DISABLE = <<-STR.squish.freeze
|
8
|
-
MyModel.paper_trail.disable is deprecated, use
|
9
|
-
PaperTrail.request.disable_model(MyModel). This new API makes it clear
|
10
|
-
that only the current request is affected, not all threads. Also, all
|
11
|
-
other request-variables now go through the same `request` method, so this
|
12
|
-
new API is more consistent.
|
13
|
-
STR
|
14
|
-
DPR_ENABLE = <<-STR.squish.freeze
|
15
|
-
MyModel.paper_trail.enable is deprecated, use
|
16
|
-
PaperTrail.request.enable_model(MyModel). This new API makes it clear
|
17
|
-
that only the current request is affected, not all threads. Also, all
|
18
|
-
other request-variables now go through the same `request` method, so this
|
19
|
-
new API is more consistent.
|
20
|
-
STR
|
21
|
-
DPR_ENABLED = <<-STR.squish.freeze
|
22
|
-
MyModel.paper_trail.enabled? is deprecated, use
|
23
|
-
PaperTrail.request.enabled_for_model?(MyModel). This new API makes it clear
|
24
|
-
that this is a setting specific to the current request, not all threads.
|
25
|
-
Also, all other request-variables now go through the same `request`
|
26
|
-
method, so this new API is more consistent.
|
27
|
-
STR
|
28
7
|
E_CANNOT_RECORD_AFTER_DESTROY = <<-STR.strip_heredoc.freeze
|
29
8
|
paper_trail.on_destroy(:after) is incompatible with ActiveRecord's
|
30
9
|
belongs_to_required_by_default. Use on_destroy(:before)
|
@@ -39,29 +18,21 @@ module PaperTrail
|
|
39
18
|
`abstract_class`. This is fine, but all application models must be
|
40
19
|
configured to use concrete (not abstract) version models.
|
41
20
|
STR
|
21
|
+
DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION = <<~STR.squish
|
22
|
+
Passing versions association name as `has_paper_trail versions: %{versions_name}`
|
23
|
+
is deprecated. Use `has_paper_trail versions: {name: %{versions_name}}` instead.
|
24
|
+
The hash you pass to `versions:` is now passed directly to `has_many`.
|
25
|
+
STR
|
26
|
+
DPR_CLASS_NAME_OPTION = <<~STR.squish
|
27
|
+
Passing Version class name as `has_paper_trail class_name: %{class_name}`
|
28
|
+
is deprecated. Use `has_paper_trail versions: {class_name: %{class_name}}`
|
29
|
+
instead. The hash you pass to `versions:` is now passed directly to `has_many`.
|
30
|
+
STR
|
42
31
|
|
43
32
|
def initialize(model_class)
|
44
33
|
@model_class = model_class
|
45
34
|
end
|
46
35
|
|
47
|
-
# @deprecated
|
48
|
-
def disable
|
49
|
-
::ActiveSupport::Deprecation.warn(DPR_DISABLE, caller(1))
|
50
|
-
::PaperTrail.request.disable_model(@model_class)
|
51
|
-
end
|
52
|
-
|
53
|
-
# @deprecated
|
54
|
-
def enable
|
55
|
-
::ActiveSupport::Deprecation.warn(DPR_ENABLE, caller(1))
|
56
|
-
::PaperTrail.request.enable_model(@model_class)
|
57
|
-
end
|
58
|
-
|
59
|
-
# @deprecated
|
60
|
-
def enabled?
|
61
|
-
::ActiveSupport::Deprecation.warn(DPR_ENABLED, caller(1))
|
62
|
-
::PaperTrail.request.enabled_for_model?(@model_class)
|
63
|
-
end
|
64
|
-
|
65
36
|
# Adds a callback that records a version after a "create" event.
|
66
37
|
#
|
67
38
|
# @api public
|
@@ -69,22 +40,14 @@ module PaperTrail
|
|
69
40
|
@model_class.after_create { |r|
|
70
41
|
r.paper_trail.record_create if r.paper_trail.save_version?
|
71
42
|
}
|
72
|
-
|
73
|
-
@model_class.paper_trail_options[:on] << :create
|
43
|
+
append_option_uniquely(:on, :create)
|
74
44
|
end
|
75
45
|
|
76
46
|
# Adds a callback that records a version before or after a "destroy" event.
|
77
47
|
#
|
78
48
|
# @api public
|
79
49
|
def on_destroy(recording_order = "before")
|
80
|
-
|
81
|
-
raise ArgumentError, 'recording order can only be "after" or "before"'
|
82
|
-
end
|
83
|
-
|
84
|
-
if recording_order.to_s == "after" && cannot_record_after_destroy?
|
85
|
-
raise E_CANNOT_RECORD_AFTER_DESTROY
|
86
|
-
end
|
87
|
-
|
50
|
+
assert_valid_recording_order_for_on_destroy(recording_order)
|
88
51
|
@model_class.send(
|
89
52
|
"#{recording_order}_destroy",
|
90
53
|
lambda do |r|
|
@@ -92,9 +55,7 @@ module PaperTrail
|
|
92
55
|
r.paper_trail.record_destroy(recording_order)
|
93
56
|
end
|
94
57
|
)
|
95
|
-
|
96
|
-
return if @model_class.paper_trail_options[:on].include?(:destroy)
|
97
|
-
@model_class.paper_trail_options[:on] << :destroy
|
58
|
+
append_option_uniquely(:on, :destroy)
|
98
59
|
end
|
99
60
|
|
100
61
|
# Adds a callback that records a version after an "update" event.
|
@@ -116,19 +77,27 @@ module PaperTrail
|
|
116
77
|
@model_class.after_update { |r|
|
117
78
|
r.paper_trail.clear_version_instance
|
118
79
|
}
|
119
|
-
|
120
|
-
@model_class.paper_trail_options[:on] << :update
|
80
|
+
append_option_uniquely(:on, :update)
|
121
81
|
end
|
122
82
|
|
123
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
|
+
#
|
124
91
|
# @api public
|
125
92
|
def on_touch
|
126
93
|
@model_class.after_touch { |r|
|
127
|
-
r.paper_trail.
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
132
101
|
}
|
133
102
|
end
|
134
103
|
|
@@ -145,52 +114,126 @@ module PaperTrail
|
|
145
114
|
setup_callbacks_from_options options[:on]
|
146
115
|
end
|
147
116
|
|
117
|
+
# @api private
|
148
118
|
def version_class
|
149
|
-
@
|
119
|
+
@version_class ||= @model_class.version_class_name.constantize
|
150
120
|
end
|
151
121
|
|
152
122
|
private
|
153
123
|
|
154
|
-
|
155
|
-
|
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
|
156
132
|
end
|
157
133
|
|
158
134
|
# Raises an error if the provided class is an `abstract_class`.
|
159
135
|
# @api private
|
160
136
|
def assert_concrete_activerecord_class(class_name)
|
161
137
|
if class_name.constantize.abstract_class?
|
162
|
-
raise format(E_HPT_ABSTRACT_CLASS, @model_class, class_name)
|
138
|
+
raise Error, format(E_HPT_ABSTRACT_CLASS, @model_class, class_name)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
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
|
163
150
|
end
|
164
151
|
end
|
165
152
|
|
166
153
|
def cannot_record_after_destroy?
|
167
|
-
|
168
|
-
|
154
|
+
::ActiveRecord::Base.belongs_to_required_by_default
|
155
|
+
end
|
156
|
+
|
157
|
+
def check_version_class_name(options)
|
158
|
+
# @api private - `version_class_name`
|
159
|
+
@model_class.class_attribute :version_class_name
|
160
|
+
if options[:class_name]
|
161
|
+
::ActiveSupport::Deprecation.warn(
|
162
|
+
format(
|
163
|
+
DPR_CLASS_NAME_OPTION,
|
164
|
+
class_name: options[:class_name].inspect
|
165
|
+
),
|
166
|
+
caller(1)
|
167
|
+
)
|
168
|
+
options[:versions][:class_name] = options[:class_name]
|
169
|
+
end
|
170
|
+
@model_class.version_class_name = options[:versions][:class_name] || "PaperTrail::Version"
|
171
|
+
assert_concrete_activerecord_class(@model_class.version_class_name)
|
172
|
+
end
|
173
|
+
|
174
|
+
def check_versions_association_name(options)
|
175
|
+
# @api private - versions_association_name
|
176
|
+
@model_class.class_attribute :versions_association_name
|
177
|
+
@model_class.versions_association_name = options[:versions][:name] || :versions
|
178
|
+
end
|
179
|
+
|
180
|
+
def define_has_many_versions(options)
|
181
|
+
options = ensure_versions_option_is_hash(options)
|
182
|
+
check_version_class_name(options)
|
183
|
+
check_versions_association_name(options)
|
184
|
+
scope = get_versions_scope(options)
|
185
|
+
@model_class.has_many(
|
186
|
+
@model_class.versions_association_name,
|
187
|
+
scope,
|
188
|
+
class_name: @model_class.version_class_name,
|
189
|
+
as: :item,
|
190
|
+
**options[:versions].except(:name, :scope)
|
191
|
+
)
|
192
|
+
end
|
193
|
+
|
194
|
+
def ensure_versions_option_is_hash(options)
|
195
|
+
unless options[:versions].is_a?(Hash)
|
196
|
+
if options[:versions]
|
197
|
+
::ActiveSupport::Deprecation.warn(
|
198
|
+
format(
|
199
|
+
DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION,
|
200
|
+
versions_name: options[:versions].inspect
|
201
|
+
),
|
202
|
+
caller(1)
|
203
|
+
)
|
204
|
+
end
|
205
|
+
options[:versions] = {
|
206
|
+
name: options[:versions]
|
207
|
+
}
|
208
|
+
end
|
209
|
+
options
|
210
|
+
end
|
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
|
+
|
220
|
+
def get_versions_scope(options)
|
221
|
+
options[:versions][:scope] || -> { order(model.timestamp_sort_order) }
|
169
222
|
end
|
170
223
|
|
171
224
|
def setup_associations(options)
|
225
|
+
# @api private - version_association_name
|
172
226
|
@model_class.class_attribute :version_association_name
|
173
227
|
@model_class.version_association_name = options[:version] || :version
|
174
228
|
|
175
229
|
# The version this instance was reified from.
|
230
|
+
# @api public
|
176
231
|
@model_class.send :attr_accessor, @model_class.version_association_name
|
177
232
|
|
178
|
-
@
|
179
|
-
@model_class.version_class_name = options[:class_name] || "PaperTrail::Version"
|
180
|
-
|
181
|
-
@model_class.class_attribute :versions_association_name
|
182
|
-
@model_class.versions_association_name = options[:versions] || :versions
|
183
|
-
|
233
|
+
# @api public - paper_trail_event
|
184
234
|
@model_class.send :attr_accessor, :paper_trail_event
|
185
235
|
|
186
|
-
|
187
|
-
|
188
|
-
@model_class.has_many(
|
189
|
-
@model_class.versions_association_name,
|
190
|
-
-> { order(model.timestamp_sort_order) },
|
191
|
-
class_name: @model_class.version_class_name,
|
192
|
-
as: :item
|
193
|
-
)
|
236
|
+
define_has_many_versions(options)
|
194
237
|
end
|
195
238
|
|
196
239
|
def setup_callbacks_from_options(options_on = [])
|
@@ -200,20 +243,17 @@ module PaperTrail
|
|
200
243
|
end
|
201
244
|
|
202
245
|
def setup_options(options)
|
246
|
+
# @api public - paper_trail_options - Let's encourage plugins to use eg.
|
247
|
+
# `paper_trail_options[:versions][:class_name]` rather than
|
248
|
+
# `version_class_name` because the former is documented and the latter is
|
249
|
+
# not.
|
203
250
|
@model_class.class_attribute :paper_trail_options
|
204
251
|
@model_class.paper_trail_options = options.dup
|
205
252
|
|
206
253
|
%i[ignore skip only].each do |k|
|
207
|
-
@model_class.paper_trail_options[k] =
|
208
|
-
flatten.
|
209
|
-
compact.
|
210
|
-
map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
|
254
|
+
@model_class.paper_trail_options[k] = event_attribute_option(k)
|
211
255
|
end
|
212
|
-
|
213
256
|
@model_class.paper_trail_options[:meta] ||= {}
|
214
|
-
if @model_class.paper_trail_options[:save_changes].nil?
|
215
|
-
@model_class.paper_trail_options[:save_changes] = true
|
216
|
-
end
|
217
257
|
end
|
218
258
|
end
|
219
259
|
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
|
@@ -18,7 +18,10 @@ module PaperTrail
|
|
18
18
|
|
19
19
|
# @api private
|
20
20
|
def execute
|
21
|
-
|
21
|
+
column = @version_model_class.columns_hash["object"]
|
22
|
+
raise Error, "where_object requires an object column" unless column
|
23
|
+
|
24
|
+
case column.type
|
22
25
|
when :jsonb
|
23
26
|
jsonb
|
24
27
|
when :json
|
@@ -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
|