purgatory 2.7.0 → 2.8.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- Y2MzNDQ3NjcwNDU5NGRkMmU4YTM1N2RmYzk4ZTY2NTRmZTg2ZDRhZQ==
4
+ Y2M1NWE4MjU0ZDg0OWE0YTcwNzA3ZGMxYzAwM2U0MWE0ZTVlZTQ5NQ==
5
5
  data.tar.gz: !binary |-
6
- OThkNGVlOTc5YWY4YjYzMDExMWJiOGRiZTFmZDhjMzI5YzViNjM2OA==
6
+ OTEwZDI1MDM3YzhhMGFjNmYxM2RjMTk0NzFhOTY0MmZmMzI3ZjE5Yg==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NjNkNGExMmE3OTQzZmJlMTFjNjkwZGU1ZGU5YTA3OTg5YTQ5YTAxNzViMmUy
10
- Y2JlOTg4NWJjZTg0MTlhOWRlNzA5MDJhYWFkMzVhMjQwOWJkYzQ3YjQxODM3
11
- NDAxMjQ3ZTM0OWJlODViMzYzZTMyNjA3MzJmZmI1NGVlZjE3NDI=
9
+ YjRmYTU3MGZhNzE1ZDZhZjQ0MDBlNWEwOTNmNThlZDU5YTVjYTVlOGUyZmQz
10
+ NGQzZTBiMTMyMzVkNDM5ZmM3NGUyMjU2OGJjOGJlNzEwMDVhMDQ2MWIyMmI5
11
+ NDc3ZTExY2I1NGU1MmI4ZjBjZThjM2M3M2MxMThhMDIzZDQ2Mzk=
12
12
  data.tar.gz: !binary |-
13
- NzczZTJmMWUzYWUyODM1OGQ1ZmE1ZDc1YzIyNjc4NDg2YjlkZWY1YTEzMGZm
14
- YTY5ZDgwYjBkYTc2N2U1YTkwNWI3ZjZkNjFjMzVjOTEzODExODI3MjUwZWYx
15
- YTgxNDYzOGYyMGUwYmIwYzkxNTNiMTI5MTZjYjQ3MmViNjZmMzg=
13
+ ZTdlZjc1NzlkZmMxYWY2Y2MyNzRhOTlhMTVmNGNhNDA5NTU2ZDIxZjIyOTU1
14
+ MmU1Yzc3YzdlYzEzNTc0MTY3NzdlNzhlMjlhY2Q1NGNiNjhmMDVhMGEzNDRi
15
+ ZDY1MTVjODRhYWUxMTY4MjFiZWM4YTBlNmQ2MjM3MjY1Njg3ZGY=
data/README.markdown CHANGED
@@ -59,6 +59,55 @@ Here are some handy class and instance methods available to you:
59
59
  purgatory.pending? # Returns true if the purgatory is pending, false otherwise
60
60
  purgatory.approved? # Returns true if the purgatory has been approved, false otherwise
61
61
 
62
+ ### ActiveRecord Lifecycle
63
+
64
+ When using purgatory, the timing of the activerecord lifecycle differs a bit. Normally when you run "ActiveRecord#save", the entire lifecycle of activerecord gets triggered by default. But when you replace "save" with "purgatory!", the callbacks woould only run until after_validation. It's only after you run "approve!" when the rest of callbacks (ie. before_save, after_create) gets triggered as well.
65
+
66
+ ## Normal lifecycle
67
+
68
+ before_validation
69
+ after_validation
70
+ before_save
71
+ around_save
72
+ before_create
73
+ around_create
74
+ after_create
75
+ after_save
76
+
77
+ ## Purgatory lifecycle
78
+
79
+ # purgatory!
80
+ before_validation
81
+ after_validation
82
+
83
+ # approve!
84
+ before_validation
85
+ after_validation
86
+ before_save
87
+ around_save
88
+ before_create
89
+ around_create
90
+ after_create
91
+ after_save
92
+
93
+
94
+ ### Handling Virtual Attributes
95
+
96
+ Virtual attributes allow users store variables into active record objects temporarily without saving them into the database (usually created using attr_accessor). Because these attributes are sometimes being used even on later stages of activerecord lifecycle (i.e after_create, after_save), it's important that these virtual attributes continue to exist while using purgatory. If you're using purgatory, by default, these virtual attributes would only exist on "purgatory!" phase, and would get lost on the "approve!" phase. To make sure this doesn't happen, there are 2 ways of handling it.
97
+
98
+ 1. Manual
99
+
100
+ use_purgatory :local_attributes => [:foo, :bar]
101
+
102
+ When you pass a hash that contains a list of virtual attributes that you want to save, purgatory will save the values of these attributes into the purgatory table during the "purgatory!" phase, so that when "approve!" is later called, the virtual attributes will be retrieved and will continue to be available in the remaining activerecord lifecyle.
103
+
104
+ 2. Automatic
105
+
106
+ use_purgatory :local_attributes => :all
107
+
108
+ By specifying all, Purgatory will programmatically determine what the virtual attributes are and save them when "purgatory!" is called, so that they will be available during "approve!".
109
+
110
+
62
111
  ## Contributing to Purgatory
63
112
 
64
113
  * Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.7.0
1
+ 2.8.0
@@ -7,6 +7,7 @@ class CreatePurgatories < ActiveRecord::Migration
7
7
  t.integer :approver_id
8
8
  t.datetime :approved_at
9
9
  t.text :requested_changes
10
+ t.string :attr_accessor_fields
10
11
 
11
12
  t.timestamps
12
13
  end
@@ -0,0 +1,21 @@
1
+ module ActiveRecordDescendantAttributeAccessors
2
+
3
+ def self.attr_accessor_instance_variables(obj)
4
+ include_ancestor_methods = false
5
+
6
+ ancestors_before_active_record_base = obj.class.ancestors.take_while { |klass| klass != ActiveRecord::Base }
7
+
8
+ instance_methods_of_ancestors_before_active_record_base = ancestors_before_active_record_base
9
+ .map { |klass| klass.instance_methods(include_ancestor_methods) }
10
+ .flatten
11
+
12
+ setter_methods_of_ancestors_before_active_record_base = instance_methods_of_ancestors_before_active_record_base
13
+ .select { |meth| meth.to_s.last == '=' }
14
+
15
+ possible_instance_variables_from_setter_methods = setter_methods_of_ancestors_before_active_record_base
16
+ .map { |meth| meth.to_s.prepend('@').chop.to_sym }
17
+
18
+ obj.instance_variables & possible_instance_variables_from_setter_methods
19
+ end
20
+
21
+ end
@@ -0,0 +1,24 @@
1
+ require 'active_support/core_ext/module/attribute_accessors.rb'
2
+
3
+ require 'purgatory/active_record_descendant_attribute_accessors'
4
+
5
+ module AttributeAccessorFields
6
+
7
+ mattr_accessor :local_attributes
8
+
9
+ def self.determine_attr_accessor_fields(obj)
10
+ variables = if @@local_attributes == :all
11
+ ActiveRecordDescendantAttributeAccessors.attr_accessor_instance_variables(obj)
12
+ else
13
+ Array(@@local_attributes).map { |attribute|
14
+ attribute.to_s.prepend('@').to_sym
15
+ }
16
+ end
17
+
18
+ variables.inject({}) do |hash,var|
19
+ hash[var] = obj.instance_variable_get(var)
20
+ hash
21
+ end
22
+ end
23
+
24
+ end
@@ -10,6 +10,7 @@ class Purgatory < ActiveRecord::Base
10
10
  validates :soul_type, presence: true
11
11
 
12
12
  serialize :requested_changes
13
+ serialize :attr_accessor_fields
13
14
 
14
15
  def self.pending
15
16
  where(approved_at: nil)
@@ -38,6 +39,7 @@ class Purgatory < ActiveRecord::Base
38
39
  def approve!(approver = nil)
39
40
  return false if approved?
40
41
  requested_changes.each{|k,v| soul.send(:write_attribute, k, v[1])}
42
+ attr_accessor_fields.each{|k,v| soul.instance_variable_set(k, v)}
41
43
  if soul.save
42
44
  self.approver = approver
43
45
  self.approved_at = Time.now
@@ -1,8 +1,11 @@
1
+ require 'purgatory/attribute_accessor_fields'
2
+
1
3
  module PurgatoryModule
2
4
  extend ActiveSupport::Concern
3
5
 
4
6
  module ClassMethods
5
- def use_purgatory
7
+ def use_purgatory(options={})
8
+ AttributeAccessorFields.local_attributes = options[:local_attributes]
6
9
  self.has_many :purgatories, as: :soul
7
10
  end
8
11
  end
@@ -10,7 +13,7 @@ module PurgatoryModule
10
13
  def purgatory!(requester = nil, options = {})
11
14
  return nil if self.invalid?
12
15
  return nil if Purgatory.pending_with_matching_soul(self).any? && options[:fail_if_matching_soul]
13
- Purgatory.create soul: self, requester: requester
16
+ Purgatory.create soul: self, requester: requester, attr_accessor_fields: AttributeAccessorFields.determine_attr_accessor_fields(self)
14
17
  end
15
18
 
16
19
  class Configuration
@@ -27,4 +30,5 @@ module PurgatoryModule
27
30
  @_configuration ||= Configuration.new
28
31
  end
29
32
  end
33
+
30
34
  end
data/purgatory.gemspec CHANGED
@@ -2,15 +2,15 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: purgatory 2.7.0 ruby lib
5
+ # stub: purgatory 2.8.0 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "purgatory"
9
- s.version = "2.7.0"
9
+ s.version = "2.8.0"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.authors = ["Elan Dubrofsky"]
13
- s.date = "2013-11-27"
13
+ s.date = "2013-11-28"
14
14
  s.description = "Put your model changes in purgatory and allow them to remain lost souls until they are approved"
15
15
  s.email = "elan.dubrofsky@gmail.com"
16
16
  s.extra_rdoc_files = [
@@ -29,12 +29,15 @@ Gem::Specification.new do |s|
29
29
  "lib/generators/purgatory/purgatory_generator.rb",
30
30
  "lib/generators/purgatory/templates/create_purgatories.rb",
31
31
  "lib/purgatory.rb",
32
+ "lib/purgatory/active_record_descendant_attribute_accessors.rb",
33
+ "lib/purgatory/attribute_accessor_fields.rb",
32
34
  "lib/purgatory/purgatory.rb",
33
35
  "lib/purgatory/purgatory_module.rb",
34
36
  "purgatory.gemspec",
35
37
  "spec/purgatory_spec.rb",
36
38
  "spec/support/active_record.rb",
37
39
  "spec/support/animal.rb",
40
+ "spec/support/item.rb",
38
41
  "spec/support/user.rb",
39
42
  "spec/support/widget.rb"
40
43
  ]
@@ -2,6 +2,7 @@ require 'support/active_record'
2
2
  require 'support/widget'
3
3
  require 'support/user'
4
4
  require 'support/animal'
5
+ require 'support/item'
5
6
  require 'purgatory/purgatory'
6
7
 
7
8
  describe Purgatory do
@@ -68,6 +69,26 @@ describe Purgatory do
68
69
  Purgatory.count.should == 2
69
70
  end
70
71
  end
72
+
73
+ context "valid changes with attr_accessor" do
74
+ before do
75
+ create_object_change_purgatory_with_attr_accessor
76
+ @item = Item.find(@item.id)
77
+ end
78
+
79
+ it "should not change the object" do
80
+ @item.name.should == 'foo'
81
+ @item.price.should == 100
82
+ end
83
+
84
+ it "should not save attr_accessor variable of object" do
85
+ @item.dante.should == nil
86
+ end
87
+
88
+ it "should store the attr_accessor variables in the Purgatory object" do
89
+ @purgatory.attr_accessor_fields.should == { :@dante => "inferno" }
90
+ end
91
+ end
71
92
 
72
93
  it "should not allow invalid changes to be put into purgatory" do
73
94
  widget = Widget.create name: 'foo'
@@ -140,6 +161,24 @@ describe Purgatory do
140
161
  end
141
162
  end
142
163
 
164
+ context "valid object with attr_accessor" do
165
+ before do
166
+ create_new_object_purgatory_with_attr_accessor
167
+ end
168
+
169
+ it "should store the attr_accessor variables in the Purgatory object" do
170
+ @purgatory.attr_accessor_fields.should == { :@dante => "inferno" }
171
+ end
172
+
173
+ it "should store the requester and requested changes" do
174
+ @purgatory.requester.should == user1
175
+ @purgatory.requested_changes['name'].first.should == nil
176
+ @purgatory.requested_changes['name'].last.should == 'foo'
177
+ @purgatory.requested_changes['price'].first.should == nil
178
+ @purgatory.requested_changes['price'].last.should == 100
179
+ end
180
+ end
181
+
143
182
  it "should not allow invalid object creation to be put into purgatory" do
144
183
  widget = Widget.new name: ''
145
184
  widget.purgatory!(user1).should be_nil
@@ -175,6 +214,36 @@ describe Purgatory do
175
214
  @purgatory.approve!(user2).should be_false
176
215
  end
177
216
  end
217
+
218
+ context "approving object change purgatory with attr_accessor" do
219
+ before do
220
+ create_object_change_purgatory_with_attr_accessor
221
+ @purgatory.approve!(user2)
222
+ @item = Item.find(@item.id)
223
+ end
224
+
225
+ it "should apply the changes" do
226
+ @item.name.should == 'bar'
227
+ @item.price.should == 200
228
+ end
229
+
230
+ it "should apply changes that depend on attr_accessor instance_variable" do
231
+ @item.original_name.should == "inferno"
232
+ end
233
+
234
+ it "should mark purgatory as approved and store approver" do
235
+ @purgatory.approver.should == user2
236
+ @purgatory.should be_approved
237
+ @purgatory.should_not be_pending
238
+ Purgatory.pending.count.should be_zero
239
+ Purgatory.approved.count.should == 1
240
+ Purgatory.approved.first.should == @purgatory
241
+ end
242
+
243
+ it "should fail if you try to approve again" do
244
+ @purgatory.approve!(user2).should be_false
245
+ end
246
+ end
178
247
 
179
248
  context "approving new object creation" do
180
249
  before do
@@ -217,7 +286,25 @@ describe Purgatory do
217
286
  dog.original_name.should == 'doggy'
218
287
  dog.price.should == Dog::DEFAULT_PRICE
219
288
  end
289
+ end
290
+
291
+ context "approving new object creation with attr_accessor" do
292
+ before do
293
+ create_new_object_purgatory_with_attr_accessor
294
+ @purgatory.approve!(user2)
295
+ end
220
296
 
297
+ it "should create the new object and apply any callbacks" do
298
+ Item.count.should == 1
299
+ item = Item.first
300
+ item.name.should == 'foo'
301
+ item.price.should == 100
302
+ end
303
+
304
+ it "should apply changes that depend on attr_accessor instance_variable" do
305
+ Item.first.original_name.should == 'inferno'
306
+ end
307
+
221
308
  it "should mark purgatory as approved and store approver" do
222
309
  @purgatory.approver.should == user2
223
310
  @purgatory.should be_approved
@@ -232,6 +319,161 @@ describe Purgatory do
232
319
  end
233
320
  end
234
321
  end
322
+
323
+ describe "determine_attr_accessor_fields" do
324
+ before do
325
+ AttributeAccessorFields.local_attributes = nil
326
+ end
327
+
328
+ after do
329
+ AttributeAccessorFields.local_attributes = nil
330
+ end
331
+
332
+ context "obj has no attr_accessors" do
333
+ before do
334
+ @obj = Widget.new
335
+ end
336
+
337
+ it "should not contain any thing" do
338
+ AttributeAccessorFields.determine_attr_accessor_fields(@obj).should == {}
339
+ end
340
+ end
341
+
342
+ context "obj has attr_accessors" do
343
+ before do
344
+ klass = create_subclass_of(Widget)
345
+ klass.instance_eval { attr_accessor :dante, :minos, :charon }
346
+
347
+ @obj = klass.new
348
+
349
+ @obj.dante = "inferno"
350
+ @obj.minos = "inferno"
351
+ @obj.charon = "inferno"
352
+ end
353
+
354
+ context "local_attributes is empty" do
355
+ it "should not contain any attr_accessor values" do
356
+ AttributeAccessorFields.determine_attr_accessor_fields(@obj).should == {}
357
+ end
358
+ end
359
+
360
+ context "local_attributes is array" do
361
+ context "array size is 1" do
362
+ before do
363
+ AttributeAccessorFields.local_attributes = [:dante]
364
+ end
365
+
366
+ it "should only contain attr_accessors specified in array" do
367
+ AttributeAccessorFields.determine_attr_accessor_fields(@obj).should == { :@dante => "inferno" }
368
+ end
369
+ end
370
+ context "array size is more than 1" do
371
+ before do
372
+ AttributeAccessorFields.local_attributes = [:dante, :minos]
373
+ end
374
+
375
+ it "should only contain attr_accessors specified in array" do
376
+ AttributeAccessorFields.determine_attr_accessor_fields(@obj).should == { :@dante => "inferno", :@minos => "inferno" }
377
+ end
378
+
379
+ end
380
+ end
381
+
382
+ context "value of local_variables is :all" do
383
+ before do
384
+ AttributeAccessorFields.local_attributes = :all
385
+ end
386
+
387
+ it "should automatically determine attr_accessor values that doesnt include ones belonging to AR::Base and its ancestors, and then store these values" do
388
+ AttributeAccessorFields.determine_attr_accessor_fields(@obj).should == { :@dante => "inferno", :@minos => "inferno", :@charon => "inferno" }
389
+ end
390
+ end
391
+ end
392
+ end
393
+
394
+ describe "#attr_accessor_instance_variables" do
395
+ context "attr_accessor defined in a module/class that is an ancestor of ActiveRecord::Base" do
396
+ before do
397
+ @active_record_ancestor = ActiveRecord::Base.ancestors[1]
398
+ @active_record_ancestor.instance_eval { attr_accessor :dante }
399
+ @widget = Widget.new name: 'foo', price: 100
400
+ @widget.dante = "inferno"
401
+ end
402
+
403
+ after do
404
+ @active_record_ancestor.class_eval { undef :dante }
405
+ @active_record_ancestor.class_eval { undef :dante= }
406
+ end
407
+
408
+ it "should not include instance_variables that belong to ancestor of ActiveRecord::Base" do
409
+ ActiveRecordDescendantAttributeAccessors.attr_accessor_instance_variables(@widget).should == []
410
+ end
411
+ end
412
+
413
+ context "attr_accessor defined in ActiveRecord::Base" do
414
+ before do
415
+ ActiveRecord::Base.instance_eval { attr_accessor :dante }
416
+ @widget = Widget.new name: 'foo', price: 100
417
+ @widget.dante = "inferno"
418
+ end
419
+
420
+ after do
421
+ ActiveRecord::Base.class_eval { undef :dante }
422
+ ActiveRecord::Base.class_eval { undef :dante= }
423
+ end
424
+
425
+ it "should not include instance_variables that belong to ActiveRecord::Base" do
426
+ ActiveRecordDescendantAttributeAccessors.attr_accessor_instance_variables(@widget).should == []
427
+ end
428
+ end
429
+
430
+ context "attr_accessor defined in a module/class that is not an ancestor of ActiveRecord::Base" do
431
+ context "attr_accessor defined in a module mixin" do
432
+ before do
433
+ klass = create_subclass_of(Widget)
434
+ module A; attr_accessor :dante; end
435
+ klass.instance_eval { include A }
436
+
437
+ @widget = klass.new name: 'foo', price: 100
438
+ @widget.dante = "inferno"
439
+ end
440
+
441
+ it "should include instance_variables from attr_accessors that belong to descendant of ActiveRecord::Base" do
442
+ ActiveRecordDescendantAttributeAccessors.attr_accessor_instance_variables(@widget).should == [:@dante]
443
+ end
444
+ end
445
+
446
+ context "attr_accessor defined in a superclass" do
447
+ before do
448
+ klass = create_subclass_of(Widget)
449
+ subklass = create_subclass_of(klass)
450
+
451
+ klass.instance_eval { attr_accessor :dante }
452
+
453
+ @widget = subklass.new name: 'foo', price: 100
454
+ @widget.dante = "inferno"
455
+ end
456
+
457
+ it "should include instance_variables from attr_accessors that belong to descendant of ActiveRecord::Base" do
458
+ ActiveRecordDescendantAttributeAccessors.attr_accessor_instance_variables(@widget).should == [:@dante]
459
+ end
460
+ end
461
+
462
+ context "attr_accessor defined in a class" do
463
+ before do
464
+ klass = create_subclass_of(Widget)
465
+ klass.instance_eval { attr_accessor :dante }
466
+
467
+ @widget = klass.new name: 'foo', price: 100
468
+ @widget.dante = "inferno"
469
+ end
470
+
471
+ it "should include instance_variables from attr_accessors that belong to descendant of ActiveRecord::Base" do
472
+ ActiveRecordDescendantAttributeAccessors.attr_accessor_instance_variables(@widget).should == [:@dante]
473
+ end
474
+ end
475
+ end
476
+ end
235
477
 
236
478
  private
237
479
 
@@ -243,7 +485,7 @@ describe Purgatory do
243
485
  @purgatory = Purgatory.find(purgatory.id)
244
486
  @widget.reload
245
487
  end
246
-
488
+
247
489
  def create_new_object_purgatory
248
490
  widget = Widget.new name: 'foo', price: 100
249
491
  purgatory = widget.purgatory! user1
@@ -256,4 +498,23 @@ describe Purgatory do
256
498
  @purgatory = Purgatory.find(purgatory.id)
257
499
  end
258
500
 
501
+ def create_object_change_purgatory_with_attr_accessor
502
+ @item = Item.create name: 'foo', price: 100, dante: "classic"
503
+ @item.name = 'bar'
504
+ @item.price = 200
505
+ @item.dante = "inferno"
506
+ purgatory = @item.purgatory! user1
507
+ @purgatory = Purgatory.find(purgatory.id)
508
+ @item.reload
509
+ end
510
+
511
+ def create_new_object_purgatory_with_attr_accessor
512
+ item = Item.new name: 'foo', price: 100, dante: "inferno"
513
+ purgatory = item.purgatory! user1
514
+ @purgatory = Purgatory.find(purgatory.id)
515
+ end
516
+
517
+ def create_subclass_of(klass)
518
+ Class.new(klass)
519
+ end
259
520
  end
@@ -21,6 +21,12 @@ ActiveRecord::Migration.create_table :animals do |t|
21
21
  t.string :type
22
22
  t.integer :price
23
23
  t.string :original_name
24
+ end
25
+
26
+ ActiveRecord::Migration.create_table :items do |t|
27
+ t.string :name
28
+ t.integer :price
29
+ t.string :original_name
24
30
  t.timestamps
25
31
  end
26
32
 
@@ -0,0 +1,17 @@
1
+ require 'purgatory'
2
+
3
+ class Item < ActiveRecord::Base
4
+ use_purgatory local_attributes: [:dante]
5
+
6
+ validates :name, presence: true
7
+
8
+ attr_accessor :dante
9
+
10
+ after_save :set_original_name
11
+
12
+ private
13
+
14
+ def set_original_name
15
+ update_column(:original_name, @dante)
16
+ end
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: purgatory
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.7.0
4
+ version: 2.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elan Dubrofsky
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-27 00:00:00.000000000 Z
11
+ date: 2013-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rdoc
@@ -72,12 +72,15 @@ files:
72
72
  - lib/generators/purgatory/purgatory_generator.rb
73
73
  - lib/generators/purgatory/templates/create_purgatories.rb
74
74
  - lib/purgatory.rb
75
+ - lib/purgatory/active_record_descendant_attribute_accessors.rb
76
+ - lib/purgatory/attribute_accessor_fields.rb
75
77
  - lib/purgatory/purgatory.rb
76
78
  - lib/purgatory/purgatory_module.rb
77
79
  - purgatory.gemspec
78
80
  - spec/purgatory_spec.rb
79
81
  - spec/support/active_record.rb
80
82
  - spec/support/animal.rb
83
+ - spec/support/item.rb
81
84
  - spec/support/user.rb
82
85
  - spec/support/widget.rb
83
86
  homepage: http://github.com/financeit/purgatory