draftsman 0.2.1 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9fe742f1e72bfd060d5d737fb6de9ee7cec1119f
4
- data.tar.gz: f846ec1b196415f5f33609d2a8516ca2fc087294
3
+ metadata.gz: 4594a89239d63ca33dfc4b008728952b04ff3d77
4
+ data.tar.gz: a41330672fbc6204b0a9b459db106b05cddb3675
5
5
  SHA512:
6
- metadata.gz: a355e08da8ff267cc30be5b6a8bfa7dab673ca84bbda56997bd34dbc574606735b7ef312155cfd840a9ecc42e31513f352c98ef7f682888c4d316c8efaa854c8
7
- data.tar.gz: 8f0d1d623df2fa66e332e1d715afa0c80a25f1d5218c6816c6a2110e283bef31587319dafec28f491217e8d4856b23bd44c8983c25f30d453475e7a1dc8d28b8
6
+ metadata.gz: 4a5197c59c7c471a2abeaf558274b19fa5938eba14b319ba09fe571bca17b7df2363122c87ff00dd9f4bbe34e83177056e904a535add241b7c702e2b1ce45c00
7
+ data.tar.gz: 31ad8696e86643c2c230fd40df6c66510360db0c8c98728fb89c27902738cde76dc5575ee7b8baf09c5c1f2b8574c923a1279c25fa8d6fac12c85408c453ad54
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 0.3.0 - July 29, 2014
4
+
5
+ - Commit [1e2a59f678](https://github.com/live-editor/draftsman/commit/1e2a59f678cc4d88222dfc1976d564b5649cd329) - Add support for PostgreSQL JSON data type for `object`, `object_changes`, and `previous_draft` columns.
6
+
3
7
  ## v0.2.1 - June 28, 2014
4
8
 
5
9
  - Commit [dbc6c83abb](https://github.com/live-editor/draftsman/commit/dbc6c83abbea5211f67ad883f4a2d18a9f5ac181) - Reifying a record that was drafted for destruction uses data from a drafted update before that if that's what happened.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- draftsman (0.2.1)
4
+ draftsman (0.3.0)
5
5
  activerecord (>= 3.0, < 5.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Draftsman v0.2.1 (alpha)
1
+ # Draftsman v0.3.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.
@@ -50,7 +50,7 @@ Works well with Rails, Sinatra, or any other application that depends on ActiveR
50
50
  Add Draftsman to your `Gemfile`.
51
51
 
52
52
  ```ruby
53
- gem 'draftsman', '0.2.1'
53
+ gem 'draftsman', '0.3.0'
54
54
  ```
55
55
 
56
56
  Or if you want to grab the latest from `master`:
@@ -61,14 +61,16 @@ gem 'draftsman', :github => 'live-editor/draftsman'
61
61
 
62
62
  Generate a migration which will add a `drafts` table to your database.
63
63
 
64
- rails g draftsman:install
64
+ $ rails g draftsman:install
65
65
 
66
- You can pass zero, one, or both of these options to the generator:
66
+ You can pass zero or any combination of these options to the generator:
67
67
 
68
68
  $ rails g draftsman:install --skip-initializer # Skip generation of the boilerplate initializer at
69
69
  # `config/initializers/draftsman.rb`.
70
70
 
71
- $ rails g draftsman:install --with-changes # Store changeset (diff) with each draft
71
+ $ rails g draftsman:install --with-changes # Store changeset (diff) with each draft.
72
+
73
+ $ rails g draftsman:install --with-pg-json # Use PostgreSQL JSON data type for serialized data.
72
74
 
73
75
  Run the migration(s).
74
76
 
@@ -22,6 +22,16 @@ class Draftsman::Draft < ActiveRecord::Base
22
22
  where :event => 'destroy'
23
23
  end
24
24
 
25
+ # Returns whether the `object` column is using the `json` type supported by PostgreSQL.
26
+ def self.object_col_is_json?
27
+ @object_col_is_json ||= columns_hash['object'].type == :json
28
+ end
29
+
30
+ # Returns whether the `object_changes` column is using the `json` type supported by PostgreSQL.
31
+ def self.object_changes_col_is_json?
32
+ @object_changes_col_is_json ||= columns_hash['object_changes'].type == :json
33
+ end
34
+
25
35
  def self.updates
26
36
  where :event => 'update'
27
37
  end
@@ -31,7 +41,9 @@ class Draftsman::Draft < ActiveRecord::Base
31
41
  def changeset
32
42
  return nil unless self.class.column_names.include? 'object_changes'
33
43
 
34
- HashWithIndifferentAccess.new(Draftsman.serializer.load(object_changes)).tap do |changes|
44
+ _changes = self.class.object_changes_col_is_json? ? self.object_changes : Draftsman.serializer.load(self.object_changes)
45
+
46
+ @changeset ||= HashWithIndifferentAccess.new(_changes).tap do |changes|
35
47
  item_type.constantize.unserialize_draft_attribute_changes(changes)
36
48
  end
37
49
  rescue
@@ -179,7 +191,7 @@ class Draftsman::Draft < ActiveRecord::Base
179
191
  end
180
192
  end
181
193
 
182
- # Returns instance of item restored to its pre-draft state.
194
+ # Returns instance of item converted to its drafted state.
183
195
  #
184
196
  # Example usage:
185
197
  #
@@ -194,7 +206,10 @@ class Draftsman::Draft < ActiveRecord::Base
194
206
 
195
207
  model = item.reload
196
208
 
197
- Draftsman.serializer.load(self.object).each do |key, value|
209
+ attrs = self.class.object_col_is_json? ? self.object : Draftsman.serializer.load(object)
210
+ model.class.unserialize_attributes_for_draftsman attrs
211
+
212
+ attrs.each do |key, value|
198
213
  # Skip counter_cache columns
199
214
  if model.respond_to?("#{key}=") && !key.end_with?('_count')
200
215
  model.send "#{key}=", value
@@ -211,9 +226,9 @@ class Draftsman::Draft < ActiveRecord::Base
211
226
 
212
227
  # Reverts this draft.
213
228
  # - For create drafts, destroys the draft and the item.
214
- # - For update drafts, destyors the draft only.
215
- # - For destroy drafts, destroys the draft and undoes the `trashed_at` timestamp on the item. If a draft was drafted
216
- # for destroy, restores the draft.
229
+ # - For update drafts, destroys the draft only.
230
+ # - For destroy drafts, destroys the draft and undoes the `trashed_at` timestamp on the item. If a previous draft was
231
+ # drafted for destroy, restores the draft.
217
232
  def revert!
218
233
  ActiveRecord::Base.transaction do
219
234
  case self.event
@@ -256,7 +271,9 @@ private
256
271
  draft = self.class.new
257
272
 
258
273
  without_identity_map do
259
- Draftsman.serializer.load(self.previous_draft).each do |key, value|
274
+ attrs = self.class.object_col_is_json? ? self.previous_draft : Draftsman.serializer.load(self.previous_draft)
275
+
276
+ attrs.each do |key, value|
260
277
  if key.to_sym != :id && draft.respond_to?("#{key}=")
261
278
  draft.send "#{key}=", value
262
279
  elsif key.to_sym != :id
@@ -268,22 +285,6 @@ private
268
285
  draft
269
286
  end
270
287
 
271
- # Saves associated draft dependencies by reflecting `belongs_to` associations and identifying which ones are
272
- # draftable.
273
- #def save_draft_dependencies
274
- # self.item.class.reflect_on_all_associations(:belongs_to).each do |association|
275
- # associated_object = self.item.send(association.name)
276
- #
277
- # if associated_object.present? && associated_object.respond_to?(:draft?)
278
- # if associated_object.reload.draft?
279
- # Draftsman::DraftDependency.create(:draft_id => self.id, :dependency_id => associated_object.id)
280
- # else
281
- # Draftsman::DraftDependency.where(:draft_id => self.id, :dependency_id => associated_object.id).delete_all
282
- # end
283
- # end
284
- # end
285
- #end
286
-
287
288
  def without_identity_map(&block)
288
289
  if defined?(ActiveRecord::IdentityMap) && ActiveRecord::IdentityMap.respond_to?(:without)
289
290
  ActiveRecord::IdentityMap.without &block
@@ -110,6 +110,11 @@ module Draftsman
110
110
  end)
111
111
  end
112
112
 
113
+ # Returns draft class.
114
+ def draft_class
115
+ @draft_class ||= draft_class_name.constantize
116
+ end
117
+
113
118
  # Returns whether or not `has_drafts` has been called on this model.
114
119
  def draftable?
115
120
  method_defined?(:draftsman_options)
@@ -122,6 +127,9 @@ module Draftsman
122
127
 
123
128
  # Serializes attribute changes for `Draft#object_changes` attribute.
124
129
  def serialize_draft_attribute_changes(changes)
130
+ # Don't serialize values before inserting into columns of type `JSON` on PostgreSQL databases.
131
+ return changes if self.draft_class.object_changes_col_is_json?
132
+
125
133
  serialized_attributes.each do |key, coder|
126
134
  if changes.key?(key)
127
135
  coder = Draftsman::Serializers::Yaml unless coder.respond_to?(:dump) # Fall back to YAML if `coder` has no `dump` method
@@ -133,6 +141,9 @@ module Draftsman
133
141
 
134
142
  # Used for `Draft#object` attribute
135
143
  def serialize_attributes_for_draftsman(attributes)
144
+ # Don't serialize values before inserting into columns of type `JSON` on PostgreSQL databases.
145
+ return attributes if self.draft_class.object_col_is_json?
146
+
136
147
  serialized_attributes.each do |key, coder|
137
148
  if attributes.key?(key)
138
149
  coder = Draftsman::Serializers::Yaml unless coder.respond_to?(:dump) # Fall back to YAML if `coder` has no `dump` method
@@ -148,6 +159,9 @@ module Draftsman
148
159
 
149
160
  # Unserializes attribute changes for `Draft#object_changes` attribute.
150
161
  def unserialize_draft_attribute_changes(changes)
162
+ # Don't serialize values before inserting into columns of type `JSON` on PostgreSQL databases.
163
+ return changes if self.draft_class.object_changes_col_is_json?
164
+
151
165
  serialized_attributes.each do |key, coder|
152
166
  if changes.key?(key)
153
167
  coder = Draftsman::Serializers::Yaml unless coder.respond_to?(:dump)
@@ -159,6 +173,9 @@ module Draftsman
159
173
 
160
174
  # Used for `Draft#object` attribute
161
175
  def unserialize_attributes_for_draftsman(attributes)
176
+ # Don't serialize values before inserting into columns of type `JSON` on PostgreSQL databases.
177
+ return attributes if self.draft_class.object_col_is_json?
178
+
162
179
  serialized_attributes.each do |key, coder|
163
180
  if attributes.key?(key)
164
181
  coder = Draftsman::Serializers::Yaml unless coder.respond_to?(:dump)
@@ -190,9 +207,9 @@ module Draftsman
190
207
  :item => self,
191
208
  :event => 'create',
192
209
  :whodunnit => Draftsman.whodunnit,
193
- :object => object_to_string_for_draft
210
+ :object => object_attrs_for_draft_record
194
211
  }
195
- data[:object_changes] = Draftsman.serializer.dump(changes_for_draftsman(previous_changes: true)) if track_object_changes_for_draft?
212
+ data[:object_changes] = changes_for_draftsman(previous_changes: true) if track_object_changes_for_draft?
196
213
  data = merge_metadata_for_draft(data)
197
214
 
198
215
  send "build_#{self.class.draft_association_name}", data
@@ -214,7 +231,7 @@ module Draftsman
214
231
  :item => self,
215
232
  :event => 'destroy',
216
233
  :whodunnit => Draftsman.whodunnit,
217
- :object => object_to_string_for_draft
234
+ :object => object_attrs_for_draft_record
218
235
  }
219
236
 
220
237
  # Stash previous draft in case it needs to be reverted later
@@ -266,11 +283,11 @@ module Draftsman
266
283
  data = {
267
284
  :item => self,
268
285
  :whodunnit => Draftsman.whodunnit,
269
- :object => object_to_string_for_draft
286
+ :object => object_attrs_for_draft_record
270
287
  }
271
288
 
272
289
  if track_object_changes_for_draft?
273
- data[:object_changes] = Draftsman.serializer.dump(changes_for_draftsman(changed_from: self.send(self.class.draft_association_name).changeset))
290
+ data[:object_changes] = changes_for_draftsman(changed_from: self.send(self.class.draft_association_name).changeset)
274
291
  end
275
292
 
276
293
  data = merge_metadata_for_draft(data)
@@ -288,21 +305,18 @@ module Draftsman
288
305
  data = {
289
306
  :item => self,
290
307
  :whodunnit => Draftsman.whodunnit,
291
- :object => object_to_string_for_draft
308
+ :object => object_attrs_for_draft_record
292
309
  }
293
310
  data = merge_metadata_for_draft(data)
294
311
 
295
312
  # If there's already a draft, update it
296
313
  if send(self.class.draft_association_name).present?
297
- if track_object_changes_for_draft?
298
- data[:object_changes] = Draftsman.serializer.dump(changes_for_draftsman)
299
- end
300
-
314
+ data[:object_changes] = changes_for_draftsman if track_object_changes_for_draft?
301
315
  send(self.class.draft_association_name).update_attributes data
302
316
  # If there's not draft, create an update draft
303
317
  else
304
318
  data[:event] = 'update'
305
- data[:object_changes] = Draftsman.serializer.dump(changes_for_draftsman) if track_object_changes_for_draft?
319
+ data[:object_changes] = changes_for_draftsman if track_object_changes_for_draft?
306
320
  send "build_#{self.class.draft_association_name}", data
307
321
 
308
322
  if send(self.class.draft_association_name).save
@@ -317,9 +331,9 @@ module Draftsman
317
331
  data = {
318
332
  :item => self,
319
333
  :whodunnit => Draftsman.whodunnit,
320
- :object => object_to_string_for_draft
334
+ :object => object_attrs_for_draft_record
321
335
  }
322
- data[:object_changes] = Draftsman.serializer.dump(changes_for_draftsman(changed_from: @object.draft.changeset)) if track_object_changes_for_draft?
336
+ data[:object_changes] = changes_for_draftsman(changed_from: @object.draft.changeset) if track_object_changes_for_draft?
323
337
  data = merge_metadata_for_draft(data)
324
338
 
325
339
  send(self.class.draft_association_name).update_attributes data
@@ -333,14 +347,14 @@ module Draftsman
333
347
  end
334
348
 
335
349
  # Returns serialized object representing this drafted item.
336
- def object_to_string_for_draft(object = nil)
350
+ def object_attrs_for_draft_record(object = nil)
337
351
  object ||= self
338
352
 
339
353
  _attrs = object.attributes.except(*self.class.draftsman_options[:skip]).tap do |attributes|
340
354
  self.class.serialize_attributes_for_draftsman attributes
341
355
  end
342
356
 
343
- Draftsman.serializer.dump(_attrs)
357
+ self.class.draft_class.object_col_is_json? ? _attrs : Draftsman.serializer.dump(_attrs)
344
358
  end
345
359
 
346
360
  # Returns whether or not this item has been published at any point in its lifecycle.
@@ -395,12 +409,9 @@ module Draftsman
395
409
 
396
410
  # We need to merge any previous changes so they are not lost on further updates before committing or
397
411
  # reverting
398
- options[:changed_from].merge new_changes
399
- end
412
+ my_changes = options[:changed_from].merge new_changes
400
413
 
401
- # Returns draft class.
402
- def draft_class
403
- self.draft_class_name.constantize
414
+ self.class.draft_class.object_changes_col_is_json? ? my_changes : Draftsman.serializer.dump(my_changes)
404
415
  end
405
416
 
406
417
  # Merges model-level metadata from `meta` and `controller_info` into draft object.
@@ -450,7 +461,7 @@ module Draftsman
450
461
 
451
462
  # Returns whether or not the draft class includes an `object_changes` attribute.
452
463
  def track_object_changes_for_draft?
453
- draft_class.column_names.include? 'object_changes'
464
+ self.class.draft_class.column_names.include? 'object_changes'
454
465
  end
455
466
 
456
467
  # Sets `trashed_at` attribute to now and saves to the database immediately.
@@ -1,3 +1,3 @@
1
1
  module Draftsman
2
- VERSION = '0.2.1'
2
+ VERSION = '0.3.0'
3
3
  end
@@ -6,14 +6,28 @@ module Draftsman
6
6
  class InstallGenerator < ::Rails::Generators::Base
7
7
  include ::Rails::Generators::Migration
8
8
 
9
- desc 'Generates (but does not run) a migration to add a drafts table.'
9
+ desc 'Creates config initializer and generates (but does not run) a migration to add a drafts table.'
10
10
  source_root File.expand_path('../templates', __FILE__)
11
11
  class_option :skip_initializer, :type => :boolean, :default => false, :desc => 'Skip generation of the boilerplate initializer at `config/initializers/draftsman.rb`.'
12
- class_option :with_changes, :type => :boolean, :default => false, :desc => 'Store changeset (diff) with each draft'
12
+ class_option :with_changes, :type => :boolean, :default => false, :desc => 'Store changeset (diff) with each draft.'
13
+ class_option :with_pg_json, :type => :boolean, :default => false, :desc => 'Use PostgreSQL JSON data type for serialized data.'
13
14
 
14
15
  def create_migration_file
15
- migration_template 'create_drafts.rb', 'db/migrate/create_drafts.rb'
16
- migration_template 'add_object_changes_column_to_drafts.rb', 'db/migrate/add_object_changes_column_to_drafts.rb' if options.with_changes?
16
+ if options.with_pg_json?
17
+ migration_template 'create_drafts_json.rb', 'db/migrate/create_drafts.rb'
18
+
19
+ if options.with_changes?
20
+ migration_template 'add_object_changes_column_to_drafts_json.rb',
21
+ 'db/migrate/add_object_changes_column_to_drafts.rb'
22
+ end
23
+ else
24
+ migration_template 'create_drafts.rb', 'db/migrate/create_drafts.rb'
25
+
26
+ if options.with_changes?
27
+ migration_template 'add_object_changes_column_to_drafts.rb',
28
+ 'db/migrate/add_object_changes_column_to_drafts.rb'
29
+ end
30
+ end
17
31
  end
18
32
 
19
33
  def self.next_migration_number(dirname)
@@ -0,0 +1,9 @@
1
+ class AddObjectChangesColumnToDrafts < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :drafts, :object_changes, :json
4
+ end
5
+
6
+ def self.down
7
+ remove_column :drafts, :object_changes
8
+ end
9
+ end
@@ -1,10 +1,14 @@
1
1
  # Override global `draft` class. For example, perhaps you want your own class at `app/models/draft.rb` that adds
2
- # extra attributes, validations, associations, etc. Be sure that this new model class extends `Draftsman::Draft`.
2
+ # extra attributes, validations, associations, methods, etc. Be sure that this new model class extends
3
+ # `Draftsman::Draft`.
3
4
  # Draftsman.draft_class_name = 'Draftsman::Draft'
4
5
 
5
6
  # Serializer for `object`, `object_changes`, and `previous_draft` columns. To use the JSON serializer, change to
6
7
  # `Draftsman::Serializers::Json`. You could implement your own serializer if you really wanted to. See files in
7
8
  # `lib/draftsman/serializers`.
9
+ #
10
+ # Note: this option is not needed if you're using the PostgreSQL JSON data type for the `object`,
11
+ # `object_changes`, and `previous_draft` columns.
8
12
  # Draftsman.serializer = Draftsman::Serializers::Json
9
13
 
10
14
  # Field which records when a draft was created.
@@ -0,0 +1,22 @@
1
+ class CreateDrafts < ActiveRecord::Migration
2
+ def change
3
+ create_table :drafts do |t|
4
+ t.string :item_type, :null => false
5
+ t.integer :item_id, :null => false
6
+ t.string :event, :null => false
7
+ t.string :whodunnit# :null => false
8
+ t.json :object
9
+ t.json :previous_draft
10
+ t.timestamps
11
+ end
12
+
13
+ change_table :drafts do |t|
14
+ t.index :item_type
15
+ t.index :item_id
16
+ t.index :event
17
+ t.index :whodunnit
18
+ t.index :created_at
19
+ t.index :updated_at
20
+ end
21
+ end
22
+ end
@@ -1,34 +1,23 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe Draftsman::Draft do
4
- let(:trashable) { Trashable.new :name => 'Bob' }
5
- subject { trashable.draft }
6
-
7
- describe :event, :create?, :update?, :destroy?, :object, :changeset do
8
- context 'with `create` draft' do
9
- before { trashable.draft_creation }
10
- its(:event) { should eql 'create' }
11
- its(:create?) { should be_true }
12
- its(:update?) { should be_false }
13
- its(:destroy?) { should be_false }
14
- its(:object) { should be_present }
15
- its(:changeset) { should include :id }
16
- its(:changeset) { should include :name }
17
- its(:changeset) { should_not include :title }
18
- its(:changeset) { should include :created_at }
19
- its(:changeset) { should include :updated_at }
20
- its(:previous_draft) { should be_nil }
21
-
22
- context 'updated create' do
23
- before do
24
- trashable.name = 'Sam'
25
- trashable.draft_update
26
- end
4
+ describe 'class methods' do
5
+ subject { Draftsman::Draft }
6
+ its(:object_col_is_json?) { should be_false }
7
+ its(:object_changes_col_is_json?) { should be_false }
8
+ end
27
9
 
28
- it { should be_create }
29
- it { should_not be_update }
30
- it { should_not be_destroy }
10
+ describe 'instance methods' do
11
+ let(:trashable) { Trashable.new :name => 'Bob' }
12
+ subject { trashable.draft }
13
+
14
+ describe :event, :create?, :update?, :destroy?, :object, :changeset do
15
+ context 'with `create` draft' do
16
+ before { trashable.draft_creation }
31
17
  its(:event) { should eql 'create' }
18
+ its(:create?) { should be_true }
19
+ its(:update?) { should be_false }
20
+ its(:destroy?) { should be_false }
32
21
  its(:object) { should be_present }
33
22
  its(:changeset) { should include :id }
34
23
  its(:changeset) { should include :name }
@@ -36,32 +25,32 @@ describe Draftsman::Draft do
36
25
  its(:changeset) { should include :created_at }
37
26
  its(:changeset) { should include :updated_at }
38
27
  its(:previous_draft) { should be_nil }
39
- end
40
- end
41
28
 
42
- context 'with `update` draft' do
43
- before do
44
- trashable.save!
45
- trashable.name = 'Sam'
46
- trashable.title = 'My Title'
47
- trashable.draft_update
29
+ context 'updated create' do
30
+ before do
31
+ trashable.name = 'Sam'
32
+ trashable.draft_update
33
+ end
34
+
35
+ it { should be_create }
36
+ it { should_not be_update }
37
+ it { should_not be_destroy }
38
+ its(:event) { should eql 'create' }
39
+ its(:object) { should be_present }
40
+ its(:changeset) { should include :id }
41
+ its(:changeset) { should include :name }
42
+ its(:changeset) { should_not include :title }
43
+ its(:changeset) { should include :created_at }
44
+ its(:changeset) { should include :updated_at }
45
+ its(:previous_draft) { should be_nil }
46
+ end
48
47
  end
49
48
 
50
- it { should_not be_create }
51
- it { should be_update }
52
- it { should_not be_destroy }
53
- its(:event) { should eql 'update' }
54
- its(:object) { should be_present }
55
- its(:changeset) { should_not include :id }
56
- its(:changeset) { should include :name }
57
- its(:changeset) { should include :title }
58
- its(:changeset) { should_not include :created_at }
59
- its(:changeset) { should_not include :updated_at }
60
- its(:previous_draft) { should be_nil }
61
-
62
- context 'updating the update' do
49
+ context 'with `update` draft' do
63
50
  before do
64
- trashable.title = nil
51
+ trashable.save!
52
+ trashable.name = 'Sam'
53
+ trashable.title = 'My Title'
65
54
  trashable.draft_update
66
55
  end
67
56
 
@@ -72,186 +61,180 @@ describe Draftsman::Draft do
72
61
  its(:object) { should be_present }
73
62
  its(:changeset) { should_not include :id }
74
63
  its(:changeset) { should include :name }
75
- its(:changeset) { should_not include :title }
64
+ its(:changeset) { should include :title }
76
65
  its(:changeset) { should_not include :created_at }
77
66
  its(:changeset) { should_not include :updated_at }
78
67
  its(:previous_draft) { should be_nil }
79
- end
80
- end
81
68
 
82
- context 'with `destroy` draft' do
83
- context 'without previous draft' do
84
- before do
85
- trashable.save!
86
- trashable.draft_destroy
69
+ context 'updating the update' do
70
+ before do
71
+ trashable.title = nil
72
+ trashable.draft_update
73
+ end
74
+
75
+ it { should_not be_create }
76
+ it { should be_update }
77
+ it { should_not be_destroy }
78
+ its(:event) { should eql 'update' }
79
+ its(:object) { should be_present }
80
+ its(:changeset) { should_not include :id }
81
+ its(:changeset) { should include :name }
82
+ its(:changeset) { should_not include :title }
83
+ its(:changeset) { should_not include :created_at }
84
+ its(:changeset) { should_not include :updated_at }
85
+ its(:previous_draft) { should be_nil }
87
86
  end
88
-
89
- it { should_not be_create }
90
- it { should_not be_update }
91
- it { should be_destroy }
92
- it { should_not be_destroyed }
93
- its(:event) { should eql 'destroy' }
94
- its(:object) { should be_present }
95
- its(:changeset) { should eql Hash.new }
96
- its(:previous_draft) { should be_nil }
97
87
  end
98
88
 
99
- context 'with previous `create` draft' do
100
- before do
101
- trashable.draft_creation
102
- trashable.draft_destroy
89
+ context 'with `destroy` draft' do
90
+ context 'without previous draft' do
91
+ before do
92
+ trashable.save!
93
+ trashable.draft_destroy
94
+ end
95
+
96
+ it { should_not be_create }
97
+ it { should_not be_update }
98
+ it { should be_destroy }
99
+ it { should_not be_destroyed }
100
+ its(:event) { should eql 'destroy' }
101
+ its(:object) { should be_present }
102
+ its(:changeset) { should eql Hash.new }
103
+ its(:previous_draft) { should be_nil }
103
104
  end
104
105
 
105
- it { should_not be_create }
106
- it { should_not be_update }
107
- it { should be_destroy }
108
- it { should_not be_destroyed }
109
- its(:event) { should eql 'destroy' }
110
- its(:object) { should be_present }
111
- its(:changeset) { should include :id }
112
- its(:changeset) { should include :name }
113
- its(:changeset) { should_not include :title }
114
- its(:changeset) { should include :created_at }
115
- its(:changeset) { should include :updated_at }
116
- its(:previous_draft) { should be_present }
117
- end
118
- end
119
- end
120
-
121
- describe :publish! do
122
- context 'with `create` draft' do
123
- before { trashable.draft_creation }
124
- subject { trashable.draft.publish!; return trashable.reload }
125
- it { expect { subject }.to_not raise_exception }
126
- it { should be_published }
127
- it { should_not be_trashed }
128
- it { should_not be_draft }
129
- its(:published_at) { should be_present }
130
- its(:draft_id) { should be_nil }
131
- its(:draft) { should be_nil }
132
- its(:trashed_at) { should be_nil }
133
-
134
- it 'deletes the draft' do
135
- expect { subject }.to change(Draftsman::Draft, :count).by(-1)
106
+ context 'with previous `create` draft' do
107
+ before do
108
+ trashable.draft_creation
109
+ trashable.draft_destroy
110
+ end
111
+
112
+ it { should_not be_create }
113
+ it { should_not be_update }
114
+ it { should be_destroy }
115
+ it { should_not be_destroyed }
116
+ its(:event) { should eql 'destroy' }
117
+ its(:object) { should be_present }
118
+ its(:changeset) { should include :id }
119
+ its(:changeset) { should include :name }
120
+ its(:changeset) { should_not include :title }
121
+ its(:changeset) { should include :created_at }
122
+ its(:changeset) { should include :updated_at }
123
+ its(:previous_draft) { should be_present }
124
+ end
136
125
  end
137
126
  end
138
127
 
139
- context 'with `update` draft' do
140
- before do
141
- trashable.save!
142
- trashable.name = 'Sam'
143
- trashable.draft_update
144
- end
145
-
146
- subject { trashable.draft.publish!; return trashable.reload }
147
- it { expect { subject }.to_not raise_exception }
148
- it { should be_published }
149
- it { should_not be_draft }
150
- it { should_not be_trashed }
151
- its(:name) { should eql 'Sam' }
152
- its(:published_at) { should be_present }
153
- its(:draft_id) { should be_nil }
154
- its(:draft) { should be_nil }
155
- its(:trashed_at) { should be_nil }
156
-
157
- it 'deletes the draft' do
158
- expect { subject }.to change(Draftsman::Draft, :count).by(-1)
159
- end
128
+ describe :publish! do
129
+ context 'with `create` draft' do
130
+ before { trashable.draft_creation }
131
+ subject { trashable.draft.publish!; return trashable.reload }
132
+ it { expect { subject }.to_not raise_exception }
133
+ it { should be_published }
134
+ it { should_not be_trashed }
135
+ it { should_not be_draft }
136
+ its(:published_at) { should be_present }
137
+ its(:draft_id) { should be_nil }
138
+ its(:draft) { should be_nil }
139
+ its(:trashed_at) { should be_nil }
160
140
 
161
- it 'does not delete the associated item' do
162
- expect { subject }.to_not change(Trashable, :count)
141
+ it 'deletes the draft' do
142
+ expect { subject }.to change(Draftsman::Draft, :count).by(-1)
143
+ end
163
144
  end
164
- end
165
145
 
166
- context 'with `destroy` draft' do
167
- context 'without previous draft' do
146
+ context 'with `update` draft' do
168
147
  before do
169
148
  trashable.save!
170
- trashable.draft_destroy
149
+ trashable.name = 'Sam'
150
+ trashable.draft_update
171
151
  end
172
152
 
173
- subject { trashable.draft.publish! }
153
+ subject { trashable.draft.publish!; return trashable.reload }
154
+ it { expect { subject }.to_not raise_exception }
155
+ it { should be_published }
156
+ it { should_not be_draft }
157
+ it { should_not be_trashed }
158
+ its(:name) { should eql 'Sam' }
159
+ its(:published_at) { should be_present }
160
+ its(:draft_id) { should be_nil }
161
+ its(:draft) { should be_nil }
162
+ its(:trashed_at) { should be_nil }
174
163
 
175
- it 'destroys the draft' do
164
+ it 'deletes the draft' do
176
165
  expect { subject }.to change(Draftsman::Draft, :count).by(-1)
177
166
  end
178
167
 
179
- it 'deletes the associated item' do
180
- expect { subject }.to change(Trashable, :count).by(-1)
168
+ it 'does not delete the associated item' do
169
+ expect { subject }.to_not change(Trashable, :count)
181
170
  end
182
171
  end
183
172
 
184
- context 'with previous `create` draft' do
185
- before do
186
- trashable.draft_creation
187
- trashable.draft_destroy
188
- end
173
+ context 'with `destroy` draft' do
174
+ context 'without previous draft' do
175
+ before do
176
+ trashable.save!
177
+ trashable.draft_destroy
178
+ end
189
179
 
190
- subject { trashable.draft.publish! }
180
+ subject { trashable.draft.publish! }
191
181
 
192
- it 'destroys the draft' do
193
- expect { subject }.to change(Draftsman::Draft, :count).by(-1)
194
- end
182
+ it 'destroys the draft' do
183
+ expect { subject }.to change(Draftsman::Draft, :count).by(-1)
184
+ end
195
185
 
196
- it 'deletes the associated item' do
197
- expect { subject }.to change(Trashable, :count).by(-1)
186
+ it 'deletes the associated item' do
187
+ expect { subject }.to change(Trashable, :count).by(-1)
188
+ end
198
189
  end
199
- end
200
- end
201
- end
202
190
 
203
- describe :revert! do
204
- context 'with `create` draft' do
205
- before { trashable.draft_creation }
206
- subject { trashable.draft.revert! }
207
- it { expect { subject }.to_not raise_exception }
191
+ context 'with previous `create` draft' do
192
+ before do
193
+ trashable.draft_creation
194
+ trashable.draft_destroy
195
+ end
208
196
 
209
- it 'deletes the draft' do
210
- expect { subject }.to change(Draftsman::Draft, :count).by(-1)
211
- end
197
+ subject { trashable.draft.publish! }
212
198
 
213
- it 'deletes associated item' do
214
- expect { subject }.to change(Trashable, :count).by(-1)
215
- end
216
- end
199
+ it 'destroys the draft' do
200
+ expect { subject }.to change(Draftsman::Draft, :count).by(-1)
201
+ end
217
202
 
218
- context 'with `update` draft' do
219
- before do
220
- trashable.save!
221
- trashable.name = 'Sam'
222
- trashable.draft_update
203
+ it 'deletes the associated item' do
204
+ expect { subject }.to change(Trashable, :count).by(-1)
205
+ end
206
+ end
223
207
  end
208
+ end
224
209
 
225
- subject { trashable.draft.revert!; return trashable.reload }
226
- it { expect { subject }.to_not raise_exception }
227
- it { should_not be_draft }
228
- its(:name) { should eql 'Bob' }
229
- its(:draft_id) { should be_nil }
230
- its(:draft) { should be_nil }
210
+ describe :revert! do
211
+ context 'with `create` draft' do
212
+ before { trashable.draft_creation }
213
+ subject { trashable.draft.revert! }
214
+ it { expect { subject }.to_not raise_exception }
231
215
 
232
- it 'deletes the draft' do
233
- expect { subject }.to change(Draftsman::Draft, :count).by(-1)
234
- end
216
+ it 'deletes the draft' do
217
+ expect { subject }.to change(Draftsman::Draft, :count).by(-1)
218
+ end
235
219
 
236
- it 'does not delete the associated item' do
237
- expect { subject }.to_not change(Trashable, :count)
220
+ it 'deletes associated item' do
221
+ expect { subject }.to change(Trashable, :count).by(-1)
222
+ end
238
223
  end
239
- end
240
224
 
241
- context 'with `destroy` draft' do
242
- context 'without previous draft' do
225
+ context 'with `update` draft' do
243
226
  before do
244
227
  trashable.save!
245
- trashable.draft_destroy
228
+ trashable.name = 'Sam'
229
+ trashable.draft_update
246
230
  end
247
231
 
248
232
  subject { trashable.draft.revert!; return trashable.reload }
249
233
  it { expect { subject }.to_not raise_exception }
250
- it { should_not be_trashed }
251
234
  it { should_not be_draft }
235
+ its(:name) { should eql 'Bob' }
252
236
  its(:draft_id) { should be_nil }
253
237
  its(:draft) { should be_nil }
254
- its(:trashed_at) { should be_nil }
255
238
 
256
239
  it 'deletes the draft' do
257
240
  expect { subject }.to change(Draftsman::Draft, :count).by(-1)
@@ -262,112 +245,137 @@ describe Draftsman::Draft do
262
245
  end
263
246
  end
264
247
 
265
- context 'with previous `create` draft' do
266
- before do
267
- trashable.draft_creation
268
- trashable.draft_destroy
248
+ context 'with `destroy` draft' do
249
+ context 'without previous draft' do
250
+ before do
251
+ trashable.save!
252
+ trashable.draft_destroy
253
+ end
254
+
255
+ subject { trashable.draft.revert!; return trashable.reload }
256
+ it { expect { subject }.to_not raise_exception }
257
+ it { should_not be_trashed }
258
+ it { should_not be_draft }
259
+ its(:draft_id) { should be_nil }
260
+ its(:draft) { should be_nil }
261
+ its(:trashed_at) { should be_nil }
262
+
263
+ it 'deletes the draft' do
264
+ expect { subject }.to change(Draftsman::Draft, :count).by(-1)
265
+ end
266
+
267
+ it 'does not delete the associated item' do
268
+ expect { subject }.to_not change(Trashable, :count)
269
+ end
269
270
  end
270
271
 
271
- subject { trashable.draft.revert!; return trashable.reload }
272
- it { expect { subject }.to_not raise_exception }
273
- it { should_not be_trashed }
274
- it { should be_draft }
275
- its(:draft_id) { should be_present }
276
- its(:draft) { should be_present }
277
- its(:trashed_at) { should be_nil }
278
-
279
- it 'deletes the `destroy` draft' do
280
- expect { subject }.to change(Draftsman::Draft.where(:event => 'destroy'), :count).by(-1)
272
+ context 'with previous `create` draft' do
273
+ before do
274
+ trashable.draft_creation
275
+ trashable.draft_destroy
276
+ end
277
+
278
+ subject { trashable.draft.revert!; return trashable.reload }
279
+ it { expect { subject }.to_not raise_exception }
280
+ it { should_not be_trashed }
281
+ it { should be_draft }
282
+ its(:draft_id) { should be_present }
283
+ its(:draft) { should be_present }
284
+ its(:trashed_at) { should be_nil }
285
+
286
+ it 'deletes the `destroy` draft' do
287
+ expect { subject }.to change(Draftsman::Draft.where(:event => 'destroy'), :count).by(-1)
288
+ end
289
+
290
+ it 'reifies the previous `create` draft' do
291
+ expect { subject }.to change(Draftsman::Draft.where(:event => 'create'), :count).by(1)
292
+ end
293
+
294
+ it 'does not delete the associated item' do
295
+ expect { subject }.to_not change(Trashable, :count)
296
+ end
297
+
298
+ its "draft's previous draft should be nil" do
299
+ subject.draft.previous_draft.should be_nil
300
+ end
281
301
  end
302
+ end
303
+ end
282
304
 
283
- it 'reifies the previous `create` draft' do
284
- expect { subject }.to change(Draftsman::Draft.where(:event => 'create'), :count).by(1)
285
- end
305
+ describe :reify do
306
+ subject { trashable.draft.reify }
286
307
 
287
- it 'does not delete the associated item' do
288
- expect { subject }.to_not change(Trashable, :count)
289
- end
308
+ context 'with `create` draft' do
309
+ before { trashable.draft_creation }
310
+ its(:title) { should eql trashable.title }
311
+
312
+ context 'updated create' do
313
+ before do
314
+ trashable.name = 'Sam'
315
+ trashable.draft_update
316
+ end
290
317
 
291
- its "draft's previous draft should be nil" do
292
- subject.draft.previous_draft.should be_nil
318
+ its(:name) { should eql 'Sam' }
319
+ its(:title) { should be_nil }
293
320
  end
294
321
  end
295
- end
296
- end
297
-
298
- describe :reify do
299
- subject { trashable.draft.reify }
300
322
 
301
- context 'with `create` draft' do
302
- before { trashable.draft_creation }
303
- its(:title) { should eql trashable.title }
304
-
305
- context 'updated create' do
323
+ context 'with `update` draft' do
306
324
  before do
325
+ trashable.save!
307
326
  trashable.name = 'Sam'
327
+ trashable.title = 'My Title'
308
328
  trashable.draft_update
309
329
  end
310
330
 
311
331
  its(:name) { should eql 'Sam' }
312
- its(:title) { should be_nil }
313
- end
314
- end
315
-
316
- context 'with `update` draft' do
317
- before do
318
- trashable.save!
319
- trashable.name = 'Sam'
320
- trashable.title = 'My Title'
321
- trashable.draft_update
322
- end
332
+ its(:title) { should eql 'My Title' }
323
333
 
324
- its(:name) { should eql 'Sam' }
325
- its(:title) { should eql 'My Title' }
334
+ context 'updating the update' do
335
+ before do
336
+ trashable.title = nil
337
+ trashable.draft_update
338
+ end
326
339
 
327
- context 'updating the update' do
328
- before do
329
- trashable.title = nil
330
- trashable.draft_update
340
+ its(:name) { should eql 'Sam' }
341
+ its(:title) { should be_nil }
331
342
  end
332
-
333
- its(:name) { should eql 'Sam' }
334
- its(:title) { should be_nil }
335
343
  end
336
- end
337
344
 
338
- context 'with `destroy` draft' do
339
- context 'without previous draft' do
340
- before do
341
- trashable.save!
342
- trashable.draft_destroy
343
- end
344
-
345
- its(:name) { should eql 'Bob' }
346
- its(:title) { should be_nil }
347
- end
345
+ context 'with `destroy` draft' do
346
+ context 'without previous draft' do
347
+ before do
348
+ trashable.save!
349
+ trashable.draft_destroy
350
+ end
348
351
 
349
- context 'with previous `create` draft' do
350
- before do
351
- trashable.draft_creation
352
- trashable.draft_destroy
352
+ its(:name) { should eql 'Bob' }
353
+ its(:title) { should be_nil }
353
354
  end
354
355
 
355
- its(:name) { should eql 'Bob' }
356
- its(:title) { should be_nil }
357
- end
356
+ context 'with previous `create` draft' do
357
+ before do
358
+ trashable.draft_creation
359
+ trashable.draft_destroy
360
+ end
358
361
 
359
- context 'with previous `update` draft' do
360
- before do
361
- trashable.save!
362
- trashable.name = 'Sam'
363
- trashable.title = 'My Title'
364
- trashable.draft_update
365
- # Typically, 2 draft operations won't happen in the same request, so reload before draft-destroying.
366
- trashable.reload.draft_destroy
362
+ its(:name) { should eql 'Bob' }
363
+ its(:title) { should be_nil }
367
364
  end
368
365
 
369
- its(:name) { should eql 'Sam' }
370
- its(:title) { should eql 'My Title' }
366
+ context 'with previous `update` draft' do
367
+ before do
368
+ trashable.save!
369
+ trashable.name = 'Sam'
370
+ trashable.title = 'My Title'
371
+ trashable.draft_update
372
+ # Typically, 2 draft operations won't happen in the same request, so reload before draft-destroying.
373
+ trashable.reload.draft_destroy
374
+ end
375
+
376
+ its(:name) { should eql 'Sam' }
377
+ its(:title) { should eql 'My Title' }
378
+ end
371
379
  end
372
380
  end
373
381
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: draftsman
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Peters
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-06-28 00:00:00.000000000 Z
11
+ date: 2014-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -165,8 +165,10 @@ files:
165
165
  - lib/draftsman/version.rb
166
166
  - lib/generators/draftsman/install_generator.rb
167
167
  - lib/generators/draftsman/templates/add_object_changes_column_to_drafts.rb
168
+ - lib/generators/draftsman/templates/add_object_changes_column_to_drafts_json.rb
168
169
  - lib/generators/draftsman/templates/config/initializers/draftsman.rb
169
170
  - lib/generators/draftsman/templates/create_drafts.rb
171
+ - lib/generators/draftsman/templates/create_drafts_json.rb
170
172
  - spec/controllers/informants_controller_spec.rb
171
173
  - spec/controllers/users_controller_spec.rb
172
174
  - spec/controllers/whodunnits_controller_spec.rb