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.
@@ -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