geothird_vestal_versions 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +196 -0
- data/lib/generators/vestal_versions.rb +11 -0
- data/lib/generators/vestal_versions/migration/migration_generator.rb +17 -0
- data/lib/generators/vestal_versions/migration/templates/initializer.rb +9 -0
- data/lib/generators/vestal_versions/migration/templates/migration.rb +28 -0
- data/lib/vestal_versions.rb +126 -0
- data/lib/vestal_versions/changes.rb +120 -0
- data/lib/vestal_versions/conditions.rb +55 -0
- data/lib/vestal_versions/control.rb +197 -0
- data/lib/vestal_versions/creation.rb +91 -0
- data/lib/vestal_versions/deletion.rb +37 -0
- data/lib/vestal_versions/options.rb +41 -0
- data/lib/vestal_versions/reload.rb +15 -0
- data/lib/vestal_versions/reset.rb +22 -0
- data/lib/vestal_versions/reversion.rb +80 -0
- data/lib/vestal_versions/users.rb +53 -0
- data/lib/vestal_versions/version.rb +81 -0
- data/lib/vestal_versions/version_num.rb +3 -0
- data/lib/vestal_versions/version_tagging.rb +48 -0
- data/lib/vestal_versions/versioned.rb +27 -0
- data/lib/vestal_versions/versions.rb +74 -0
- data/spec/spec_helper.rb +20 -0
- data/spec/support/models.rb +19 -0
- data/spec/support/schema.rb +25 -0
- data/spec/vestal_versions/changes_spec.rb +134 -0
- data/spec/vestal_versions/conditions_spec.rb +103 -0
- data/spec/vestal_versions/control_spec.rb +120 -0
- data/spec/vestal_versions/creation_spec.rb +90 -0
- data/spec/vestal_versions/deletion_spec.rb +86 -0
- data/spec/vestal_versions/options_spec.rb +45 -0
- data/spec/vestal_versions/reload_spec.rb +18 -0
- data/spec/vestal_versions/reset_spec.rb +111 -0
- data/spec/vestal_versions/reversion_spec.rb +103 -0
- data/spec/vestal_versions/users_spec.rb +21 -0
- data/spec/vestal_versions/version_spec.rb +61 -0
- data/spec/vestal_versions/version_tagging_spec.rb +39 -0
- data/spec/vestal_versions/versioned_spec.rb +16 -0
- data/spec/vestal_versions/versions_spec.rb +176 -0
- metadata +169 -0
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe VestalVersions::Deletion do
|
4
|
+
let(:name){ 'Steve Richert' }
|
5
|
+
subject{ DeletedUser.create(:first_name => 'Steve', :last_name => 'Richert') }
|
6
|
+
|
7
|
+
context "a deleted version's changes" do
|
8
|
+
|
9
|
+
before do
|
10
|
+
subject.update_attribute(:last_name, 'Jobs')
|
11
|
+
end
|
12
|
+
|
13
|
+
it "removes the original record" do
|
14
|
+
subject.destroy
|
15
|
+
|
16
|
+
DeletedUser.find_by_id(subject.id).should be_nil
|
17
|
+
end
|
18
|
+
|
19
|
+
it "creates another version record" do
|
20
|
+
expect{ subject.destroy }.to change{ VestalVersions::Version.count }.by(1)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "creates a version with a tag 'deleted'" do
|
24
|
+
subject.destroy
|
25
|
+
VestalVersions::Version.last.tag.should == 'deleted'
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
context "deleted versions" do
|
31
|
+
let(:last_version){ VestalVersions::Version.last }
|
32
|
+
before do
|
33
|
+
subject.update_attribute(:last_name, 'Jobs')
|
34
|
+
subject.destroy
|
35
|
+
end
|
36
|
+
|
37
|
+
context "restoring a record with a bang" do
|
38
|
+
it "is able to restore the user record" do
|
39
|
+
last_version.restore!
|
40
|
+
|
41
|
+
last_version.versioned.should == subject
|
42
|
+
end
|
43
|
+
|
44
|
+
it "removes the last versioned entry" do
|
45
|
+
expect{
|
46
|
+
last_version.restore!
|
47
|
+
}.to change{ VestalVersions::Version.count }.by(-1)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "works properly even if it's not on the proper version" do
|
51
|
+
another_version = VestalVersions::Version.where(
|
52
|
+
:versioned_id => last_version.versioned_id,
|
53
|
+
:versioned_type => last_version.versioned_type
|
54
|
+
).first
|
55
|
+
|
56
|
+
another_version.should_not == last_version
|
57
|
+
|
58
|
+
another_version.restore!.should == subject
|
59
|
+
end
|
60
|
+
|
61
|
+
it "restores even if the schema has changed" do
|
62
|
+
new_mods = last_version.modifications.merge(:old_column => 'old')
|
63
|
+
last_version.update_attributes(:modifications => new_mods)
|
64
|
+
|
65
|
+
last_version.restore.should == subject
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "restoring a record without a save" do
|
70
|
+
it "does not save the DeletedUser when restoring" do
|
71
|
+
last_version.restore.should be_new_record
|
72
|
+
end
|
73
|
+
|
74
|
+
it "restores the user object properly" do
|
75
|
+
last_version.restore.should == subject
|
76
|
+
end
|
77
|
+
|
78
|
+
it "does not decrement the versions table" do
|
79
|
+
expect{
|
80
|
+
last_version.restore
|
81
|
+
}.to change{ VestalVersions::Version.count }.by(0)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe VestalVersions::Options do
|
4
|
+
context 'with explicit configuration' do
|
5
|
+
let(:options){ {:dependent => :destroy} }
|
6
|
+
let(:prepared_options){ User.prepare_versioned_options(options.dup) }
|
7
|
+
|
8
|
+
before do
|
9
|
+
VestalVersions::Version.config.clear
|
10
|
+
VestalVersions::Version.config.class_name = 'MyCustomVersion'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'has symbolized keys' do
|
14
|
+
User.vestal_versions_options.keys.all?{|k| k.is_a?(Symbol) }
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'combines class-level and global configuration options' do
|
18
|
+
prepared_options.slice(:dependent, :class_name).should == {
|
19
|
+
:dependent => :destroy,
|
20
|
+
:class_name => 'MyCustomVersion'
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'default configuration options' do
|
27
|
+
subject { User.prepare_versioned_options({}) }
|
28
|
+
|
29
|
+
it 'defaults to "VestalVersions::Version" for :class_name' do
|
30
|
+
subject[:class_name].should == 'VestalVersions::Version'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'defaults to :delete_all for :dependent' do
|
34
|
+
subject[:dependent].should == :delete_all
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'forces the :as option value to :versioned' do
|
38
|
+
subject[:as].should == :versioned
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'defaults to [VestalVersions::Versions] for :extend' do
|
42
|
+
subject[:extend].should == [VestalVersions::Versions]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe VestalVersions::Reload do
|
4
|
+
let(:user){ User.create(:name => 'Steve Richert') }
|
5
|
+
|
6
|
+
before do
|
7
|
+
first_version = user.version
|
8
|
+
user.update_attribute(:last_name, 'Jobs')
|
9
|
+
@last_version = user.version
|
10
|
+
user.revert_to(first_version)
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'resets the version number to the most recent version' do
|
14
|
+
user.version.should_not == @last_version
|
15
|
+
user.reload
|
16
|
+
user.version.should == @last_version
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe VestalVersions::Reset do
|
4
|
+
let(:names){
|
5
|
+
['Steve Richert', 'Stephen Richert', 'Stephen Jobs', 'Steve Jobs']
|
6
|
+
}
|
7
|
+
|
8
|
+
subject{ User.new }
|
9
|
+
let(:versions){ names.map{ |name|
|
10
|
+
subject.update_attribute :name, name
|
11
|
+
subject.version
|
12
|
+
} }
|
13
|
+
|
14
|
+
before do
|
15
|
+
@dependent = User.reflect_on_association(:versions).options[:dependent]
|
16
|
+
end
|
17
|
+
|
18
|
+
after do
|
19
|
+
User.reflect_on_association(:versions).options[:dependent] = @dependent
|
20
|
+
end
|
21
|
+
|
22
|
+
it "properly reverts the model's attributes" do
|
23
|
+
versions.reverse.each_with_index do |version, i|
|
24
|
+
subject.reset_to!(version)
|
25
|
+
subject.name.should == names.reverse[i]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'dissociates all versions after the target' do
|
30
|
+
versions.reverse.each do |version|
|
31
|
+
subject.reset_to!(version)
|
32
|
+
subject.versions(true).after(version).count.should == 0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'with the :dependent option as :delete_all' do
|
37
|
+
before do
|
38
|
+
User.reflect_on_association(:versions).options[:dependent] = :delete_all
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'deletes all versions after the target version' do
|
42
|
+
versions.reverse.each do |version|
|
43
|
+
later_versions = subject.versions.after(version)
|
44
|
+
subject.reset_to!(version)
|
45
|
+
|
46
|
+
later_versions.each do |later_version|
|
47
|
+
expect{
|
48
|
+
later_version.reload
|
49
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'does not destroy all versions after the target version' do
|
55
|
+
expect {
|
56
|
+
versions.reverse.each do |version|
|
57
|
+
subject.reset_to! version
|
58
|
+
end
|
59
|
+
}.to_not change{ VestalVersions::Version.count }
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'with the :dependent option as :destroy' do
|
64
|
+
before do
|
65
|
+
User.reflect_on_association(:versions).options[:dependent] = :destroy
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'deletes all versions after the target version' do
|
69
|
+
versions.reverse.each do |version|
|
70
|
+
later_versions = subject.versions.after(version)
|
71
|
+
subject.reset_to!(version)
|
72
|
+
|
73
|
+
later_versions.each do |later_version|
|
74
|
+
expect{
|
75
|
+
later_version.reload
|
76
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'destroys all versions after the target version' do
|
82
|
+
expect {
|
83
|
+
versions.reverse.each do |version|
|
84
|
+
later_versions = subject.versions.after(version)
|
85
|
+
|
86
|
+
subject.reset_to!(version)
|
87
|
+
end
|
88
|
+
}.to change{ VestalVersions::Version.count }.by(-versions.size + 1)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'with the :dependent option as :nullify' do
|
93
|
+
before do
|
94
|
+
User.reflect_on_association(:versions).options[:dependent] = :nullify
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'leaves all versions after the target version' do
|
98
|
+
versions.reverse.each do |version|
|
99
|
+
later_versions = subject.versions.after(version)
|
100
|
+
subject.reset_to!(version)
|
101
|
+
|
102
|
+
later_versions.each do |later_version|
|
103
|
+
expect{
|
104
|
+
later_version.reload
|
105
|
+
}.to_not raise_error
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe VestalVersions::Reversion do
|
4
|
+
subject{ User.new }
|
5
|
+
let(:attributes){ {} }
|
6
|
+
let(:first_version){ attributes.keys.min }
|
7
|
+
let(:last_version){ attributes.keys.max }
|
8
|
+
let(:times){ {} }
|
9
|
+
let(:names){
|
10
|
+
['Steve Richert', 'Stephen Richert', 'Stephen Jobs', 'Steve Jobs']
|
11
|
+
}
|
12
|
+
|
13
|
+
before do
|
14
|
+
time = names.size.hours.ago
|
15
|
+
|
16
|
+
names.each do |name|
|
17
|
+
subject.update_attribute(:name, name)
|
18
|
+
attributes[subject.version] = subject.attributes
|
19
|
+
time += 1.hour
|
20
|
+
|
21
|
+
subject.versions.last.try(:update_attribute, :created_at, time)
|
22
|
+
|
23
|
+
times[subject.version] = time
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'returns the new version number' do
|
28
|
+
subject.revert_to(first_version).should == first_version
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'changes the version number when saved' do
|
32
|
+
expect{ subject.revert_to! first_version }.to change{ subject.version }
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'does nothing for a invalid argument' do
|
36
|
+
[nil, :bogus, 'bogus', (1..2)].each do |invalid|
|
37
|
+
expect{ subject.revert_to(invalid) }.to_not change{ subject.version }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'is able to target a version number' do
|
42
|
+
subject.revert_to(1)
|
43
|
+
subject.version.should == 1
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'is able to target a date and time' do
|
47
|
+
times.each do |version, time|
|
48
|
+
subject.revert_to(time + 1.second)
|
49
|
+
subject.version.should == version
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'is able to target a version object' do
|
54
|
+
subject.versions.each do |version|
|
55
|
+
subject.revert_to(version)
|
56
|
+
subject.version.should == version.number
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "correctly rolls back the model's attributes" do
|
61
|
+
except = %w(created_at created_on updated_at updated_on)
|
62
|
+
|
63
|
+
attributes.each do |version, attributes|
|
64
|
+
subject.revert_to!(version)
|
65
|
+
subject.attributes.except(*except).should == attributes.except(*except)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
it "stores the reverted_from pointing to the previous version" do
|
70
|
+
subject.revert_to!(1)
|
71
|
+
subject.versions.last.reverted_from.should == 1
|
72
|
+
end
|
73
|
+
|
74
|
+
it "does not store the reverted_from for subsequent saves" do
|
75
|
+
subject.revert_to!(1)
|
76
|
+
subject.update_attributes(:name => 'Bill Gates')
|
77
|
+
subject.versions.last.reverted_from.should be_nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it "stores the reverted_from pointing to the version it was reverted from when save is called later" do
|
81
|
+
subject.revert_to(1)
|
82
|
+
subject.name = "Reverted"
|
83
|
+
subject.save
|
84
|
+
subject.versions.last.reverted_from.should == 1
|
85
|
+
end
|
86
|
+
|
87
|
+
it "does not store the reverted_from for subsequent saves when the revert_to-save is called later" do
|
88
|
+
subject.revert_to(1)
|
89
|
+
subject.name = "Reverted"
|
90
|
+
subject.save
|
91
|
+
subject.update_attributes(:name => 'Bill Gates')
|
92
|
+
subject.versions.last.reverted_from.should be_nil
|
93
|
+
end
|
94
|
+
|
95
|
+
it "clears the reverted_from if the model is reloaded after a revert_to without a save" do
|
96
|
+
subject.revert_to(1)
|
97
|
+
subject.reload
|
98
|
+
subject.update_attributes(:name => 'Bill Gates')
|
99
|
+
|
100
|
+
subject.versions.last.reverted_from.should be_nil
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe VestalVersions::Users do
|
4
|
+
let(:updated_by){ User.create(:name => 'Steve Jobs') }
|
5
|
+
let(:user){ User.create(:name => 'Steve Richert') }
|
6
|
+
|
7
|
+
it 'defaults to nil' do
|
8
|
+
user.update_attributes(:first_name => 'Stephen')
|
9
|
+
user.versions.last.user.should be_nil
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'accepts and returns an ActiveRecord user' do
|
13
|
+
user.update_attributes(:first_name => 'Stephen', :updated_by => updated_by)
|
14
|
+
user.versions.last.user.should == updated_by
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'accepts and returns a string user name' do
|
18
|
+
user.update_attributes(:first_name => 'Stephen', :updated_by => updated_by.name)
|
19
|
+
user.versions.last.user.should == updated_by.name
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe VestalVersions::Versions do
|
4
|
+
let(:user){ User.create(:name => 'Stephen Richert') }
|
5
|
+
|
6
|
+
before do
|
7
|
+
user.update_attribute(:name, 'Steve Jobs')
|
8
|
+
user.update_attribute(:last_name, 'Richert')
|
9
|
+
@first_version, @last_version = user.versions.first, user.versions.last
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'is comparable to another version based on version number' do
|
13
|
+
@first_version.should == @first_version
|
14
|
+
@last_version.should == @last_version
|
15
|
+
@first_version.should_not == @last_version
|
16
|
+
@last_version.should_not == @first_version
|
17
|
+
@first_version.should < @last_version
|
18
|
+
@last_version.should > @first_version
|
19
|
+
@first_version.should <= @last_version
|
20
|
+
@last_version.should >= @first_version
|
21
|
+
end
|
22
|
+
|
23
|
+
it "is not equal a separate model's version with the same number" do
|
24
|
+
other = User.create(:name => 'Stephen Richert')
|
25
|
+
other.update_attribute(:name, 'Steve Jobs')
|
26
|
+
other.update_attribute(:last_name, 'Richert')
|
27
|
+
first_version, last_version = other.versions.first, other.versions.last
|
28
|
+
|
29
|
+
@first_version.should_not == first_version
|
30
|
+
@last_version.should_not == last_version
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'defaults to ordering by number when finding through association' do
|
34
|
+
numbers = user.versions.map(&:number)
|
35
|
+
numbers.sort.should == numbers
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'returns true for the "initial?" method when the version number is 1' do
|
39
|
+
version = user.versions.build(:number => 1)
|
40
|
+
version.number.should == 1
|
41
|
+
version.should be_initial
|
42
|
+
end
|
43
|
+
|
44
|
+
it "sreturn the version number if it is not a revert" do
|
45
|
+
user.version.should == user.versions.last.original_number
|
46
|
+
end
|
47
|
+
|
48
|
+
it "return the reverted_version if it is a revert" do
|
49
|
+
user.revert_to!(1)
|
50
|
+
user.versions.last.original_number.should == 1
|
51
|
+
end
|
52
|
+
|
53
|
+
it "return the original version if it is a double revert" do
|
54
|
+
user.revert_to!(2)
|
55
|
+
version = user.version
|
56
|
+
user.update_attributes(:last_name => 'Gates')
|
57
|
+
user.revert_to!(version)
|
58
|
+
user.versions.last.original_number.should == 2
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|