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