paper_trail 6.0.1 → 6.0.2
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/.github/CONTRIBUTING.md +45 -9
- data/.rubocop_todo.yml +2 -2
- data/CHANGELOG.md +22 -0
- data/README.md +1 -1
- data/lib/paper_trail/model_config.rb +6 -16
- data/lib/paper_trail/queries/versions/where_object.rb +60 -0
- data/lib/paper_trail/queries/versions/where_object_changes.rb +68 -0
- data/lib/paper_trail/record_trail.rb +71 -45
- data/lib/paper_trail/reifier.rb +243 -164
- data/lib/paper_trail/version_concern.rb +48 -48
- data/lib/paper_trail/version_number.rb +1 -1
- data/paper_trail.gemspec +23 -3
- data/spec/controllers/articles_controller_spec.rb +28 -0
- data/test/dummy/config/boot.rb +20 -6
- metadata +16 -11
- data/test/functional/enabled_for_controller_test.rb +0 -28
data/lib/paper_trail/reifier.rb
CHANGED
@@ -10,36 +10,7 @@ module PaperTrail
|
|
10
10
|
def reify(version, options)
|
11
11
|
options = apply_defaults_to(options, version)
|
12
12
|
attrs = version.object_deserialized
|
13
|
-
|
14
|
-
# Normally a polymorphic belongs_to relationship allows us to get the
|
15
|
-
# object we belong to by calling, in this case, `item`. However this
|
16
|
-
# returns nil if `item` has been destroyed, and we need to be able to
|
17
|
-
# retrieve destroyed objects.
|
18
|
-
#
|
19
|
-
# In this situation we constantize the `item_type` to get hold of the
|
20
|
-
# class...except when the stored object's attributes include a `type`
|
21
|
-
# key. If this is the case, the object we belong to is using single
|
22
|
-
# table inheritance and the `item_type` will be the base class, not the
|
23
|
-
# actual subclass. If `type` is present but empty, the class is the base
|
24
|
-
# class.
|
25
|
-
if options[:dup] != true && version.item
|
26
|
-
model = version.item
|
27
|
-
if options[:unversioned_attributes] == :nil
|
28
|
-
init_unversioned_attrs(attrs, model)
|
29
|
-
end
|
30
|
-
else
|
31
|
-
klass = version_reification_class(version, attrs)
|
32
|
-
# The `dup` option always returns a new object, otherwise we should
|
33
|
-
# attempt to look for the item outside of default scope(s).
|
34
|
-
find_cond = { klass.primary_key => version.item_id }
|
35
|
-
if options[:dup] || (item_found = klass.unscoped.where(find_cond).first).nil?
|
36
|
-
model = klass.new
|
37
|
-
elsif options[:unversioned_attributes] == :nil
|
38
|
-
model = item_found
|
39
|
-
init_unversioned_attrs(attrs, model)
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
13
|
+
model = init_model(attrs, options, version)
|
43
14
|
reify_attributes(model, version, attrs)
|
44
15
|
model.send "#{model.class.version_association_name}=", version
|
45
16
|
reify_associations(model, options, version)
|
@@ -71,6 +42,43 @@ module PaperTrail
|
|
71
42
|
end
|
72
43
|
end
|
73
44
|
|
45
|
+
# Initialize a model object suitable for reifying `version` into. Does
|
46
|
+
# not perform reification, merely instantiates the appropriate model
|
47
|
+
# class and, if specified by `options[:unversioned_attributes]`, sets
|
48
|
+
# unversioned attributes to `nil`.
|
49
|
+
#
|
50
|
+
# Normally a polymorphic belongs_to relationship allows us to get the
|
51
|
+
# object we belong to by calling, in this case, `item`. However this
|
52
|
+
# returns nil if `item` has been destroyed, and we need to be able to
|
53
|
+
# retrieve destroyed objects.
|
54
|
+
#
|
55
|
+
# In this situation we constantize the `item_type` to get hold of the
|
56
|
+
# class...except when the stored object's attributes include a `type`
|
57
|
+
# key. If this is the case, the object we belong to is using single
|
58
|
+
# table inheritance (STI) and the `item_type` will be the base class,
|
59
|
+
# not the actual subclass. If `type` is present but empty, the class is
|
60
|
+
# the base class.
|
61
|
+
def init_model(attrs, options, version)
|
62
|
+
if options[:dup] != true && version.item
|
63
|
+
model = version.item
|
64
|
+
if options[:unversioned_attributes] == :nil
|
65
|
+
init_unversioned_attrs(attrs, model)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
klass = version_reification_class(version, attrs)
|
69
|
+
# The `dup` option always returns a new object, otherwise we should
|
70
|
+
# attempt to look for the item outside of default scope(s).
|
71
|
+
find_cond = { klass.primary_key => version.item_id }
|
72
|
+
if options[:dup] || (item_found = klass.unscoped.where(find_cond).first).nil?
|
73
|
+
model = klass.new
|
74
|
+
elsif options[:unversioned_attributes] == :nil
|
75
|
+
model = item_found
|
76
|
+
init_unversioned_attrs(attrs, model)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
model
|
80
|
+
end
|
81
|
+
|
74
82
|
# Examine the `source_reflection`, i.e. the "source" of `assoc` the
|
75
83
|
# `ThroughReflection`. The source can be a `BelongsToReflection`
|
76
84
|
# or a `HasManyReflection`.
|
@@ -109,23 +117,12 @@ module PaperTrail
|
|
109
117
|
end
|
110
118
|
|
111
119
|
# @api private
|
112
|
-
def hmt_collection_through_belongs_to(through_collection, assoc, options,
|
113
|
-
|
120
|
+
def hmt_collection_through_belongs_to(through_collection, assoc, options, tx_id)
|
121
|
+
ids = through_collection.map { |through_model|
|
114
122
|
through_model.send(assoc.source_reflection.foreign_key)
|
115
123
|
}
|
116
|
-
|
117
|
-
|
118
|
-
where("item_type = ?", assoc.class_name).
|
119
|
-
where("item_id IN (?)", collection_keys).
|
120
|
-
where(
|
121
|
-
"created_at >= ? OR transaction_id = ?",
|
122
|
-
options[:version_at],
|
123
|
-
transaction_id
|
124
|
-
).
|
125
|
-
group("item_id").
|
126
|
-
to_sql
|
127
|
-
versions = versions_by_id(assoc.klass, version_id_subquery)
|
128
|
-
collection = Array.new assoc.klass.where(assoc.klass.primary_key => collection_keys)
|
124
|
+
versions = load_versions_for_hmt_association(assoc, ids, tx_id, options[:version_at])
|
125
|
+
collection = Array.new assoc.klass.where(assoc.klass.primary_key => ids)
|
129
126
|
prepare_array_for_has_many(collection, options, versions)
|
130
127
|
collection
|
131
128
|
end
|
@@ -137,6 +134,35 @@ module PaperTrail
|
|
137
134
|
(model.attribute_names - attrs.keys).each { |k| attrs[k] = nil }
|
138
135
|
end
|
139
136
|
|
137
|
+
# Given a `belongs_to` association and a `version`, return a record that
|
138
|
+
# can be assigned in order to reify that association.
|
139
|
+
# @api private
|
140
|
+
def load_record_for_bt_association(assoc, id, options, version)
|
141
|
+
if version.nil?
|
142
|
+
assoc.klass.where(assoc.klass.primary_key => id).first
|
143
|
+
else
|
144
|
+
version.reify(
|
145
|
+
options.merge(
|
146
|
+
has_many: false,
|
147
|
+
has_one: false,
|
148
|
+
belongs_to: false,
|
149
|
+
has_and_belongs_to_many: false
|
150
|
+
)
|
151
|
+
)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Given a `belongs_to` association and an `id`, return a version record
|
156
|
+
# from the point in time identified by `transaction_id` or `version_at`.
|
157
|
+
# @api private
|
158
|
+
def load_version_for_bt_association(assoc, id, transaction_id, version_at)
|
159
|
+
assoc.klass.paper_trail.version_class.
|
160
|
+
where("item_type = ?", assoc.class_name).
|
161
|
+
where("item_id = ?", id).
|
162
|
+
where("created_at >= ? OR transaction_id = ?", version_at, transaction_id).
|
163
|
+
order("id").limit(1).first
|
164
|
+
end
|
165
|
+
|
140
166
|
# Given a HABTM association `assoc` and an `id`, return a version record
|
141
167
|
# from the point in time identified by `transaction_id` or `version_at`.
|
142
168
|
# @api private
|
@@ -164,6 +190,41 @@ module PaperTrail
|
|
164
190
|
first
|
165
191
|
end
|
166
192
|
|
193
|
+
# Given a `has_many` association on `model`, return the version records
|
194
|
+
# from the point in time identified by `tx_id` or `version_at`.
|
195
|
+
# @api private
|
196
|
+
def load_versions_for_hm_association(assoc, model, version_table, tx_id, version_at)
|
197
|
+
version_id_subquery = ::PaperTrail::VersionAssociation.
|
198
|
+
joins(model.class.version_association_name).
|
199
|
+
select("MIN(version_id)").
|
200
|
+
where("foreign_key_name = ?", assoc.foreign_key).
|
201
|
+
where("foreign_key_id = ?", model.id).
|
202
|
+
where("#{version_table}.item_type = ?", assoc.class_name).
|
203
|
+
where("created_at >= ? OR transaction_id = ?", version_at, tx_id).
|
204
|
+
group("item_id").
|
205
|
+
to_sql
|
206
|
+
versions_by_id(model.class, version_id_subquery)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Given a `has_many(through:)` association and an array of `ids`, return
|
210
|
+
# the version records from the point in time identified by `tx_id` or
|
211
|
+
# `version_at`.
|
212
|
+
# @api private
|
213
|
+
def load_versions_for_hmt_association(assoc, ids, tx_id, version_at)
|
214
|
+
version_id_subquery = assoc.klass.paper_trail.version_class.
|
215
|
+
select("MIN(id)").
|
216
|
+
where("item_type = ?", assoc.class_name).
|
217
|
+
where("item_id IN (?)", ids).
|
218
|
+
where(
|
219
|
+
"created_at >= ? OR transaction_id = ?",
|
220
|
+
version_at,
|
221
|
+
tx_id
|
222
|
+
).
|
223
|
+
group("item_id").
|
224
|
+
to_sql
|
225
|
+
versions_by_id(assoc.klass, version_id_subquery)
|
226
|
+
end
|
227
|
+
|
167
228
|
# Set all the attributes in this version on the model.
|
168
229
|
def reify_attributes(model, version, attrs)
|
169
230
|
enums = model.class.respond_to?(:defined_enums) ? model.class.defined_enums : {}
|
@@ -179,7 +240,7 @@ module PaperTrail
|
|
179
240
|
model[k.to_sym] = v
|
180
241
|
elsif model.respond_to?("#{k}=")
|
181
242
|
model.send("#{k}=", v)
|
182
|
-
|
243
|
+
elsif version.logger
|
183
244
|
version.logger.warn(
|
184
245
|
"Attribute #{k} does not exist on #{version.item_type} (Version id: #{version.id})."
|
185
246
|
)
|
@@ -240,76 +301,78 @@ module PaperTrail
|
|
240
301
|
nil
|
241
302
|
end
|
242
303
|
|
304
|
+
# @api private
|
243
305
|
def reify_associations(model, options, version)
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
306
|
+
if options[:has_one]
|
307
|
+
reify_has_one_associations(version.transaction_id, model, options)
|
308
|
+
end
|
309
|
+
if options[:belongs_to]
|
310
|
+
reify_belongs_to_associations(version.transaction_id, model, options)
|
311
|
+
end
|
312
|
+
if options[:has_many]
|
313
|
+
reify_has_manys(version.transaction_id, model, options)
|
314
|
+
end
|
250
315
|
if options[:has_and_belongs_to_many]
|
251
|
-
|
316
|
+
reify_habtm_associations version.transaction_id, model, options
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
# Reify a single `has_one` association of `model`.
|
321
|
+
# @api private
|
322
|
+
def reify_has_one_association(assoc, model, options, transaction_id)
|
323
|
+
version = load_version_for_has_one(assoc, model, transaction_id, options[:version_at])
|
324
|
+
return unless version
|
325
|
+
if version.event == "create"
|
326
|
+
if options[:mark_for_destruction]
|
327
|
+
model.send(assoc.name).mark_for_destruction if model.send(assoc.name, true)
|
328
|
+
else
|
329
|
+
model.paper_trail.appear_as_new_record do
|
330
|
+
model.send "#{assoc.name}=", nil
|
331
|
+
end
|
332
|
+
end
|
333
|
+
else
|
334
|
+
child = version.reify(
|
335
|
+
options.merge(
|
336
|
+
has_many: false,
|
337
|
+
has_one: false,
|
338
|
+
belongs_to: false,
|
339
|
+
has_and_belongs_to_many: false
|
340
|
+
)
|
341
|
+
)
|
342
|
+
model.paper_trail.appear_as_new_record do
|
343
|
+
without_persisting(child) do
|
344
|
+
model.send "#{assoc.name}=", child
|
345
|
+
end
|
346
|
+
end
|
252
347
|
end
|
253
348
|
end
|
254
349
|
|
255
350
|
# Restore the `model`'s has_one associations as they were when this
|
256
351
|
# version was superseded by the next (because that's what the user was
|
257
352
|
# looking at when they made the change).
|
258
|
-
|
353
|
+
# @api private
|
354
|
+
def reify_has_one_associations(transaction_id, model, options = {})
|
259
355
|
associations = model.class.reflect_on_all_associations(:has_one)
|
260
356
|
each_enabled_association(associations) do |assoc|
|
261
|
-
|
262
|
-
next unless version
|
263
|
-
if version.event == "create"
|
264
|
-
if options[:mark_for_destruction]
|
265
|
-
model.send(assoc.name).mark_for_destruction if model.send(assoc.name, true)
|
266
|
-
else
|
267
|
-
model.paper_trail.appear_as_new_record do
|
268
|
-
model.send "#{assoc.name}=", nil
|
269
|
-
end
|
270
|
-
end
|
271
|
-
else
|
272
|
-
child = version.reify(
|
273
|
-
options.merge(
|
274
|
-
has_many: false,
|
275
|
-
has_one: false,
|
276
|
-
belongs_to: false,
|
277
|
-
has_and_belongs_to_many: false
|
278
|
-
)
|
279
|
-
)
|
280
|
-
model.paper_trail.appear_as_new_record do
|
281
|
-
without_persisting(child) do
|
282
|
-
model.send "#{assoc.name}=", child
|
283
|
-
end
|
284
|
-
end
|
285
|
-
end
|
357
|
+
reify_has_one_association(assoc, model, options, transaction_id)
|
286
358
|
end
|
287
359
|
end
|
288
360
|
|
289
|
-
|
361
|
+
# Reify a single `belongs_to` association of `model`.
|
362
|
+
# @api private
|
363
|
+
def reify_belongs_to_association(assoc, model, options, transaction_id)
|
364
|
+
id = model.send(assoc.association_foreign_key)
|
365
|
+
version = load_version_for_bt_association(assoc, id, transaction_id, options[:version_at])
|
366
|
+
record = load_record_for_bt_association(assoc, id, options, version)
|
367
|
+
model.send("#{assoc.name}=".to_sym, record)
|
368
|
+
end
|
369
|
+
|
370
|
+
# Reify all `belongs_to` associations of `model`.
|
371
|
+
# @api private
|
372
|
+
def reify_belongs_to_associations(transaction_id, model, options = {})
|
290
373
|
associations = model.class.reflect_on_all_associations(:belongs_to)
|
291
374
|
each_enabled_association(associations) do |assoc|
|
292
|
-
|
293
|
-
version = assoc.klass.paper_trail.version_class.
|
294
|
-
where("item_type = ?", assoc.class_name).
|
295
|
-
where("item_id = ?", collection_key).
|
296
|
-
where("created_at >= ? OR transaction_id = ?", options[:version_at], transaction_id).
|
297
|
-
order("id").limit(1).first
|
298
|
-
|
299
|
-
collection = if version.nil?
|
300
|
-
assoc.klass.where(assoc.klass.primary_key => collection_key).first
|
301
|
-
else
|
302
|
-
version.reify(
|
303
|
-
options.merge(
|
304
|
-
has_many: false,
|
305
|
-
has_one: false,
|
306
|
-
belongs_to: false,
|
307
|
-
has_and_belongs_to_many: false
|
308
|
-
)
|
309
|
-
)
|
310
|
-
end
|
311
|
-
|
312
|
-
model.send("#{assoc.name}=".to_sym, collection)
|
375
|
+
reify_belongs_to_association(assoc, model, options, transaction_id)
|
313
376
|
end
|
314
377
|
end
|
315
378
|
|
@@ -320,85 +383,101 @@ module PaperTrail
|
|
320
383
|
assoc_has_many_through, assoc_has_many_directly =
|
321
384
|
model.class.reflect_on_all_associations(:has_many).
|
322
385
|
partition { |assoc| assoc.options[:through] }
|
323
|
-
|
324
|
-
|
386
|
+
reify_has_many_associations(transaction_id, assoc_has_many_directly, model, options)
|
387
|
+
reify_has_many_through_associations(transaction_id, assoc_has_many_through, model, options)
|
325
388
|
end
|
326
389
|
|
327
|
-
#
|
328
|
-
#
|
329
|
-
def
|
390
|
+
# Reify a single, direct (not `through`) `has_many` association of `model`.
|
391
|
+
# @api private
|
392
|
+
def reify_has_many_association(assoc, model, options, transaction_id, version_table_name)
|
393
|
+
versions = load_versions_for_hm_association(
|
394
|
+
assoc,
|
395
|
+
model,
|
396
|
+
version_table_name,
|
397
|
+
transaction_id,
|
398
|
+
options[:version_at]
|
399
|
+
)
|
400
|
+
collection = Array.new model.send(assoc.name).reload # to avoid cache
|
401
|
+
prepare_array_for_has_many(collection, options, versions)
|
402
|
+
model.send(assoc.name).proxy_association.target = collection
|
403
|
+
end
|
404
|
+
|
405
|
+
# Reify all direct (not `through`) `has_many` associations of `model`.
|
406
|
+
# @api private
|
407
|
+
def reify_has_many_associations(transaction_id, associations, model, options = {})
|
330
408
|
version_table_name = model.class.paper_trail.version_class.table_name
|
331
409
|
each_enabled_association(associations) do |assoc|
|
332
|
-
|
333
|
-
joins(model.class.version_association_name).
|
334
|
-
select("MIN(version_id)").
|
335
|
-
where("foreign_key_name = ?", assoc.foreign_key).
|
336
|
-
where("foreign_key_id = ?", model.id).
|
337
|
-
where("#{version_table_name}.item_type = ?", assoc.class_name).
|
338
|
-
where("created_at >= ? OR transaction_id = ?", options[:version_at], transaction_id).
|
339
|
-
group("item_id").
|
340
|
-
to_sql
|
341
|
-
versions = versions_by_id(model.class, version_id_subquery)
|
342
|
-
collection = Array.new model.send(assoc.name).reload # to avoid cache
|
343
|
-
prepare_array_for_has_many(collection, options, versions)
|
344
|
-
model.send(assoc.name).proxy_association.target = collection
|
410
|
+
reify_has_many_association(assoc, model, options, transaction_id, version_table_name)
|
345
411
|
end
|
346
412
|
end
|
347
413
|
|
348
|
-
#
|
349
|
-
#
|
350
|
-
|
351
|
-
|
414
|
+
# Reify a single HMT association of `model`.
|
415
|
+
# @api private
|
416
|
+
def reify_has_many_through_association(assoc, model, options, transaction_id)
|
417
|
+
# Load the collection of through-models. For example, if `model` is a
|
418
|
+
# Chapter, having many Paragraphs through Sections, then
|
419
|
+
# `through_collection` will contain Sections.
|
420
|
+
through_collection = model.send(assoc.options[:through])
|
421
|
+
|
422
|
+
# Now, given the collection of "through" models (e.g. sections), load
|
423
|
+
# the collection of "target" models (e.g. paragraphs)
|
424
|
+
collection = hmt_collection(through_collection, assoc, options, transaction_id)
|
425
|
+
|
426
|
+
# Finally, assign the `collection` of "target" models, e.g. to
|
427
|
+
# `model.paragraphs`.
|
428
|
+
model.send(assoc.name).proxy_association.target = collection
|
429
|
+
end
|
430
|
+
|
431
|
+
# Reify all HMT associations of `model`. This must be called after the
|
432
|
+
# direct (non-`through`) has_manys have been reified.
|
433
|
+
# @api private
|
434
|
+
def reify_has_many_through_associations(transaction_id, associations, model, options = {})
|
352
435
|
each_enabled_association(associations) do |assoc|
|
353
|
-
|
354
|
-
# Chapter, having many Paragraphs through Sections, then
|
355
|
-
# `through_collection` will contain Sections.
|
356
|
-
through_collection = model.send(assoc.options[:through])
|
357
|
-
|
358
|
-
# Now, given the collection of "through" models (e.g. sections), load
|
359
|
-
# the collection of "target" models (e.g. paragraphs)
|
360
|
-
collection = hmt_collection(through_collection, assoc, options, transaction_id)
|
361
|
-
|
362
|
-
# Finally, assign the `collection` of "target" models, e.g. to
|
363
|
-
# `model.paragraphs`.
|
364
|
-
model.send(assoc.name).proxy_association.target = collection
|
436
|
+
reify_has_many_through_association(assoc, model, options, transaction_id)
|
365
437
|
end
|
366
438
|
end
|
367
439
|
|
368
|
-
|
440
|
+
# Reify a single HABTM association of `model`.
|
441
|
+
# @api private
|
442
|
+
def reify_habtm_association(assoc, model, options, papertrail_enabled, transaction_id)
|
443
|
+
version_ids = PaperTrail::VersionAssociation.
|
444
|
+
where("foreign_key_name = ?", assoc.name).
|
445
|
+
where("version_id = ?", transaction_id).
|
446
|
+
pluck(:foreign_key_id)
|
447
|
+
|
448
|
+
model.send(assoc.name).proxy_association.target =
|
449
|
+
version_ids.map do |id|
|
450
|
+
if papertrail_enabled
|
451
|
+
version = load_version_for_habtm(
|
452
|
+
assoc,
|
453
|
+
id,
|
454
|
+
transaction_id,
|
455
|
+
options[:version_at]
|
456
|
+
)
|
457
|
+
if version
|
458
|
+
next version.reify(
|
459
|
+
options.merge(
|
460
|
+
has_many: false,
|
461
|
+
has_one: false,
|
462
|
+
belongs_to: false,
|
463
|
+
has_and_belongs_to_many: false
|
464
|
+
)
|
465
|
+
)
|
466
|
+
end
|
467
|
+
end
|
468
|
+
assoc.klass.where(assoc.klass.primary_key => id).first
|
469
|
+
end
|
470
|
+
end
|
471
|
+
|
472
|
+
# Reify all HABTM associations of `model`.
|
473
|
+
# @api private
|
474
|
+
def reify_habtm_associations(transaction_id, model, options = {})
|
369
475
|
model.class.reflect_on_all_associations(:has_and_belongs_to_many).each do |assoc|
|
370
476
|
papertrail_enabled = assoc.klass.paper_trail.enabled?
|
371
477
|
next unless
|
372
478
|
model.class.paper_trail_save_join_tables.include?(assoc.name) ||
|
373
479
|
papertrail_enabled
|
374
|
-
|
375
|
-
version_ids = PaperTrail::VersionAssociation.
|
376
|
-
where("foreign_key_name = ?", assoc.name).
|
377
|
-
where("version_id = ?", transaction_id).
|
378
|
-
pluck(:foreign_key_id)
|
379
|
-
|
380
|
-
model.send(assoc.name).proxy_association.target =
|
381
|
-
version_ids.map do |id|
|
382
|
-
if papertrail_enabled
|
383
|
-
version = load_version_for_habtm(
|
384
|
-
assoc,
|
385
|
-
id,
|
386
|
-
transaction_id,
|
387
|
-
options[:version_at]
|
388
|
-
)
|
389
|
-
if version
|
390
|
-
next version.reify(
|
391
|
-
options.merge(
|
392
|
-
has_many: false,
|
393
|
-
has_one: false,
|
394
|
-
belongs_to: false,
|
395
|
-
has_and_belongs_to_many: false
|
396
|
-
)
|
397
|
-
)
|
398
|
-
end
|
399
|
-
end
|
400
|
-
assoc.klass.where(assoc.klass.primary_key => id).first
|
401
|
-
end
|
480
|
+
reify_habtm_association(assoc, model, options, papertrail_enabled, transaction_id)
|
402
481
|
end
|
403
482
|
end
|
404
483
|
|