draftsman 0.2.1 → 0.3.0

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