# +vestal_versions+ keeps track of updates to ActiveRecord models, leveraging the introduction of # dirty attributes in Rails 2.1. By storing only the updated attributes in a serialized column of a # single version model, the history is kept DRY and no additional schema changes are necessary. # # Author:: Steve Richert # Copyright:: Copyright (c) 2009 Steve Richert # License:: MIT License (http://www.opensource.org/licenses/mit-license.php) # # To enable versioning on a model, simply use the +versioned+ method: # # class User < ActiveRecord::Base # versioned # end # # user = User.create(:name => "Steve Richert") # user.version # => 1 # user.update_attribute(:name, "Steve Jobs") # user.version # => 2 # user.revert_to(1) # user.name # => "Steve Richert" # # See the +versioned+ documentation for more details. require File.expand_path(File.join(File.dirname(__FILE__), '..', 'generators', 'vestal_versions', 'vestal_versions_generator')) Dir[File.join(File.dirname(__FILE__), 'vestal_versions', '*.rb')].each{|f| require f } # The base module that gets included in ActiveRecord::Base. See the documentation for # VestalVersions::ClassMethods for more useful information. module VestalVersions def self.included(base) # :nodoc: base.class_eval do extend ClassMethods extend Versioned end end module ClassMethods # +versioned+ associates an ActiveRecord model with many versions. When the object is updated, # a new version containing the changes is created. There are several options available to the # +versioned+ method, most of which are passed to the +has_many+ association itself: # * :class_name: The class name of the version model to use for the association. By # default, this is set to "VestalVersions::Version", representing the built-in version class. # By specifying this option, you can override the version class, to include custom version # behavior. It's recommended that a custom version inherit from VestalVersions::Version. # * :dependent: Also common to +has_many+ associations, this describes the behavior of # version records when the parent object is destroyed. This defaults to :delete_all, which # will permanently remove all associated versions *without* triggering any destroy callbacks. # Other options are :destroy which removes the associated versions *with* callbacks, or # :nullify which leaves the version records in the database, but dissociates them from the # parent object by setting the foreign key columns to +nil+ values. # * :except: An update will trigger version creation as long as at least one column # outside those specified here was updated. Also, upon version creation, the columns # specified here will be excluded from the change history. This is useful when dealing with # unimportant, constantly changing, or sensitive information. This option accepts a symbol, # string or an array of either, representing column names to exclude. It is completely # optional and defaults to +nil+, allowing all columns to be versioned. This option is also # ignored if the +only+ option is used. # * :extend: This option allows you to extend the +has_many+ association proxy with a # module or an array of modules. Any methods defined in those modules become available on the # +versions+ association. The VestalVersions::Versions module is essential to the # functionality of +vestal_versions+ and so is prepended to any additional modules that you # might specify here. # * :if: Accepts a symbol, a proc or an array of either to be evaluated when the parent # object is updated to determine whether a new version should be created. +to_proc+ is called # on any symbols given and the resulting procs are called, passing in the object itself. If # an array is given, all must be evaluate to +true+ in order for a version to be created. # * :initial_version: When set to true, an initial version is always created when the # parent object is created. This initial version will have nil changes however it can be # used to store who created the original version. # * :only: An update will trigger version creation as long as at least one updated # column falls within those specified here. Also, upon version creation, only the columns # specified here will be included in the change history. This option accepts a symbol, string # or an array of either, representing column names to include. It is completely optional and # defaults to +nil+, allowing all columns to be versioned. This option takes precedence over # the +except+ option if both are specified. # * :unless: Accepts a symbol, a proc or an array of either to be evaluated when the # parent object is updated to determine whether version creation should be skipped. +to_proc+ # is called on any symbols given and the resulting procs are called, passing in the object # itself. If an array is given and any element evaluates as +true+, the version creation will # be skipped. def versioned(options = {}, &block) return if versioned? include Options include Changes include Creation include Users include Reversion include Reset include Conditions include Control include Tagging include Reload prepare_versioned_options(options) has_many :versions, options, &block end end extend Configuration end ActiveRecord::Base.send(:include, VestalVersions)