draftsman 0.3.7 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +23 -0
- data/README.md +61 -13
- data/draftsman.gemspec +3 -3
- data/lib/draftsman/model.rb +147 -122
- data/lib/draftsman/version.rb +1 -1
- data/lib/generators/draftsman/templates/create_drafts.rb +1 -1
- data/lib/generators/draftsman/templates/create_drafts_json.rb +1 -1
- data/spec/dummy/app/controllers/application_controller.rb +1 -1
- data/spec/dummy/app/models/enumable.rb +4 -0
- data/spec/dummy/app/models/talkative.rb +66 -0
- data/spec/dummy/config/environments/development.rb +1 -1
- data/spec/dummy/db/migrate/20150404203627_add_talkatives_table_to_tests.rb +18 -0
- data/spec/dummy/db/migrate/20160328184419_create_enumables.rb +9 -0
- data/spec/dummy/db/schema.rb +28 -11
- data/spec/models/child_spec.rb +6 -6
- data/spec/models/draft_spec.rb +9 -9
- data/spec/models/enumable_spec.rb +14 -0
- data/spec/models/parent_spec.rb +6 -6
- data/spec/models/skipper_spec.rb +1 -1
- data/spec/models/talkative_spec.rb +224 -0
- data/spec/models/trashable_spec.rb +17 -15
- data/spec/models/vanilla_spec.rb +1 -1
- data/spec/models/whitelister_spec.rb +1 -1
- metadata +21 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd9e61138b818bff3ff18a8f3039e2e890cd6f90
|
4
|
+
data.tar.gz: c84a08a47b0e0d19995a4c00c6a3a5de0ae48d61
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 200f68df8dbc6ab78eef9fad8dbfbdfcf33f5a57be1c4f5d3fdc8f8cb199254b20800f7170f37a99bb2d05aca071bd21704fafd8a78e42e653938fff7fd689d9
|
7
|
+
data.tar.gz: 613df4b0ad438fa2fb73fe3930d8201df1941e68640df7e42fb17c3f616b1a739519f16879bcf9b648aa3c3f92425cff1daf8404f96d44ea2edfe553ef7c7445
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,28 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 0.4.0 - April 5, 2016
|
4
|
+
|
5
|
+
- [@npafundi](https://github.com/npafundi)
|
6
|
+
[Implemented](https://github.com/liveeditor/draftsman/pull/20)
|
7
|
+
[#20](https://github.com/liveeditor/draftsman/pull/20) -
|
8
|
+
Adding callbacks for draft creation, update, and destroy
|
9
|
+
- [@chrisdpeters](https://github.com/chrisdpeters)
|
10
|
+
[Implemented](https://github.com/liveeditor/draftsman/commit/b3cecfa17f5cf296e7451cca56aeee41eac75f11)
|
11
|
+
[#16](https://github.com/liveeditor/draftsman/issues/16) -
|
12
|
+
Rename `draft_destroy` to `draft_destruction`
|
13
|
+
- [@defbyte](https://github.com/defbyte)
|
14
|
+
[Fixed](https://github.com/liveeditor/draftsman/pull/38)
|
15
|
+
[#39](https://github.com/liveeditor/draftsman/issues/39) -
|
16
|
+
Uh oh, ActiveSupport::DeprecationException error when running generated migrations
|
17
|
+
- [@chrisdpeters](https://github.com/chrisdpeters)
|
18
|
+
[Fixed](https://github.com/liveeditor/draftsman/commit/b0e328276e1e90ab877a6003f1d3165c7032267d)
|
19
|
+
[#40](https://github.com/liveeditor/draftsman/issues/40) -
|
20
|
+
Docs say publish! is available on the model instance, but it is not
|
21
|
+
- [@chrisdpeters](https://github.com/chrisdpeters)
|
22
|
+
[Fixed](https://github.com/liveeditor/draftsman/commit/bae427d2d38715da5b892888ff86d23bf5e39cb0)
|
23
|
+
[#17](https://github.com/liveeditor/draftsman/issues/17) -
|
24
|
+
Fix "open-ended dependency on rake" warning on gem build
|
25
|
+
|
3
26
|
## 0.3.7 - November 4, 2015
|
4
27
|
|
5
28
|
- [@bdunham](https://github.com/bdunham)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Draftsman v0.
|
1
|
+
# Draftsman v0.4.0 (alpha)
|
2
2
|
|
3
3
|
Draftsman is a Ruby gem that lets you create draft versions of your database records. If you're developing a system in
|
4
4
|
need of simple drafts or a publishing approval queue, then Draftsman just might be what you need.
|
@@ -30,11 +30,13 @@ source: it's a nice clean example of a gem that hooks into Rails and Sinatra.
|
|
30
30
|
- Allows you to store arbitrary model-level metadata with each draft (useful for filtering).
|
31
31
|
- Allows you to store arbitrary controller-level information with each draft (e.g., remote IP, current account ID).
|
32
32
|
- Only saves drafts when you explicitly tell it to via instance methods like `draft_creation`, `draft_update`, and
|
33
|
-
`
|
33
|
+
`draft_destruction`.
|
34
34
|
- Stores everything in a single database table by default (generates migration for you), or you can use separate tables
|
35
35
|
for separate models.
|
36
36
|
- Supports custom draft classes so different models' drafts can have different behavior.
|
37
37
|
- Supports custom name for `draft` association.
|
38
|
+
- Supports `before`, `after`, and `around` callbacks on each draft persistence method, such as `before_draft_creation`
|
39
|
+
or `around_draft_update`.
|
38
40
|
- Threadsafe.
|
39
41
|
|
40
42
|
## Compatibility
|
@@ -50,7 +52,7 @@ Works well with Rails, Sinatra, or any other application that depends on ActiveR
|
|
50
52
|
Add Draftsman to your `Gemfile`.
|
51
53
|
|
52
54
|
```ruby
|
53
|
-
gem 'draftsman', '0.
|
55
|
+
gem 'draftsman', '~> 0.4.0'
|
54
56
|
```
|
55
57
|
|
56
58
|
Or if you want to grab the latest from `master`:
|
@@ -202,15 +204,12 @@ widget.draft_update
|
|
202
204
|
|
203
205
|
# Trashes object and records a draft for a `destroy` event. (The `trashed_at` attribute must be set up on your model for
|
204
206
|
# this to work.)
|
205
|
-
widget.
|
207
|
+
widget.draft_destruction
|
206
208
|
|
207
209
|
# Returns whether or not this item has been published at any point in its lifecycle.
|
208
210
|
widget.published?
|
209
211
|
|
210
|
-
#
|
211
|
-
widget.publish!
|
212
|
-
|
213
|
-
# Returns whether or not this item has been trashed via `draft_destroy`
|
212
|
+
# Returns whether or not this item has been trashed via `draft_destruction`.
|
214
213
|
widget.trashed?
|
215
214
|
```
|
216
215
|
|
@@ -292,6 +291,31 @@ draft.draft_publication_dependencies
|
|
292
291
|
draft.draft_reversion_dependencies
|
293
292
|
```
|
294
293
|
|
294
|
+
### Callbacks
|
295
|
+
|
296
|
+
Draftsman supports callbacks for draft creation, update, and destroy. These callbacks can be defined in any model
|
297
|
+
that `has_drafts`.
|
298
|
+
|
299
|
+
Draft callbacks work similarly to ActiveRecord callbacks; pass any functions that you would like called
|
300
|
+
before/around/after a draft persistence method.
|
301
|
+
|
302
|
+
Available callbacks:
|
303
|
+
```ruby
|
304
|
+
before_draft_creation # called before draft is created
|
305
|
+
around_draft_creation # called function must yield to `draft_creation`
|
306
|
+
after_draft_creation # called after draft is created
|
307
|
+
|
308
|
+
before_draft_update # called before draft is updated
|
309
|
+
around_draft_update # called function must yield to `draft_update`
|
310
|
+
after_draft_update # called after draft is updated
|
311
|
+
|
312
|
+
before_draft_destruction # called before draft is destroyed
|
313
|
+
around_draft_destruction # called function must yield to `draft_destruction`
|
314
|
+
after_draft_destruction # called after draft is destroyed
|
315
|
+
```
|
316
|
+
|
317
|
+
Note that callbacks must be defined after your call to `has_drafts`.
|
318
|
+
|
295
319
|
## Basic Usage
|
296
320
|
|
297
321
|
A basic `widgets` admin controller in Rails that saves all of the user's actions as drafts would look something like
|
@@ -349,8 +373,8 @@ class Admin::WidgetsController < Admin::BaseController
|
|
349
373
|
end
|
350
374
|
|
351
375
|
def destroy
|
352
|
-
# Instead of calling `destroy`, you call `
|
353
|
-
@widget.
|
376
|
+
# Instead of calling `destroy`, you call `draft_destruction` to "trash" it as a draft
|
377
|
+
@widget.draft_destruction
|
354
378
|
flash[:success] = 'The widget was moved to the trash.'
|
355
379
|
redirect_to admin_widgets_path
|
356
380
|
end
|
@@ -458,6 +482,30 @@ end
|
|
458
482
|
|
459
483
|
```
|
460
484
|
|
485
|
+
If you would like your `Widget` to have callbacks, it might look something like this:
|
486
|
+
|
487
|
+
```ruby
|
488
|
+
class Widget < ActiveRecord::Base
|
489
|
+
has_drafts
|
490
|
+
|
491
|
+
before_draft_creation :say_hi
|
492
|
+
around_draft_update :surround_update
|
493
|
+
|
494
|
+
private
|
495
|
+
|
496
|
+
def say_hi
|
497
|
+
self.some_attr = 'Hi!'
|
498
|
+
end
|
499
|
+
|
500
|
+
def surround_update
|
501
|
+
# do something before update
|
502
|
+
yield
|
503
|
+
# do something after update
|
504
|
+
end
|
505
|
+
end
|
506
|
+
```
|
507
|
+
|
508
|
+
|
461
509
|
## Differences from PaperTrail
|
462
510
|
|
463
511
|
If you are familiar with the PaperTrail gem, some parts of the Draftsman gem will look very familiar.
|
@@ -465,8 +513,8 @@ If you are familiar with the PaperTrail gem, some parts of the Draftsman gem wil
|
|
465
513
|
However, there are some differences:
|
466
514
|
|
467
515
|
* PaperTrail hooks into ActiveRecord callbacks so that versions can be saved automatically with your normal CRUD
|
468
|
-
operations (`save`, `create`, `
|
469
|
-
|
516
|
+
operations (`save`, `create`, `update`, `destroy`, etc.). Draftsman requires that you explicitly call its own
|
517
|
+
CRUD methods in order to save a draft (`draft_creation`, `draft_update`, and `draft_destruction`).
|
470
518
|
|
471
519
|
* PaperTrail's `Version#object` column looks "backwards" and records the object's state _before_ the changes occurred.
|
472
520
|
Because drafts record changes as they will look in the future, they must work differently. Draftsman's `Draft#object`
|
@@ -518,7 +566,7 @@ work on features or find bugs!
|
|
518
566
|
|
519
567
|
## License
|
520
568
|
|
521
|
-
Copyright 2013-
|
569
|
+
Copyright 2013-2016 Minimal Orange, LLC.
|
522
570
|
|
523
571
|
Draftsman is released under the [MIT License][9].
|
524
572
|
|
data/draftsman.gemspec
CHANGED
@@ -4,8 +4,8 @@ require 'draftsman/version'
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = 'draftsman'
|
6
6
|
s.version = Draftsman::VERSION
|
7
|
-
s.summary =
|
8
|
-
s.description =
|
7
|
+
s.summary = 'Create draft versions of your database records.'
|
8
|
+
s.description = "Stores draft versions of your ActiveRecord models' data in a single table or split up into separate tables. Works with Ruby on Rails and Sinatra."
|
9
9
|
s.homepage = 'https://github.com/liveeditor/draftsman'
|
10
10
|
s.authors = ['Chris Peters']
|
11
11
|
s.email = 'chris@minimalorange.com'
|
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
|
19
19
|
s.add_dependency 'activerecord', ['>= 3.0', '< 5.0']
|
20
20
|
|
21
|
-
s.add_development_dependency 'rake'
|
21
|
+
s.add_development_dependency 'rake', '~> 10.5'
|
22
22
|
s.add_development_dependency 'railties', ['>= 3.0', '< 5.0']
|
23
23
|
s.add_development_dependency 'sinatra', '~> 1.0'
|
24
24
|
s.add_development_dependency 'rspec-rails', '3.2.1'
|
data/lib/draftsman/model.rb
CHANGED
@@ -45,6 +45,10 @@ module Draftsman
|
|
45
45
|
# any more ActiveRecord models than we need to.
|
46
46
|
send :include, InstanceMethods
|
47
47
|
|
48
|
+
# Define before/around/after callbacks on each drafted model
|
49
|
+
send :extend, ActiveModel::Callbacks
|
50
|
+
define_model_callbacks :draft_creation, :draft_update, :draft_destruction, :draft_destroy
|
51
|
+
|
48
52
|
class_attribute :draftsman_options
|
49
53
|
self.draftsman_options = options.dup
|
50
54
|
|
@@ -194,33 +198,155 @@ module Draftsman
|
|
194
198
|
# Creates object and records a draft for the object's creation. Returns `true` or `false` depending on whether or not
|
195
199
|
# the objects passed validation and the save was successful.
|
196
200
|
def draft_creation
|
197
|
-
|
198
|
-
|
199
|
-
|
201
|
+
run_callbacks :draft_creation do
|
202
|
+
transaction do
|
203
|
+
# We want to save the draft after create
|
204
|
+
return false unless self.save
|
200
205
|
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
206
|
+
data = {
|
207
|
+
:item => self,
|
208
|
+
:event => 'create',
|
209
|
+
:whodunnit => Draftsman.whodunnit,
|
210
|
+
:object => object_attrs_for_draft_record
|
211
|
+
}
|
212
|
+
data[:object_changes] = changes_for_draftsman(previous_changes: true) if track_object_changes_for_draft?
|
213
|
+
data = merge_metadata_for_draft(data)
|
209
214
|
|
210
|
-
|
215
|
+
send "build_#{self.class.draft_association_name}", data
|
211
216
|
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
217
|
+
if send(self.class.draft_association_name).save
|
218
|
+
write_attribute "#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id
|
219
|
+
self.update_column "#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id
|
220
|
+
else
|
221
|
+
raise ActiveRecord::Rollback and return false
|
222
|
+
end
|
218
223
|
end
|
219
224
|
end
|
225
|
+
return true
|
220
226
|
end
|
221
227
|
|
222
|
-
#
|
228
|
+
# DEPRECATED: Use `draft_destruction` instead.
|
223
229
|
def draft_destroy
|
230
|
+
ActiveSupport::Deprecation.warn('`draft_destroy` is deprecated and will be removed from Draftsman 1.0. Use `draft_destruction` instead.')
|
231
|
+
|
232
|
+
run_callbacks :draft_destroy do
|
233
|
+
_draft_destruction
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
# Trashes object and records a draft for a `destroy` event.
|
238
|
+
def draft_destruction
|
239
|
+
run_callbacks :draft_destruction do
|
240
|
+
_draft_destruction
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Updates object and records a draft for an `update` event. If the draft is being updated to the object's original
|
245
|
+
# state, the draft is destroyed. Returns `true` or `false` depending on if the object passed validation and the save
|
246
|
+
# was successful.
|
247
|
+
def draft_update
|
248
|
+
run_callbacks :draft_update do
|
249
|
+
transaction do
|
250
|
+
save_only_columns_for_draft
|
251
|
+
|
252
|
+
# We want to save the draft before update
|
253
|
+
return false unless self.valid?
|
254
|
+
|
255
|
+
# If updating a creation draft, also update this item
|
256
|
+
if self.draft? && send(self.class.draft_association_name).create?
|
257
|
+
data = {
|
258
|
+
:item => self,
|
259
|
+
:whodunnit => Draftsman.whodunnit,
|
260
|
+
:object => object_attrs_for_draft_record
|
261
|
+
}
|
262
|
+
|
263
|
+
if track_object_changes_for_draft?
|
264
|
+
data[:object_changes] = changes_for_draftsman(changed_from: self.send(self.class.draft_association_name).changeset)
|
265
|
+
end
|
266
|
+
data = merge_metadata_for_draft(data)
|
267
|
+
send(self.class.draft_association_name).update_attributes data
|
268
|
+
self.save
|
269
|
+
# Destroy the draft if this record has changed back to the original record
|
270
|
+
elsif changed_to_original_for_draft?
|
271
|
+
nilified_draft = send(self.class.draft_association_name)
|
272
|
+
send "#{self.class.draft_association_name}_id=", nil
|
273
|
+
self.save
|
274
|
+
nilified_draft.destroy
|
275
|
+
# Save a draft if record is changed notably
|
276
|
+
elsif changed_notably_for_draft?
|
277
|
+
data = {
|
278
|
+
:item => self,
|
279
|
+
:whodunnit => Draftsman.whodunnit,
|
280
|
+
:object => object_attrs_for_draft_record
|
281
|
+
}
|
282
|
+
data = merge_metadata_for_draft(data)
|
283
|
+
|
284
|
+
# If there's already a draft, update it.
|
285
|
+
if send(self.class.draft_association_name).present?
|
286
|
+
data[:object_changes] = changes_for_draftsman if track_object_changes_for_draft?
|
287
|
+
send(self.class.draft_association_name).update_attributes data
|
288
|
+
update_skipped_attributes
|
289
|
+
# If there's not draft, create an update draft.
|
290
|
+
else
|
291
|
+
data[:event] = 'update'
|
292
|
+
data[:object_changes] = changes_for_draftsman if track_object_changes_for_draft?
|
293
|
+
send "build_#{self.class.draft_association_name}", data
|
294
|
+
|
295
|
+
if send(self.class.draft_association_name).save
|
296
|
+
update_column "#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id
|
297
|
+
update_skipped_attributes
|
298
|
+
else
|
299
|
+
raise ActiveRecord::Rollback and return false
|
300
|
+
end
|
301
|
+
end
|
302
|
+
# If record is a draft and not changed notably, then update the draft.
|
303
|
+
elsif self.draft?
|
304
|
+
data = {
|
305
|
+
:item => self,
|
306
|
+
:whodunnit => Draftsman.whodunnit,
|
307
|
+
:object => object_attrs_for_draft_record
|
308
|
+
}
|
309
|
+
data[:object_changes] = changes_for_draftsman(changed_from: @object.draft.changeset) if track_object_changes_for_draft?
|
310
|
+
data = merge_metadata_for_draft(data)
|
311
|
+
send(self.class.draft_association_name).update_attributes data
|
312
|
+
update_skipped_attributes
|
313
|
+
# Otherwise, just save the record
|
314
|
+
else
|
315
|
+
self.save
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
rescue Exception => e
|
320
|
+
false
|
321
|
+
end
|
322
|
+
|
323
|
+
# Returns serialized object representing this drafted item.
|
324
|
+
def object_attrs_for_draft_record(object = nil)
|
325
|
+
object ||= self
|
326
|
+
|
327
|
+
_attrs = object.attributes.except(*self.class.draftsman_options[:skip]).tap do |attributes|
|
328
|
+
self.class.serialize_attributes_for_draftsman attributes
|
329
|
+
end
|
330
|
+
|
331
|
+
self.class.draft_class.object_col_is_json? ? _attrs : Draftsman.serializer.dump(_attrs)
|
332
|
+
end
|
333
|
+
|
334
|
+
# Returns whether or not this item has been published at any point in its lifecycle.
|
335
|
+
def published?
|
336
|
+
self.published_at.present?
|
337
|
+
end
|
338
|
+
|
339
|
+
# Returns whether or not this item has been trashed
|
340
|
+
def trashed?
|
341
|
+
send(self.class.trashed_at_attribute_name).present?
|
342
|
+
end
|
343
|
+
|
344
|
+
private
|
345
|
+
|
346
|
+
# This is only abstracted away at this moment because of the
|
347
|
+
# `draft_destroy` deprecation. Move all of this logic back into
|
348
|
+
# `draft_destruction` after `draft_destroy is removed.`
|
349
|
+
def _draft_destruction
|
224
350
|
transaction do
|
225
351
|
data = {
|
226
352
|
:item => self,
|
@@ -263,114 +389,13 @@ module Draftsman
|
|
263
389
|
dependents = [dependents] if (dependents && association.macro == :has_one)
|
264
390
|
|
265
391
|
dependents.each do |dependent|
|
266
|
-
dependent.
|
392
|
+
dependent.draft_destruction unless dependent.draft? && dependent.send(dependent.class.draft_association_name).destroy?
|
267
393
|
end if dependents
|
268
394
|
end
|
269
395
|
end
|
270
396
|
end
|
271
397
|
end
|
272
398
|
|
273
|
-
# Updates object and records a draft for an `update` event. If the draft is being updated to the object's original
|
274
|
-
# state, the draft is destroyed. Returns `true` or `false` depending on if the object passed validation and the save
|
275
|
-
# was successful.
|
276
|
-
def draft_update
|
277
|
-
transaction do
|
278
|
-
save_only_columns_for_draft
|
279
|
-
|
280
|
-
# We want to save the draft before update
|
281
|
-
return false unless self.valid?
|
282
|
-
|
283
|
-
# If updating a creation draft, also update this item
|
284
|
-
if self.draft? && send(self.class.draft_association_name).create?
|
285
|
-
data = {
|
286
|
-
:item => self,
|
287
|
-
:whodunnit => Draftsman.whodunnit,
|
288
|
-
:object => object_attrs_for_draft_record
|
289
|
-
}
|
290
|
-
|
291
|
-
if track_object_changes_for_draft?
|
292
|
-
data[:object_changes] = changes_for_draftsman(changed_from: self.send(self.class.draft_association_name).changeset)
|
293
|
-
end
|
294
|
-
|
295
|
-
data = merge_metadata_for_draft(data)
|
296
|
-
send(self.class.draft_association_name).update_attributes data
|
297
|
-
self.save
|
298
|
-
# Destroy the draft if this record has changed back to the original record
|
299
|
-
elsif changed_to_original_for_draft?
|
300
|
-
nilified_draft = send(self.class.draft_association_name)
|
301
|
-
send "#{self.class.draft_association_name}_id=", nil
|
302
|
-
self.save
|
303
|
-
nilified_draft.destroy
|
304
|
-
# Save a draft if record is changed notably
|
305
|
-
elsif changed_notably_for_draft?
|
306
|
-
data = {
|
307
|
-
:item => self,
|
308
|
-
:whodunnit => Draftsman.whodunnit,
|
309
|
-
:object => object_attrs_for_draft_record
|
310
|
-
}
|
311
|
-
data = merge_metadata_for_draft(data)
|
312
|
-
|
313
|
-
# If there's already a draft, update it.
|
314
|
-
if send(self.class.draft_association_name).present?
|
315
|
-
data[:object_changes] = changes_for_draftsman if track_object_changes_for_draft?
|
316
|
-
send(self.class.draft_association_name).update_attributes data
|
317
|
-
update_skipped_attributes
|
318
|
-
# If there's not draft, create an update draft.
|
319
|
-
else
|
320
|
-
data[:event] = 'update'
|
321
|
-
data[:object_changes] = changes_for_draftsman if track_object_changes_for_draft?
|
322
|
-
send "build_#{self.class.draft_association_name}", data
|
323
|
-
|
324
|
-
if send(self.class.draft_association_name).save
|
325
|
-
update_column "#{self.class.draft_association_name}_id", send(self.class.draft_association_name).id
|
326
|
-
update_skipped_attributes
|
327
|
-
else
|
328
|
-
raise ActiveRecord::Rollback and return false
|
329
|
-
end
|
330
|
-
end
|
331
|
-
# If record is a draft and not changed notably, then update the draft.
|
332
|
-
elsif self.draft?
|
333
|
-
data = {
|
334
|
-
:item => self,
|
335
|
-
:whodunnit => Draftsman.whodunnit,
|
336
|
-
:object => object_attrs_for_draft_record
|
337
|
-
}
|
338
|
-
data[:object_changes] = changes_for_draftsman(changed_from: @object.draft.changeset) if track_object_changes_for_draft?
|
339
|
-
data = merge_metadata_for_draft(data)
|
340
|
-
send(self.class.draft_association_name).update_attributes data
|
341
|
-
update_skipped_attributes
|
342
|
-
# Otherwise, just save the record
|
343
|
-
else
|
344
|
-
self.save
|
345
|
-
end
|
346
|
-
end
|
347
|
-
rescue Exception => e
|
348
|
-
false
|
349
|
-
end
|
350
|
-
|
351
|
-
# Returns serialized object representing this drafted item.
|
352
|
-
def object_attrs_for_draft_record(object = nil)
|
353
|
-
object ||= self
|
354
|
-
|
355
|
-
_attrs = object.attributes.except(*self.class.draftsman_options[:skip]).tap do |attributes|
|
356
|
-
self.class.serialize_attributes_for_draftsman attributes
|
357
|
-
end
|
358
|
-
|
359
|
-
self.class.draft_class.object_col_is_json? ? _attrs : Draftsman.serializer.dump(_attrs)
|
360
|
-
end
|
361
|
-
|
362
|
-
# Returns whether or not this item has been published at any point in its lifecycle.
|
363
|
-
def published?
|
364
|
-
self.published_at.present?
|
365
|
-
end
|
366
|
-
|
367
|
-
# Returns whether or not this item has been trashed
|
368
|
-
def trashed?
|
369
|
-
send(self.class.trashed_at_attribute_name).present?
|
370
|
-
end
|
371
|
-
|
372
|
-
private
|
373
|
-
|
374
399
|
# Returns changes on this object, excluding attributes defined in the options for `:ignore` and `:skip`.
|
375
400
|
def changed_and_not_ignored_for_draft(options = {})
|
376
401
|
options[:previous_changes] ||= false
|
@@ -452,7 +477,7 @@ module Draftsman
|
|
452
477
|
if self.class.draftsman_options[:only].any?
|
453
478
|
only_changes = {}
|
454
479
|
only_changed_attributes = self.changed - self.class.draftsman_options[:only]
|
455
|
-
|
480
|
+
|
456
481
|
only_changed_attributes.each do |attribute|
|
457
482
|
only_changes[attribute] = self.changes[attribute].last
|
458
483
|
end
|
data/lib/draftsman/version.rb
CHANGED
@@ -0,0 +1,66 @@
|
|
1
|
+
class Talkative < ActiveRecord::Base
|
2
|
+
has_drafts
|
3
|
+
|
4
|
+
# draft_creation callbacks
|
5
|
+
before_draft_creation :do_this_before_draft_creation
|
6
|
+
around_draft_creation :do_this_around_draft_creation
|
7
|
+
after_draft_creation :do_this_after_draft_creation
|
8
|
+
|
9
|
+
# draft_update callbacks
|
10
|
+
before_draft_update :do_this_before_draft_update
|
11
|
+
around_draft_update :do_this_around_draft_update
|
12
|
+
after_draft_update :do_this_after_draft_update
|
13
|
+
|
14
|
+
# # draft_destruction callbacks
|
15
|
+
before_draft_destruction :do_this_before_draft_destruction
|
16
|
+
around_draft_destruction :do_this_around_draft_destruction
|
17
|
+
after_draft_destruction :do_this_after_draft_destruction
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def do_this_before_draft_creation
|
22
|
+
self.before_comment = "I changed before creation"
|
23
|
+
end
|
24
|
+
|
25
|
+
def do_this_around_draft_creation
|
26
|
+
self.around_early_comment = "I changed around creation (before yield)"
|
27
|
+
yield
|
28
|
+
self.around_late_comment = "I changed around creation (after yield)"
|
29
|
+
end
|
30
|
+
|
31
|
+
def do_this_after_draft_creation
|
32
|
+
self.after_comment = "I changed after creation"
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
def do_this_before_draft_update
|
38
|
+
self.before_comment = "I changed before update"
|
39
|
+
end
|
40
|
+
|
41
|
+
def do_this_around_draft_update
|
42
|
+
self.around_early_comment = "I changed around update (before yield)"
|
43
|
+
yield
|
44
|
+
self.around_late_comment = "I changed around update (after yield)"
|
45
|
+
end
|
46
|
+
|
47
|
+
def do_this_after_draft_update
|
48
|
+
self.after_comment = "I changed after update"
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
|
53
|
+
def do_this_before_draft_destruction
|
54
|
+
self.before_comment = "I changed before destroy"
|
55
|
+
end
|
56
|
+
|
57
|
+
def do_this_around_draft_destruction
|
58
|
+
self.around_early_comment = "I changed around destroy (before yield)"
|
59
|
+
yield
|
60
|
+
self.around_late_comment = "I changed around destroy (after yield)"
|
61
|
+
end
|
62
|
+
|
63
|
+
def do_this_after_draft_destruction
|
64
|
+
self.after_comment = "I changed after destroy"
|
65
|
+
end
|
66
|
+
end
|
@@ -14,7 +14,7 @@ Dummy::Application.configure do
|
|
14
14
|
config.action_controller.perform_caching = false
|
15
15
|
|
16
16
|
# Don't care if the mailer can't send
|
17
|
-
config.action_mailer.raise_delivery_errors = false
|
17
|
+
# config.action_mailer.raise_delivery_errors = false
|
18
18
|
|
19
19
|
# Print deprecation notices to the Rails logger
|
20
20
|
config.active_support.deprecation = :log
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class AddTalkativesTableToTests < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table :talkatives, :force => true do |t|
|
4
|
+
t.string :before_comment
|
5
|
+
t.string :around_early_comment
|
6
|
+
t.string :around_late_comment
|
7
|
+
t.string :after_comment
|
8
|
+
t.references :draft
|
9
|
+
t.datetime :trashed_at
|
10
|
+
t.datetime :published_at
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.down
|
16
|
+
drop_table :talkatives
|
17
|
+
end
|
18
|
+
end
|