Module: ActiveRecord::Acts::Versioned::ClassMethods
- Defined in:
- lib/acts_as_versioned.rb
Instance Method Summary collapse
-
#acts_as_versioned(options = {}, &extension) ⇒ Object
Configuration options.
Instance Method Details
#acts_as_versioned(options = {}, &extension) ⇒ Object
Configuration options
-
class_name
- versioned model class name (default: PageVersion in the above example) -
table_name
- versioned model table name (default: page_versions in the above example) -
foreign_key
- foreign key used to relate the versioned model to the original model (default: page_id in the above example) -
inheritance_column
- name of the column to save the model’s inheritance_column value for STI. (default: versioned_type) -
version_column
- name of the column in the model that keeps the version number (default: version) -
sequence_name
- name of the custom sequence to be used by the versioned model. -
limit
- number of revisions to keep, defaults to unlimited -
if
- symbol of method to check before saving a new version. If this method returns false, a new version is not saved. For finer control, pass either a Proc or modify Model#version_condition_met?acts_as_versioned :if => Proc.new { |auction| !auction.expired? }
or…
class Auction def version_condition_met? # totally bypasses the <tt>:if</tt> option !expired? end end
-
if_changed
- Simple way of specifying attributes that are required to be changed before saving a model. This takes either a symbol or array of symbols. -
extend
- Lets you specify a module to be mixed in both the original and versioned models. You can also just pass a block to create an anonymous mixin:class Auction acts_as_versioned do def started? !started_at.nil? end end end
or…
module AuctionExtension def started? !started_at.nil? end end class Auction acts_as_versioned :extend => AuctionExtension end
Example code:
@auction = Auction.find(1)
@auction.started?
@auction.versions.first.started?
Database Schema
The model that you’re versioning needs to have a ‘version’ attribute. The model is versioned into a table called #model_versions where the model name is singlular. The _versions table should contain all the fields you want versioned, the same version column, and a #model_id foreign key field.
A lock_version field is also accepted if your model uses Optimistic Locking. If your table uses Single Table inheritance, then that field is reflected in the versioned model as ‘versioned_type’ by default.
Acts_as_versioned comes prepared with the ActiveRecord::Acts::Versioned::ActMethods::ClassMethods#create_versioned_table method, perfect for a migration. It will also create the version column if the main model does not already have it.
class AddVersions < ActiveRecord::Migration
def self.up
# create_versioned_table takes the same options hash
# that create_table does
Post.create_versioned_table
end
def self.down
Post.drop_versioned_table
end
end
Changing What Fields Are Versioned
By default, acts_as_versioned will version all but these fields:
[self.primary_key, inheritance_column, 'version', 'lock_version', versioned_inheritance_column]
You can add or change those by modifying #non_versioned_columns. Note that this takes strings and not symbols.
class Post < ActiveRecord::Base
acts_as_versioned
self.non_versioned_columns << 'comments_count'
end
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 |
# File 'lib/acts_as_versioned.rb', line 165 def acts_as_versioned( = {}, &extension) # don't allow multiple calls return if self.included_modules.include?(ActiveRecord::Acts::Versioned::ActMethods) send :include, ActiveRecord::Acts::Versioned::ActMethods cattr_accessor :versioned_class_name, :versioned_foreign_key, :versioned_table_name, :versioned_inheritance_column, :version_column, :max_version_limit, :track_altered_attributes, :version_condition, :version_sequence_name, :non_versioned_columns, :version_association_options, :version_if_changed self.versioned_class_name = [:class_name] || "Version" self.versioned_foreign_key = [:foreign_key] || self.to_s.foreign_key self.versioned_table_name = [:table_name] || "#{table_name_prefix}#{base_class.name.demodulize.underscore}_versions#{table_name_suffix}" self.versioned_inheritance_column = [:inheritance_column] || "versioned_#{inheritance_column}" self.version_column = [:version_column] || 'version' self.version_sequence_name = [:sequence_name] self.max_version_limit = [:limit].to_i self.version_condition = [:if] || true self.non_versioned_columns = [self.primary_key, inheritance_column, self.version_column, 'lock_version', versioned_inheritance_column] + [:non_versioned_columns].to_a.map(&:to_s) self. = { :class_name => "#{self.to_s}::#{versioned_class_name}", :foreign_key => versioned_foreign_key, :dependent => :delete_all }.merge([:association_options] || {}) if block_given? extension_module_name = "#{versioned_class_name}Extension" silence_warnings do self.const_set(extension_module_name, Module.new(&extension)) end [:extend] = self.const_get(extension_module_name) end class_eval <<-CLASS_METHODS has_many :versions, version_association_options do # finds earliest version of this record def earliest @earliest ||= find(:first, :order => '#{version_column}') end # find latest version of this record def latest @latest ||= find(:first, :order => '#{version_column} desc') end end before_save :set_new_version after_save :save_version after_save :clear_old_versions unless options[:if_changed].nil? self.track_altered_attributes = true options[:if_changed] = [options[:if_changed]] unless options[:if_changed].is_a?(Array) self.version_if_changed = options[:if_changed].map(&:to_s) end include options[:extend] if options[:extend].is_a?(Module) CLASS_METHODS # create the dynamic versioned model const_set(versioned_class_name, Class.new(ActiveRecord::Base)).class_eval do def self.reloadable? ; false ; end # find first version before the given version def self.before(version) find :first, :order => 'version desc', :conditions => ["#{original_class.versioned_foreign_key} = ? and version < ?", version.send(original_class.versioned_foreign_key), version.version] end # find first version after the given version. def self.after(version) find :first, :order => 'version', :conditions => ["#{original_class.versioned_foreign_key} = ? and version > ?", version.send(original_class.versioned_foreign_key), version.version] end def previous self.class.before(self) end def next self.class.after(self) end def versions_count page.version end end versioned_class.cattr_accessor :original_class versioned_class.original_class = self versioned_class.set_table_name versioned_table_name versioned_class.belongs_to self.to_s.demodulize.underscore.to_sym, :class_name => "::#{self.to_s}", :foreign_key => versioned_foreign_key versioned_class.send :include, [:extend] if [:extend].is_a?(Module) versioned_class.set_sequence_name version_sequence_name if version_sequence_name end |