bitfluent-vestal_versions 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/.gitignore +23 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +175 -0
  4. data/Rakefile +45 -0
  5. data/VERSION +1 -0
  6. data/generators/vestal_versions/templates/initializer.rb +9 -0
  7. data/generators/vestal_versions/templates/migration.rb +27 -0
  8. data/generators/vestal_versions/vestal_versions_generator.rb +24 -0
  9. data/init.rb +1 -0
  10. data/lib/vestal_versions.rb +103 -0
  11. data/lib/vestal_versions/changes.rb +125 -0
  12. data/lib/vestal_versions/conditions.rb +69 -0
  13. data/lib/vestal_versions/configuration.rb +40 -0
  14. data/lib/vestal_versions/control.rb +175 -0
  15. data/lib/vestal_versions/creation.rb +100 -0
  16. data/lib/vestal_versions/options.rb +45 -0
  17. data/lib/vestal_versions/reload.rb +23 -0
  18. data/lib/vestal_versions/reset.rb +28 -0
  19. data/lib/vestal_versions/reversion.rb +69 -0
  20. data/lib/vestal_versions/tagging.rb +50 -0
  21. data/lib/vestal_versions/users.rb +57 -0
  22. data/lib/vestal_versions/version.rb +32 -0
  23. data/lib/vestal_versions/versioned.rb +30 -0
  24. data/lib/vestal_versions/versions.rb +74 -0
  25. data/test/changes_test.rb +169 -0
  26. data/test/conditions_test.rb +137 -0
  27. data/test/configuration_test.rb +39 -0
  28. data/test/control_test.rb +152 -0
  29. data/test/creation_test.rb +148 -0
  30. data/test/options_test.rb +52 -0
  31. data/test/reload_test.rb +19 -0
  32. data/test/reset_test.rb +112 -0
  33. data/test/reversion_test.rb +68 -0
  34. data/test/schema.rb +43 -0
  35. data/test/tagging_test.rb +39 -0
  36. data/test/test_helper.rb +12 -0
  37. data/test/users_test.rb +25 -0
  38. data/test/version_test.rb +43 -0
  39. data/test/versioned_test.rb +18 -0
  40. data/test/versions_test.rb +172 -0
  41. data/vestal_versions.gemspec +105 -0
  42. 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
@@ -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
@@ -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
@@ -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
@@ -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