genki-acts_as_versioned 0.5.2.1

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.
data/CHANGELOG ADDED
@@ -0,0 +1,82 @@
1
+ *GIT* (version numbers are overrated)
2
+
3
+ * (16 Jun 2008) Backwards Compatibility is overrated (big updates for rails 2.1)
4
+
5
+ * Use ActiveRecord 2.1's dirty attribute checking instead [Asa Calow]
6
+ * Remove last traces of #non_versioned_fields
7
+ * Remove AR::Base.find_version and AR::Base.find_versions, rely on AR association proxies and named_scope
8
+ * Remove #versions_count, rely on AR association counter caching.
9
+ * Remove #versioned_attributes, basically the same as AR::Base.versioned_columns
10
+
11
+ * (5 Oct 2006) Allow customization of #versions association options [Dan Peterson]
12
+
13
+ *0.5.1*
14
+
15
+ * (8 Aug 2006) Versioned models now belong to the unversioned model. @article_version.article.class => Article [Aslak Hellesoy]
16
+
17
+ *0.5* # do versions even matter for plugins?
18
+
19
+ * (21 Apr 2006) Added without_locking and without_revision methods.
20
+
21
+ Foo.without_revision do
22
+ @foo.update_attributes ...
23
+ end
24
+
25
+ *0.4*
26
+
27
+ * (28 March 2006) Rename non_versioned_fields to non_versioned_columns (old one is kept for compatibility).
28
+ * (28 March 2006) Made explicit documentation note that string column names are required for non_versioned_columns.
29
+
30
+ *0.3.1*
31
+
32
+ * (7 Jan 2006) explicitly set :foreign_key option for the versioned model's belongs_to assocation for STI [Caged]
33
+ * (7 Jan 2006) added tests to prove has_many :through joins work
34
+
35
+ *0.3*
36
+
37
+ * (2 Jan 2006) added ability to share a mixin with versioned class
38
+ * (2 Jan 2006) changed the dynamic version model to MyModel::Version
39
+
40
+ *0.2.4*
41
+
42
+ * (27 Nov 2005) added note about possible destructive behavior of if_changed? [Michael Schuerig]
43
+
44
+ *0.2.3*
45
+
46
+ * (12 Nov 2005) fixed bug with old behavior of #blank? [Michael Schuerig]
47
+ * (12 Nov 2005) updated tests to use ActiveRecord Schema
48
+
49
+ *0.2.2*
50
+
51
+ * (3 Nov 2005) added documentation note to #acts_as_versioned [Martin Jul]
52
+
53
+ *0.2.1*
54
+
55
+ * (6 Oct 2005) renamed dirty? to changed? to keep it uniform. it was aliased to keep it backwards compatible.
56
+
57
+ *0.2*
58
+
59
+ * (6 Oct 2005) added find_versions and find_version class methods.
60
+
61
+ * (6 Oct 2005) removed transaction from create_versioned_table().
62
+ this way you can specify your own transaction around a group of operations.
63
+
64
+ * (30 Sep 2005) fixed bug where find_versions() would order by 'version' twice. (found by Joe Clark)
65
+
66
+ * (26 Sep 2005) added :sequence_name option to acts_as_versioned to set the sequence name on the versioned model
67
+
68
+ *0.1.3* (18 Sep 2005)
69
+
70
+ * First RubyForge release
71
+
72
+ *0.1.2*
73
+
74
+ * check if module is already included when acts_as_versioned is called
75
+
76
+ *0.1.1*
77
+
78
+ * Adding tests and rdocs
79
+
80
+ *0.1*
81
+
82
+ * Initial transfer from Rails ticket: http://dev.rubyonrails.com/ticket/1974
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
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.
data/README ADDED
@@ -0,0 +1,28 @@
1
+ = acts_as_versioned
2
+
3
+ This library adds simple versioning to an ActiveRecord module. ActiveRecord is required.
4
+
5
+ == Resources
6
+
7
+ Install
8
+
9
+ * gem install acts_as_versioned
10
+
11
+ Rubyforge project
12
+
13
+ * http://rubyforge.org/projects/ar-versioned
14
+
15
+ RDocs
16
+
17
+ * http://ar-versioned.rubyforge.org
18
+
19
+ Subversion
20
+
21
+ * http://techno-weenie.net/svn/projects/acts_as_versioned
22
+
23
+ Collaboa
24
+
25
+ * http://collaboa.techno-weenie.net/repository/browse/acts_as_versioned
26
+
27
+ Special thanks to Dreamer on ##rubyonrails for help in early testing. His ServerSideWiki (http://serversidewiki.com)
28
+ was the first project to use acts_as_versioned <em>in the wild</em>.
@@ -0,0 +1,41 @@
1
+ == Creating the test database
2
+
3
+ The default name for the test databases is "activerecord_versioned". If you
4
+ want to use another database name then be sure to update the connection
5
+ adapter setups you want to test with in test/connections/<your database>/connection.rb.
6
+ When you have the database online, you can import the fixture tables with
7
+ the test/fixtures/db_definitions/*.sql files.
8
+
9
+ Make sure that you create database objects with the same user that you specified in i
10
+ connection.rb otherwise (on Postgres, at least) tests for default values will fail.
11
+
12
+ == Running with Rake
13
+
14
+ The easiest way to run the unit tests is through Rake. The default task runs
15
+ the entire test suite for all the adapters. You can also run the suite on just
16
+ one adapter by using the tasks test_mysql_ruby, test_ruby_mysql, test_sqlite,
17
+ or test_postresql. For more information, checkout the full array of rake tasks with "rake -T"
18
+
19
+ Rake can be found at http://rake.rubyforge.org
20
+
21
+ == Running by hand
22
+
23
+ Unit tests are located in test directory. If you only want to run a single test suite,
24
+ or don't want to bother with Rake, you can do so with something like:
25
+
26
+ cd test; ruby -I "connections/native_mysql" base_test.rb
27
+
28
+ That'll run the base suite using the MySQL-Ruby adapter. Change the adapter
29
+ and test suite name as needed.
30
+
31
+ == Faster tests
32
+
33
+ If you are using a database that supports transactions, you can set the
34
+ "AR_TX_FIXTURES" environment variable to "yes" to use transactional fixtures.
35
+ This gives a very large speed boost. With rake:
36
+
37
+ rake AR_TX_FIXTURES=yes
38
+
39
+ Or, by hand:
40
+
41
+ AR_TX_FIXTURES=yes ruby -I connections/native_sqlite3 base_test.rb
@@ -0,0 +1,486 @@
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
+
22
+ module ActiveRecord #:nodoc:
23
+ module Acts #:nodoc:
24
+ # Specify this act if you want to save a copy of the row in a versioned table. This assumes there is a
25
+ # versioned table ready and that your model has a version field. This works with optimistic locking if the lock_version
26
+ # column is present as well.
27
+ #
28
+ # 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
29
+ # your container for the changes to be reflected. In development mode this usually means restarting WEBrick.
30
+ #
31
+ # class Page < ActiveRecord::Base
32
+ # # assumes pages_versions table
33
+ # acts_as_versioned
34
+ # end
35
+ #
36
+ # Example:
37
+ #
38
+ # page = Page.create(:title => 'hello world!')
39
+ # page.version # => 1
40
+ #
41
+ # page.title = 'hello world'
42
+ # page.save
43
+ # page.version # => 2
44
+ # page.versions.size # => 2
45
+ #
46
+ # page.revert_to(1) # using version number
47
+ # page.title # => 'hello world!'
48
+ #
49
+ # page.revert_to(page.versions.last) # using versioned instance
50
+ # page.title # => 'hello world'
51
+ #
52
+ # page.versions.earliest # efficient query to find the first version
53
+ # page.versions.latest # efficient query to find the most recently created version
54
+ #
55
+ #
56
+ # Simple Queries to page between versions
57
+ #
58
+ # page.versions.before(version)
59
+ # page.versions.after(version)
60
+ #
61
+ # Access the previous/next versions from the versioned model itself
62
+ #
63
+ # version = page.versions.latest
64
+ # version.previous # go back one version
65
+ # version.next # go forward one version
66
+ #
67
+ # See ActiveRecord::Acts::Versioned::ClassMethods#acts_as_versioned for configuration options
68
+ module Versioned
69
+ CALLBACKS = [:set_new_version, :save_version, :save_version?]
70
+ def self.included(base) # :nodoc:
71
+ base.extend ClassMethods
72
+ end
73
+
74
+ module ClassMethods
75
+ # == Configuration options
76
+ #
77
+ # * <tt>class_name</tt> - versioned model class name (default: PageVersion in the above example)
78
+ # * <tt>table_name</tt> - versioned model table name (default: page_versions in the above example)
79
+ # * <tt>foreign_key</tt> - foreign key used to relate the versioned model to the original model (default: page_id in the above example)
80
+ # * <tt>inheritance_column</tt> - name of the column to save the model's inheritance_column value for STI. (default: versioned_type)
81
+ # * <tt>version_column</tt> - name of the column in the model that keeps the version number (default: version)
82
+ # * <tt>sequence_name</tt> - name of the custom sequence to be used by the versioned model.
83
+ # * <tt>limit</tt> - number of revisions to keep, defaults to unlimited
84
+ # * <tt>if</tt> - symbol of method to check before saving a new version. If this method returns false, a new version is not saved.
85
+ # For finer control, pass either a Proc or modify Model#version_condition_met?
86
+ #
87
+ # acts_as_versioned :if => Proc.new { |auction| !auction.expired? }
88
+ #
89
+ # or...
90
+ #
91
+ # class Auction
92
+ # def version_condition_met? # totally bypasses the <tt>:if</tt> option
93
+ # !expired?
94
+ # end
95
+ # end
96
+ #
97
+ # * <tt>if_changed</tt> - Simple way of specifying attributes that are required to be changed before saving a model. This takes
98
+ # either a symbol or array of symbols.
99
+ #
100
+ # * <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
101
+ # to create an anonymous mixin:
102
+ #
103
+ # class Auction
104
+ # acts_as_versioned do
105
+ # def started?
106
+ # !started_at.nil?
107
+ # end
108
+ # end
109
+ # end
110
+ #
111
+ # or...
112
+ #
113
+ # module AuctionExtension
114
+ # def started?
115
+ # !started_at.nil?
116
+ # end
117
+ # end
118
+ # class Auction
119
+ # acts_as_versioned :extend => AuctionExtension
120
+ # end
121
+ #
122
+ # Example code:
123
+ #
124
+ # @auction = Auction.find(1)
125
+ # @auction.started?
126
+ # @auction.versions.first.started?
127
+ #
128
+ # == Database Schema
129
+ #
130
+ # The model that you're versioning needs to have a 'version' attribute. The model is versioned
131
+ # into a table called #{model}_versions where the model name is singlular. The _versions table should
132
+ # contain all the fields you want versioned, the same version column, and a #{model}_id foreign key field.
133
+ #
134
+ # A lock_version field is also accepted if your model uses Optimistic Locking. If your table uses Single Table inheritance,
135
+ # then that field is reflected in the versioned model as 'versioned_type' by default.
136
+ #
137
+ # Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table
138
+ # method, perfect for a migration. It will also create the version column if the main model does not already have it.
139
+ #
140
+ # class AddVersions < ActiveRecord::Migration
141
+ # def self.up
142
+ # # create_versioned_table takes the same options hash
143
+ # # that create_table does
144
+ # Post.create_versioned_table
145
+ # end
146
+ #
147
+ # def self.down
148
+ # Post.drop_versioned_table
149
+ # end
150
+ # end
151
+ #
152
+ # == Changing What Fields Are Versioned
153
+ #
154
+ # By default, acts_as_versioned will version all but these fields:
155
+ #
156
+ # [self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column]
157
+ #
158
+ # You can add or change those by modifying #non_versioned_columns. Note that this takes strings and not symbols.
159
+ #
160
+ # class Post < ActiveRecord::Base
161
+ # acts_as_versioned
162
+ # self.non_versioned_columns << 'comments_count'
163
+ # end
164
+ #
165
+ def acts_as_versioned(options = {}, &extension)
166
+ # don't allow multiple calls
167
+ return if self.included_modules.include?(ActiveRecord::Acts::Versioned::ActMethods)
168
+
169
+ send :include, ActiveRecord::Acts::Versioned::ActMethods
170
+
171
+ cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column,
172
+ :version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns,
173
+ :version_association_options, :version_if_changed
174
+
175
+ self.versioned_class_name = options[:class_name] || "Version"
176
+ self.versioned_foreign_key = options[:foreign_key] || self.to_s.foreign_key
177
+ self.versioned_table_name = options[:table_name] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}"
178
+ self.versioned_inheritance_column = options[:inheritance_column] || "versioned_#{inheritance_column}"
179
+ self.version_column = options[:version_column] || 'version'
180
+ self.version_sequence_name = options[:sequence_name]
181
+ self.max_version_limit = options[:limit].to_i
182
+ self.version_condition = options[:if] || true
183
+ 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)
184
+ self.version_association_options = {
185
+ :class_name => "#{self.to_s}::#{versioned_class_name}",
186
+ :foreign_key => versioned_foreign_key,
187
+ :dependent => :delete_all
188
+ }.merge(options[:association_options] || {})
189
+
190
+ if block_given?
191
+ extension_module_name = "#{versioned_class_name}Extension"
192
+ silence_warnings do
193
+ self.const_set(extension_module_name, Module.new(&extension))
194
+ end
195
+
196
+ options[:extend] = self.const_get(extension_module_name)
197
+ end
198
+
199
+ class_eval <<-CLASS_METHODS
200
+ has_many :versions, version_association_options do
201
+ # finds earliest version of this record
202
+ def earliest
203
+ @earliest ||= find(:first, :order => '#{version_column}')
204
+ end
205
+
206
+ # find latest version of this record
207
+ def latest
208
+ @latest ||= find(:first, :order => '#{version_column} desc')
209
+ end
210
+ end
211
+ before_save :set_new_version
212
+ after_save :save_version
213
+ after_save :clear_old_versions
214
+
215
+ unless options[:if_changed].nil?
216
+ self.track_altered_attributes = true
217
+ options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array)
218
+ self.version_if_changed = options[:if_changed].map(&:to_s)
219
+ end
220
+
221
+ include options[:extend] if options[:extend].is_a?(Module)
222
+ CLASS_METHODS
223
+
224
+ # create the dynamic versioned model
225
+ const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do
226
+ def self.reloadable? ; false ; end
227
+ # find first version before the given version
228
+ def self.before(version)
229
+ find :first, :order => 'version desc',
230
+ :conditions => ["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version]
231
+ end
232
+
233
+ # find first version after the given version.
234
+ def self.after(version)
235
+ find :first, :order => 'version',
236
+ :conditions => ["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version]
237
+ end
238
+
239
+ def previous
240
+ self.class.before(self)
241
+ end
242
+
243
+ def next
244
+ self.class.after(self)
245
+ end
246
+
247
+ def versions_count
248
+ page.version
249
+ end
250
+ end
251
+
252
+ versioned_class.cattr_accessor :original_class
253
+ versioned_class.original_class = self
254
+ versioned_class.set_table_name versioned_table_name
255
+ versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym,
256
+ :class_name => "::#{self.to_s}",
257
+ :foreign_key => versioned_foreign_key
258
+ versioned_class.send :include, options[:extend] if options[:extend].is_a?(Module)
259
+ versioned_class.set_sequence_name version_sequence_name if version_sequence_name
260
+ end
261
+ end
262
+
263
+ module ActMethods
264
+ def self.included(base) # :nodoc:
265
+ base.extend ClassMethods
266
+ end
267
+
268
+ # Saves a version of the model in the versioned table. This is called in the after_save callback by default
269
+ def save_version
270
+ if @saving_version
271
+ @saving_version = nil
272
+ rev = self.class.versioned_class.new
273
+ clone_versioned_model(self, rev)
274
+ rev.send("#{self.class.version_column}=", send(self.class.version_column))
275
+ rev.send("#{self.class.versioned_foreign_key}=", id)
276
+ rev.save
277
+ end
278
+ end
279
+
280
+ # Clears old revisions if a limit is set with the :limit option in <tt>acts_as_versioned</tt>.
281
+ # Override this method to set your own criteria for clearing old versions.
282
+ def clear_old_versions
283
+ return if self.class.max_version_limit == 0
284
+ excess_baggage = send(self.class.version_column).to_i - self.class.max_version_limit
285
+ if excess_baggage > 0
286
+ self.class.versioned_class.delete_all ["#{self.class.version_column} <= ? and #{self.class.versioned_foreign_key} = ?", excess_baggage, id]
287
+ end
288
+ end
289
+
290
+ # Reverts a model to a given version. Takes either a version number or an instance of the versioned model
291
+ def revert_to(version)
292
+ if version.is_a?(self.class.versioned_class)
293
+ return false unless version.send(self.class.versioned_foreign_key) == id and !version.new_record?
294
+ else
295
+ return false unless version = versions.send("find_by_#{self.class.version_column}", version)
296
+ end
297
+ self.clone_versioned_model(version, self)
298
+ send("#{self.class.version_column}=", version.send(self.class.version_column))
299
+ true
300
+ end
301
+
302
+ # Reverts a model to a given version and saves the model.
303
+ # Takes either a version number or an instance of the versioned model
304
+ def revert_to!(version)
305
+ revert_to(version) ? save_without_revision : false
306
+ end
307
+
308
+ # Temporarily turns off Optimistic Locking while saving. Used when reverting so that a new version is not created.
309
+ def save_without_revision
310
+ save_without_revision!
311
+ true
312
+ rescue
313
+ false
314
+ end
315
+
316
+ def save_without_revision!
317
+ without_locking do
318
+ without_revision do
319
+ save!
320
+ end
321
+ end
322
+ end
323
+
324
+ def altered?
325
+ track_altered_attributes ? (version_if_changed - changed).length < version_if_changed.length : changed?
326
+ end
327
+
328
+ # Clones a model. Used when saving a new version or reverting a model's version.
329
+ def clone_versioned_model(orig_model, new_model)
330
+ self.class.versioned_columns.each do |col|
331
+ new_model.send("#{col.name}=", orig_model.send(col.name)) if orig_model.has_attribute?(col.name)
332
+ end
333
+
334
+ if orig_model.is_a?(self.class.versioned_class)
335
+ new_model[new_model.class.inheritance_column] = orig_model[self.class.versioned_inheritance_column]
336
+ elsif new_model.is_a?(self.class.versioned_class)
337
+ new_model[self.class.versioned_inheritance_column] = orig_model[orig_model.class.inheritance_column]
338
+ end
339
+ end
340
+
341
+ # Checks whether a new version shall be saved or not. Calls <tt>version_condition_met?</tt> and <tt>changed?</tt>.
342
+ def save_version?
343
+ version_condition_met? && altered?
344
+ end
345
+
346
+ # Checks condition set in the :if option to check whether a revision should be created or not. Override this for
347
+ # custom version condition checking.
348
+ def version_condition_met?
349
+ case
350
+ when version_condition.is_a?(Symbol)
351
+ send(version_condition)
352
+ when version_condition.respond_to?(:call) && (version_condition.arity == 1 || version_condition.arity == -1)
353
+ version_condition.call(self)
354
+ else
355
+ version_condition
356
+ end
357
+ end
358
+
359
+ # Executes the block with the versioning callbacks disabled.
360
+ #
361
+ # @foo.without_revision do
362
+ # @foo.save
363
+ # end
364
+ #
365
+ def without_revision(&block)
366
+ self.class.without_revision(&block)
367
+ end
368
+
369
+ # Turns off optimistic locking for the duration of the block
370
+ #
371
+ # @foo.without_locking do
372
+ # @foo.save
373
+ # end
374
+ #
375
+ def without_locking(&block)
376
+ self.class.without_locking(&block)
377
+ end
378
+
379
+ def empty_callback() end #:nodoc:
380
+
381
+ protected
382
+ # sets the new version before saving, unless you're using optimistic locking. In that case, let it take care of the version.
383
+ def set_new_version
384
+ @saving_version = new_record? || save_version?
385
+ self.send("#{self.class.version_column}=", next_version) if new_record? || (!locking_enabled? && save_version?)
386
+ end
387
+
388
+ # Gets the next available version for the current record, or 1 for a new record
389
+ def next_version
390
+ (new_record? ? 0 : versions.calculate(:max, version_column).to_i) + 1
391
+ end
392
+
393
+ module ClassMethods
394
+ # Returns an array of columns that are versioned. See non_versioned_columns
395
+ def versioned_columns
396
+ @versioned_columns ||= columns.select { |c| !non_versioned_columns.include?(c.name) }
397
+ end
398
+
399
+ # Returns an instance of the dynamic versioned model
400
+ def versioned_class
401
+ const_get versioned_class_name
402
+ end
403
+
404
+ # Rake migration task to create the versioned table using options passed to acts_as_versioned
405
+ def create_versioned_table(create_table_options = {})
406
+ # create version column in main table if it does not exist
407
+ if !self.content_columns.find { |c| [version_column.to_s, 'lock_version'].include? c.name }
408
+ self.connection.add_column table_name, version_column, :integer
409
+ self.reset_column_information
410
+ end
411
+
412
+ return if connection.table_exists?(versioned_table_name)
413
+
414
+ self.connection.create_table(versioned_table_name, create_table_options) do |t|
415
+ t.column versioned_foreign_key, :integer
416
+ t.column version_column, :integer
417
+ end
418
+
419
+ self.versioned_columns.each do |col|
420
+ self.connection.add_column versioned_table_name, col.name, col.type,
421
+ :limit => col.limit,
422
+ :default => col.default,
423
+ :scale => col.scale,
424
+ :precision => col.precision
425
+ end
426
+
427
+ if type_col = self.columns_hash[inheritance_column]
428
+ self.connection.add_column versioned_table_name, versioned_inheritance_column, type_col.type,
429
+ :limit => type_col.limit,
430
+ :default => type_col.default,
431
+ :scale => type_col.scale,
432
+ :precision => type_col.precision
433
+ end
434
+
435
+ self.connection.add_index versioned_table_name, versioned_foreign_key
436
+ end
437
+
438
+ # Rake migration task to drop the versioned table
439
+ def drop_versioned_table
440
+ self.connection.drop_table versioned_table_name
441
+ end
442
+
443
+ # Executes the block with the versioning callbacks disabled.
444
+ #
445
+ # Foo.without_revision do
446
+ # @foo.save
447
+ # end
448
+ #
449
+ def without_revision(&block)
450
+ class_eval do
451
+ CALLBACKS.each do |attr_name|
452
+ alias_method "orig_#{attr_name}".to_sym, attr_name
453
+ alias_method attr_name, :empty_callback
454
+ end
455
+ end
456
+ block.call
457
+ ensure
458
+ class_eval do
459
+ CALLBACKS.each do |attr_name|
460
+ alias_method attr_name, "orig_#{attr_name}".to_sym
461
+ end
462
+ end
463
+ end
464
+
465
+ # Turns off optimistic locking for the duration of the block
466
+ #
467
+ # Foo.without_locking do
468
+ # @foo.save
469
+ # end
470
+ #
471
+ def without_locking(&block)
472
+ current = ActiveRecord::Base.lock_optimistically
473
+ ActiveRecord::Base.lock_optimistically = false if current
474
+ begin
475
+ block.call
476
+ ensure
477
+ ActiveRecord::Base.lock_optimistically = true if current
478
+ end
479
+ end
480
+ end
481
+ end
482
+ end
483
+ end
484
+ end
485
+
486
+ ActiveRecord::Base.send :include, ActiveRecord::Acts::Versioned