set_vestal_versions 1.2.2

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.
Files changed (40) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +196 -0
  3. data/lib/generators/vestal_versions.rb +11 -0
  4. data/lib/generators/vestal_versions/migration/migration_generator.rb +17 -0
  5. data/lib/generators/vestal_versions/migration/templates/initializer.rb +9 -0
  6. data/lib/generators/vestal_versions/migration/templates/migration.rb +28 -0
  7. data/lib/vestal_versions.rb +126 -0
  8. data/lib/vestal_versions/changes.rb +122 -0
  9. data/lib/vestal_versions/conditions.rb +57 -0
  10. data/lib/vestal_versions/control.rb +200 -0
  11. data/lib/vestal_versions/creation.rb +93 -0
  12. data/lib/vestal_versions/deletion.rb +39 -0
  13. data/lib/vestal_versions/options.rb +41 -0
  14. data/lib/vestal_versions/reload.rb +17 -0
  15. data/lib/vestal_versions/reset.rb +24 -0
  16. data/lib/vestal_versions/reversion.rb +82 -0
  17. data/lib/vestal_versions/users.rb +55 -0
  18. data/lib/vestal_versions/version.rb +80 -0
  19. data/lib/vestal_versions/version_num.rb +3 -0
  20. data/lib/vestal_versions/version_tagging.rb +50 -0
  21. data/lib/vestal_versions/versioned.rb +27 -0
  22. data/lib/vestal_versions/versions.rb +74 -0
  23. data/spec/spec_helper.rb +20 -0
  24. data/spec/support/models.rb +19 -0
  25. data/spec/support/schema.rb +25 -0
  26. data/spec/vestal_versions/changes_spec.rb +134 -0
  27. data/spec/vestal_versions/conditions_spec.rb +103 -0
  28. data/spec/vestal_versions/control_spec.rb +120 -0
  29. data/spec/vestal_versions/creation_spec.rb +90 -0
  30. data/spec/vestal_versions/deletion_spec.rb +86 -0
  31. data/spec/vestal_versions/options_spec.rb +45 -0
  32. data/spec/vestal_versions/reload_spec.rb +18 -0
  33. data/spec/vestal_versions/reset_spec.rb +111 -0
  34. data/spec/vestal_versions/reversion_spec.rb +103 -0
  35. data/spec/vestal_versions/users_spec.rb +21 -0
  36. data/spec/vestal_versions/version_spec.rb +61 -0
  37. data/spec/vestal_versions/version_tagging_spec.rb +39 -0
  38. data/spec/vestal_versions/versioned_spec.rb +16 -0
  39. data/spec/vestal_versions/versions_spec.rb +176 -0
  40. metadata +165 -0
@@ -0,0 +1,25 @@
1
+ ActiveRecord::Base.establish_connection(
2
+ :adapter => 'sqlite3',
3
+ :database => File.expand_path('../../test.db', __FILE__)
4
+ )
5
+
6
+ class CreateSchema < ActiveRecord::Migration
7
+ def self.up
8
+ create_table :users, :force => true do |t|
9
+ t.string :first_name
10
+ t.string :last_name
11
+ t.timestamps
12
+ end
13
+
14
+ create_table :versions, :force => true do |t|
15
+ t.belongs_to :versioned, :polymorphic => true
16
+ t.belongs_to :user, :polymorphic => true
17
+ t.string :user_name
18
+ t.text :modifications
19
+ t.integer :number
20
+ t.integer :reverted_from
21
+ t.string :tag
22
+ t.timestamps
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,134 @@
1
+ require 'spec_helper'
2
+
3
+ describe VestalVersions::Changes do
4
+ context "a version's changes" do
5
+ let(:user){ User.create(:name => 'Steve Richert') }
6
+ subject{ user.versions.last.changes }
7
+
8
+ before do
9
+ user.update_attribute(:last_name, 'Jobs')
10
+ end
11
+
12
+ it { should be_a(Hash) }
13
+ it { should_not be_empty }
14
+
15
+ it 'has string keys' do
16
+ subject.keys.each{ |key| key.should be_a(String) }
17
+ end
18
+
19
+ it 'has two-element array values' do
20
+ subject.values.each do |key|
21
+ key.should be_a(Array)
22
+ key.size.should == 2
23
+ end
24
+ end
25
+
26
+ it 'has unique-element values' do
27
+ subject.values.each{ |v| v.uniq.should == v }
28
+ end
29
+
30
+ it "equals the model's changes" do
31
+ user.first_name = 'Stephen'
32
+ model_changes = user.changes
33
+ user.save
34
+ changes = user.versions.last.changes
35
+
36
+ model_changes.should == changes
37
+ end
38
+ end
39
+
40
+ context 'a hash of changes' do
41
+ let(:changes){ {'first_name' => ['Steve', 'Stephen']} }
42
+ let(:other){ {'first_name' => ['Catie', 'Catherine']} }
43
+
44
+ it 'properly appends other changes' do
45
+ expected = {'first_name' => ['Steve', 'Catherine']}
46
+
47
+ changes.append_changes(other).should == expected
48
+
49
+ changes.append_changes!(other)
50
+ changes.should == expected
51
+ end
52
+
53
+ it 'properly prepends other changes' do
54
+ expected = {'first_name' => ['Catie', 'Stephen']}
55
+
56
+ changes.prepend_changes(other).should == expected
57
+
58
+ changes.prepend_changes!(other)
59
+ changes.should == expected
60
+ end
61
+
62
+ it 'is reversible' do
63
+ expected = {'first_name' => ['Stephen', 'Steve']}
64
+
65
+ changes.reverse_changes.should == expected
66
+
67
+ changes.reverse_changes!
68
+ changes.should == expected
69
+ end
70
+ end
71
+
72
+ context 'the changes between two versions' do
73
+ let(:name){ 'Steve Richert' }
74
+ let(:user){ User.create(:name => name) } # 1
75
+ let(:version){ user.version }
76
+
77
+ before do
78
+ user.update_attribute(:last_name, 'Jobs') # 2
79
+ user.update_attribute(:first_name, 'Stephen') # 3
80
+ user.update_attribute(:last_name, 'Richert') # 4
81
+ user.update_attribute(:name, name) # 5
82
+ end
83
+
84
+ it 'is a hash' do
85
+ 1.upto(version) do |i|
86
+ 1.upto(version) do |j|
87
+ user.changes_between(i, j).should be_a(Hash)
88
+ end
89
+ end
90
+ end
91
+
92
+ it 'has string keys' do
93
+ 1.upto(version) do |i|
94
+ 1.upto(version) do |j|
95
+ user.changes_between(i, j).keys.each{ |key| key.should be_a(String) }
96
+ end
97
+ end
98
+ end
99
+
100
+ it 'has two-element arrays with unique values' do
101
+ 1.upto(version) do |i|
102
+ 1.upto(version) do |j|
103
+ user.changes_between(i, j).values.each do |value|
104
+ value.should be_a(Array)
105
+ value.size.should == 2
106
+ value.uniq.should == value
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ it 'is empty between identical versions' do
113
+ user.changes_between(1, version).should be_empty
114
+ user.changes_between(version, 1).should be_empty
115
+ end
116
+
117
+ it 'is should reverse with direction' do
118
+ 1.upto(version) do |i|
119
+ i.upto(version) do |j|
120
+ up = user.changes_between(i, j)
121
+ down = user.changes_between(j, i)
122
+ up.should == down.reverse_changes
123
+ end
124
+ end
125
+ end
126
+
127
+ it 'is empty with invalid arguments' do
128
+ 1.upto(version) do |i|
129
+ user.changes_between(i, nil).should be_blank
130
+ user.changes_between(nil, i).should be_blank
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ describe VestalVersions::Conditions do
4
+ shared_examples_for 'a conditional option' do |option|
5
+ before do
6
+ User.class_eval do
7
+ def true; true; end
8
+ end
9
+ end
10
+
11
+ it 'is an array' do
12
+ User.vestal_versions_options[option].should be_a(Array)
13
+ User.prepare_versioned_options(option => :true)
14
+ User.vestal_versions_options[option].should be_a(Array)
15
+ end
16
+
17
+ it 'has proc values' do
18
+ User.prepare_versioned_options(option => :true)
19
+ User.vestal_versions_options[option].each{|i| i.should be_a(Proc) }
20
+ end
21
+ end
22
+
23
+ it_should_behave_like 'a conditional option', :if
24
+ it_should_behave_like 'a conditional option', :unless
25
+
26
+ context 'a new version' do
27
+ subject{ User.create(:name => 'Steve Richert') }
28
+ let(:count){ subject.versions.count }
29
+
30
+ before do
31
+ User.class_eval do
32
+ def true; true; end
33
+ def false; false; end
34
+ end
35
+ count # memoize this value
36
+ end
37
+
38
+ after do
39
+ User.prepare_versioned_options(:if => [], :unless => [])
40
+ end
41
+
42
+ context 'with :if conditions' do
43
+ context 'that pass' do
44
+ before do
45
+ User.prepare_versioned_options(:if => [:true])
46
+ subject.update_attribute(:last_name, 'Jobs')
47
+ end
48
+
49
+ its('versions.count'){ should == count + 1 }
50
+ end
51
+
52
+ context 'that fail' do
53
+ before do
54
+ User.prepare_versioned_options(:if => [:false])
55
+ subject.update_attribute(:last_name, 'Jobs')
56
+ end
57
+
58
+ its('versions.count'){ should == count }
59
+ end
60
+ end
61
+
62
+ context 'with :unless conditions' do
63
+ context 'that pass' do
64
+ before do
65
+ User.prepare_versioned_options(:unless => [:true])
66
+ subject.update_attribute(:last_name, 'Jobs')
67
+ end
68
+
69
+ its('versions.count'){ should == count }
70
+ end
71
+
72
+ context 'that fail' do
73
+ before do
74
+ User.prepare_versioned_options(:unless => [:false])
75
+ subject.update_attribute(:last_name, 'Jobs')
76
+ end
77
+
78
+ its('versions.count'){ should == count + 1 }
79
+ end
80
+ end
81
+
82
+ context 'with :if and :unless conditions' do
83
+ context 'that pass' do
84
+ before do
85
+ User.prepare_versioned_options(:if => [:true], :unless => [:true])
86
+ subject.update_attribute(:last_name, 'Jobs')
87
+ end
88
+
89
+ its('versions.count'){ should == count }
90
+ end
91
+
92
+ context 'that fail' do
93
+ before do
94
+ User.prepare_versioned_options(:if => [:false], :unless => [:false])
95
+ subject.update_attribute(:last_name, 'Jobs')
96
+ end
97
+
98
+ its('versions.count'){ should == count }
99
+ end
100
+ end
101
+
102
+ end
103
+ end
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ describe VestalVersions::Control do
4
+ let(:user){ User.create(:name => 'Steve Richert') }
5
+ let(:other_user){ User.create(:name => 'Michael Rossin') }
6
+ before do
7
+ @count = user.versions.count
8
+ end
9
+
10
+ shared_examples_for 'a version preserver' do |method|
11
+ it 'creates one version with a model update' do
12
+ user.send(method){ user.update_attribute(:last_name, 'Jobs') }
13
+
14
+ user.versions.count.should == @count
15
+ end
16
+
17
+ it 'creates one version with multiple model updates' do
18
+ user.send(method) do
19
+ user.update_attribute(:first_name, 'Stephen')
20
+ user.update_attribute(:last_name, 'Jobs')
21
+ user.update_attribute(:first_name, 'Steve')
22
+ end
23
+
24
+ user.versions.count.should == @count
25
+ end
26
+
27
+ end
28
+
29
+ shared_examples_for 'a version incrementer' do |method|
30
+ it 'creates one version with a model update' do
31
+ user.send(method){ user.update_attribute(:last_name, 'Jobs') }
32
+
33
+ user.versions.count.should == @count + 1
34
+ end
35
+
36
+ it 'creates one version with multiple model updates' do
37
+ user.send(method) do
38
+ user.update_attribute(:first_name, 'Stephen')
39
+ user.update_attribute(:last_name, 'Jobs')
40
+ user.update_attribute(:first_name, 'Steve')
41
+ end
42
+
43
+ user.versions.count.should == @count + 1
44
+ end
45
+
46
+ end
47
+
48
+ it_should_behave_like 'a version preserver', :skip_version
49
+ it_should_behave_like 'a version incrementer', :merge_version
50
+
51
+ context "when operating on the class level" do
52
+ before do
53
+ @count = user.versions.count
54
+ @other_user_count = other_user.versions.count
55
+ end
56
+ it 'skip_version doesn\' create versions on multiple models' do
57
+ other_user_count = other_user.versions.count
58
+
59
+ User.skip_version do
60
+ user.update_attribute(:first_name, 'Stephen')
61
+ user.update_attribute(:last_name, 'Jobs')
62
+ user.update_attribute(:first_name, 'Steve')
63
+
64
+ other_user.update_attribute(:first_name, 'Stephen')
65
+ other_user.update_attribute(:last_name, 'Jobs')
66
+ other_user.update_attribute(:first_name, 'Steve')
67
+ end
68
+ user.versions.count.should == @count
69
+ other_user.versions.count.should == @other_user_count
70
+ end
71
+
72
+ end
73
+
74
+ context 'within a append_version block' do
75
+
76
+ context 'when no versions exist' do
77
+ it_should_behave_like 'a version incrementer', :append_version
78
+ end
79
+
80
+ context 'when versions exist' do
81
+ let(:last_version){ user.versions.last }
82
+
83
+ before do
84
+ user.update_attribute(:last_name, 'Jobs')
85
+ user.update_attribute(:last_name, 'Richert')
86
+
87
+ @count = user.versions.count
88
+ end
89
+
90
+ it_should_behave_like 'a version preserver', :append_version
91
+
92
+ it "updates the last version with one update" do
93
+ original_id = last_version.id
94
+ original_attrs = last_version.attributes
95
+
96
+ user.append_version{ user.update_attribute(:last_name, 'Jobs') }
97
+
98
+ other_last_version = user.versions(true).last
99
+ other_last_version.id.should == original_id
100
+ other_last_version.attributes.should_not == original_attrs
101
+ end
102
+
103
+ it "updates the last version with multiple updates" do
104
+ original_id = last_version.id
105
+ original_attrs = last_version.attributes
106
+
107
+ user.append_version do
108
+ user.update_attribute(:first_name, 'Stephen')
109
+ user.update_attribute(:last_name, 'Jobs')
110
+ user.update_attribute(:first_name, 'Steve')
111
+ end
112
+
113
+ other_last_version = user.versions(true).last
114
+ other_last_version.id.should == original_id
115
+ other_last_version.attributes.should_not == original_attrs
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,90 @@
1
+ require 'spec_helper'
2
+
3
+ describe VestalVersions::Creation do
4
+ let(:name){ 'Steve Richert' }
5
+ subject{ User.create(:name => name) }
6
+
7
+ context 'the number of versions' do
8
+
9
+ its('versions.count'){ should == 0 }
10
+
11
+ context 'with :initial_version option' do
12
+ before do
13
+ User.prepare_versioned_options(:initial_version => true)
14
+ end
15
+
16
+ its('versions.count'){ should == 1 }
17
+ end
18
+
19
+ it 'does not increase when no changes are made in an update' do
20
+ expect {
21
+ subject.update_attribute(:name, name)
22
+ }.to change{ subject.versions.count }.by(0)
23
+ end
24
+
25
+ it 'does not increase when no changes are made before a save' do
26
+ expect{ subject.save }.to change{ subject.versions.count }.by(0)
27
+ end
28
+
29
+ it 'increases by one after an update' do
30
+ expect{
31
+ subject.update_attribute(:last_name, 'Jobs')
32
+ }.to change{ subject.versions.count }.by(1)
33
+ end
34
+
35
+ it 'increases multiple times after multiple updates' do
36
+ expect{
37
+ subject.update_attribute(:last_name, 'Jobs')
38
+ subject.update_attribute(:first_name, 'Brian')
39
+ }.to change{ subject.versions.count }.by(2)
40
+ end
41
+
42
+ end
43
+
44
+ context "a created version's changes" do
45
+ before do
46
+ subject.update_attribute(:last_name, 'Jobs')
47
+ end
48
+
49
+ it 'does not contain Rails timestamps' do
50
+ %w(created_at created_on updated_at updated_on).each do |timestamp|
51
+ subject.versions.last.changes.keys.should_not include(timestamp)
52
+ end
53
+ end
54
+
55
+ it 'allows the columns tracked to be restricted via :only' do
56
+ User.prepare_versioned_options(:only => [:first_name])
57
+ subject.update_attribute(:name, 'Steven Tyler')
58
+
59
+ subject.versions.last.changes.keys.should == ['first_name']
60
+ end
61
+
62
+ it 'allows specific columns to be excluded via :except' do
63
+ User.prepare_versioned_options(:except => [:first_name])
64
+ subject.update_attribute(:name, 'Steven Tyler')
65
+
66
+ subject.versions.last.changes.keys.should_not include('first_name')
67
+ end
68
+
69
+ it "prefers :only to :except" do
70
+ User.prepare_versioned_options(:only => [:first_name],
71
+ :except => [:first_name])
72
+ subject.update_attribute(:name, 'Steven Tyler')
73
+
74
+ subject.versions.last.changes.keys.should == ['first_name']
75
+ end
76
+ end
77
+
78
+ context 'first version' do
79
+ it 'is number 2 after an update' do
80
+ subject.update_attribute(:last_name, 'Jobs')
81
+ subject.versions.first.number.should == 2
82
+ end
83
+
84
+ it "is number 1 if :initial_version is true" do
85
+ User.prepare_versioned_options(:initial_version => true)
86
+ subject.versions.first.number.should == 1
87
+ end
88
+ end
89
+
90
+ end