paper_trail 9.2.0 → 12.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|