simply_versioned 0.9.3.2

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.
@@ -0,0 +1,29 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Files
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Files</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Files</h1>
22
+ <div id="index-entries">
23
+ <a href="files/README.html">README</a><br />
24
+ <a href="files/lib/simply_versioned_rb.html">lib/simply_versioned.rb</a><br />
25
+ <a href="files/lib/version_rb.html">lib/version.rb</a><br />
26
+ </div>
27
+ </div>
28
+ </body>
29
+ </html>
@@ -0,0 +1,36 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Methods
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Methods</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Methods</h1>
22
+ <div id="index-entries">
23
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned/VersionsProxyMethods.html#M000005">clean (SoftwareHeretics::ActiveRecord::SimplyVersioned::VersionsProxyMethods)</a><br />
24
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned/VersionsProxyMethods.html#M000004">current (SoftwareHeretics::ActiveRecord::SimplyVersioned::VersionsProxyMethods)</a><br />
25
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned/VersionsProxyMethods.html#M000003">first (SoftwareHeretics::ActiveRecord::SimplyVersioned::VersionsProxyMethods)</a><br />
26
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned/VersionsProxyMethods.html#M000002">get (SoftwareHeretics::ActiveRecord::SimplyVersioned::VersionsProxyMethods)</a><br />
27
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned.html#M000001">included (SoftwareHeretics::ActiveRecord::SimplyVersioned)</a><br />
28
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned/VersionsProxyMethods.html#M000006">next (SoftwareHeretics::ActiveRecord::SimplyVersioned::VersionsProxyMethods)</a><br />
29
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned/VersionsProxyMethods.html#M000007">prev (SoftwareHeretics::ActiveRecord::SimplyVersioned::VersionsProxyMethods)</a><br />
30
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned/InstanceMethods.html#M000009">revert_to_version (SoftwareHeretics::ActiveRecord::SimplyVersioned::InstanceMethods)</a><br />
31
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned/ClassMethods.html#M000008">simply_versioned (SoftwareHeretics::ActiveRecord::SimplyVersioned::ClassMethods)</a><br />
32
+ <a href="classes/SoftwareHeretics/ActiveRecord/SimplyVersioned/InstanceMethods.html#M000010">simply_versioned_create_version (SoftwareHeretics::ActiveRecord::SimplyVersioned::InstanceMethods)</a><br />
33
+ </div>
34
+ </div>
35
+ </body>
36
+ </html>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
5
+
6
+ <!--
7
+
8
+ SimplyVersioned
9
+
10
+ -->
11
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
12
+ <head>
13
+ <title>SimplyVersioned</title>
14
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
15
+ </head>
16
+ <frameset rows="20%, 80%">
17
+ <frameset cols="25%,35%,45%">
18
+ <frame src="fr_file_index.html" title="Files" name="Files" />
19
+ <frame src="fr_class_index.html" name="Classes" />
20
+ <frame src="fr_method_index.html" name="Methods" />
21
+ </frameset>
22
+ <frame src="files/README.html" name="docwin" />
23
+ </frameset>
24
+ </html>
@@ -0,0 +1,208 @@
1
+
2
+ body {
3
+ font-family: Verdana,Arial,Helvetica,sans-serif;
4
+ font-size: 90%;
5
+ margin: 0;
6
+ margin-left: 40px;
7
+ padding: 0;
8
+ background: white;
9
+ }
10
+
11
+ h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
12
+ h1 { font-size: 150%; }
13
+ h2,h3,h4 { margin-top: 1em; }
14
+
15
+ a { background: #eef; color: #039; text-decoration: none; }
16
+ a:hover { background: #039; color: #eef; }
17
+
18
+ /* Override the base stylesheet's Anchor inside a table cell */
19
+ td > a {
20
+ background: transparent;
21
+ color: #039;
22
+ text-decoration: none;
23
+ }
24
+
25
+ /* and inside a section title */
26
+ .section-title > a {
27
+ background: transparent;
28
+ color: #eee;
29
+ text-decoration: none;
30
+ }
31
+
32
+ /* === Structural elements =================================== */
33
+
34
+ div#index {
35
+ margin: 0;
36
+ margin-left: -40px;
37
+ padding: 0;
38
+ font-size: 90%;
39
+ }
40
+
41
+
42
+ div#index a {
43
+ margin-left: 0.7em;
44
+ }
45
+
46
+ div#index .section-bar {
47
+ margin-left: 0px;
48
+ padding-left: 0.7em;
49
+ background: #ccc;
50
+ font-size: small;
51
+ }
52
+
53
+
54
+ div#classHeader, div#fileHeader {
55
+ width: auto;
56
+ color: white;
57
+ padding: 0.5em 1.5em 0.5em 1.5em;
58
+ margin: 0;
59
+ margin-left: -40px;
60
+ border-bottom: 3px solid #006;
61
+ }
62
+
63
+ div#classHeader a, div#fileHeader a {
64
+ background: inherit;
65
+ color: white;
66
+ }
67
+
68
+ div#classHeader td, div#fileHeader td {
69
+ background: inherit;
70
+ color: white;
71
+ }
72
+
73
+
74
+ div#fileHeader {
75
+ background: #057;
76
+ }
77
+
78
+ div#classHeader {
79
+ background: #048;
80
+ }
81
+
82
+
83
+ .class-name-in-header {
84
+ font-size: 180%;
85
+ font-weight: bold;
86
+ }
87
+
88
+
89
+ div#bodyContent {
90
+ padding: 0 1.5em 0 1.5em;
91
+ }
92
+
93
+ div#description {
94
+ padding: 0.5em 1.5em;
95
+ background: #efefef;
96
+ border: 1px dotted #999;
97
+ }
98
+
99
+ div#description h1,h2,h3,h4,h5,h6 {
100
+ color: #125;;
101
+ background: transparent;
102
+ }
103
+
104
+ div#validator-badges {
105
+ text-align: center;
106
+ }
107
+ div#validator-badges img { border: 0; }
108
+
109
+ div#copyright {
110
+ color: #333;
111
+ background: #efefef;
112
+ font: 0.75em sans-serif;
113
+ margin-top: 5em;
114
+ margin-bottom: 0;
115
+ padding: 0.5em 2em;
116
+ }
117
+
118
+
119
+ /* === Classes =================================== */
120
+
121
+ table.header-table {
122
+ color: white;
123
+ font-size: small;
124
+ }
125
+
126
+ .type-note {
127
+ font-size: small;
128
+ color: #DEDEDE;
129
+ }
130
+
131
+ .xxsection-bar {
132
+ background: #eee;
133
+ color: #333;
134
+ padding: 3px;
135
+ }
136
+
137
+ .section-bar {
138
+ color: #333;
139
+ border-bottom: 1px solid #999;
140
+ margin-left: -20px;
141
+ }
142
+
143
+
144
+ .section-title {
145
+ background: #79a;
146
+ color: #eee;
147
+ padding: 3px;
148
+ margin-top: 2em;
149
+ margin-left: -30px;
150
+ border: 1px solid #999;
151
+ }
152
+
153
+ .top-aligned-row { vertical-align: top }
154
+ .bottom-aligned-row { vertical-align: bottom }
155
+
156
+ /* --- Context section classes ----------------------- */
157
+
158
+ .context-row { }
159
+ .context-item-name { font-family: monospace; font-weight: bold; color: black; }
160
+ .context-item-value { font-size: small; color: #448; }
161
+ .context-item-desc { color: #333; padding-left: 2em; }
162
+
163
+ /* --- Method classes -------------------------- */
164
+ .method-detail {
165
+ background: #efefef;
166
+ padding: 0;
167
+ margin-top: 0.5em;
168
+ margin-bottom: 1em;
169
+ border: 1px dotted #ccc;
170
+ }
171
+ .method-heading {
172
+ color: black;
173
+ background: #ccc;
174
+ border-bottom: 1px solid #666;
175
+ padding: 0.2em 0.5em 0 0.5em;
176
+ }
177
+ .method-signature { color: black; background: inherit; }
178
+ .method-name { font-weight: bold; }
179
+ .method-args { font-style: italic; }
180
+ .method-description { padding: 0 0.5em 0 0.5em; }
181
+
182
+ /* --- Source code sections -------------------- */
183
+
184
+ a.source-toggle { font-size: 90%; }
185
+ div.method-source-code {
186
+ background: #262626;
187
+ color: #ffdead;
188
+ margin: 1em;
189
+ padding: 0.5em;
190
+ border: 1px dashed #999;
191
+ overflow: hidden;
192
+ }
193
+
194
+ div.method-source-code pre { color: #ffdead; overflow: hidden; }
195
+
196
+ /* --- Ruby keyword styles --------------------- */
197
+
198
+ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
199
+
200
+ .ruby-constant { color: #7fffd4; background: transparent; }
201
+ .ruby-keyword { color: #00ffff; background: transparent; }
202
+ .ruby-ivar { color: #eedd82; background: transparent; }
203
+ .ruby-operator { color: #00ffee; background: transparent; }
204
+ .ruby-identifier { color: #ffdead; background: transparent; }
205
+ .ruby-node { color: #ffa07a; background: transparent; }
206
+ .ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
207
+ .ruby-regexp { color: #ffa07a; background: transparent; }
208
+ .ruby-value { color: #7fffd4; background: transparent; }
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib/', __FILE__)
3
+ $:.unshift lib unless $:.include?(lib)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "simply_versioned"
7
+ s.version = "0.9.3.2"
8
+ s.date = %q{2010-04-05}
9
+ s.platform = Gem::Platform::RUBY
10
+ s.authors = ["Matt Mower", "Jérôme Lipowicz"]
11
+ s.homepage = "http://github.com/jerome/simply_versioned"
12
+ s.summary = "SimplyVersioned is a simple, non-invasive, approach to versioning ActiveRecord models."
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+
16
+ s.files = Dir.glob("{.,generators,lib,rdoc,tasks,test}/**/*")
17
+ s.require_path = 'lib'
18
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :simply_versioned do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,326 @@
1
+ require File.join( File.dirname( __FILE__ ), 'test_helper' )
2
+
3
+ require 'mocha'
4
+
5
+ class Aardvark < ActiveRecord::Base
6
+ simply_versioned :keep => 3
7
+ end
8
+
9
+ class Gnu < ActiveRecord::Base
10
+ simply_versioned :keep => 4
11
+ end
12
+
13
+ class Undine < ActiveRecord::Base
14
+ end
15
+
16
+ class Heffalump < ActiveRecord::Base
17
+ simply_versioned :automatic => false
18
+ end
19
+
20
+ class Saproling < ActiveRecord::Base
21
+ simply_versioned :exclude => [:trouble]
22
+ end
23
+
24
+ class SimplyVersionedTest < FixturedTestCase
25
+
26
+ def self.suite_setup
27
+ ActiveRecord::Schema.define do
28
+ create_table :aardvarks, :force => true do |t|
29
+ t.string :name
30
+ t.integer :age
31
+ end
32
+
33
+ create_table :gnus, :force => true do |t|
34
+ t.string :name
35
+ t.text :description
36
+ end
37
+
38
+ create_table :undines, :force => true do |t|
39
+ t.string :name
40
+ t.integer :married, :default => 0
41
+ end
42
+
43
+ create_table :heffalumps, :force => true do |t|
44
+ t.string :name
45
+ end
46
+
47
+ create_table :saprolings, :force => true do |t|
48
+ t.string :name
49
+ t.string :trouble
50
+ end
51
+
52
+ create_table :versions, :force => true do |t|
53
+ t.integer :versionable_id
54
+ t.string :versionable_type
55
+ t.integer :number
56
+ t.text :yaml
57
+ t.datetime :created_at
58
+ end
59
+ end
60
+ end
61
+
62
+ def self.suite_teardown
63
+ ActiveRecord::Schema.define do
64
+ drop_table :versions
65
+ drop_table :saprolings
66
+ drop_table :heffalumps
67
+ drop_table :undines
68
+ drop_table :gnus
69
+ drop_table :aardvarks
70
+ end
71
+ end
72
+
73
+ def test_should_reject_unknown_options
74
+ assert_raises SoftwareHeretics::ActiveRecord::SimplyVersioned::BadOptions do
75
+ cls = Class.new( ActiveRecord::Base ) do
76
+ simply_versioned :bogus => true
77
+ end
78
+ end
79
+ end
80
+
81
+ def test_should_start_with_empty_versions
82
+ anthony = Aardvark.new( :name => 'Anthony', :age => 35 )
83
+ assert anthony.versions.empty?
84
+ assert anthony.unversioned?
85
+ end
86
+
87
+ def test_should_not_be_versioned
88
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 )
89
+ assert anthony.unversioned?
90
+ end
91
+
92
+ def test_should_not_be_versioned_when_new_record_is_still_unchanged
93
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 )
94
+ anthony.save!
95
+ assert anthony.unversioned?
96
+ end
97
+
98
+ def test_should_be_versioned
99
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 )
100
+ anthony.age += 1
101
+ anthony.save!
102
+ assert anthony.versioned?
103
+ end
104
+
105
+ def test_should_get_versions
106
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 )
107
+ anthony.age += 1
108
+ anthony.save!
109
+
110
+ assert_equal 35, anthony.versions.get_version(1).model.age
111
+ assert_equal 35, anthony.versions.get(1).model.age
112
+
113
+ assert_equal 36, anthony.versions.get_version(2).model.age
114
+ assert_equal 36, anthony.versions.get(2).model.age
115
+ end
116
+
117
+ def test_should_not_version_on_create
118
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 )
119
+ assert_equal 0, anthony.versions.count
120
+ assert_equal 0, anthony.versions.current_version.number
121
+ assert_equal 0, anthony.versions.current.number
122
+ end
123
+
124
+ def test_should_version_on_save
125
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 )
126
+ anthony.age += 1
127
+ anthony.save!
128
+ assert_equal 2, anthony.versions.count
129
+ end
130
+
131
+ def test_should_trim_versions
132
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 ) # v1
133
+ anthony.age += 1
134
+ anthony.save! #v2
135
+
136
+ anthony.age += 1
137
+ anthony.save! #v3
138
+
139
+ anthony.age += 1
140
+ anthony.save! #v4 !!
141
+
142
+ assert_equal 3, anthony.versions.count
143
+ assert_equal 36, anthony.versions.first_version.model.age
144
+ assert_equal 36, anthony.versions.first.model.age
145
+ assert_equal 38, anthony.versions.current_version.model.age
146
+ assert_equal 38, anthony.versions.current.model.age
147
+ end
148
+
149
+ def test_should_revert
150
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 ) # v1
151
+ anthony.age += 1
152
+ anthony.save! #v2
153
+
154
+ anthony.revert_to_version( 1 )
155
+ assert_equal 35, anthony.age
156
+ end
157
+
158
+ def test_should_revert_except
159
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 ) # v1
160
+
161
+ anthony.name = "Tony"
162
+ anthony.age = 45
163
+ anthony.save! # v2
164
+
165
+ anthony.revert_to_version( 1, :except => [:name,:created_at,:updated_at] )
166
+ assert_equal "Tony", anthony.name
167
+ assert_equal 35, anthony.age
168
+ end
169
+
170
+ def test_should_raise_on_reversion_to_bad_version
171
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 ) # v1
172
+ anthony.name = "Tony"
173
+ anthony.age = 45
174
+ anthony.save! # v2
175
+
176
+ assert_raise RuntimeError do
177
+ anthony.revert_to_version( -1 )
178
+ end
179
+
180
+ assert_raise RuntimeError do
181
+ anthony.revert_to_version( "a" )
182
+ end
183
+
184
+ assert_raise RuntimeError do
185
+ anthony.revert_to_version( nil )
186
+ end
187
+ end
188
+
189
+ def test_should_delete_dependent_versions
190
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 ) # v1
191
+ anthony.age += 1
192
+ anthony.save! #v2
193
+
194
+ assert_difference( 'Version.count', -2 ) do
195
+ anthony.destroy
196
+ end
197
+ end
198
+
199
+ def test_should_isolate_versioned_models
200
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 )
201
+ gary = Gnu.create!( :name => 'Gary', :description => 'Gary the GNU' )
202
+
203
+ assert_equal 2, Version.count
204
+ assert_equal 1, anthony.versions.first.number
205
+ assert_equal 1, gary.versions.first.number
206
+ end
207
+
208
+ def test_should_be_able_to_control_versioning
209
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 ) # v1
210
+
211
+ assert_no_difference( 'anthony.versions.count' ) do
212
+ anthony.age += 1
213
+ anthony.with_versioning( false, &:save! )
214
+ end
215
+
216
+ assert_difference( 'anthony.versions.count' ) do
217
+ anthony.age += 1
218
+ anthony.with_versioning( true, &:save! )
219
+ end
220
+ end
221
+
222
+ def test_should_not_version_in_block
223
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 ) # v1
224
+
225
+ assert_no_difference( 'anthony.versions.count' ) do
226
+ anthony.age += 1
227
+ anthony.with_versioning( false ) do
228
+ anthony.save!
229
+ end
230
+ end
231
+ end
232
+
233
+ def test_should_begin_versioning_existing_record
234
+ ulrica = Undine.create!( :name => 'Ulrica' )
235
+
236
+ # Now we begin versioning this kind of record
237
+ Undine.class_eval do
238
+ simply_versioned
239
+ end
240
+
241
+ ulrica = Undine.find_by_name( 'Ulrica' )
242
+ assert ulrica.unversioned?
243
+
244
+ ulrica.update_attributes( :married => 1 )
245
+ assert !ulrica.unversioned?
246
+ assert_equal 1, ulrica.versions.size
247
+ assert_equal 1, ulrica.versions.first.model.married
248
+ end
249
+
250
+ def test_should_follow_back_and_forth
251
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 ) # v1
252
+ anthony.age += 1
253
+ anthony.save! #v2
254
+ anthony.age += 1
255
+ anthony.save!
256
+
257
+ assert_equal anthony.versions.current_version, anthony.versions.first_version.next.next
258
+ assert_equal anthony.versions.first_version, anthony.versions.current_version.previous.previous
259
+ end
260
+
261
+ def test_should_be_trustworthy_collection
262
+ anthony = Aardvark.create!( :name => 'Anthony', :age => 35 ) # v1
263
+ anthony.age += 1
264
+ anthony.save! #v2
265
+
266
+ # Now create another record
267
+
268
+ alan = Aardvark.create!( :name => 'Alan', :age => 35 )
269
+ assert_equal 1, alan.versions.size
270
+ end
271
+
272
+ def test_should_not_delete_old_versions_by_default
273
+ ulrica = Undine.create!( :name => 'Ulrica' )
274
+ ulrica.versions.expects( :clean_old_versions ).never
275
+ 100.times do
276
+ ulrica.update_attribute( :married, 1 - ulrica.married )
277
+ end
278
+ end
279
+
280
+ def test_should_not_automatically_create_versions
281
+ henry = Heffalump.create!( :name => 'Henry' )
282
+ assert_equal 0, henry.versions.count
283
+
284
+ assert_no_difference( 'henry.versions.count' ) do
285
+ henry.name = 'Harry'
286
+ henry.save!
287
+ end
288
+ end
289
+
290
+ def test_should_create_versions_on_demand
291
+ henry = Heffalump.create!( :name => 'Henry' )
292
+
293
+ assert_difference( "henry.versions.count", 1 ) do
294
+ henry.with_versioning( true, &:save )
295
+ end
296
+ end
297
+
298
+ def test_should_exclude_columns
299
+ assert_equal ["trouble"], Saproling.simply_versioned_excluded_columns
300
+
301
+ sylvia = Saproling.create!( :name => 'Sylvia', :trouble => "big" )
302
+
303
+ yaml = YAML::load( sylvia.versions.first.yaml )
304
+ assert_equal Set.new( [ "id", "name" ] ), Set.new( yaml.keys )
305
+ end
306
+
307
+ def test_should_exclude_column_equivalent_syntax
308
+ klass = Class.new( ActiveRecord::Base )
309
+ klass.module_eval <<-DEFN
310
+ simply_versioned :exclude => :some_column
311
+ DEFN
312
+ assert_equal ['some_column'], klass.simply_versioned_excluded_columns
313
+ end
314
+
315
+ def test_should_report_version_number
316
+ anthony = Aardvark.new( :name => 'Anthony', :age => 35 )
317
+ assert_equal 0, anthony.version_number
318
+
319
+ anthony.save!
320
+ assert_equal 1, anthony.version_number
321
+
322
+ anthony.save!
323
+ assert_equal 2, anthony.version_number # and so on
324
+ end
325
+
326
+ end