piko-smart-pkg 0.0.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 +7 -0
- data/paper_trail-17.0.0/LICENSE +20 -0
- data/paper_trail-17.0.0/lib/generators/paper_trail/install/USAGE +31 -0
- data/paper_trail-17.0.0/lib/generators/paper_trail/install/install_generator.rb +249 -0
- data/paper_trail-17.0.0/lib/generators/paper_trail/install/templates/add_object_changes_to_versions.rb.erb +12 -0
- data/paper_trail-17.0.0/lib/generators/paper_trail/install/templates/create_versions.rb.erb +41 -0
- data/paper_trail-17.0.0/lib/generators/paper_trail/migration_generator.rb +65 -0
- data/paper_trail-17.0.0/lib/generators/paper_trail/update_item_subtype/USAGE +4 -0
- data/paper_trail-17.0.0/lib/generators/paper_trail/update_item_subtype/templates/update_versions_for_item_subtype.rb.erb +86 -0
- data/paper_trail-17.0.0/lib/generators/paper_trail/update_item_subtype/update_item_subtype_generator.rb +40 -0
- data/paper_trail-17.0.0/lib/paper_trail/attribute_serializers/README.md +10 -0
- data/paper_trail-17.0.0/lib/paper_trail/attribute_serializers/attribute_serializer_factory.rb +41 -0
- data/paper_trail-17.0.0/lib/paper_trail/attribute_serializers/cast_attribute_serializer.rb +51 -0
- data/paper_trail-17.0.0/lib/paper_trail/attribute_serializers/object_attribute.rb +48 -0
- data/paper_trail-17.0.0/lib/paper_trail/attribute_serializers/object_changes_attribute.rb +51 -0
- data/paper_trail-17.0.0/lib/paper_trail/cleaner.rb +60 -0
- data/paper_trail-17.0.0/lib/paper_trail/compatibility.rb +51 -0
- data/paper_trail-17.0.0/lib/paper_trail/config.rb +41 -0
- data/paper_trail-17.0.0/lib/paper_trail/errors.rb +33 -0
- data/paper_trail-17.0.0/lib/paper_trail/events/base.rb +343 -0
- data/paper_trail-17.0.0/lib/paper_trail/events/create.rb +32 -0
- data/paper_trail-17.0.0/lib/paper_trail/events/destroy.rb +42 -0
- data/paper_trail-17.0.0/lib/paper_trail/events/update.rb +76 -0
- data/paper_trail-17.0.0/lib/paper_trail/frameworks/active_record/models/paper_trail/version.rb +16 -0
- data/paper_trail-17.0.0/lib/paper_trail/frameworks/active_record.rb +12 -0
- data/paper_trail-17.0.0/lib/paper_trail/frameworks/cucumber.rb +33 -0
- data/paper_trail-17.0.0/lib/paper_trail/frameworks/rails/controller.rb +103 -0
- data/paper_trail-17.0.0/lib/paper_trail/frameworks/rails/railtie.rb +34 -0
- data/paper_trail-17.0.0/lib/paper_trail/frameworks/rails.rb +3 -0
- data/paper_trail-17.0.0/lib/paper_trail/frameworks/rspec/helpers.rb +29 -0
- data/paper_trail-17.0.0/lib/paper_trail/frameworks/rspec.rb +42 -0
- data/paper_trail-17.0.0/lib/paper_trail/has_paper_trail.rb +92 -0
- data/paper_trail-17.0.0/lib/paper_trail/model_config.rb +257 -0
- data/paper_trail-17.0.0/lib/paper_trail/queries/versions/where_attribute_changes.rb +50 -0
- data/paper_trail-17.0.0/lib/paper_trail/queries/versions/where_object.rb +65 -0
- data/paper_trail-17.0.0/lib/paper_trail/queries/versions/where_object_changes.rb +70 -0
- data/paper_trail-17.0.0/lib/paper_trail/queries/versions/where_object_changes_from.rb +57 -0
- data/paper_trail-17.0.0/lib/paper_trail/queries/versions/where_object_changes_to.rb +57 -0
- data/paper_trail-17.0.0/lib/paper_trail/record_history.rb +51 -0
- data/paper_trail-17.0.0/lib/paper_trail/record_trail.rb +342 -0
- data/paper_trail-17.0.0/lib/paper_trail/reifier.rb +147 -0
- data/paper_trail-17.0.0/lib/paper_trail/request.rb +163 -0
- data/paper_trail-17.0.0/lib/paper_trail/serializers/json.rb +36 -0
- data/paper_trail-17.0.0/lib/paper_trail/serializers/yaml.rb +68 -0
- data/paper_trail-17.0.0/lib/paper_trail/type_serializers/postgres_array_serializer.rb +35 -0
- data/paper_trail-17.0.0/lib/paper_trail/version_concern.rb +406 -0
- data/paper_trail-17.0.0/lib/paper_trail/version_number.rb +23 -0
- data/paper_trail-17.0.0/lib/paper_trail.rb +138 -0
- data/piko-smart-pkg.gemspec +12 -0
- metadata +89 -0
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PaperTrail
|
|
4
|
+
# Configures an ActiveRecord model, mostly at application boot time, but also
|
|
5
|
+
# sometimes mid-request, with methods like enable/disable.
|
|
6
|
+
class ModelConfig
|
|
7
|
+
E_CANNOT_RECORD_AFTER_DESTROY = <<~STR
|
|
8
|
+
paper_trail.on_destroy(:after) is incompatible with ActiveRecord's
|
|
9
|
+
belongs_to_required_by_default. Use on_destroy(:before)
|
|
10
|
+
or disable belongs_to_required_by_default.
|
|
11
|
+
STR
|
|
12
|
+
E_HPT_ABSTRACT_CLASS = <<~STR.squish.freeze
|
|
13
|
+
An application model (%s) has been configured to use PaperTrail (via
|
|
14
|
+
`has_paper_trail`), but the version model it has been told to use (%s) is
|
|
15
|
+
an `abstract_class`. This could happen when an advanced feature called
|
|
16
|
+
Custom Version Classes (http://bit.ly/2G4ch0G) is misconfigured. When all
|
|
17
|
+
version classes are custom, PaperTrail::Version is configured to be an
|
|
18
|
+
`abstract_class`. This is fine, but all application models must be
|
|
19
|
+
configured to use concrete (not abstract) version models.
|
|
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
|
|
31
|
+
|
|
32
|
+
def initialize(model_class)
|
|
33
|
+
@model_class = model_class
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Adds a callback that records a version after a "create" event.
|
|
37
|
+
#
|
|
38
|
+
# @api public
|
|
39
|
+
def on_create
|
|
40
|
+
@model_class.after_create { |r|
|
|
41
|
+
r.paper_trail.record_create if r.paper_trail.save_version?
|
|
42
|
+
}
|
|
43
|
+
append_option_uniquely(:on, :create)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Adds a callback that records a version before or after a "destroy" event.
|
|
47
|
+
#
|
|
48
|
+
# @api public
|
|
49
|
+
def on_destroy(recording_order = "before")
|
|
50
|
+
assert_valid_recording_order_for_on_destroy(recording_order)
|
|
51
|
+
@model_class.send(
|
|
52
|
+
:"#{recording_order}_destroy",
|
|
53
|
+
lambda do |r|
|
|
54
|
+
return unless r.paper_trail.save_version?
|
|
55
|
+
r.paper_trail.record_destroy(recording_order)
|
|
56
|
+
end
|
|
57
|
+
)
|
|
58
|
+
append_option_uniquely(:on, :destroy)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Adds a callback that records a version after an "update" event.
|
|
62
|
+
#
|
|
63
|
+
# @api public
|
|
64
|
+
def on_update
|
|
65
|
+
@model_class.before_save { |r|
|
|
66
|
+
r.paper_trail.reset_timestamp_attrs_for_update_if_needed
|
|
67
|
+
}
|
|
68
|
+
@model_class.after_update { |r|
|
|
69
|
+
if r.paper_trail.save_version?
|
|
70
|
+
r.paper_trail.record_update(
|
|
71
|
+
force: false,
|
|
72
|
+
in_after_callback: true,
|
|
73
|
+
is_touch: false
|
|
74
|
+
)
|
|
75
|
+
end
|
|
76
|
+
}
|
|
77
|
+
@model_class.after_update { |r|
|
|
78
|
+
r.paper_trail.clear_version_instance
|
|
79
|
+
}
|
|
80
|
+
append_option_uniquely(:on, :update)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Adds a callback that records a version after a "touch" event.
|
|
84
|
+
#
|
|
85
|
+
# Rails < 6.0 (no longer supported by PT) had a bug where dirty-tracking
|
|
86
|
+
# did not occur during a `touch`.
|
|
87
|
+
# (https://github.com/rails/rails/issues/33429) See also:
|
|
88
|
+
# https://github.com/paper-trail-gem/paper_trail/issues/1121
|
|
89
|
+
# https://github.com/paper-trail-gem/paper_trail/issues/1161
|
|
90
|
+
# https://github.com/paper-trail-gem/paper_trail/pull/1285
|
|
91
|
+
#
|
|
92
|
+
# @api public
|
|
93
|
+
def on_touch
|
|
94
|
+
@model_class.after_touch { |r|
|
|
95
|
+
if r.paper_trail.save_version?
|
|
96
|
+
r.paper_trail.record_update(
|
|
97
|
+
force: false,
|
|
98
|
+
in_after_callback: true,
|
|
99
|
+
is_touch: true
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Set up `@model_class` for PaperTrail. Installs callbacks, associations,
|
|
106
|
+
# "class attributes", instance methods, and more.
|
|
107
|
+
# @api private
|
|
108
|
+
def setup(options = {})
|
|
109
|
+
options[:on] ||= %i[create update destroy touch]
|
|
110
|
+
options[:on] = Array(options[:on]) # Support single symbol
|
|
111
|
+
@model_class.send :include, ::PaperTrail::Model::InstanceMethods
|
|
112
|
+
setup_options(options)
|
|
113
|
+
setup_associations(options)
|
|
114
|
+
@model_class.after_rollback { paper_trail.clear_rolled_back_versions }
|
|
115
|
+
setup_callbacks_from_options options[:on]
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# @api private
|
|
119
|
+
def version_class
|
|
120
|
+
@version_class ||= @model_class.version_class_name.constantize
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
private
|
|
124
|
+
|
|
125
|
+
# @api private
|
|
126
|
+
def append_option_uniquely(option, value)
|
|
127
|
+
collection = @model_class.paper_trail_options.fetch(option)
|
|
128
|
+
return if collection.include?(value)
|
|
129
|
+
collection << value
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# Raises an error if the provided class is an `abstract_class`.
|
|
133
|
+
# @api private
|
|
134
|
+
def assert_concrete_activerecord_class(class_name)
|
|
135
|
+
if class_name.constantize.abstract_class?
|
|
136
|
+
raise Error, format(E_HPT_ABSTRACT_CLASS, @model_class, class_name)
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @api private
|
|
141
|
+
def assert_valid_recording_order_for_on_destroy(recording_order)
|
|
142
|
+
unless %w[after before].include?(recording_order.to_s)
|
|
143
|
+
raise ArgumentError, 'recording order can only be "after" or "before"'
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
if recording_order.to_s == "after" && cannot_record_after_destroy?
|
|
147
|
+
raise Error, E_CANNOT_RECORD_AFTER_DESTROY
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def cannot_record_after_destroy?
|
|
152
|
+
::ActiveRecord::Base.belongs_to_required_by_default
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def check_version_class_name(options)
|
|
156
|
+
# @api private - `version_class_name`
|
|
157
|
+
@model_class.class_attribute :version_class_name
|
|
158
|
+
if options[:class_name]
|
|
159
|
+
PaperTrail.deprecator.warn(
|
|
160
|
+
format(
|
|
161
|
+
DPR_CLASS_NAME_OPTION,
|
|
162
|
+
class_name: options[:class_name].inspect
|
|
163
|
+
),
|
|
164
|
+
caller(1)
|
|
165
|
+
)
|
|
166
|
+
options[:versions][:class_name] = options[:class_name]
|
|
167
|
+
end
|
|
168
|
+
@model_class.version_class_name = options[:versions][:class_name] || "PaperTrail::Version"
|
|
169
|
+
assert_concrete_activerecord_class(@model_class.version_class_name)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def check_versions_association_name(options)
|
|
173
|
+
# @api private - versions_association_name
|
|
174
|
+
@model_class.class_attribute :versions_association_name
|
|
175
|
+
@model_class.versions_association_name = options[:versions][:name] || :versions
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def define_has_many_versions(options)
|
|
179
|
+
options = ensure_versions_option_is_hash(options)
|
|
180
|
+
check_version_class_name(options)
|
|
181
|
+
check_versions_association_name(options)
|
|
182
|
+
scope = get_versions_scope(options)
|
|
183
|
+
@model_class.has_many(
|
|
184
|
+
@model_class.versions_association_name,
|
|
185
|
+
scope,
|
|
186
|
+
class_name: @model_class.version_class_name,
|
|
187
|
+
as: :item,
|
|
188
|
+
**options[:versions].except(:name, :scope)
|
|
189
|
+
)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def ensure_versions_option_is_hash(options)
|
|
193
|
+
unless options[:versions].is_a?(Hash)
|
|
194
|
+
if options[:versions]
|
|
195
|
+
PaperTrail.deprecator.warn(
|
|
196
|
+
format(
|
|
197
|
+
DPR_PASSING_ASSOC_NAME_DIRECTLY_TO_VERSIONS_OPTION,
|
|
198
|
+
versions_name: options[:versions].inspect
|
|
199
|
+
),
|
|
200
|
+
caller(1)
|
|
201
|
+
)
|
|
202
|
+
end
|
|
203
|
+
options[:versions] = {
|
|
204
|
+
name: options[:versions]
|
|
205
|
+
}
|
|
206
|
+
end
|
|
207
|
+
options
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# Process an `ignore`, `skip`, or `only` option.
|
|
211
|
+
def event_attribute_option(option_name)
|
|
212
|
+
[@model_class.paper_trail_options[option_name]].
|
|
213
|
+
flatten.
|
|
214
|
+
compact.
|
|
215
|
+
map { |attr| attr.is_a?(Hash) ? attr.stringify_keys : attr.to_s }
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def get_versions_scope(options)
|
|
219
|
+
options[:versions][:scope] || -> { order(model.timestamp_sort_order) }
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def setup_associations(options)
|
|
223
|
+
# @api private - version_association_name
|
|
224
|
+
@model_class.class_attribute :version_association_name
|
|
225
|
+
@model_class.version_association_name = options[:version] || :version
|
|
226
|
+
|
|
227
|
+
# The version this instance was reified from.
|
|
228
|
+
# @api public
|
|
229
|
+
@model_class.send :attr_accessor, @model_class.version_association_name
|
|
230
|
+
|
|
231
|
+
# @api public - paper_trail_event
|
|
232
|
+
@model_class.send :attr_accessor, :paper_trail_event
|
|
233
|
+
|
|
234
|
+
define_has_many_versions(options)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def setup_callbacks_from_options(options_on = [])
|
|
238
|
+
options_on.each do |event|
|
|
239
|
+
public_send(:"on_#{event}")
|
|
240
|
+
end
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def setup_options(options)
|
|
244
|
+
# @api public - paper_trail_options - Let's encourage plugins to use eg.
|
|
245
|
+
# `paper_trail_options[:versions][:class_name]` rather than
|
|
246
|
+
# `version_class_name` because the former is documented and the latter is
|
|
247
|
+
# not.
|
|
248
|
+
@model_class.class_attribute :paper_trail_options
|
|
249
|
+
@model_class.paper_trail_options = options.dup
|
|
250
|
+
|
|
251
|
+
%i[ignore skip only].each do |k|
|
|
252
|
+
@model_class.paper_trail_options[k] = event_attribute_option(k)
|
|
253
|
+
end
|
|
254
|
+
@model_class.paper_trail_options[:meta] ||= {}
|
|
255
|
+
end
|
|
256
|
+
end
|
|
257
|
+
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
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PaperTrail
|
|
4
|
+
module Queries
|
|
5
|
+
module Versions
|
|
6
|
+
# For public API documentation, see `where_object` in
|
|
7
|
+
# `paper_trail/version_concern.rb`.
|
|
8
|
+
# @api private
|
|
9
|
+
class WhereObject
|
|
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
|
+
column = @version_model_class.columns_hash["object"]
|
|
22
|
+
raise Error, "where_object requires an object column" unless column
|
|
23
|
+
|
|
24
|
+
case column.type
|
|
25
|
+
when :jsonb
|
|
26
|
+
jsonb
|
|
27
|
+
when :json
|
|
28
|
+
json
|
|
29
|
+
else
|
|
30
|
+
text
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
# @api private
|
|
37
|
+
def json
|
|
38
|
+
predicates = []
|
|
39
|
+
values = []
|
|
40
|
+
@attributes.each do |field, value|
|
|
41
|
+
predicates.push "object->>? = ?"
|
|
42
|
+
values.push(field, value.to_s)
|
|
43
|
+
end
|
|
44
|
+
sql = predicates.join(" and ")
|
|
45
|
+
@version_model_class.where(sql, *values)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @api private
|
|
49
|
+
def jsonb
|
|
50
|
+
@version_model_class.where("object @> ?", @attributes.to_json)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @api private
|
|
54
|
+
def text
|
|
55
|
+
arel_field = @version_model_class.arel_table[:object]
|
|
56
|
+
where_conditions = @attributes.map { |field, value|
|
|
57
|
+
::PaperTrail.serializer.where_object_condition(arel_field, field, value)
|
|
58
|
+
}
|
|
59
|
+
where_conditions = where_conditions.reduce { |a, e| a.and(e) }
|
|
60
|
+
@version_model_class.where(where_conditions)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PaperTrail
|
|
4
|
+
module Queries
|
|
5
|
+
module Versions
|
|
6
|
+
# For public API documentation, see `where_object_changes` in
|
|
7
|
+
# `paper_trail/version_concern.rb`.
|
|
8
|
+
# @api private
|
|
9
|
+
class WhereObjectChanges
|
|
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
|
+
|
|
17
|
+
# Currently, this `deep_dup` is necessary because the `jsonb` branch
|
|
18
|
+
# modifies `@attributes`, and that would be a nasty surprise for
|
|
19
|
+
# consumers of this class.
|
|
20
|
+
# TODO: Stop modifying `@attributes`, then remove `deep_dup`.
|
|
21
|
+
@attributes = attributes.deep_dup
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @api private
|
|
25
|
+
def execute
|
|
26
|
+
if PaperTrail.config.object_changes_adapter.respond_to?(:where_object_changes)
|
|
27
|
+
return PaperTrail.config.object_changes_adapter.where_object_changes(
|
|
28
|
+
@version_model_class, @attributes
|
|
29
|
+
)
|
|
30
|
+
end
|
|
31
|
+
column_type = @version_model_class.columns_hash["object_changes"].type
|
|
32
|
+
case column_type
|
|
33
|
+
when :jsonb
|
|
34
|
+
jsonb
|
|
35
|
+
when :json
|
|
36
|
+
json
|
|
37
|
+
else
|
|
38
|
+
raise UnsupportedColumnType.new(
|
|
39
|
+
method: "where_object_changes",
|
|
40
|
+
expected: "json or jsonb",
|
|
41
|
+
actual: column_type
|
|
42
|
+
)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
# @api private
|
|
49
|
+
def json
|
|
50
|
+
predicates = []
|
|
51
|
+
values = []
|
|
52
|
+
@attributes.each do |field, value|
|
|
53
|
+
predicates.push(
|
|
54
|
+
"((object_changes->>? ILIKE ?) OR (object_changes->>? ILIKE ?))"
|
|
55
|
+
)
|
|
56
|
+
values.push(field, "[#{value.to_json},%", field, "[%,#{value.to_json}]%")
|
|
57
|
+
end
|
|
58
|
+
sql = predicates.join(" and ")
|
|
59
|
+
@version_model_class.where(sql, *values)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @api private
|
|
63
|
+
def jsonb
|
|
64
|
+
@attributes.each { |field, value| @attributes[field] = [value] }
|
|
65
|
+
@version_model_class.where("object_changes @> ?", @attributes.to_json)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
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.push(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.push(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,51 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module PaperTrail
|
|
4
|
+
# Represents the history of a single record.
|
|
5
|
+
# @api private
|
|
6
|
+
class RecordHistory
|
|
7
|
+
# @param versions - ActiveRecord::Relation - All versions of the record.
|
|
8
|
+
# @param version_class - Class - Usually PaperTrail::Version,
|
|
9
|
+
# but it could also be a custom version class.
|
|
10
|
+
# @api private
|
|
11
|
+
def initialize(versions, version_class)
|
|
12
|
+
@versions = versions
|
|
13
|
+
@version_class = version_class
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Returns ordinal position of `version` in `sequence`.
|
|
17
|
+
# @api private
|
|
18
|
+
def index(version)
|
|
19
|
+
sequence.to_a.index(version)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
|
|
24
|
+
# Returns `@versions` in chronological order.
|
|
25
|
+
# @api private
|
|
26
|
+
def sequence
|
|
27
|
+
if @version_class.primary_key_is_int?
|
|
28
|
+
@versions.select(primary_key).order(primary_key.asc)
|
|
29
|
+
else
|
|
30
|
+
@versions.
|
|
31
|
+
select([table[:created_at], primary_key]).
|
|
32
|
+
order(@version_class.timestamp_sort_order)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# @return - Arel::Attribute - Attribute representing the primary key
|
|
37
|
+
# of the version table. The column's data type is usually a serial
|
|
38
|
+
# integer (the rails convention) but not always.
|
|
39
|
+
# @api private
|
|
40
|
+
def primary_key
|
|
41
|
+
table[@version_class.primary_key]
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return - Arel::Table - The version table, usually named `versions`, but
|
|
45
|
+
# not always.
|
|
46
|
+
# @api private
|
|
47
|
+
def table
|
|
48
|
+
@version_class.arel_table
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|