vestal_versions 1.0.2 → 2.0.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 (69) hide show
  1. data/.gitignore +19 -20
  2. data/.travis.yml +22 -0
  3. data/CHANGELOG.md +7 -0
  4. data/Gemfile +10 -0
  5. data/README.rdoc +63 -36
  6. data/Rakefile +4 -43
  7. data/gemfiles/activerecord_3_0.gemfile +10 -0
  8. data/gemfiles/activerecord_3_1.gemfile +10 -0
  9. data/gemfiles/activerecord_3_2.gemfile +10 -0
  10. data/gemfiles/activerecord_4_0.gemfile +10 -0
  11. data/lib/generators/vestal_versions.rb +11 -0
  12. data/lib/generators/vestal_versions/migration/migration_generator.rb +17 -0
  13. data/{generators/vestal_versions → lib/generators/vestal_versions/migration}/templates/initializer.rb +0 -0
  14. data/{generators/vestal_versions → lib/generators/vestal_versions/migration}/templates/migration.rb +4 -3
  15. data/lib/vestal_versions.rb +39 -12
  16. data/lib/vestal_versions/changes.rb +43 -47
  17. data/lib/vestal_versions/conditions.rb +31 -43
  18. data/lib/vestal_versions/control.rb +162 -138
  19. data/lib/vestal_versions/creation.rb +67 -59
  20. data/lib/vestal_versions/deletion.rb +37 -0
  21. data/lib/vestal_versions/options.rb +6 -10
  22. data/lib/vestal_versions/reload.rb +7 -14
  23. data/lib/vestal_versions/reset.rb +15 -19
  24. data/lib/vestal_versions/reversion.rb +64 -52
  25. data/lib/vestal_versions/users.rb +36 -39
  26. data/lib/vestal_versions/version.rb +57 -2
  27. data/lib/vestal_versions/version_tagging.rb +51 -0
  28. data/lib/vestal_versions/versioned.rb +14 -17
  29. data/lib/vestal_versions/versions.rb +22 -7
  30. data/spec/spec_helper.rb +28 -0
  31. data/spec/support/models.rb +19 -0
  32. data/spec/support/schema.rb +25 -0
  33. data/spec/vestal_versions/changes_spec.rb +134 -0
  34. data/spec/vestal_versions/conditions_spec.rb +103 -0
  35. data/spec/vestal_versions/control_spec.rb +120 -0
  36. data/spec/vestal_versions/creation_spec.rb +90 -0
  37. data/spec/vestal_versions/deletion_spec.rb +86 -0
  38. data/spec/vestal_versions/options_spec.rb +45 -0
  39. data/spec/vestal_versions/reload_spec.rb +18 -0
  40. data/spec/vestal_versions/reset_spec.rb +111 -0
  41. data/spec/vestal_versions/reversion_spec.rb +103 -0
  42. data/spec/vestal_versions/users_spec.rb +21 -0
  43. data/spec/vestal_versions/version_spec.rb +61 -0
  44. data/spec/vestal_versions/version_tagging_spec.rb +39 -0
  45. data/spec/vestal_versions/versioned_spec.rb +16 -0
  46. data/spec/vestal_versions/versions_spec.rb +176 -0
  47. data/vestal_versions.gemspec +18 -100
  48. metadata +153 -102
  49. data/VERSION +0 -1
  50. data/generators/vestal_versions/vestal_versions_generator.rb +0 -10
  51. data/init.rb +0 -1
  52. data/lib/vestal_versions/configuration.rb +0 -40
  53. data/lib/vestal_versions/tagging.rb +0 -50
  54. data/test/changes_test.rb +0 -169
  55. data/test/conditions_test.rb +0 -137
  56. data/test/configuration_test.rb +0 -39
  57. data/test/control_test.rb +0 -152
  58. data/test/creation_test.rb +0 -110
  59. data/test/options_test.rb +0 -52
  60. data/test/reload_test.rb +0 -19
  61. data/test/reset_test.rb +0 -112
  62. data/test/reversion_test.rb +0 -68
  63. data/test/schema.rb +0 -43
  64. data/test/tagging_test.rb +0 -39
  65. data/test/test_helper.rb +0 -11
  66. data/test/users_test.rb +0 -25
  67. data/test/version_test.rb +0 -43
  68. data/test/versioned_test.rb +0 -18
  69. data/test/versions_test.rb +0 -172
@@ -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
@@ -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