cure_acts_as_versioned 0.6.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.
@@ -0,0 +1,501 @@
1
+ # Copyright (c) 2005 Rick Olson
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+ require 'active_support/concern'
22
+
23
+ module ActiveRecord #:nodoc:
24
+ module Acts #:nodoc:
25
+ # Specify this act if you want to save a copy of the row in a versioned table. This assumes there is a
26
+ # versioned table ready and that your model has a version field. This works with optimistic locking if the lock_version
27
+ # column is present as well.
28
+ #
29
+ # The class for the versioned model is derived the first time it is seen. Therefore, if you change your database schema you have to restart
30
+ # your container for the changes to be reflected. In development mode this usually means restarting WEBrick.
31
+ #
32
+ # class Page < ActiveRecord::Base
33
+ # # assumes pages_versions table
34
+ # acts_as_versioned
35
+ # end
36
+ #
37
+ # Example:
38
+ #
39
+ # page = Page.create(:title => 'hello world!')
40
+ # page.version # => 1
41
+ #
42
+ # page.title = 'hello world'
43
+ # page.save
44
+ # page.version # => 2
45
+ # page.versions.size # => 2
46
+ #
47
+ # page.revert_to(1) # using version number
48
+ # page.title # => 'hello world!'
49
+ #
50
+ # page.revert_to(page.versions.last) # using versioned instance
51
+ # page.title # => 'hello world'
52
+ #
53
+ # page.versions.earliest # efficient query to find the first version
54
+ # page.versions.latest # efficient query to find the most recently created version
55
+ #
56
+ #
57
+ # Simple Queries to page between versions
58
+ #
59
+ # page.versions.before(version)
60
+ # page.versions.after(version)
61
+ #
62
+ # Access the previous/next versions from the versioned model itself
63
+ #
64
+ # version = page.versions.latest
65
+ # version.previous # go back one version
66
+ # version.next # go forward one version
67
+ #
68
+ # See ActiveRecord::Acts::Versioned::ClassMethods#acts_as_versioned for configuration options
69
+ module Versioned
70
+ VERSION = "0.6.0"
71
+ CALLBACKS = [:set_new_version, :save_version, :save_version?]
72
+
73
+ # == Configuration options
74
+ #
75
+ # * <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example)
76
+ # * <tt>table_name</tt> - versioned model table name (default: page_versions in the above example)
77
+ # * <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example)
78
+ # * <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI. (default: versioned_type)
79
+ # * <tt>version_column</tt> - name of the column in the model that keeps the version number (default: version)
80
+ # * <tt>sequence_name</tt> - name of the custom sequence to be used by the versioned model.
81
+ # * <tt>limit</tt> - number of revisions to keep, defaults to unlimited
82
+ # * <tt>if</tt> - symbol of method to check before saving a new version. If this method returns false, a new version is not saved.
83
+ # For finer control, pass either a Proc or modify Model#version_condition_met?
84
+ #
85
+ # acts_as_versioned :if => Proc.new { |auction| !auction.expired? }
86
+ #
87
+ # or...
88
+ #
89
+ # class Auction
90
+ # def version_condition_met? # totally bypasses the <tt>:if</tt> option
91
+ # !expired?
92
+ # end
93
+ # end
94
+ #
95
+ # * <tt>if_changed</tt> - Simple way of specifying attributes that are required to be changed before saving a model. This takes
96
+ # either a symbol or array of symbols.
97
+ #
98
+ # * <tt>extend</tt> - Lets you specify a module to be mixed in both the original and versioned models. You can also just pass a block
99
+ # to create an anonymous mixin:
100
+ #
101
+ # class Auction
102
+ # acts_as_versioned do
103
+ # def started?
104
+ # !started_at.nil?
105
+ # end
106
+ # end
107
+ # end
108
+ #
109
+ # or...
110
+ #
111
+ # module AuctionExtension
112
+ # def started?
113
+ # !started_at.nil?
114
+ # end
115
+ # end
116
+ # class Auction
117
+ # acts_as_versioned :extend => AuctionExtension
118
+ # end
119
+ #
120
+ # Example code:
121
+ #
122
+ # @auction = Auction.find(1)
123
+ # @auction.started?
124
+ # @auction.versions.first.started?
125
+ #
126
+ # == Database Schema
127
+ #
128
+ # The model that you're versioning needs to have a 'version' attribute. The model is versioned
129
+ # into a table called #{model}_versions where the model name is singlular. The _versions table should
130
+ # contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field.
131
+ #
132
+ # A lock_version field is also accepted if your model uses Optimistic Locking. If your table uses Single Table inheritance,
133
+ # then that field is reflected in the versioned model as 'versioned_type' by default.
134
+ #
135
+ # Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table
136
+ # method, perfect for a migration. It will also create the version column if the main model does not already have it.
137
+ #
138
+ # class AddVersions < ActiveRecord::Migration
139
+ # def self.up
140
+ # # create_versioned_table takes the same options hash
141
+ # # that create_table does
142
+ # Post.create_versioned_table
143
+ # end
144
+ #
145
+ # def self.down
146
+ # Post.drop_versioned_table
147
+ # end
148
+ # end
149
+ #
150
+ # == Changing What Fields Are Versioned
151
+ #
152
+ # By default, acts_as_versioned will version all but these fields:
153
+ #
154
+ # [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column]
155
+ #
156
+ # You can add or change those by modifying #non_versioned_columns. Note that this takes strings and not symbols.
157
+ #
158
+ # class Post < ActiveRecord::Base
159
+ # acts_as_versioned
160
+ # self.non_versioned_columns << 'comments_count'
161
+ # end
162
+ #
163
+ def acts_as_versioned(options = {}, &extension)
164
+ # don't allow multiple calls
165
+ return if self.included_modules.include?(ActiveRecord::Acts::Versioned::Behaviors)
166
+
167
+ cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column,
168
+ :version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns,
169
+ :version_association_options, :version_if_changed
170
+
171
+ self.versioned_class_name = options[:class_name] || "Version"
172
+ self.versioned_foreign_key = options[:foreign_key] || self.to_s.foreign_key
173
+ self.versioned_table_name = options[:table_name] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}"
174
+ self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}"
175
+ self.version_column = options[:version_column] || 'version'
176
+ self.version_sequence_name = options[:sequence_name]
177
+ self.max_version_limit = options[:limit].to_i
178
+ self.version_condition = options[:if] || true
179
+ self.non_versioned_columns = [self.primary_key, inheritance_column, self.version_column, 'lock_version', versioned_inheritance_column] + options[:non_versioned_columns].to_a.map(&:to_s)
180
+ self.version_association_options = {
181
+ :class_name => "#{self.to_s}::#{versioned_class_name}",
182
+ :foreign_key => versioned_foreign_key,
183
+ :dependent => :delete_all
184
+ }.merge(options[:association_options] || {})
185
+
186
+ if block_given?
187
+ extension_module_name = "#{versioned_class_name}Extension"
188
+ silence_warnings do
189
+ self.const_set(extension_module_name, Module.new(&extension))
190
+ end
191
+
192
+ options[:extend] = self.const_get(extension_module_name)
193
+ end
194
+
195
+ unless options[:if_changed].nil?
196
+ self.track_altered_attributes = true
197
+ options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)
198
+ self.version_if_changed = options[:if_changed].map(&:to_s)
199
+ end
200
+
201
+ include options[:extend] if options[:extend].is_a?(Module)
202
+
203
+ include ActiveRecord::Acts::Versioned::Behaviors
204
+
205
+ #
206
+ # Create the dynamic versioned model
207
+ #
208
+ const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do
209
+ def self.reloadable?;
210
+ false;
211
+ end
212
+
213
+ # find first version before the given version
214
+ def self.before(version)
215
+ where(["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version]).
216
+ order('version DESC').
217
+ first
218
+ end
219
+
220
+ # find first version after the given version.
221
+ def self.after(version)
222
+ where(["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version]).
223
+ order('version ASC').
224
+ first
225
+ end
226
+
227
+ # finds earliest version of this record
228
+ def self.earliest
229
+ order("#{original_class.version_column}").first
230
+ end
231
+
232
+ # find latest version of this record
233
+ def self.latest
234
+ order("#{original_class.version_column} desc").first
235
+ end
236
+
237
+ def previous
238
+ self.class.before(self)
239
+ end
240
+
241
+ def next
242
+ self.class.after(self)
243
+ end
244
+
245
+ def versions_count
246
+ page.version
247
+ end
248
+ end
249
+
250
+ versioned_class.cattr_accessor :original_class
251
+ versioned_class.original_class = self
252
+ versioned_class.set_table_name versioned_table_name
253
+ versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym,
254
+ :class_name => "::#{self.to_s}",
255
+ :foreign_key => versioned_foreign_key
256
+ versioned_class.send :include, options[:extend] if options[:extend].is_a?(Module)
257
+ versioned_class.set_sequence_name version_sequence_name if version_sequence_name
258
+ end
259
+
260
+ module Behaviors
261
+ extend ActiveSupport::Concern
262
+
263
+ included do
264
+ has_many :versions, self.version_association_options
265
+
266
+ before_save :set_new_version
267
+ after_save :save_version
268
+ after_save :clear_old_versions
269
+ end
270
+
271
+ module InstanceMethods
272
+ # Saves a version of the model in the versioned table. This is called in the after_save callback by default
273
+ def save_version
274
+ if @saving_version
275
+ @saving_version = nil
276
+ rev = self.class.versioned_class.new
277
+ clone_versioned_model(self, rev)
278
+ rev.send("#{self.class.version_column}=", send(self.class.version_column))
279
+ rev.send("#{self.class.versioned_foreign_key}=", id)
280
+ rev.save
281
+ end
282
+ end
283
+
284
+ # Clears old revisions if a limit is set with the :limit option in <tt>acts_as_versioned</tt>.
285
+ # Override this method to set your own criteria for clearing old versions.
286
+ def clear_old_versions
287
+ return if self.class.max_version_limit == 0
288
+ excess_baggage = send(self.class.version_column).to_i - self.class.max_version_limit
289
+ if excess_baggage > 0
290
+ self.class.versioned_class.delete_all ["#{self.class.version_column} <= ? and #{self.class.versioned_foreign_key} = ?", excess_baggage, id]
291
+ end
292
+ end
293
+
294
+ # Reverts a model to a given version. Takes either a version number or an instance of the versioned model
295
+ def revert_to(version)
296
+ if version.is_a?(self.class.versioned_class)
297
+ return false unless version.send(self.class.versioned_foreign_key) == id and !version.new_record?
298
+ else
299
+ return false unless version = versions.where(self.class.version_column => version).first
300
+ end
301
+ self.clone_versioned_model(version, self)
302
+ send("#{self.class.version_column}=", version.send(self.class.version_column))
303
+ true
304
+ end
305
+
306
+ # Reverts a model to a given version and saves the model.
307
+ # Takes either a version number or an instance of the versioned model
308
+ def revert_to!(version)
309
+ revert_to(version) ? save_without_revision : false
310
+ end
311
+
312
+ # Temporarily turns off Optimistic Locking while saving. Used when reverting so that a new version is not created.
313
+ def save_without_revision
314
+ save_without_revision!
315
+ true
316
+ rescue
317
+ false
318
+ end
319
+
320
+ def save_without_revision!
321
+ without_locking do
322
+ without_revision do
323
+ save!
324
+ end
325
+ end
326
+ end
327
+
328
+ def altered?
329
+ track_altered_attributes ? (version_if_changed - changed).length < version_if_changed.length : changed?
330
+ end
331
+
332
+ # Clones a model. Used when saving a new version or reverting a model's version.
333
+ def clone_versioned_model(orig_model, new_model)
334
+ self.class.versioned_columns.each do |col|
335
+ new_model[col.name] = orig_model.send(col.name) if orig_model.has_attribute?(col.name)
336
+ end
337
+
338
+ if orig_model.is_a?(self.class.versioned_class)
339
+ new_model[new_model.class.inheritance_column] = orig_model[self.class.versioned_inheritance_column]
340
+ elsif new_model.is_a?(self.class.versioned_class)
341
+ new_model[self.class.versioned_inheritance_column] = orig_model[orig_model.class.inheritance_column]
342
+ end
343
+ end
344
+
345
+ # Checks whether a new version shall be saved or not. Calls <tt>version_condition_met?</tt> and <tt>changed?</tt>.
346
+ def save_version?
347
+ version_condition_met? && altered?
348
+ end
349
+
350
+ # Checks condition set in the :if option to check whether a revision should be created or not. Override this for
351
+ # custom version condition checking.
352
+ def version_condition_met?
353
+ case
354
+ when version_condition.is_a?(Symbol)
355
+ send(version_condition)
356
+ when version_condition.respond_to?(:call) && (version_condition.arity == 1 || version_condition.arity == -1)
357
+ version_condition.call(self)
358
+ else
359
+ version_condition
360
+ end
361
+ end
362
+
363
+ # Executes the block with the versioning callbacks disabled.
364
+ #
365
+ # @foo.without_revision do
366
+ # @foo.save
367
+ # end
368
+ #
369
+ def without_revision(&block)
370
+ self.class.without_revision(&block)
371
+ end
372
+
373
+ # Turns off optimistic locking for the duration of the block
374
+ #
375
+ # @foo.without_locking do
376
+ # @foo.save
377
+ # end
378
+ #
379
+ def without_locking(&block)
380
+ self.class.without_locking(&block)
381
+ end
382
+
383
+ def empty_callback()
384
+ end
385
+
386
+ #:nodoc:
387
+
388
+ protected
389
+ # sets the new version before saving, unless you're using optimistic locking. In that case, let it take care of the version.
390
+ def set_new_version
391
+ @saving_version = new_record? || save_version?
392
+ self.send("#{self.class.version_column}=", next_version) if new_record? || (!locking_enabled? && save_version?)
393
+ end
394
+
395
+ # Gets the next available version for the current record, or 1 for a new record
396
+ def next_version
397
+ (new_record? ? 0 : versions.calculate(:maximum, version_column).to_i) + 1
398
+ end
399
+ end
400
+
401
+ module ClassMethods
402
+ # Returns an array of columns that are versioned. See non_versioned_columns
403
+ def versioned_columns
404
+ @versioned_columns ||= columns.select { |c| !non_versioned_columns.include?(c.name) }
405
+ end
406
+
407
+ # Returns an instance of the dynamic versioned model
408
+ def versioned_class
409
+ const_get versioned_class_name
410
+ end
411
+
412
+ # Rake migration task to create the versioned table using options passed to acts_as_versioned
413
+ def create_versioned_table(create_table_options = {})
414
+ # create version column in main table if it does not exist
415
+ if !self.content_columns.find { |c| [version_column.to_s, 'lock_version'].include? c.name }
416
+ self.connection.add_column table_name, version_column, :integer
417
+ self.reset_column_information
418
+ end
419
+
420
+ return if connection.table_exists?(versioned_table_name)
421
+
422
+ self.connection.create_table(versioned_table_name, create_table_options) do |t|
423
+ t.column versioned_foreign_key, :integer
424
+ t.column version_column, :integer
425
+ end
426
+
427
+ self.versioned_columns.each do |col|
428
+ limit = col.limit
429
+ if col.limit == 10 and col.type == :integer
430
+ # Avoid 'No integer type has byte size 10' under MySQL
431
+ limit = 8
432
+ end
433
+ self.connection.add_column versioned_table_name, col.name, col.type,
434
+ :limit => limit,
435
+ :default => col.default,
436
+ :scale => col.scale,
437
+ :precision => col.precision
438
+ end
439
+
440
+ if type_col = self.columns_hash[inheritance_column]
441
+ self.connection.add_column versioned_table_name, versioned_inheritance_column, type_col.type,
442
+ :limit => type_col.limit,
443
+ :default => type_col.default,
444
+ :scale => type_col.scale,
445
+ :precision => type_col.precision
446
+ end
447
+
448
+ # Make sure not to create an index that is too long (rails limits index names to 64 characters from version 3.0.3)
449
+ name = 'index_' + versioned_table_name + '_on_' + versioned_foreign_key
450
+ self.connection.add_index versioned_table_name, versioned_foreign_key, :name => name[0,63]
451
+ end
452
+
453
+ # Rake migration task to drop the versioned table
454
+ def drop_versioned_table
455
+ self.connection.drop_table versioned_table_name
456
+ end
457
+
458
+ # Executes the block with the versioning callbacks disabled.
459
+ #
460
+ # Foo.without_revision do
461
+ # @foo.save
462
+ # end
463
+ #
464
+ def without_revision(&block)
465
+ class_eval do
466
+ CALLBACKS.each do |attr_name|
467
+ alias_method "orig_#{attr_name}".to_sym, attr_name
468
+ alias_method attr_name, :empty_callback
469
+ end
470
+ end
471
+ block.call
472
+ ensure
473
+ class_eval do
474
+ CALLBACKS.each do |attr_name|
475
+ alias_method attr_name, "orig_#{attr_name}".to_sym
476
+ end
477
+ end
478
+ end
479
+
480
+ # Turns off optimistic locking for the duration of the block
481
+ #
482
+ # Foo.without_locking do
483
+ # @foo.save
484
+ # end
485
+ #
486
+ def without_locking(&block)
487
+ current = ActiveRecord::Base.lock_optimistically
488
+ ActiveRecord::Base.lock_optimistically = false if current
489
+ begin
490
+ block.call
491
+ ensure
492
+ ActiveRecord::Base.lock_optimistically = true if current
493
+ end
494
+ end
495
+ end
496
+ end
497
+ end
498
+ end
499
+ end
500
+
501
+ ActiveRecord::Base.extend ActiveRecord::Acts::Versioned
@@ -0,0 +1,49 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.setup(:default, :development)
4
+
5
+ $:.unshift(File.dirname(__FILE__) + '/../lib')
6
+ require 'test/unit'
7
+ require 'active_support'
8
+ require 'active_record'
9
+ require 'active_record/fixtures'
10
+ require 'active_record/test_case'
11
+
12
+ begin
13
+ require 'ruby-debug'
14
+ Debugger.start
15
+ rescue LoadError
16
+ end
17
+
18
+ require 'acts_as_versioned'
19
+
20
+ config = YAML::load(IO.read(File.dirname(__FILE__) + '/database.yml'))
21
+ ActiveRecord::Base.logger = Logger.new(File.dirname(__FILE__) + "/debug.log")
22
+ ActiveRecord::Base.configurations = {'test' => config[ENV['DB'] || 'sqlite3']}
23
+ ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test'])
24
+
25
+ load(File.dirname(__FILE__) + "/schema.rb")
26
+
27
+ # set up custom sequence on widget_versions for DBs that support sequences
28
+ if ENV['DB'] == 'postgresql'
29
+ ActiveRecord::Base.connection.execute "DROP SEQUENCE widgets_seq;" rescue nil
30
+ ActiveRecord::Base.connection.remove_column :widget_versions, :id
31
+ ActiveRecord::Base.connection.execute "CREATE SEQUENCE widgets_seq START 101;"
32
+ ActiveRecord::Base.connection.execute "ALTER TABLE widget_versions ADD COLUMN id INTEGER PRIMARY KEY DEFAULT nextval('widgets_seq');"
33
+ end
34
+
35
+ class ActiveSupport::TestCase #:nodoc:
36
+ include ActiveRecord::TestFixtures
37
+
38
+ self.fixture_path = File.dirname(__FILE__) + "/fixtures/"
39
+
40
+ # Turn off transactional fixtures if you're working with MyISAM tables in MySQL
41
+ self.use_transactional_fixtures = true
42
+
43
+ # Instantiated fixtures are slow, but give you @david where you otherwise would need people(:david)
44
+ self.use_instantiated_fixtures = false
45
+
46
+ # Add more helper methods to be used by all tests here...
47
+ end
48
+
49
+ $:.unshift(ActiveSupport::TestCase.fixture_path)
data/test/database.yml ADDED
@@ -0,0 +1,18 @@
1
+ sqlite:
2
+ adapter: sqlite
3
+ dbfile: acts_as_versioned_plugin.sqlite.db
4
+ sqlite3:
5
+ adapter: sqlite3
6
+ database: acts_as_versioned_plugin.sqlite3.db
7
+ postgresql:
8
+ adapter: postgresql
9
+ username: postgres
10
+ password: postgres
11
+ database: acts_as_versioned_plugin_test
12
+ min_messages: ERROR
13
+ mysql:
14
+ adapter: mysql
15
+ host: localhost
16
+ username: rails
17
+ password:
18
+ database: acts_as_versioned_plugin_test
@@ -0,0 +1,6 @@
1
+ caged:
2
+ id: 1
3
+ name: caged
4
+ mly:
5
+ id: 2
6
+ name: mly
@@ -0,0 +1,3 @@
1
+ class Landmark < ActiveRecord::Base
2
+ acts_as_versioned :if_changed => [ :name, :longitude, :latitude ]
3
+ end
@@ -0,0 +1,7 @@
1
+ washington:
2
+ id: 1
3
+ landmark_id: 1
4
+ version: 1
5
+ name: Washington, D.C.
6
+ latitude: 38.895
7
+ longitude: -77.036667
@@ -0,0 +1,7 @@
1
+ washington:
2
+ id: 1
3
+ name: Washington, D.C.
4
+ latitude: 38.895
5
+ longitude: -77.036667
6
+ doesnt_trigger_version: This is not important
7
+ version: 1
@@ -0,0 +1,10 @@
1
+ welcome:
2
+ id: 1
3
+ title: Welcome to the weblog
4
+ lock_version: 24
5
+ type: LockedPage
6
+ thinking:
7
+ id: 2
8
+ title: So I was thinking
9
+ lock_version: 24
10
+ type: SpecialLockedPage
@@ -0,0 +1,27 @@
1
+ welcome_1:
2
+ id: 1
3
+ page_id: 1
4
+ title: Welcome to the weblg
5
+ lock_version: 23
6
+ version_type: LockedPage
7
+
8
+ welcome_2:
9
+ id: 2
10
+ page_id: 1
11
+ title: Welcome to the weblog
12
+ lock_version: 24
13
+ version_type: LockedPage
14
+
15
+ thinking_1:
16
+ id: 3
17
+ page_id: 2
18
+ title: So I was thinking!!!
19
+ lock_version: 23
20
+ version_type: SpecialLockedPage
21
+
22
+ thinking_2:
23
+ id: 4
24
+ page_id: 2
25
+ title: So I was thinking
26
+ lock_version: 24
27
+ version_type: SpecialLockedPage
@@ -0,0 +1,15 @@
1
+ class AddVersionedTables < ActiveRecord::Migration
2
+ def self.up
3
+ create_table("things") do |t|
4
+ t.column :title, :text
5
+ t.column :price, :decimal, :precision => 7, :scale => 2
6
+ t.column :type, :string
7
+ end
8
+ Thing.create_versioned_table
9
+ end
10
+
11
+ def self.down
12
+ Thing.drop_versioned_table
13
+ drop_table "things" rescue nil
14
+ end
15
+ end