bitfluent-vestal_versions 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +23 -0
- data/LICENSE +20 -0
- data/README.rdoc +175 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/generators/vestal_versions/templates/initializer.rb +9 -0
- data/generators/vestal_versions/templates/migration.rb +27 -0
- data/generators/vestal_versions/vestal_versions_generator.rb +24 -0
- data/init.rb +1 -0
- data/lib/vestal_versions.rb +103 -0
- data/lib/vestal_versions/changes.rb +125 -0
- data/lib/vestal_versions/conditions.rb +69 -0
- data/lib/vestal_versions/configuration.rb +40 -0
- data/lib/vestal_versions/control.rb +175 -0
- data/lib/vestal_versions/creation.rb +100 -0
- data/lib/vestal_versions/options.rb +45 -0
- data/lib/vestal_versions/reload.rb +23 -0
- data/lib/vestal_versions/reset.rb +28 -0
- data/lib/vestal_versions/reversion.rb +69 -0
- data/lib/vestal_versions/tagging.rb +50 -0
- data/lib/vestal_versions/users.rb +57 -0
- data/lib/vestal_versions/version.rb +32 -0
- data/lib/vestal_versions/versioned.rb +30 -0
- data/lib/vestal_versions/versions.rb +74 -0
- data/test/changes_test.rb +169 -0
- data/test/conditions_test.rb +137 -0
- data/test/configuration_test.rb +39 -0
- data/test/control_test.rb +152 -0
- data/test/creation_test.rb +148 -0
- data/test/options_test.rb +52 -0
- data/test/reload_test.rb +19 -0
- data/test/reset_test.rb +112 -0
- data/test/reversion_test.rb +68 -0
- data/test/schema.rb +43 -0
- data/test/tagging_test.rb +39 -0
- data/test/test_helper.rb +12 -0
- data/test/users_test.rb +25 -0
- data/test/version_test.rb +43 -0
- data/test/versioned_test.rb +18 -0
- data/test/versions_test.rb +172 -0
- data/vestal_versions.gemspec +105 -0
- metadata +167 -0
@@ -0,0 +1,52 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class OptionsTest < Test::Unit::TestCase
|
4
|
+
context 'Configuration options' do
|
5
|
+
setup do
|
6
|
+
@options = {:dependent => :destroy}
|
7
|
+
@configuration = {:class_name => 'MyCustomVersion'}
|
8
|
+
|
9
|
+
VestalVersions::Configuration.options.clear
|
10
|
+
@configuration.each{|k,v| VestalVersions::Configuration.send("#{k}=", v) }
|
11
|
+
|
12
|
+
@prepared_options = User.prepare_versioned_options(@options.dup)
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'have symbolized keys' do
|
16
|
+
assert User.vestal_versions_options.keys.all?{|k| k.is_a?(Symbol) }
|
17
|
+
end
|
18
|
+
|
19
|
+
should 'combine class-level and global configuration options' do
|
20
|
+
combined_keys = (@options.keys + @configuration.keys).map(&:to_sym).uniq
|
21
|
+
combined_options = @configuration.symbolize_keys.merge(@options.symbolize_keys)
|
22
|
+
assert_equal @prepared_options.slice(*combined_keys), combined_options
|
23
|
+
end
|
24
|
+
|
25
|
+
teardown do
|
26
|
+
VestalVersions::Configuration.options.clear
|
27
|
+
User.prepare_versioned_options({})
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'Given no options, configuration options' do
|
32
|
+
setup do
|
33
|
+
@prepared_options = User.prepare_versioned_options({})
|
34
|
+
end
|
35
|
+
|
36
|
+
should 'default to "VestalVersions::Version" for :class_name' do
|
37
|
+
assert_equal 'VestalVersions::Version', @prepared_options[:class_name]
|
38
|
+
end
|
39
|
+
|
40
|
+
should 'default to :delete_all for :dependent' do
|
41
|
+
assert_equal :delete_all, @prepared_options[:dependent]
|
42
|
+
end
|
43
|
+
|
44
|
+
should 'force the :as option value to :versioned' do
|
45
|
+
assert_equal :versioned, @prepared_options[:as]
|
46
|
+
end
|
47
|
+
|
48
|
+
should 'default to [VestalVersions::Versions] for :extend' do
|
49
|
+
assert_equal [VestalVersions::Versions], @prepared_options[:extend]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/test/reload_test.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class ReloadTest < Test::Unit::TestCase
|
4
|
+
context 'Reloading a reverted model' do
|
5
|
+
setup do
|
6
|
+
@user = User.create(:name => 'Steve Richert')
|
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
|
+
should 'reset the version number to the most recent version' do
|
14
|
+
assert_not_equal @last_version, @user.version
|
15
|
+
@user.reload
|
16
|
+
assert_equal @last_version, @user.version
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/test/reset_test.rb
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class ResetTest < Test::Unit::TestCase
|
4
|
+
context 'Resetting a model' do
|
5
|
+
setup do
|
6
|
+
@original_dependent = User.reflect_on_association(:versions).options[:dependent]
|
7
|
+
@user, @versions = User.new, []
|
8
|
+
@names = ['Steve Richert', 'Stephen Richert', 'Stephen Jobs', 'Steve Jobs']
|
9
|
+
@names.each do |name|
|
10
|
+
@user.update_attribute(:name, name)
|
11
|
+
@versions << @user.version
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
should "properly revert the model's attributes" do
|
16
|
+
@versions.reverse.each_with_index do |version, i|
|
17
|
+
@user.reset_to!(version)
|
18
|
+
assert_equal @names.reverse[i], @user.name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'dissociate all versions after the target' do
|
23
|
+
@versions.reverse.each do |version|
|
24
|
+
@user.reset_to!(version)
|
25
|
+
assert_equal 0, @user.versions(true).after(version).count
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'with the :dependent option as :delete_all' do
|
30
|
+
setup do
|
31
|
+
User.reflect_on_association(:versions).options[:dependent] = :delete_all
|
32
|
+
end
|
33
|
+
|
34
|
+
should 'delete all versions after the target version' do
|
35
|
+
@versions.reverse.each do |version|
|
36
|
+
later_versions = @user.versions.after(version)
|
37
|
+
@user.reset_to!(version)
|
38
|
+
later_versions.each do |later_version|
|
39
|
+
assert_raise ActiveRecord::RecordNotFound do
|
40
|
+
later_version.reload
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'not destroy all versions after the target version' do
|
47
|
+
VestalVersions::Version.any_instance.stubs(:destroy).raises(RuntimeError)
|
48
|
+
@versions.reverse.each do |version|
|
49
|
+
assert_nothing_raised do
|
50
|
+
@user.reset_to!(version)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'with the :dependent option as :destroy' do
|
57
|
+
setup do
|
58
|
+
User.reflect_on_association(:versions).options[:dependent] = :destroy
|
59
|
+
end
|
60
|
+
|
61
|
+
should 'delete all versions after the target version' do
|
62
|
+
@versions.reverse.each do |version|
|
63
|
+
later_versions = @user.versions.after(version)
|
64
|
+
@user.reset_to!(version)
|
65
|
+
later_versions.each do |later_version|
|
66
|
+
assert_raise ActiveRecord::RecordNotFound do
|
67
|
+
later_version.reload
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
should 'destroy all versions after the target version' do
|
74
|
+
VestalVersions::Version.any_instance.stubs(:destroy).raises(RuntimeError)
|
75
|
+
@versions.reverse.each do |version|
|
76
|
+
later_versions = @user.versions.after(version)
|
77
|
+
if later_versions.empty?
|
78
|
+
assert_nothing_raised do
|
79
|
+
@user.reset_to!(version)
|
80
|
+
end
|
81
|
+
else
|
82
|
+
assert_raise RuntimeError do
|
83
|
+
@user.reset_to!(version)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'with the :dependent option as :nullify' do
|
91
|
+
setup do
|
92
|
+
User.reflect_on_association(:versions).options[:dependent] = :nullify
|
93
|
+
end
|
94
|
+
|
95
|
+
should 'leave all versions after the target version' do
|
96
|
+
@versions.reverse.each do |version|
|
97
|
+
later_versions = @user.versions.after(version)
|
98
|
+
@user.reset_to!(version)
|
99
|
+
later_versions.each do |later_version|
|
100
|
+
assert_nothing_raised do
|
101
|
+
later_version.reload
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
teardown do
|
109
|
+
User.reflect_on_association(:versions).options[:dependent] = @original_dependent
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class ReversionTest < Test::Unit::TestCase
|
4
|
+
context 'A model reversion' do
|
5
|
+
setup do
|
6
|
+
@user, @attributes, @times = User.new, {}, {}
|
7
|
+
names = ['Steve Richert', 'Stephen Richert', 'Stephen Jobs', 'Steve Jobs']
|
8
|
+
time = names.size.hours.ago
|
9
|
+
names.each do |name|
|
10
|
+
@user.update_attribute(:name, name)
|
11
|
+
@attributes[@user.version] = @user.attributes
|
12
|
+
time += 1.hour
|
13
|
+
if last_version = @user.versions.last
|
14
|
+
last_version.update_attribute(:created_at, time)
|
15
|
+
end
|
16
|
+
@times[@user.version] = time
|
17
|
+
end
|
18
|
+
@user.reload.versions.reload
|
19
|
+
@first_version, @last_version = @attributes.keys.min, @attributes.keys.max
|
20
|
+
end
|
21
|
+
|
22
|
+
should 'return the new version number' do
|
23
|
+
new_version = @user.revert_to(@first_version)
|
24
|
+
assert_equal @first_version, new_version
|
25
|
+
end
|
26
|
+
|
27
|
+
should 'change the version number when saved' do
|
28
|
+
current_version = @user.version
|
29
|
+
@user.revert_to!(@first_version)
|
30
|
+
assert_not_equal current_version, @user.version
|
31
|
+
end
|
32
|
+
|
33
|
+
should 'do nothing for a invalid argument' do
|
34
|
+
current_version = @user.version
|
35
|
+
[nil, :bogus, 'bogus', (1..2)].each do |invalid|
|
36
|
+
@user.revert_to(invalid)
|
37
|
+
assert_equal current_version, @user.version
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
should 'be able to target a version number' do
|
42
|
+
@user.revert_to(1)
|
43
|
+
assert_equal 1, @user.version
|
44
|
+
end
|
45
|
+
|
46
|
+
should 'be able to target a date and time' do
|
47
|
+
@times.each do |version, time|
|
48
|
+
@user.revert_to(time + 1.second)
|
49
|
+
assert_equal version, @user.version
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
should 'be able to target a version object' do
|
54
|
+
@user.versions.each do |version|
|
55
|
+
@user.revert_to(version)
|
56
|
+
assert_equal version.number, @user.version
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
should "correctly roll back the model's attributes" do
|
61
|
+
timestamps = %w(created_at created_on updated_at updated_on)
|
62
|
+
@attributes.each do |version, attributes|
|
63
|
+
@user.revert_to!(version)
|
64
|
+
assert_equal attributes.except(*timestamps), @user.attributes.except(*timestamps)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/test/schema.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
ActiveRecord::Base.establish_connection(
|
2
|
+
:adapter => defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' ? 'jdbcsqlite3' : 'sqlite3',
|
3
|
+
:database => File.join(File.dirname(__FILE__), 'test.db')
|
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.string :tag
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
CreateSchema.suppress_messages do
|
27
|
+
CreateSchema.migrate(:up)
|
28
|
+
end
|
29
|
+
|
30
|
+
class User < ActiveRecord::Base
|
31
|
+
versioned
|
32
|
+
|
33
|
+
def name
|
34
|
+
[first_name, last_name].compact.join(' ')
|
35
|
+
end
|
36
|
+
|
37
|
+
def name=(names)
|
38
|
+
self[:first_name], self[:last_name] = names.split(' ', 2)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class MyCustomVersion < VestalVersions::Version
|
43
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class TaggingTest < Test::Unit::TestCase
|
4
|
+
context 'Tagging a version' do
|
5
|
+
setup do
|
6
|
+
@user = User.create(:name => 'Steve Richert')
|
7
|
+
@user.update_attribute(:last_name, 'Jobs')
|
8
|
+
end
|
9
|
+
|
10
|
+
should "update the version record's tag column" do
|
11
|
+
tag_name = 'TAG'
|
12
|
+
last_version = @user.versions.last
|
13
|
+
assert_not_equal tag_name, last_version.tag
|
14
|
+
@user.tag_version(tag_name)
|
15
|
+
assert_equal tag_name, last_version.reload.tag
|
16
|
+
end
|
17
|
+
|
18
|
+
should 'create a version record for an initial version' do
|
19
|
+
@user.revert_to(1)
|
20
|
+
assert_nil @user.versions.at(1)
|
21
|
+
@user.tag_version('TAG')
|
22
|
+
assert_not_nil @user.versions.at(1)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context 'A tagged version' do
|
27
|
+
setup do
|
28
|
+
user = User.create(:name => 'Steve Richert')
|
29
|
+
user.update_attribute(:last_name, 'Jobs')
|
30
|
+
user.tag_version('TAG')
|
31
|
+
@version = user.versions.last
|
32
|
+
end
|
33
|
+
|
34
|
+
should 'return true for the "tagged?" method' do
|
35
|
+
assert @version.respond_to?(:tagged?)
|
36
|
+
assert_equal true, @version.tagged?
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
$: << File.dirname(__FILE__)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'test/unit'
|
6
|
+
require 'active_record'
|
7
|
+
require 'shoulda'
|
8
|
+
require 'mocha'
|
9
|
+
# require 'vestal_versions'
|
10
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/vestal_versions'))
|
11
|
+
require 'schema'
|
12
|
+
begin; require 'redgreen'; rescue LoadError; end
|
data/test/users_test.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class UsersTest < Test::Unit::TestCase
|
4
|
+
context 'The user responsible for an update' do
|
5
|
+
setup do
|
6
|
+
@updated_by = User.create(:name => 'Steve Jobs')
|
7
|
+
@user = User.create(:name => 'Steve Richert')
|
8
|
+
end
|
9
|
+
|
10
|
+
should 'default to nil' do
|
11
|
+
@user.update_attributes(:first_name => 'Stephen')
|
12
|
+
assert_nil @user.versions.last.user
|
13
|
+
end
|
14
|
+
|
15
|
+
should 'accept and return an ActiveRecord user' do
|
16
|
+
@user.update_attributes(:first_name => 'Stephen', :updated_by => @updated_by)
|
17
|
+
assert_equal @updated_by, @user.versions.last.user
|
18
|
+
end
|
19
|
+
|
20
|
+
should 'accept and return a string user name' do
|
21
|
+
@user.update_attributes(:first_name => 'Stephen', :updated_by => @updated_by.name)
|
22
|
+
assert_equal @updated_by.name, @user.versions.last.user
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class VersionTest < Test::Unit::TestCase
|
4
|
+
context 'Versions' do
|
5
|
+
setup do
|
6
|
+
@user = User.create(:name => 'Stephen Richert')
|
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
|
+
should 'be comparable to another version based on version number' do
|
13
|
+
assert @first_version == @first_version
|
14
|
+
assert @last_version == @last_version
|
15
|
+
assert @first_version != @last_version
|
16
|
+
assert @last_version != @first_version
|
17
|
+
assert @first_version < @last_version
|
18
|
+
assert @last_version > @first_version
|
19
|
+
assert @first_version <= @last_version
|
20
|
+
assert @last_version >= @first_version
|
21
|
+
end
|
22
|
+
|
23
|
+
should "not equal a separate model's version with the same number" do
|
24
|
+
user = User.create(:name => 'Stephen Richert')
|
25
|
+
user.update_attribute(:name, 'Steve Jobs')
|
26
|
+
user.update_attribute(:last_name, 'Richert')
|
27
|
+
first_version, last_version = user.versions.first, user.versions.last
|
28
|
+
assert_not_equal @first_version, first_version
|
29
|
+
assert_not_equal @last_version, last_version
|
30
|
+
end
|
31
|
+
|
32
|
+
should 'default to ordering by number when finding through association' do
|
33
|
+
numbers = @user.versions.map(&:number)
|
34
|
+
assert_equal numbers.sort, numbers
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'return true for the "initial?" method when the version number is 1' do
|
38
|
+
version = @user.versions.build(:number => 1)
|
39
|
+
assert_equal 1, version.number
|
40
|
+
assert_equal true, version.initial?
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'test_helper'))
|
2
|
+
|
3
|
+
class VersionedTest < Test::Unit::TestCase
|
4
|
+
context 'ActiveRecord models' do
|
5
|
+
should 'respond to the "versioned?" method' do
|
6
|
+
assert ActiveRecord::Base.respond_to?(:versioned?)
|
7
|
+
assert User.respond_to?(:versioned?)
|
8
|
+
end
|
9
|
+
|
10
|
+
should 'return true for the "versioned?" method if the model is versioned' do
|
11
|
+
assert_equal true, User.versioned?
|
12
|
+
end
|
13
|
+
|
14
|
+
should 'return false for the "versioned?" method if the model is not versioned' do
|
15
|
+
assert_equal false, ActiveRecord::Base.versioned?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|