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.
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