mongo_trails 10.3.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/.gitattributes +2 -0
- data/.gitignore +1 -0
- data/.travis.yml +13 -0
- data/Appraisals +7 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +62 -0
- data/LICENSE +20 -0
- data/README.md +36 -0
- data/Rakefile +13 -0
- data/gemfiles/rails_5.gemfile +9 -0
- data/gemfiles/rails_5.gemfile.lock +63 -0
- data/gemfiles/rails_6.gemfile +9 -0
- data/gemfiles/rails_6.gemfile.lock +63 -0
- data/lib/mongo_trails.rb +154 -0
- data/lib/mongo_trails/attribute_serializers/README.md +10 -0
- data/lib/mongo_trails/attribute_serializers/attribute_serializer_factory.rb +27 -0
- data/lib/mongo_trails/attribute_serializers/cast_attribute_serializer.rb +51 -0
- data/lib/mongo_trails/attribute_serializers/object_attribute.rb +41 -0
- data/lib/mongo_trails/attribute_serializers/object_changes_attribute.rb +44 -0
- data/lib/mongo_trails/cleaner.rb +60 -0
- data/lib/mongo_trails/compatibility.rb +51 -0
- data/lib/mongo_trails/config.rb +41 -0
- data/lib/mongo_trails/events/base.rb +323 -0
- data/lib/mongo_trails/events/create.rb +32 -0
- data/lib/mongo_trails/events/destroy.rb +42 -0
- data/lib/mongo_trails/events/update.rb +60 -0
- data/lib/mongo_trails/frameworks/cucumber.rb +33 -0
- data/lib/mongo_trails/frameworks/rails.rb +4 -0
- data/lib/mongo_trails/frameworks/rails/controller.rb +109 -0
- data/lib/mongo_trails/frameworks/rails/engine.rb +43 -0
- data/lib/mongo_trails/frameworks/rspec.rb +43 -0
- data/lib/mongo_trails/frameworks/rspec/helpers.rb +29 -0
- data/lib/mongo_trails/has_paper_trail.rb +86 -0
- data/lib/mongo_trails/model_config.rb +249 -0
- data/lib/mongo_trails/mongo_support/config.rb +9 -0
- data/lib/mongo_trails/mongo_support/version.rb +56 -0
- data/lib/mongo_trails/queries/versions/where_object.rb +65 -0
- data/lib/mongo_trails/queries/versions/where_object_changes.rb +75 -0
- data/lib/mongo_trails/record_history.rb +51 -0
- data/lib/mongo_trails/record_trail.rb +304 -0
- data/lib/mongo_trails/reifier.rb +130 -0
- data/lib/mongo_trails/request.rb +166 -0
- data/lib/mongo_trails/serializers/json.rb +46 -0
- data/lib/mongo_trails/serializers/yaml.rb +43 -0
- data/lib/mongo_trails/type_serializers/postgres_array_serializer.rb +48 -0
- data/lib/mongo_trails/version_concern.rb +336 -0
- data/lib/mongo_trails/version_number.rb +23 -0
- data/mongo_trails.gemspec +38 -0
- metadata +180 -0
@@ -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
|
@@ -0,0 +1,304 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mongo_trails/events/create"
|
4
|
+
require "mongo_trails/events/destroy"
|
5
|
+
require "mongo_trails/events/update"
|
6
|
+
|
7
|
+
module PaperTrail
|
8
|
+
# Represents the "paper trail" for a single record.
|
9
|
+
class RecordTrail
|
10
|
+
RAILS_GTE_5_1 = ::ActiveRecord.gem_version >= ::Gem::Version.new("5.1.0.beta1")
|
11
|
+
|
12
|
+
def initialize(record)
|
13
|
+
@record = record
|
14
|
+
end
|
15
|
+
|
16
|
+
# Invoked after rollbacks to ensure versions records are not created for
|
17
|
+
# changes that never actually took place. Optimization: Use lazy `reset`
|
18
|
+
# instead of eager `reload` because, in many use cases, the association will
|
19
|
+
# not be used.
|
20
|
+
def clear_rolled_back_versions
|
21
|
+
versions_reset
|
22
|
+
end
|
23
|
+
|
24
|
+
def versions_reset
|
25
|
+
@record.class.paper_trail.version_class.reset
|
26
|
+
end
|
27
|
+
|
28
|
+
# Invoked via`after_update` callback for when a previous version is
|
29
|
+
# reified and then saved.
|
30
|
+
def clear_version_instance
|
31
|
+
@record.send("#{@record.class.version_association_name}=", nil)
|
32
|
+
end
|
33
|
+
|
34
|
+
# Is PT enabled for this particular record?
|
35
|
+
# @api private
|
36
|
+
def enabled?
|
37
|
+
PaperTrail.enabled? &&
|
38
|
+
PaperTrail.request.enabled? &&
|
39
|
+
PaperTrail.request.enabled_for_model?(@record.class)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Returns true if this instance is the current, live one;
|
43
|
+
# returns false if this instance came from a previous version.
|
44
|
+
def live?
|
45
|
+
source_version.nil?
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the object (not a Version) as it became next.
|
49
|
+
# NOTE: if self (the item) was not reified from a version, i.e. it is the
|
50
|
+
# "live" item, we return nil. Perhaps we should return self instead?
|
51
|
+
def next_version
|
52
|
+
subsequent_version = source_version.next
|
53
|
+
subsequent_version ? subsequent_version.reify : @record.class.find(@record.id)
|
54
|
+
rescue StandardError # TODO: Rescue something more specific
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
# Returns who put `@record` into its current state.
|
59
|
+
#
|
60
|
+
# @api public
|
61
|
+
def originator
|
62
|
+
(source_version || versions.last).try(:whodunnit)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the object (not a Version) as it was most recently.
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def previous_version
|
69
|
+
(source_version ? source_version.previous : versions.last).try(:reify)
|
70
|
+
end
|
71
|
+
|
72
|
+
def record_create
|
73
|
+
return unless enabled?
|
74
|
+
|
75
|
+
build_version_on_create(in_after_callback: true).tap do |version|
|
76
|
+
version.save!
|
77
|
+
# Because the version object was created using version_class.new instead
|
78
|
+
# of versions_assoc.build?, the association cache is unaware. So, we
|
79
|
+
# invalidate the `versions` association cache with `reset`.
|
80
|
+
versions_reset
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# PT-AT extends this method to add its transaction id.
|
85
|
+
#
|
86
|
+
# @api private
|
87
|
+
def data_for_create
|
88
|
+
{}
|
89
|
+
end
|
90
|
+
|
91
|
+
# `recording_order` is "after" or "before". See ModelConfig#on_destroy.
|
92
|
+
#
|
93
|
+
# @api private
|
94
|
+
# @return - The created version object, so that plugins can use it, e.g.
|
95
|
+
# paper_trail-association_tracking
|
96
|
+
def record_destroy(recording_order)
|
97
|
+
return unless enabled? && !@record.new_record?
|
98
|
+
in_after_callback = recording_order == "after"
|
99
|
+
event = Events::Destroy.new(@record, in_after_callback)
|
100
|
+
|
101
|
+
# Merge data from `Event` with data from PT-AT. We no longer use
|
102
|
+
# `data_for_destroy` but PT-AT still does.
|
103
|
+
data = event.data.merge(data_for_destroy)
|
104
|
+
|
105
|
+
version = @record.class.paper_trail.version_class.create(data)
|
106
|
+
if version.errors.any?
|
107
|
+
log_version_errors(version, :destroy)
|
108
|
+
else
|
109
|
+
assign_and_reset_version_association(version)
|
110
|
+
version
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
# PT-AT extends this method to add its transaction id.
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def data_for_destroy
|
118
|
+
{}
|
119
|
+
end
|
120
|
+
|
121
|
+
# @api private
|
122
|
+
# @return - The created version object, so that plugins can use it, e.g.
|
123
|
+
# paper_trail-association_tracking
|
124
|
+
def record_update(force:, in_after_callback:, is_touch:)
|
125
|
+
return unless enabled?
|
126
|
+
|
127
|
+
version = build_version_on_update(
|
128
|
+
force: force,
|
129
|
+
in_after_callback: in_after_callback,
|
130
|
+
is_touch: is_touch
|
131
|
+
)
|
132
|
+
return unless version
|
133
|
+
|
134
|
+
if version.save
|
135
|
+
# Because the version object was created using version_class.new instead
|
136
|
+
# of versions_assoc.build?, the association cache is unaware. So, we
|
137
|
+
# invalidate the `versions` association cache with `reset`.
|
138
|
+
versions_reset
|
139
|
+
version
|
140
|
+
else
|
141
|
+
log_version_errors(version, :update)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# PT-AT extends this method to add its transaction id.
|
146
|
+
#
|
147
|
+
# @api private
|
148
|
+
def data_for_update
|
149
|
+
{}
|
150
|
+
end
|
151
|
+
|
152
|
+
# @api private
|
153
|
+
# @return - The created version object, so that plugins can use it, e.g.
|
154
|
+
# paper_trail-association_tracking
|
155
|
+
def record_update_columns(changes)
|
156
|
+
return unless enabled?
|
157
|
+
event = Events::Update.new(@record, false, false, changes)
|
158
|
+
|
159
|
+
# Merge data from `Event` with data from PT-AT. We no longer use
|
160
|
+
# `data_for_update_columns` but PT-AT still does.
|
161
|
+
data = event.data.merge(data_for_update_columns)
|
162
|
+
|
163
|
+
versions_assoc = @record.send(@record.class.versions_association_name)
|
164
|
+
version = versions_assoc.create(data)
|
165
|
+
if version.errors.any?
|
166
|
+
log_version_errors(version, :update)
|
167
|
+
else
|
168
|
+
version
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
# PT-AT extends this method to add its transaction id.
|
173
|
+
#
|
174
|
+
# @api private
|
175
|
+
def data_for_update_columns
|
176
|
+
{}
|
177
|
+
end
|
178
|
+
|
179
|
+
# Invoked via callback when a user attempts to persist a reified
|
180
|
+
# `Version`.
|
181
|
+
def reset_timestamp_attrs_for_update_if_needed
|
182
|
+
return if live?
|
183
|
+
@record.send(:timestamp_attributes_for_update_in_model).each do |column|
|
184
|
+
@record.send("restore_#{column}!")
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# AR callback.
|
189
|
+
# @api private
|
190
|
+
def save_version?
|
191
|
+
if_condition = @record.paper_trail_options[:if]
|
192
|
+
unless_condition = @record.paper_trail_options[:unless]
|
193
|
+
(if_condition.blank? || if_condition.call(@record)) && !unless_condition.try(:call, @record)
|
194
|
+
end
|
195
|
+
|
196
|
+
def source_version
|
197
|
+
version
|
198
|
+
end
|
199
|
+
|
200
|
+
# Save, and create a version record regardless of options such as `:on`,
|
201
|
+
# `:if`, or `:unless`.
|
202
|
+
#
|
203
|
+
# Arguments are passed to `save`.
|
204
|
+
#
|
205
|
+
# This is an "update" event. That is, we record the same data we would in
|
206
|
+
# the case of a normal AR `update`.
|
207
|
+
def save_with_version(*args)
|
208
|
+
::PaperTrail.request(enabled: false) do
|
209
|
+
@record.save(*args)
|
210
|
+
end
|
211
|
+
record_update(force: true, in_after_callback: false, is_touch: false)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Like the `update_column` method from `ActiveRecord::Persistence`, but also
|
215
|
+
# creates a version to record those changes.
|
216
|
+
# @api public
|
217
|
+
def update_column(name, value)
|
218
|
+
update_columns(name => value)
|
219
|
+
end
|
220
|
+
|
221
|
+
# Like the `update_columns` method from `ActiveRecord::Persistence`, but also
|
222
|
+
# creates a version to record those changes.
|
223
|
+
# @api public
|
224
|
+
def update_columns(attributes)
|
225
|
+
# `@record.update_columns` skips dirty-tracking, so we can't just use
|
226
|
+
# `@record.changes` or @record.saved_changes` from `ActiveModel::Dirty`.
|
227
|
+
# We need to build our own hash with the changes that will be made
|
228
|
+
# directly to the database.
|
229
|
+
changes = {}
|
230
|
+
attributes.each do |k, v|
|
231
|
+
changes[k] = [@record[k], v]
|
232
|
+
end
|
233
|
+
@record.update_columns(attributes)
|
234
|
+
record_update_columns(changes)
|
235
|
+
end
|
236
|
+
|
237
|
+
# Returns the object (not a Version) as it was at the given timestamp.
|
238
|
+
def version_at(timestamp, reify_options = {})
|
239
|
+
# Because a version stores how its object looked *before* the change,
|
240
|
+
# we need to look for the first version created *after* the timestamp.
|
241
|
+
v = versions.subsequent(timestamp, true).first
|
242
|
+
return v.reify(reify_options) if v
|
243
|
+
@record unless @record.destroyed?
|
244
|
+
end
|
245
|
+
|
246
|
+
# Returns the objects (not Versions) as they were between the given times.
|
247
|
+
def versions_between(start_time, end_time)
|
248
|
+
versions = send(@record.class.versions_association_name).between(start_time, end_time)
|
249
|
+
versions.collect { |version| version_at(version.created_at) }
|
250
|
+
end
|
251
|
+
|
252
|
+
private
|
253
|
+
|
254
|
+
# @api private
|
255
|
+
def assign_and_reset_version_association(version)
|
256
|
+
@record.send("#{@record.class.version_association_name}=", version)
|
257
|
+
@record.send(@record.class.versions_association_name).reset
|
258
|
+
end
|
259
|
+
|
260
|
+
# @api private
|
261
|
+
def build_version_on_create(in_after_callback:)
|
262
|
+
event = Events::Create.new(@record, in_after_callback)
|
263
|
+
|
264
|
+
# Merge data from `Event` with data from PT-AT. We no longer use
|
265
|
+
# `data_for_create` but PT-AT still does.
|
266
|
+
data = event.data.merge!(data_for_create)
|
267
|
+
|
268
|
+
# Pure `version_class.new` reduces memory usage compared to `versions_assoc.build`
|
269
|
+
@record.class.paper_trail.version_class.new(data)
|
270
|
+
end
|
271
|
+
|
272
|
+
# @api private
|
273
|
+
def build_version_on_update(force:, in_after_callback:, is_touch:)
|
274
|
+
event = Events::Update.new(@record, in_after_callback, is_touch, nil)
|
275
|
+
return unless force || event.changed_notably?
|
276
|
+
|
277
|
+
# Merge data from `Event` with data from PT-AT. We no longer use
|
278
|
+
# `data_for_update` but PT-AT still does. To save memory, we use `merge!`
|
279
|
+
# instead of `merge`.
|
280
|
+
data = event.data.merge!(data_for_update)
|
281
|
+
|
282
|
+
# Using `version_class.new` reduces memory usage compared to
|
283
|
+
# `versions_assoc.build`. It's a trade-off though. We have to clear
|
284
|
+
# the association cache (see `versions.reset`) and that could cause an
|
285
|
+
# additional query in certain applications.
|
286
|
+
@record.class.paper_trail.version_class.new(data)
|
287
|
+
end
|
288
|
+
|
289
|
+
def log_version_errors(version, action)
|
290
|
+
version.logger&.warn(
|
291
|
+
"Unable to create version for #{action} of #{@record.class.name}" \
|
292
|
+
"##{@record.id}: " + version.errors.full_messages.join(", ")
|
293
|
+
)
|
294
|
+
end
|
295
|
+
|
296
|
+
def version
|
297
|
+
@record.public_send(@record.class.version_association_name)
|
298
|
+
end
|
299
|
+
|
300
|
+
def versions
|
301
|
+
@record.public_send(@record.class.versions_association_name)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "mongo_trails/attribute_serializers/object_attribute"
|
4
|
+
|
5
|
+
module PaperTrail
|
6
|
+
# Given a version record and some options, builds a new model object.
|
7
|
+
# @api private
|
8
|
+
module Reifier
|
9
|
+
class << self
|
10
|
+
# See `VersionConcern#reify` for documentation.
|
11
|
+
# @api private
|
12
|
+
def reify(version, options)
|
13
|
+
options = apply_defaults_to(options, version)
|
14
|
+
attrs = version.object_deserialized
|
15
|
+
model = init_model(attrs, options, version)
|
16
|
+
reify_attributes(model, version, attrs)
|
17
|
+
model.send "#{model.class.version_association_name}=", version
|
18
|
+
model
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# Given a hash of `options` for `.reify`, return a new hash with default
|
24
|
+
# values applied.
|
25
|
+
# @api private
|
26
|
+
def apply_defaults_to(options, version)
|
27
|
+
{
|
28
|
+
version_at: version.created_at,
|
29
|
+
mark_for_destruction: false,
|
30
|
+
has_one: false,
|
31
|
+
has_many: false,
|
32
|
+
belongs_to: false,
|
33
|
+
has_and_belongs_to_many: false,
|
34
|
+
unversioned_attributes: :nil
|
35
|
+
}.merge(options)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Initialize a model object suitable for reifying `version` into. Does
|
39
|
+
# not perform reification, merely instantiates the appropriate model
|
40
|
+
# class and, if specified by `options[:unversioned_attributes]`, sets
|
41
|
+
# unversioned attributes to `nil`.
|
42
|
+
#
|
43
|
+
# Normally a polymorphic belongs_to relationship allows us to get the
|
44
|
+
# object we belong to by calling, in this case, `item`. However this
|
45
|
+
# returns nil if `item` has been destroyed, and we need to be able to
|
46
|
+
# retrieve destroyed objects.
|
47
|
+
#
|
48
|
+
# In this situation we constantize the `item_type` to get hold of the
|
49
|
+
# class...except when the stored object's attributes include a `type`
|
50
|
+
# key. If this is the case, the object we belong to is using single
|
51
|
+
# table inheritance (STI) and the `item_type` will be the base class,
|
52
|
+
# not the actual subclass. If `type` is present but empty, the class is
|
53
|
+
# the base class.
|
54
|
+
def init_model(attrs, options, version)
|
55
|
+
klass = version_reification_class(version, attrs)
|
56
|
+
|
57
|
+
# The `dup` option and destroyed version always returns a new object,
|
58
|
+
# otherwise we should attempt to load item or to look for the item
|
59
|
+
# outside of default scope(s).
|
60
|
+
model = if options[:dup] == true || version.event == "destroy"
|
61
|
+
klass.new
|
62
|
+
else
|
63
|
+
find_cond = { klass.primary_key => version.item_id }
|
64
|
+
|
65
|
+
version.item || klass.unscoped.where(find_cond).first || klass.new
|
66
|
+
end
|
67
|
+
|
68
|
+
if options[:unversioned_attributes] == :nil && !model.new_record?
|
69
|
+
init_unversioned_attrs(attrs, model)
|
70
|
+
end
|
71
|
+
|
72
|
+
model
|
73
|
+
end
|
74
|
+
|
75
|
+
# Look for attributes that exist in `model` and not in this version.
|
76
|
+
# These attributes should be set to nil. Modifies `attrs`.
|
77
|
+
# @api private
|
78
|
+
def init_unversioned_attrs(attrs, model)
|
79
|
+
(model.attribute_names - attrs.keys).each { |k| attrs[k] = nil }
|
80
|
+
end
|
81
|
+
|
82
|
+
# Reify onto `model` an attribute named `k` with value `v` from `version`.
|
83
|
+
#
|
84
|
+
# `ObjectAttribute#deserialize` will return the mapped enum value and in
|
85
|
+
# Rails < 5, the []= uses the integer type caster from the column
|
86
|
+
# definition (in general) and thus will turn a (usually) string to 0
|
87
|
+
# instead of the correct value.
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
def reify_attribute(k, v, model, version)
|
91
|
+
if model.has_attribute?(k)
|
92
|
+
model[k.to_sym] = v
|
93
|
+
elsif model.respond_to?("#{k}=")
|
94
|
+
model.send("#{k}=", v)
|
95
|
+
elsif version.logger
|
96
|
+
version.logger.warn(
|
97
|
+
"Attribute #{k} does not exist on #{version.item_type} (Version id: #{version.id})."
|
98
|
+
)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# Reify onto `model` all the attributes of `version`.
|
103
|
+
# @api private
|
104
|
+
def reify_attributes(model, version, attrs)
|
105
|
+
AttributeSerializers::ObjectAttribute.new(model.class).deserialize(attrs)
|
106
|
+
attrs.each do |k, v|
|
107
|
+
reify_attribute(k, v, model, version)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Given a `version`, return the class to reify. This method supports
|
112
|
+
# Single Table Inheritance (STI) with custom inheritance columns.
|
113
|
+
#
|
114
|
+
# For example, imagine a `version` whose `item_type` is "Animal". The
|
115
|
+
# `animals` table is an STI table (it has cats and dogs) and it has a
|
116
|
+
# custom inheritance column, `species`. If `attrs["species"]` is "Dog",
|
117
|
+
# this method returns the constant `Dog`. If `attrs["species"]` is blank,
|
118
|
+
# this method returns the constant `Animal`. You can see this particular
|
119
|
+
# example in action in `spec/models/animal_spec.rb`.
|
120
|
+
#
|
121
|
+
# TODO: Duplication: similar `constantize` in VersionConcern#version_limit
|
122
|
+
def version_reification_class(version, attrs)
|
123
|
+
inheritance_column_name = version.item_type.constantize.inheritance_column
|
124
|
+
inher_col_value = attrs[inheritance_column_name]
|
125
|
+
class_name = inher_col_value.blank? ? version.item_type : inher_col_value
|
126
|
+
class_name.constantize
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|