draftsman 0.5.1 → 0.6.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.
@@ -8,40 +8,50 @@ module Draftsman
8
8
  end
9
9
 
10
10
  module ClassMethods
11
- # Declare this in your model to enable the Draftsman API for it. A draft of the model is available in the `draft`
12
- # association (if one exists).
11
+ # Declare this in your model to enable the Draftsman API for it. A draft
12
+ # of the model is available in the `draft` association (if one exists).
13
13
  #
14
14
  # Options:
15
15
  #
16
16
  # :class_name
17
- # The name of a custom `Draft` class. This class should inherit from `Draftsman::Draft`. A global default can be
18
- # set for this using `Draftsman.draft_class_name=` if the default of `Draftsman::Draft` needs to be overridden.
17
+ # The name of a custom `Draft` class. This class should inherit from
18
+ # `Draftsman::Draft`. A global default can be set for this using
19
+ # `Draftsman.draft_class_name=` if the default of `Draftsman::Draft` needs
20
+ # to be overridden.
19
21
  #
20
22
  # :ignore
21
- # An array of attributes for which an update to a `Draft` will not be stored if they are the only ones changed.
23
+ # An array of attributes for which an update to a `Draft` will not be
24
+ # stored if they are the only ones changed.
22
25
  #
23
26
  # :only
24
- # Inverse of `ignore` - a new `Draft` will be created only for these attributes if supplied. It's recommended that
25
- # you only specify optional attributes for this (that can be empty).
27
+ # Inverse of `ignore` - a new `Draft` will be created only for these
28
+ # attributes if supplied. It's recommended that you only specify optional
29
+ # attributes for this (that can be empty).
26
30
  #
27
31
  # :skip
28
- # Fields to ignore completely. As with `ignore`, updates to these fields will not create a new `Draft`. In
29
- # addition, these fields will not be included in the serialized versions of the object whenever a new `Draft` is
30
- # created.
32
+ # Fields to ignore completely. As with `ignore`, updates to these fields
33
+ # will not create a new `Draft`. In addition, these fields will not be
34
+ # included in the serialized versions of the object whenever a new `Draft`
35
+ # is created.
31
36
  #
32
37
  # :meta
33
- # A hash of extra data to store. You must add a column to the `drafts` table for each key. Values are objects or
34
- # `procs` (which are called with `self`, i.e. the model with the `has_drafts`). See
35
- # `Draftsman::Controller.info_for_draftsman` for an example of how to store data from the controller.
38
+ # A hash of extra data to store. You must add a column to the `drafts`
39
+ # table for each key. Values are objects or `procs` (which are called with
40
+ # `self`, i.e. the model with the `has_drafts`). See
41
+ # `Draftsman::Controller.info_for_draftsman` for an example of how to
42
+ # store data from the controller.
36
43
  #
37
44
  # :draft
38
- # The name to use for the `draft` association shortcut method. Default is `:draft`.
45
+ # The name to use for the `draft` association shortcut method. Default is
46
+ # `:draft`.
39
47
  #
40
48
  # :published_at
41
- # The name to use for the method which returns the published timestamp. Default is `published_at`.
49
+ # The name to use for the method which returns the published timestamp.
50
+ # Default is `published_at`.
42
51
  #
43
52
  # :trashed_at
44
- # The name to use for the method which returns the soft delete timestamp. Default is `trashed_at`.
53
+ # The name to use for the method which returns the soft delete timestamp.
54
+ # Default is `trashed_at`.
45
55
  def has_drafts(options = {})
46
56
  # Lazily include the instance methods so we don't clutter up
47
57
  # any more ActiveRecord models than we need to.
@@ -50,7 +60,9 @@ module Draftsman
50
60
 
51
61
  # Define before/around/after callbacks on each drafted model
52
62
  send :extend, ActiveModel::Callbacks
53
- define_model_callbacks :draft_creation, :draft_update, :draft_destruction, :draft_destroy
63
+ # TODO: Remove `draft_creation`, `draft_update`, and `draft_destroy` in
64
+ # v1.0.
65
+ define_model_callbacks :save_draft, :draft_creation, :draft_update, :draft_destruction, :draft_destroy
54
66
 
55
67
  class_attribute :draftsman_options
56
68
  self.draftsman_options = options.dup
@@ -78,43 +90,28 @@ module Draftsman
78
90
  self.trashed_at_attribute_name = options[:trashed_at] || :trashed_at
79
91
 
80
92
  # `belongs_to :draft` association
81
- belongs_to self.draft_association_name, :class_name => self.draft_class_name, :dependent => :destroy
93
+ belongs_to(self.draft_association_name, class_name: self.draft_class_name, dependent: :destroy)
82
94
 
83
95
  # Scopes
84
- scope :drafted, (lambda do |referenced_table_name = nil|
96
+ scope :drafted, -> (referenced_table_name = nil) {
85
97
  referenced_table_name = referenced_table_name.present? ? referenced_table_name : table_name
98
+ where.not(referenced_table_name => { "#{self.draft_association_name}_id" => nil })
99
+ }
86
100
 
87
- if where_not?
88
- where.not(referenced_table_name => { "#{self.draft_association_name}_id" => nil })
89
- else
90
- where("#{referenced_table_name}.#{self.draft_association_name}_id IS NOT NULL")
91
- end
92
- end)
93
-
94
- scope :published, (lambda do |referenced_table_name = nil|
101
+ scope :published, -> (referenced_table_name = nil) {
95
102
  referenced_table_name = referenced_table_name.present? ? referenced_table_name : table_name
103
+ where.not(referenced_table_name => { self.published_at_attribute_name => nil })
104
+ }
96
105
 
97
- if where_not?
98
- where.not(referenced_table_name => { self.published_at_attribute_name => nil })
99
- else
100
- where("#{self.published_at_attribute_name} IS NOT NULL")
101
- end
102
- end)
103
-
104
- scope :trashed, (lambda do |referenced_table_name = nil|
106
+ scope :trashed, -> (referenced_table_name = nil) {
105
107
  referenced_table_name = referenced_table_name.present? ? referenced_table_name : table_name
108
+ where.not(referenced_table_name => { self.trashed_at_attribute_name => nil })
109
+ }
106
110
 
107
- if where_not?
108
- where.not(referenced_table_name => { self.trashed_at_attribute_name => nil })
109
- else
110
- where("#{self.trashed_at_attribute_name} IS NOT NULL")
111
- end
112
- end)
113
-
114
- scope :live, (lambda do |referenced_table_name = nil|
111
+ scope :live, -> (referenced_table_name = nil) {
115
112
  referenced_table_name = referenced_table_name.present? ? referenced_table_name : table_name
116
113
  where(referenced_table_name => { self.trashed_at_attribute_name => nil })
117
- end)
114
+ }
118
115
  end
119
116
 
120
117
  # Returns draft class.
@@ -131,11 +128,6 @@ module Draftsman
131
128
  def trashable?
132
129
  draftable? && method_defined?(self.trashed_at_attribute_name)
133
130
  end
134
-
135
- # Returns whether or not the included ActiveRecord can do `where.not(...)` style queries.
136
- def where_not?
137
- ActiveRecord::VERSION::STRING.to_f >= 4.0
138
- end
139
131
  end
140
132
 
141
133
  module InstanceMethods
@@ -144,39 +136,15 @@ module Draftsman
144
136
  send(self.class.draft_association_name).present?
145
137
  end
146
138
 
147
- # Creates object and records a draft for the object's creation. Returns `true` or `false` depending on whether or not
148
- # the objects passed validation and the save was successful.
139
+ # DEPRECATED: Use `#draft_save` instead.
149
140
  def draft_creation
150
- run_callbacks :draft_creation do
151
- transaction do
152
- # We want to save the draft after create
153
- return false unless self.save
154
-
155
- data = {
156
- :item => self,
157
- :event => 'create',
158
- :whodunnit => Draftsman.whodunnit,
159
- :object => object_attrs_for_draft_record
160
- }
161
- data[:object_changes] = changes_for_draftsman(previous_changes: true) if track_object_changes_for_draft?
162
- data = merge_metadata_for_draft(data)
163
-
164
- send "build_#{self.class.draft_association_name}", data
165
-
166
- if send(self.class.draft_association_name).save
167
- write_attribute "#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id
168
- self.update_column "#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id
169
- else
170
- raise ActiveRecord::Rollback and return false
171
- end
172
- end
173
- end
174
- return true
141
+ ActiveSupport::Deprecation.warn('`#draft_creation` is deprecated and will be removed from Draftsman 1.0. Use `#save_draft` instead.')
142
+ _draft_creation
175
143
  end
176
144
 
177
- # DEPRECATED: Use `draft_destruction` instead.
145
+ # DEPRECATED: Use `#draft_destruction` instead.
178
146
  def draft_destroy
179
- ActiveSupport::Deprecation.warn('`draft_destroy` is deprecated and will be removed from Draftsman 1.0. Use `draft_destruction` instead.')
147
+ ActiveSupport::Deprecation.warn('`#draft_destroy` is deprecated and will be removed from Draftsman 1.0. Use `draft_destruction` instead.')
180
148
 
181
149
  run_callbacks :draft_destroy do
182
150
  _draft_destruction
@@ -190,94 +158,25 @@ module Draftsman
190
158
  end
191
159
  end
192
160
 
193
- # Updates object and records a draft for an `update` event. If the draft is being updated to the object's original
194
- # state, the draft is destroyed. Returns `true` or `false` depending on if the object passed validation and the save
195
- # was successful.
161
+ # DEPRECATED: Use `#draft_save` instead.
196
162
  def draft_update
197
- run_callbacks :draft_update do
198
- transaction do
199
- save_only_columns_for_draft
200
-
201
- # We want to save the draft before update
202
- return false unless self.valid?
203
-
204
- # If updating a creation draft, also update this item
205
- if self.draft? && send(self.class.draft_association_name).create?
206
- data = {
207
- :item => self,
208
- :whodunnit => Draftsman.whodunnit,
209
- :object => object_attrs_for_draft_record
210
- }
211
-
212
- if track_object_changes_for_draft?
213
- data[:object_changes] = changes_for_draftsman(changed_from: self.send(self.class.draft_association_name).changeset)
214
- end
215
- data = merge_metadata_for_draft(data)
216
- send(self.class.draft_association_name).update_attributes data
217
- self.save
218
- # Destroy the draft if this record has changed back to the original record
219
- elsif changed_to_original_for_draft?
220
- nilified_draft = send(self.class.draft_association_name)
221
- send "#{self.class.draft_association_name}_id=", nil
222
- self.save
223
- nilified_draft.destroy
224
- # Save a draft if record is changed notably
225
- elsif changed_notably_for_draft?
226
- data = {
227
- :item => self,
228
- :whodunnit => Draftsman.whodunnit,
229
- :object => object_attrs_for_draft_record
230
- }
231
- data = merge_metadata_for_draft(data)
232
-
233
- # If there's already a draft, update it.
234
- if send(self.class.draft_association_name).present?
235
- data[:object_changes] = changes_for_draftsman if track_object_changes_for_draft?
236
- send(self.class.draft_association_name).update_attributes data
237
- update_skipped_attributes
238
- # If there's not draft, create an update draft.
239
- else
240
- data[:event] = 'update'
241
- data[:object_changes] = changes_for_draftsman if track_object_changes_for_draft?
242
- send "build_#{self.class.draft_association_name}", data
243
-
244
- if send(self.class.draft_association_name).save
245
- update_column "#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id
246
- update_skipped_attributes
247
- else
248
- raise ActiveRecord::Rollback and return false
249
- end
250
- end
251
- # If record is a draft and not changed notably, then update the draft.
252
- elsif self.draft?
253
- data = {
254
- :item => self,
255
- :whodunnit => Draftsman.whodunnit,
256
- :object => object_attrs_for_draft_record
257
- }
258
- data[:object_changes] = changes_for_draftsman(changed_from: @object.draft.changeset) if track_object_changes_for_draft?
259
- data = merge_metadata_for_draft(data)
260
- send(self.class.draft_association_name).update_attributes data
261
- update_skipped_attributes
262
- # Otherwise, just save the record
263
- else
264
- self.save
265
- end
266
- end
267
- end
268
- rescue Exception => e
269
- false
163
+ ActiveSupport::Deprecation.warn('`#draft_update` is deprecated and will be removed from Draftsman 1.0. Use `#save_draft` instead.')
164
+ _draft_update
270
165
  end
271
166
 
272
167
  # Returns serialized object representing this drafted item.
273
168
  def object_attrs_for_draft_record(object = nil)
274
169
  object ||= self
275
170
 
276
- _attrs = object.attributes.except(*self.class.draftsman_options[:skip]).tap do |attributes|
171
+ attrs = object.attributes.except(*self.class.draftsman_options[:skip]).tap do |attributes|
277
172
  self.class.serialize_attributes_for_draftsman attributes
278
173
  end
279
174
 
280
- self.class.draft_class.object_col_is_json? ? _attrs : Draftsman.serializer.dump(_attrs)
175
+ if self.class.draft_class.object_col_is_json?
176
+ attrs
177
+ else
178
+ Draftsman.serializer.dump(attrs)
179
+ end
281
180
  end
282
181
 
283
182
  # Returns whether or not this item has been published at any point in its lifecycle.
@@ -285,6 +184,31 @@ module Draftsman
285
184
  self.published_at.present?
286
185
  end
287
186
 
187
+ # Creates or updates draft depending on state of this item and if it has
188
+ # any drafts.
189
+ #
190
+ # - If a completely new record, persists this item to the database and
191
+ # records a `create` draft.
192
+ # - If an existing record with an existing `create` draft, updates the
193
+ # record and the existing `create` draft.
194
+ # - If an existing record with no existing draft, records changes in an
195
+ # `update` draft.
196
+ # - If an existing record with an existing draft (`create` or `update`),
197
+ # updated back to its original undrafted state, removes associated
198
+ # `draft record`.
199
+ #
200
+ # Returns `true` or `false` depending on if the object passed validation
201
+ # and the save was successful.
202
+ def save_draft
203
+ run_callbacks :save_draft do
204
+ if self.new_record?
205
+ _draft_creation
206
+ else
207
+ _draft_update
208
+ end
209
+ end
210
+ end
211
+
288
212
  # Returns whether or not this item has been trashed
289
213
  def trashed?
290
214
  send(self.class.trashed_at_attribute_name).present?
@@ -292,38 +216,73 @@ module Draftsman
292
216
 
293
217
  private
294
218
 
219
+ # Creates object and records a draft for the object's creation. Returns
220
+ # `true` or `false` depending on whether or not the objects passed
221
+ # validation and the save was successful.
222
+ def _draft_creation
223
+ transaction do
224
+ # TODO: Remove callback wrapper in v1.0.
225
+ run_callbacks :draft_creation do
226
+ # We want to save the draft after create
227
+ return false unless self.save
228
+
229
+ # Build data to store in draft record.
230
+ data = {
231
+ item: self,
232
+ event: :create,
233
+ }
234
+ data[:object] = object_attrs_for_draft_record if Draftsman.stash_drafted_changes?
235
+ data[Draftsman.whodunnit_field] = Draftsman.whodunnit
236
+ data[:object_changes] = serialized_draft_changeset(changes_for_draftsman(:create)) if track_object_changes_for_draft?
237
+ data = merge_metadata_for_draft(data)
238
+ send("build_#{self.class.draft_association_name}", data)
239
+
240
+ if send(self.class.draft_association_name).save
241
+ fk = "#{self.class.draft_association_name}_id"
242
+ id = send(self.class.draft_association_name).id
243
+ self.update_column(fk, id)
244
+ else
245
+ raise ActiveRecord::Rollback and return false
246
+ end
247
+ end
248
+ end
249
+
250
+ return true
251
+ end
252
+
295
253
  # This is only abstracted away at this moment because of the
296
254
  # `draft_destroy` deprecation. Move all of this logic back into
297
255
  # `draft_destruction` after `draft_destroy is removed.`
298
256
  def _draft_destruction
299
257
  transaction do
300
258
  data = {
301
- :item => self,
302
- :event => 'destroy',
303
- :whodunnit => Draftsman.whodunnit,
304
- :object => object_attrs_for_draft_record
259
+ item: self,
260
+ event: :destroy
305
261
  }
262
+ data[:object] = object_attrs_for_draft_record if Draftsman.stash_drafted_changes?
263
+ data[Draftsman.whodunnit_field] = Draftsman.whodunnit
306
264
 
307
265
  # Stash previous draft in case it needs to be reverted later
308
266
  if self.draft?
309
267
  attrs = send(self.class.draft_association_name).attributes
310
268
 
311
- data[:previous_draft] = if self.class.draft_class.previous_draft_col_is_json?
312
- attrs
313
- else
314
- Draftsman.serializer.dump(attrs)
315
- end
269
+ data[:previous_draft] =
270
+ if self.class.draft_class.previous_draft_col_is_json?
271
+ attrs
272
+ else
273
+ Draftsman.serializer.dump(attrs)
274
+ end
316
275
  end
317
276
 
318
277
  data = merge_metadata_for_draft(data)
319
278
 
320
279
  if send(self.class.draft_association_name).present?
321
- send(self.class.draft_association_name).update_attributes! data
280
+ send(self.class.draft_association_name).update!(data)
322
281
  else
323
282
  send("build_#{self.class.draft_association_name}", data)
324
283
  send(self.class.draft_association_name).save!
325
- send "#{self.class.draft_association_name}_id=", send(self.class.draft_association_name).id
326
- self.update_column "#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id
284
+ send("#{self.class.draft_association_name}_id=", send(self.class.draft_association_name).id)
285
+ self.update_column("#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id)
327
286
  end
328
287
 
329
288
  trash!
@@ -332,62 +291,126 @@ module Draftsman
332
291
  dependent_associations = self.class.reflect_on_all_associations(:has_one) + self.class.reflect_on_all_associations(:has_many)
333
292
 
334
293
  dependent_associations.each do |association|
335
-
336
294
  if association.klass.draftable? && association.options.has_key?(:dependent) && association.options[:dependent] == :destroy
337
295
  dependents = self.send(association.name)
338
296
  dependents = [dependents] if (dependents && association.macro == :has_one)
339
297
 
340
- dependents.each do |dependent|
341
- dependent.draft_destruction unless dependent.draft? && dependent.send(dependent.class.draft_association_name).destroy?
342
- end if dependents
298
+ if dependents
299
+ dependents.each do |dependent|
300
+ dependent.draft_destruction unless dependent.draft? && dependent.send(dependent.class.draft_association_name).destroy?
301
+ end
302
+ end
343
303
  end
344
304
  end
345
305
  end
346
306
  end
347
307
 
348
- # Returns changes on this object, excluding attributes defined in the options for `:ignore` and `:skip`.
349
- def changed_and_not_ignored_for_draft(options = {})
350
- options[:previous_changes] ||= false
351
-
352
- my_changed = options[:previous_changes] ? previous_changes.keys : self.changed
353
-
354
- ignore = self.class.draftsman_options[:ignore]
355
- skip = self.class.draftsman_options[:skip]
356
- my_changed - ignore - skip
357
- end
358
-
359
- # Returns whether or not this instance has changes that should trigger a new draft.
360
- def changed_notably_for_draft?
361
- notably_changed_attributes_for_draft.any?
362
- end
363
-
364
- # Returns whether or not the updates change this draft back to the original state
365
- def changed_to_original_for_draft?
366
- send(self.draft_association_name).present? && send(self.class.draft_association_name).update? && !changed_notably_for_draft?
367
- end
368
-
369
- # Returns array of attributes that have changed for the object.
370
- def changes_for_draftsman(options = {})
371
- options[:changed_from] ||= {}
372
- options[:previous_changes] ||= false
308
+ # Updates object and records a draft for an `update` event. If the draft
309
+ # is being updated to the object's original state, the draft is destroyed.
310
+ # Returns `true` or `false` depending on if the object passed validation
311
+ # and the save was successful.
312
+ def _draft_update
313
+ # TODO: Remove callback wrapper in v1.0.
314
+ transaction do
315
+ run_callbacks :draft_update do
316
+ # Run validations.
317
+ return false unless self.valid?
373
318
 
374
- my_changes = options[:previous_changes] ? self.previous_changes : self.changes
319
+ # If updating a create draft, also update this item.
320
+ if self.draft? && send(self.class.draft_association_name).create?
321
+ the_changes = changes_for_draftsman(:create)
322
+ data = { item: self }
323
+ data[Draftsman.whodunnit_field] = Draftsman.whodunnit
324
+ data[:object] = object_attrs_for_draft_record if Draftsman.stash_drafted_changes?
325
+ data[:object_changes] = serialized_draft_changeset(the_changes) if track_object_changes_for_draft?
375
326
 
376
- new_changes = my_changes.delete_if do |key, value|
377
- !notably_changed_attributes_for_draft(previous_changes: options[:previous_changes]).include?(key)
378
- end.tap do |changes|
379
- self.class.serialize_draft_attribute_changes(changes) # Use serialized value for attributes when necessary
327
+ data = merge_metadata_for_draft(data)
328
+ send(self.class.draft_association_name).update(data)
329
+ self.save
330
+ else
331
+ the_changes = changes_for_draftsman(:update)
332
+ save_only_columns_for_draft if Draftsman.stash_drafted_changes?
333
+
334
+ # Destroy the draft if this record has changed back to the original
335
+ # record.
336
+ if self.draft? && the_changes.empty?
337
+ nilified_draft = send(self.class.draft_association_name)
338
+ send("#{self.class.draft_association_name}_id=", nil)
339
+ self.save
340
+ nilified_draft.destroy
341
+ # Save an update draft if record is changed notably.
342
+ elsif !the_changes.empty?
343
+ data = { item: self, event: :update }
344
+ data[Draftsman.whodunnit_field] = Draftsman.whodunnit
345
+ data[:object] = object_attrs_for_draft_record if Draftsman.stash_drafted_changes?
346
+ data[:object_changes] = serialized_draft_changeset(the_changes) if track_object_changes_for_draft?
347
+ data = merge_metadata_for_draft(data)
348
+
349
+ # If there's already a draft, update it.
350
+ if self.draft?
351
+ send(self.class.draft_association_name).update(data)
352
+
353
+ if Draftsman.stash_drafted_changes?
354
+ update_skipped_attributes
355
+ else
356
+ self.save
357
+ end
358
+ # If there's not an existing draft, create an update draft.
359
+ else
360
+ send("build_#{self.class.draft_association_name}", data)
361
+
362
+ if send(self.class.draft_association_name).save
363
+ update_column("#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id)
364
+
365
+ if Draftsman.stash_drafted_changes?
366
+ update_skipped_attributes
367
+ else
368
+ self.save
369
+ end
370
+ else
371
+ raise ActiveRecord::Rollback and return false
372
+ end
373
+ end
374
+ # Otherwise, just save the record.
375
+ else
376
+ self.save
377
+ end
378
+ end
379
+ end
380
380
  end
381
+ rescue Exception => e
382
+ false
383
+ end
381
384
 
382
- new_changes.each do |attribute, value|
383
- new_changes[attribute][0] = options[:changed_from][attribute][0] if options[:changed_from].has_key?(attribute)
385
+ # Returns hash of attributes that have changed for the object, similar to
386
+ # how ActiveRecord's `changes` works.
387
+ def changes_for_draftsman(event)
388
+ the_changes = {}
389
+ ignore = self.class.draftsman_options[:ignore]
390
+ skip = self.class.draftsman_options[:skip]
391
+ only = self.class.draftsman_options[:only]
392
+ draftable_attrs = self.attributes.keys - ignore - skip
393
+ draftable_attrs = draftable_attrs & only if only.present?
394
+
395
+ # If there's already an update draft, get its changes and reconcile them
396
+ # manually.
397
+ if event == :update
398
+ # Collect all attributes' previous and new values.
399
+ draftable_attrs.each do |attr|
400
+ if self.draft? && self.draft.changeset.key?(attr)
401
+ the_changes[attr] = [self.draft.changeset[attr].first, send(attr)]
402
+ else
403
+ the_changes[attr] = [self.send("#{attr}_was"), send(attr)]
404
+ end
405
+ end
406
+ # If there is no draft or it's for a create, then all draftable
407
+ # attributes are the changes.
408
+ else
409
+ draftable_attrs.each { |attr| the_changes[attr] = [nil, send(attr)] }
384
410
  end
385
411
 
386
- # We need to merge any previous changes so they are not lost on further updates before committing or
387
- # reverting
388
- my_changes = options[:changed_from].merge new_changes
389
-
390
- self.class.draft_class.object_changes_col_is_json? ? my_changes : Draftsman.serializer.dump(my_changes)
412
+ # Purge attributes that haven't changed.
413
+ the_changes.delete_if { |key, value| value.first == value.last }
391
414
  end
392
415
 
393
416
  # Merges model-level metadata from `meta` and `controller_info` into draft object.
@@ -413,52 +436,47 @@ module Draftsman
413
436
  data.merge(Draftsman.controller_info || {})
414
437
  end
415
438
 
416
- # Returns array of attributes that were changed to trigger a draft.
417
- def notably_changed_attributes_for_draft(options = {})
418
- options[:previous_changes] ||= false
419
-
420
- only = self.class.draftsman_options[:only]
421
- only.empty? ? changed_and_not_ignored_for_draft(previous_changes: options[:previous_changes]) : (changed_and_not_ignored_for_draft(previous_changes: options[:previous_changes]) & only)
422
- end
423
-
424
439
  # Save columns outside of the `only` option directly to master table
425
440
  def save_only_columns_for_draft
426
441
  if self.class.draftsman_options[:only].any?
427
442
  only_changes = {}
428
- only_changed_attributes = self.changed - self.class.draftsman_options[:only]
443
+ only_changed_attributes = self.attributes.keys - self.class.draftsman_options[:only]
429
444
 
430
- only_changed_attributes.each do |attribute|
431
- only_changes[attribute] = self.changes[attribute].last
445
+ only_changed_attributes.each do |key|
446
+ only_changes[key] = send(key)
432
447
  end
433
448
 
434
- self.update_columns only_changes if only_changes.any?
449
+ self.update_columns(only_changes) if only_changes.any?
435
450
  end
436
451
  end
437
452
 
453
+ # Returns changeset data in format appropriate for `object_changes`
454
+ # column.
455
+ def serialized_draft_changeset(my_changes)
456
+ self.class.draft_class.object_changes_col_is_json? ? my_changes : Draftsman.serializer.dump(my_changes)
457
+ end
458
+
438
459
  # Returns whether or not the draft class includes an `object_changes` attribute.
439
460
  def track_object_changes_for_draft?
440
- self.class.draft_class.column_names.include? 'object_changes'
461
+ self.class.draft_class.column_names.include?('object_changes')
441
462
  end
442
463
 
443
464
  # Sets `trashed_at` attribute to now and saves to the database immediately.
444
465
  def trash!
445
- write_attribute self.class.trashed_at_attribute_name, Time.now
446
- self.update_column self.class.trashed_at_attribute_name, send(self.class.trashed_at_attribute_name)
466
+ self.update_column(self.class.trashed_at_attribute_name, Time.now)
447
467
  end
448
468
 
449
469
  # Updates skipped attributes' values on this model.
450
470
  def update_skipped_attributes
451
- if draftsman_options[:skip].present?
452
- changed_and_skipped_keys = self.changed.select { |key| draftsman_options[:skip].include?(key) }
453
- changed_and_skipped_attrs = {}
454
- changed_and_skipped_keys.each { |key| changed_and_skipped_attrs[key] = self.changes[key].last }
455
-
456
- self.reload
457
- self.attributes = changed_and_skipped_attrs
458
- self.save
459
- else
460
- true
461
- end
471
+ # Skip over this if nothing's being skipped.
472
+ return true unless draftsman_options[:skip].present?
473
+
474
+ keys = self.attributes.keys.select { |key| draftsman_options[:skip].include?(key) }
475
+ attrs = {}
476
+ keys.each { |key| attrs[key] = self.send(key) }
477
+
478
+ self.reload
479
+ self.update(attrs)
462
480
  end
463
481
  end
464
482
  end
@@ -1,3 +1,3 @@
1
1
  module Draftsman
2
- VERSION = '0.5.1'
2
+ VERSION = '0.6.0'
3
3
  end