draftsman 0.3.7 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|