paper_trail 9.1.1 → 9.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/lib/generators/paper_trail/install_generator.rb +0 -18
- data/lib/paper_trail.rb +4 -34
- data/lib/paper_trail/config.rb +2 -43
- data/lib/paper_trail/frameworks/active_record.rb +0 -1
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version.rb +6 -3
- data/lib/paper_trail/model_config.rb +1 -52
- data/lib/paper_trail/queries/versions/where_object_changes.rb +5 -0
- data/lib/paper_trail/record_trail.rb +15 -127
- data/lib/paper_trail/reifier.rb +0 -90
- data/lib/paper_trail/request.rb +0 -17
- data/lib/paper_trail/version_concern.rb +4 -9
- data/lib/paper_trail/version_number.rb +2 -2
- metadata +16 -11
- data/lib/generators/paper_trail/templates/add_transaction_id_column_to_versions.rb.erb +0 -13
- data/lib/generators/paper_trail/templates/create_version_associations.rb.erb +0 -22
- data/lib/paper_trail/frameworks/active_record/models/paper_trail/version_association.rb +0 -13
- data/lib/paper_trail/reifiers/belongs_to.rb +0 -50
- data/lib/paper_trail/reifiers/has_and_belongs_to_many.rb +0 -52
- data/lib/paper_trail/reifiers/has_many.rb +0 -112
- data/lib/paper_trail/reifiers/has_many_through.rb +0 -92
- data/lib/paper_trail/reifiers/has_one.rb +0 -135
- data/lib/paper_trail/version_association_concern.rb +0 -13
@@ -1,92 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PaperTrail
|
4
|
-
module Reifiers
|
5
|
-
# Reify a single HMT association of `model`.
|
6
|
-
# @api private
|
7
|
-
module HasManyThrough
|
8
|
-
class << self
|
9
|
-
# @api private
|
10
|
-
def reify(assoc, model, options, transaction_id)
|
11
|
-
# Load the collection of through-models. For example, if `model` is a
|
12
|
-
# Chapter, having many Paragraphs through Sections, then
|
13
|
-
# `through_collection` will contain Sections.
|
14
|
-
through_collection = model.send(assoc.options[:through])
|
15
|
-
|
16
|
-
# Now, given the collection of "through" models (e.g. sections), load
|
17
|
-
# the collection of "target" models (e.g. paragraphs)
|
18
|
-
collection = collection(through_collection, assoc, options, transaction_id)
|
19
|
-
|
20
|
-
# Finally, assign the `collection` of "target" models, e.g. to
|
21
|
-
# `model.paragraphs`.
|
22
|
-
model.send(assoc.name).proxy_association.target = collection
|
23
|
-
end
|
24
|
-
|
25
|
-
private
|
26
|
-
|
27
|
-
# Examine the `source_reflection`, i.e. the "source" of `assoc` the
|
28
|
-
# `ThroughReflection`. The source can be a `BelongsToReflection`
|
29
|
-
# or a `HasManyReflection`.
|
30
|
-
#
|
31
|
-
# If the association is a has_many association again, then call
|
32
|
-
# reify_has_manys for each record in `through_collection`.
|
33
|
-
#
|
34
|
-
# @api private
|
35
|
-
def collection(through_collection, assoc, options, transaction_id)
|
36
|
-
if !assoc.source_reflection.belongs_to? && through_collection.present?
|
37
|
-
collection_through_has_many(through_collection, assoc, options, transaction_id)
|
38
|
-
else
|
39
|
-
collection_through_belongs_to(through_collection, assoc, options, transaction_id)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
# @api private
|
44
|
-
def collection_through_has_many(through_collection, assoc, options, transaction_id)
|
45
|
-
through_collection.each do |through_model|
|
46
|
-
::PaperTrail::Reifier.reify_has_manys(transaction_id, through_model, options)
|
47
|
-
end
|
48
|
-
|
49
|
-
# At this point, the "through" part of the association chain has
|
50
|
-
# been reified, but not the final, "target" part. To continue our
|
51
|
-
# example, `model.sections` (including `model.sections.paragraphs`)
|
52
|
-
# has been loaded. However, the final "target" part of the
|
53
|
-
# association, that is, `model.paragraphs`, has not been loaded. So,
|
54
|
-
# we do that now.
|
55
|
-
through_collection.flat_map { |through_model|
|
56
|
-
through_model.public_send(assoc.name.to_sym).to_a
|
57
|
-
}
|
58
|
-
end
|
59
|
-
|
60
|
-
# @api private
|
61
|
-
def collection_through_belongs_to(through_collection, assoc, options, tx_id)
|
62
|
-
ids = through_collection.map { |through_model|
|
63
|
-
through_model.send(assoc.source_reflection.foreign_key)
|
64
|
-
}
|
65
|
-
versions = load_versions_for_hmt_association(assoc, ids, tx_id, options[:version_at])
|
66
|
-
collection = Array.new assoc.klass.where(assoc.klass.primary_key => ids)
|
67
|
-
Reifiers::HasMany.prepare_array(collection, options, versions)
|
68
|
-
collection
|
69
|
-
end
|
70
|
-
|
71
|
-
# Given a `has_many(through:)` association and an array of `ids`, return
|
72
|
-
# the version records from the point in time identified by `tx_id` or
|
73
|
-
# `version_at`.
|
74
|
-
# @api private
|
75
|
-
def load_versions_for_hmt_association(assoc, ids, tx_id, version_at)
|
76
|
-
version_id_subquery = assoc.klass.paper_trail.version_class.
|
77
|
-
select("MIN(id)").
|
78
|
-
where("item_type = ?", assoc.klass.base_class.name).
|
79
|
-
where("item_id IN (?)", ids).
|
80
|
-
where(
|
81
|
-
"created_at >= ? OR transaction_id = ?",
|
82
|
-
version_at,
|
83
|
-
tx_id
|
84
|
-
).
|
85
|
-
group("item_id").
|
86
|
-
to_sql
|
87
|
-
Reifiers::HasMany.versions_by_id(assoc.klass, version_id_subquery)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
@@ -1,135 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PaperTrail
|
4
|
-
module Reifiers
|
5
|
-
# Reify a single `has_one` association of `model`.
|
6
|
-
# @api private
|
7
|
-
module HasOne
|
8
|
-
# A more helpful error message, instead of the AssociationTypeMismatch
|
9
|
-
# you would get if, eg. we were to try to assign a Bicycle to the :car
|
10
|
-
# association (before, if there were multiple records we would just take
|
11
|
-
# the first and hope for the best).
|
12
|
-
# @api private
|
13
|
-
class FoundMoreThanOne < RuntimeError
|
14
|
-
MESSAGE_FMT = <<~STR
|
15
|
-
Unable to reify has_one association. Expected to find one %s,
|
16
|
-
but found %d.
|
17
|
-
|
18
|
-
This is a known issue, and a good example of why association tracking
|
19
|
-
is an experimental feature that should not be used in production.
|
20
|
-
|
21
|
-
That said, this is a rare error. In spec/models/person_spec.rb we
|
22
|
-
reproduce it by having two STI models with the same foreign_key (Car
|
23
|
-
and Bicycle are both Vehicles and the FK for both is owner_id)
|
24
|
-
|
25
|
-
If you'd like to help fix this error, please read
|
26
|
-
https://github.com/paper-trail-gem/paper_trail/issues/594
|
27
|
-
and see spec/models/person_spec.rb
|
28
|
-
STR
|
29
|
-
|
30
|
-
def initialize(base_class_name, num_records_found)
|
31
|
-
@base_class_name = base_class_name.to_s
|
32
|
-
@num_records_found = num_records_found.to_i
|
33
|
-
end
|
34
|
-
|
35
|
-
def message
|
36
|
-
format(MESSAGE_FMT, @base_class_name, @num_records_found)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class << self
|
41
|
-
# @api private
|
42
|
-
def reify(assoc, model, options, transaction_id)
|
43
|
-
version = load_version(assoc, model, transaction_id, options[:version_at])
|
44
|
-
return unless version
|
45
|
-
if version.event == "create"
|
46
|
-
create_event(assoc, model, options)
|
47
|
-
else
|
48
|
-
noncreate_event(assoc, model, options, version)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
private
|
53
|
-
|
54
|
-
# @api private
|
55
|
-
def create_event(assoc, model, options)
|
56
|
-
if options[:mark_for_destruction]
|
57
|
-
model.send(assoc.name).mark_for_destruction if model.send(assoc.name, true)
|
58
|
-
else
|
59
|
-
model.paper_trail.appear_as_new_record do
|
60
|
-
model.send "#{assoc.name}=", nil
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
# Given a has-one association `assoc` on `model`, return the version
|
66
|
-
# record from the point in time identified by `transaction_id` or `version_at`.
|
67
|
-
# @api private
|
68
|
-
def load_version(assoc, model, transaction_id, version_at)
|
69
|
-
base_class_name = assoc.klass.base_class.name
|
70
|
-
versions = load_versions(assoc, model, transaction_id, version_at, base_class_name)
|
71
|
-
case versions.length
|
72
|
-
when 0
|
73
|
-
nil
|
74
|
-
when 1
|
75
|
-
versions.first
|
76
|
-
else
|
77
|
-
case PaperTrail.config.association_reify_error_behaviour.to_s
|
78
|
-
when "warn"
|
79
|
-
version = versions.first
|
80
|
-
version.logger&.warn(
|
81
|
-
FoundMoreThanOne.new(base_class_name, versions.length).message
|
82
|
-
)
|
83
|
-
version
|
84
|
-
when "ignore"
|
85
|
-
versions.first
|
86
|
-
else # "error"
|
87
|
-
raise FoundMoreThanOne.new(base_class_name, versions.length)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# @api private
|
93
|
-
def load_versions(assoc, model, transaction_id, version_at, base_class_name)
|
94
|
-
version_table_name = model.class.paper_trail.version_class.table_name
|
95
|
-
model.class.paper_trail.version_class.joins(:version_associations).
|
96
|
-
where("version_associations.foreign_key_name = ?", assoc.foreign_key).
|
97
|
-
where("version_associations.foreign_key_id = ?", model.id).
|
98
|
-
where("#{version_table_name}.item_type = ?", base_class_name).
|
99
|
-
where("created_at >= ? OR transaction_id = ?", version_at, transaction_id).
|
100
|
-
order("#{version_table_name}.id ASC").
|
101
|
-
load
|
102
|
-
end
|
103
|
-
|
104
|
-
# @api private
|
105
|
-
def noncreate_event(assoc, model, options, version)
|
106
|
-
child = version.reify(
|
107
|
-
options.merge(
|
108
|
-
has_many: false,
|
109
|
-
has_one: false,
|
110
|
-
belongs_to: false,
|
111
|
-
has_and_belongs_to_many: false
|
112
|
-
)
|
113
|
-
)
|
114
|
-
model.paper_trail.appear_as_new_record do
|
115
|
-
without_persisting(child) do
|
116
|
-
model.send "#{assoc.name}=", child
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
# Temporarily suppress #save so we can reassociate with the reified
|
122
|
-
# master of a has_one relationship. Since ActiveRecord 5 the related
|
123
|
-
# object is saved when it is assigned to the association. ActiveRecord
|
124
|
-
# 5 also happens to be the first version that provides #suppress.
|
125
|
-
def without_persisting(record)
|
126
|
-
if record.class.respond_to? :suppress
|
127
|
-
record.class.suppress { yield }
|
128
|
-
else
|
129
|
-
yield
|
130
|
-
end
|
131
|
-
end
|
132
|
-
end
|
133
|
-
end
|
134
|
-
end
|
135
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module PaperTrail
|
4
|
-
# Functionality for `PaperTrail::VersionAssociation`. Exists in a module
|
5
|
-
# for the same reasons outlined in version_concern.rb.
|
6
|
-
module VersionAssociationConcern
|
7
|
-
extend ::ActiveSupport::Concern
|
8
|
-
|
9
|
-
included do
|
10
|
-
belongs_to :version
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|