rtiss_acts_as_versioned 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,625 @@
1
+ # -*- encoding : utf-8 -*-
2
+ # Copyright (c) 2005 Rick Olson
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ VERSION = '0.6.2'
24
+
25
+ module ActiveRecord #:nodoc:
26
+ module Acts #:nodoc:
27
+ # Specify this act if you want to save a copy of the row in a versioned table. This assumes there is a
28
+ # versioned table ready and that your model has a version field. This works with optimistic locking if the lock_version
29
+ # column is present as well.
30
+ #
31
+ # 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
32
+ # your container for the changes to be reflected. In development mode this usually means restarting WEBrick.
33
+ #
34
+ # class Page < ActiveRecord::Base
35
+ # # assumes pages_versions table
36
+ # acts_as_versioned
37
+ # end
38
+ #
39
+ # Example:
40
+ #
41
+ # page = Page.create(:title => 'hello world!')
42
+ # page.version # => 1
43
+ #
44
+ # page.title = 'hello world'
45
+ # page.save
46
+ # page.version # => 2
47
+ # page.versions.size # => 2
48
+ #
49
+ # page.revert_to(1) # using version number
50
+ # page.title # => 'hello world!'
51
+ #
52
+ # page.revert_to(page.versions.last) # using versioned instance
53
+ # page.title # => 'hello world'
54
+ #
55
+ # page.versions.earliest # efficient query to find the first version
56
+ # page.versions.latest # efficient query to find the most recently created version
57
+ #
58
+ #
59
+ # Simple Queries to page between versions
60
+ #
61
+ # page.versions.before(version)
62
+ # page.versions.after(version)
63
+ #
64
+ # Access the previous/next versions from the versioned model itself
65
+ #
66
+ # version = page.versions.latest
67
+ # version.previous # go back one version
68
+ # version.next # go forward one version
69
+ #
70
+ # See ActiveRecord::Acts::Versioned::ClassMethods#acts_as_versioned for configuration options
71
+ module Versioned
72
+ CALLBACKS = [:set_new_version, :save_version, :save_version?]
73
+ def self.included(base) # :nodoc:
74
+ base.extend ClassMethods
75
+ end
76
+
77
+ module ClassMethods
78
+ # == Configuration options
79
+ #
80
+ # * <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example)
81
+ # * <tt>table_name</tt> - versioned model table name (default: page_versions in the above example)
82
+ # * <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example)
83
+ # * <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI. (default: versioned_type)
84
+ # * <tt>version_column</tt> - name of the column in the model that keeps the version number (default: version)
85
+ # * <tt>sequence_name</tt> - name of the custom sequence to be used by the versioned model.
86
+ # * <tt>limit</tt> - number of revisions to keep, defaults to unlimited
87
+ # * <tt>if</tt> - symbol of method to check before saving a new version. If this method returns false, a new version is not saved.
88
+ # For finer control, pass either a Proc or modify Model#version_condition_met?
89
+ #
90
+ # acts_as_versioned :if => Proc.new { |auction| !auction.expired? }
91
+ #
92
+ # or...
93
+ #
94
+ # class Auction
95
+ # def version_condition_met? # totally bypasses the <tt>:if</tt> option
96
+ # !expired?
97
+ # end
98
+ # end
99
+ #
100
+ # * <tt>if_changed</tt> - Simple way of specifying attributes that are required to be changed before saving a model. This takes
101
+ # either a symbol or array of symbols.
102
+ #
103
+ # * <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
104
+ # to create an anonymous mixin:
105
+ #
106
+ # class Auction
107
+ # acts_as_versioned do
108
+ # def started?
109
+ # !started_at.nil?
110
+ # end
111
+ # end
112
+ # end
113
+ #
114
+ # or...
115
+ #
116
+ # module AuctionExtension
117
+ # def started?
118
+ # !started_at.nil?
119
+ # end
120
+ # end
121
+ # class Auction
122
+ # acts_as_versioned :extend => AuctionExtension
123
+ # end
124
+ #
125
+ # Example code:
126
+ #
127
+ # @auction = Auction.find(1)
128
+ # @auction.started?
129
+ # @auction.versions.first.started?
130
+ #
131
+ # == Database Schema
132
+ #
133
+ # The model that you're versioning needs to have a 'version' attribute. The model is versioned
134
+ # into a table called #{model}_versions where the model name is singlular. The _versions table should
135
+ # contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field.
136
+ #
137
+ # A lock_version field is also accepted if your model uses Optimistic Locking. If your table uses Single Table inheritance,
138
+ # then that field is reflected in the versioned model as 'versioned_type' by default.
139
+ #
140
+ # Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table
141
+ # method, perfect for a migration. It will also create the version column if the main model does not already have it.
142
+ #
143
+ # class AddVersions < ActiveRecord::Migration
144
+ # def self.up
145
+ # # create_versioned_table takes the same options hash
146
+ # # that create_table does
147
+ # Post.create_versioned_table
148
+ # end
149
+ #
150
+ # def self.down
151
+ # Post.drop_versioned_table
152
+ # end
153
+ # end
154
+ #
155
+ # == Changing What Fields Are Versioned
156
+ #
157
+ # By default, acts_as_versioned will version all but these fields:
158
+ #
159
+ # [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column]
160
+ #
161
+ # You can add or change those by modifying #non_versioned_columns. Note that this takes strings and not symbols.
162
+ #
163
+ # class Post < ActiveRecord::Base
164
+ # acts_as_versioned
165
+ # self.non_versioned_columns << 'comments_count'
166
+ # end
167
+ #
168
+ def acts_as_versioned(options = {}, &extension)
169
+ # don't allow multiple calls
170
+ return if self.included_modules.include?(ActiveRecord::Acts::Versioned::ActMethods)
171
+
172
+ send :include, ActiveRecord::Acts::Versioned::ActMethods
173
+
174
+ cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column,
175
+ :version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns,
176
+ :version_association_options, :version_if_changed, :deleted_in_original_table_flag, :record_restored_flag
177
+
178
+ self.versioned_class_name = options[:class_name] || "Version"
179
+ self.versioned_foreign_key = options[:foreign_key] || self.to_s.foreign_key
180
+ self.versioned_table_name = options[:table_name] || if self.table_name then "#{table_name}_h" else "#{table_name_prefix}#{base_class.name.demodulize.underscore}_h#{table_name_suffix}" end
181
+
182
+ self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}"
183
+ self.version_column = options[:version_column] || 'version'
184
+ self.deleted_in_original_table_flag = options[:deleted_in_original_table_flag] || 'deleted_in_original_table'
185
+ self.record_restored_flag = options[:record_restored_flag] || 'record_restored'
186
+ self.version_sequence_name = options[:sequence_name]
187
+ self.max_version_limit = options[:limit].to_i
188
+ self.version_condition = options[:if] || true
189
+ 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)
190
+ self.version_association_options = {
191
+ :class_name => "#{self.to_s}::#{versioned_class_name}",
192
+ :foreign_key => versioned_foreign_key
193
+ }.merge(options[:association_options] || {})
194
+
195
+ if block_given?
196
+ extension_module_name = "#{versioned_class_name}Extension"
197
+ silence_warnings do
198
+ self.const_set(extension_module_name, Module.new(&extension))
199
+ end
200
+
201
+ options[:extend] = self.const_get(extension_module_name)
202
+ end
203
+
204
+ class_eval <<-CLASS_METHODS
205
+ has_many :versions, version_association_options do
206
+ # finds earliest version of this record
207
+ def earliest
208
+ @earliest ||= find(:first, :order => '#{version_column}')
209
+ end
210
+
211
+ # find latest version of this record
212
+ def latest
213
+ @latest ||= find(:first, :order => '#{version_column} desc')
214
+ end
215
+ end
216
+ before_save :set_new_version
217
+ after_save :save_version
218
+ after_save :clear_old_versions
219
+ after_destroy :set_deleted_flag
220
+
221
+ unless options[:if_changed].nil?
222
+ self.track_altered_attributes = true
223
+ options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)
224
+ self.version_if_changed = options[:if_changed].map(&:to_s)
225
+ end
226
+
227
+ include options[:extend] if options[:extend].is_a?(Module)
228
+ CLASS_METHODS
229
+
230
+ # create the dynamic versioned model
231
+ const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do
232
+ def self.reloadable? ; false ; end
233
+ # find first version before the given version
234
+ def self.before(version)
235
+ find :first, :order => 'version desc',
236
+ :conditions => ["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version]
237
+ end
238
+
239
+ # find first version after the given version.
240
+ def self.after(version)
241
+ find :first, :order => 'version',
242
+ :conditions => ["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version]
243
+ end
244
+
245
+ def previous
246
+ self.class.before(self)
247
+ end
248
+
249
+ def next
250
+ self.class.after(self)
251
+ end
252
+
253
+ def versions_count
254
+ page.version # TODO: ?!
255
+ end
256
+
257
+ def restore(perform_validation = true)
258
+ id = self.send(self.original_class.versioned_foreign_key)
259
+ if self.original_class.exists?(id)
260
+ raise RuntimeError.new("Record exists in restore, id = #{id} class = #{self.class.name}")
261
+ end
262
+
263
+ version_hash = self.attributes
264
+ version_hash.delete "id"
265
+ version_hash.delete self.original_class.deleted_in_original_table_flag.to_s
266
+ version_hash.delete self.original_class.record_restored_flag.to_s
267
+ version_hash.delete self.original_class.versioned_foreign_key.to_s
268
+
269
+ restored_record = self.original_class.new(version_hash)
270
+ restored_record.id = id
271
+ if restored_record.respond_to? :updated_at=
272
+ restored_record.updated_at = Time.now
273
+ end
274
+ unless restored_record.save_without_revision(perform_validation)
275
+ raise RuntimeError.new("Couldn't restore the record, id = #{id} class = #{self.class.name}")
276
+ end
277
+
278
+ new_version = clone
279
+ new_version.version += 1
280
+ new_version.send("#{self.original_class.deleted_in_original_table_flag}=", false)
281
+ new_version.send("#{self.original_class.record_restored_flag}=", true)
282
+ if new_version.respond_to? :updated_at=
283
+ new_version.updated_at = Time.now
284
+ end
285
+ new_version.save!
286
+ end
287
+
288
+ def original_record_exists?
289
+ original_class.exists?(self.send original_class.versioned_foreign_key)
290
+ end
291
+ end
292
+
293
+ versioned_class.cattr_accessor :original_class
294
+ versioned_class.original_class = self
295
+ versioned_class.set_table_name versioned_table_name
296
+ versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym,
297
+ :class_name => "::#{self.to_s}",
298
+ :foreign_key => versioned_foreign_key
299
+ versioned_class.send :include, options[:extend] if options[:extend].is_a?(Module)
300
+ versioned_class.set_sequence_name version_sequence_name if version_sequence_name
301
+ end
302
+ end
303
+
304
+ module ActMethods
305
+ def self.included(base) # :nodoc:
306
+ base.extend ClassMethods
307
+ end
308
+
309
+ # Saves a version of the model in the versioned table. This is called in the after_save callback by default
310
+ def save_version
311
+ if @saving_version
312
+ @saving_version = nil
313
+ rev = self.class.versioned_class.new
314
+ clone_versioned_model(self, rev)
315
+ rev.send("#{self.class.version_column}=", send(self.class.version_column))
316
+ rev.send("#{self.class.versioned_foreign_key}=", id)
317
+ rev.send("#{self.class.deleted_in_original_table_flag}=", false)
318
+ rev.send("#{self.class.record_restored_flag}=", false)
319
+ if rev.respond_to? :updated_at=
320
+ rev.updated_at = Time.now
321
+ end
322
+ rev.save
323
+ end
324
+ end
325
+
326
+ def set_deleted_flag
327
+ return if self.id.nil?
328
+
329
+ rev = self.class.versioned_class.new
330
+ clone_versioned_model(self, rev)
331
+ rev.send("#{self.class.version_column}=", highest_version+1)
332
+ rev.send("#{self.class.versioned_foreign_key}=", id)
333
+ rev.send("#{self.class.deleted_in_original_table_flag}=", true)
334
+ rev.send("#{self.class.record_restored_flag}=", false)
335
+ if rev.respond_to? :updated_at=
336
+ rev.updated_at = Time.now
337
+ end
338
+ rev.save
339
+ end
340
+
341
+ # Clears old revisions if a limit is set with the :limit option in <tt>acts_as_versioned</tt>.
342
+ # Override this method to set your own criteria for clearing old versions.
343
+ def clear_old_versions
344
+ return if self.class.max_version_limit == 0
345
+ excess_baggage = send(self.class.version_column).to_i - self.class.max_version_limit
346
+ if excess_baggage > 0
347
+ self.class.versioned_class.delete_all ["#{self.class.version_column} <= ? and #{self.class.versioned_foreign_key} = ?", excess_baggage, id]
348
+ end
349
+ end
350
+
351
+ # Reverts a model to a given version. Takes either a version number or an instance of the versioned model
352
+ def revert_to(version)
353
+ if version.is_a?(self.class.versioned_class)
354
+ return false unless version.send(self.class.versioned_foreign_key) == id and !version.new_record?
355
+ else
356
+ return false unless version = versions.send("find_by_#{self.class.version_column}", version)
357
+ end
358
+ self.clone_versioned_model(version, self)
359
+ send("#{self.class.version_column}=", version.send(self.class.version_column))
360
+ true
361
+ end
362
+
363
+ # Reverts a model to a given version and saves the model.
364
+ # Takes either a version number or an instance of the versioned model
365
+ def revert_to!(version)
366
+ revert_to(version) ? save_without_revision : false
367
+ end
368
+
369
+ # Temporarily turns off Optimistic Locking while saving. Used when reverting so that a new version is not created.
370
+ def save_without_revision(perform_validation = true)
371
+ ret = false
372
+ without_locking do
373
+ without_revision do
374
+ ret = save(perform_validation)
375
+ end
376
+ end
377
+ return ret
378
+ end
379
+
380
+ def save_without_revision!
381
+ without_locking do
382
+ without_revision do
383
+ save!
384
+ end
385
+ end
386
+ end
387
+
388
+ def altered?
389
+ track_altered_attributes ? (version_if_changed - changed).length < version_if_changed.length : changed?
390
+ end
391
+
392
+ # Clones a model. Used when saving a new version or reverting a model's version.
393
+ def clone_versioned_model(orig_model, new_model)
394
+ self.class.versioned_columns.each do |col|
395
+ new_model.send("#{col.name}=", orig_model.send(col.name)) if orig_model.has_attribute?(col.name)
396
+ end
397
+
398
+ if orig_model.is_a?(self.class.versioned_class)
399
+ new_model[new_model.class.inheritance_column] = orig_model[self.class.versioned_inheritance_column]
400
+ elsif new_model.is_a?(self.class.versioned_class)
401
+ new_model[self.class.versioned_inheritance_column] = orig_model[orig_model.class.inheritance_column]
402
+ end
403
+ end
404
+
405
+ # Checks whether a new version shall be saved or not. Calls <tt>version_condition_met?</tt> and <tt>changed?</tt>.
406
+ def save_version?
407
+ version_condition_met? && altered?
408
+ end
409
+
410
+ # Checks condition set in the :if option to check whether a revision should be created or not. Override this for
411
+ # custom version condition checking.
412
+ def version_condition_met?
413
+ case
414
+ when version_condition.is_a?(Symbol)
415
+ send(version_condition)
416
+ when version_condition.respond_to?(:call) && (version_condition.arity == 1 || version_condition.arity == -1)
417
+ version_condition.call(self)
418
+ else
419
+ version_condition
420
+ end
421
+ end
422
+
423
+ # Executes the block with the versioning callbacks disabled.
424
+ #
425
+ # @foo.without_revision do
426
+ # @foo.save
427
+ # end
428
+ #
429
+ def without_revision(&block)
430
+ self.class.without_revision(&block)
431
+ end
432
+
433
+ # Turns off optimistic locking for the duration of the block
434
+ #
435
+ # @foo.without_locking do
436
+ # @foo.save
437
+ # end
438
+ #
439
+ def without_locking(&block)
440
+ self.class.without_locking(&block)
441
+ end
442
+
443
+ def empty_callback() end #:nodoc:
444
+
445
+ def find_versions(*args)
446
+ return [] if self.id.nil?
447
+
448
+ options = args.extract_options!
449
+ version_condition = "#{self.class.versioned_foreign_key} = #{self.id}"
450
+ if options[:conditions] then
451
+ options[:conditions] += " and #{version_condition}"
452
+ else
453
+ options[:conditions] = version_condition
454
+ end
455
+ if args.first.is_a?(Symbol)
456
+ versions.find(args.first, options)
457
+ else # TODO: is_a?(Fixnum)
458
+ versions.find(options)
459
+ end
460
+ end
461
+
462
+ def find_newest_version
463
+ return nil if self.id.nil?
464
+
465
+ self.class.versioned_class.find(:first, :conditions => "#{self.class.versioned_foreign_key} = #{self.id}", :order => "version DESC")
466
+ end
467
+
468
+ def highest_version
469
+ v = find_newest_version
470
+ if v then
471
+ v.version
472
+ else
473
+ -1
474
+ end
475
+ end
476
+
477
+ def find_version(version)
478
+ return nil if self.id.nil?
479
+
480
+ ret = self.class.versioned_class.find(:first, :conditions => "#{self.class.versioned_foreign_key} = #{self.id} and version=#{version}") # TODO: version column
481
+ raise "find_version: version #{version} not found in database" unless ret
482
+ ret
483
+ end
484
+
485
+ protected
486
+ # sets the new version before saving, unless you're using optimistic locking. In that case, let it take care of the version.
487
+ def set_new_version
488
+ @saving_version = new_record? || save_version?
489
+ self.send("#{self.class.version_column}=", next_version) if new_record? || (!locking_enabled? && save_version?)
490
+ end
491
+
492
+ # Gets the next available version for the current record, or 1 for a new record
493
+ def next_version
494
+ (new_record? ? 0 : versions.calculate(:max, version_column).to_i) + 1
495
+ end
496
+
497
+ module ClassMethods
498
+ # Returns an array of columns that are versioned. See non_versioned_columns
499
+ def versioned_columns
500
+ @versioned_columns ||= columns.select { |c| !non_versioned_columns.include?(c.name) }
501
+ end
502
+
503
+ # Returns an instance of the dynamic versioned model
504
+ def versioned_class
505
+ const_get versioned_class_name
506
+ end
507
+
508
+ # Rake migration task to create the versioned table using options passed to acts_as_versioned
509
+ def create_versioned_table(create_table_options = {})
510
+ # create version column in main table if it does not exist
511
+ if !self.content_columns.find { |c| [version_column.to_s, 'lock_version'].include? c.name }
512
+ self.connection.add_column table_name, version_column, :integer
513
+ self.reset_column_information
514
+ end
515
+
516
+ return if connection.table_exists?(versioned_table_name)
517
+
518
+ self.connection.create_table(versioned_table_name, create_table_options) do |t|
519
+ t.column versioned_foreign_key, :integer
520
+ t.column version_column, :integer
521
+ t.column deleted_in_original_table_flag, :boolean, :default => false
522
+ t.column record_restored_flag, :boolean, :default => false
523
+ end
524
+
525
+ self.versioned_columns.each do |col|
526
+ self.connection.add_column versioned_table_name, col.name, col.type,
527
+ :limit => col.limit,
528
+ :default => col.default,
529
+ :scale => col.scale,
530
+ :precision => col.precision
531
+ end
532
+
533
+ if type_col = self.columns_hash[inheritance_column]
534
+ self.connection.add_column versioned_table_name, versioned_inheritance_column, type_col.type,
535
+ :limit => type_col.limit,
536
+ :default => type_col.default,
537
+ :scale => type_col.scale,
538
+ :precision => type_col.precision
539
+ end
540
+
541
+ #self.connection.add_index versioned_table_name, versioned_foreign_key
542
+ end
543
+
544
+ # Rake migration task to drop the versioned table
545
+ def drop_versioned_table
546
+ self.connection.drop_table versioned_table_name
547
+ end
548
+
549
+ def restore_deleted(id)
550
+ version_record = versioned_class.find(:first, :conditions => "#{versioned_foreign_key} = #{id}", :order => "version DESC")
551
+ version_record.restore
552
+ end
553
+
554
+ def restore_deleted_version(id, version)
555
+ version_record = versioned_class.find(:first, :conditions => "#{versioned_foreign_key} = #{id} and version = #{version}")
556
+ version_record.restore
557
+ end
558
+
559
+ # Executes the block with the versioning callbacks disabled.
560
+ #
561
+ # Foo.without_revision do
562
+ # @foo.save
563
+ # end
564
+ #
565
+ def without_revision(&block)
566
+ class_eval do
567
+ CALLBACKS.each do |attr_name|
568
+ alias_method "orig_#{attr_name}".to_sym, attr_name
569
+ alias_method attr_name, :empty_callback
570
+ end
571
+ end
572
+ block.call
573
+ ensure
574
+ class_eval do
575
+ CALLBACKS.each do |attr_name|
576
+ alias_method attr_name, "orig_#{attr_name}".to_sym
577
+ end
578
+ end
579
+ end
580
+
581
+ # Turns off optimistic locking for the duration of the block
582
+ #
583
+ # Foo.without_locking do
584
+ # @foo.save
585
+ # end
586
+ #
587
+ def without_locking(&block)
588
+ current = ActiveRecord::Base.lock_optimistically
589
+ ActiveRecord::Base.lock_optimistically = false if current
590
+ begin
591
+ block.call
592
+ ensure
593
+ ActiveRecord::Base.lock_optimistically = true if current
594
+ end
595
+ end
596
+ end
597
+ end
598
+ end
599
+ end
600
+ end
601
+
602
+ # TISS extension: do not pull this.
603
+ # TODO: Move to TISS app (initializer)
604
+ module ActiveRecord #:nodoc:
605
+ module ConnectionAdapters #:nodoc:
606
+ class TableDefinition
607
+ ## Erzeugt 4 Spalten created_at, updated_at, version, mutator_id die in
608
+ ## jeder Daten-Tabelle notwendig sind. Verwendung:
609
+ ## create_table :adresse do |t|
610
+ ## t.standard_spalten
611
+ ## t.string strasse, :size=>120
612
+ ## t.string plz, :size=>4
613
+ ## end
614
+ def standard_spalten
615
+ column(:created_at, :datetime)
616
+ column(:updated_at, :datetime)
617
+ column(:version, :integer, :default=>1)
618
+ column(:mutator_id, :integer, :default=>0)
619
+ end
620
+ end
621
+ end
622
+ end
623
+
624
+
625
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::Versioned