mil_vestal_versions 1.2.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +19 -0
  3. data/.travis.yml +9 -0
  4. data/CHANGELOG +25 -0
  5. data/Gemfile +8 -0
  6. data/LICENSE +20 -0
  7. data/README.rdoc +200 -0
  8. data/Rakefile +6 -0
  9. data/gemfiles/activerecord_3_0.gemfile +9 -0
  10. data/gemfiles/activerecord_3_1.gemfile +9 -0
  11. data/gemfiles/activerecord_3_2.gemfile +9 -0
  12. data/lib/generators/vestal_versions.rb +11 -0
  13. data/lib/generators/vestal_versions/migration/migration_generator.rb +17 -0
  14. data/lib/generators/vestal_versions/migration/templates/initializer.rb +9 -0
  15. data/lib/generators/vestal_versions/migration/templates/migration.rb +28 -0
  16. data/lib/vestal_versions.rb +126 -0
  17. data/lib/vestal_versions/changes.rb +121 -0
  18. data/lib/vestal_versions/conditions.rb +57 -0
  19. data/lib/vestal_versions/control.rb +199 -0
  20. data/lib/vestal_versions/creation.rb +93 -0
  21. data/lib/vestal_versions/deletion.rb +37 -0
  22. data/lib/vestal_versions/options.rb +41 -0
  23. data/lib/vestal_versions/reload.rb +16 -0
  24. data/lib/vestal_versions/reset.rb +24 -0
  25. data/lib/vestal_versions/reversion.rb +81 -0
  26. data/lib/vestal_versions/users.rb +54 -0
  27. data/lib/vestal_versions/version.rb +81 -0
  28. data/lib/vestal_versions/version_tagging.rb +49 -0
  29. data/lib/vestal_versions/versioned.rb +27 -0
  30. data/lib/vestal_versions/versions.rb +74 -0
  31. data/mil_vestal_versions.gemspec +19 -0
  32. data/spec/spec_helper.rb +20 -0
  33. data/spec/support/models.rb +19 -0
  34. data/spec/support/schema.rb +25 -0
  35. data/spec/vestal_versions/changes_spec.rb +134 -0
  36. data/spec/vestal_versions/conditions_spec.rb +103 -0
  37. data/spec/vestal_versions/control_spec.rb +120 -0
  38. data/spec/vestal_versions/creation_spec.rb +90 -0
  39. data/spec/vestal_versions/deletion_spec.rb +86 -0
  40. data/spec/vestal_versions/options_spec.rb +45 -0
  41. data/spec/vestal_versions/reload_spec.rb +18 -0
  42. data/spec/vestal_versions/reset_spec.rb +111 -0
  43. data/spec/vestal_versions/reversion_spec.rb +103 -0
  44. data/spec/vestal_versions/users_spec.rb +21 -0
  45. data/spec/vestal_versions/version_spec.rb +61 -0
  46. data/spec/vestal_versions/version_tagging_spec.rb +39 -0
  47. data/spec/vestal_versions/versioned_spec.rb +16 -0
  48. data/spec/vestal_versions/versions_spec.rb +176 -0
  49. data/vestal_versions.gemspec +19 -0
  50. metadata +139 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f3a35e411e8ce4806c9de8508c50157065671a10
4
+ data.tar.gz: af8dc86b2e4dbe203f4e8f705fbdc6b3743b65f3
5
+ SHA512:
6
+ metadata.gz: ada0226af18c89a2893f54f2efddca4d2cd65d649a4d0071cf83cdc40fd4bd29a64e61ea356887b60e921e99419cc14ad4795213cfd009580a1d3b97d35bb262
7
+ data.tar.gz: 8264bf5a25349bb20c32aa1be9aca3a36ece1a9ba5a576c4fe062c1a38607cbba8e77ca803bd8e639b2804cd0ddfb02de2637a6e05c6e636483505c2a0e6293c
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .rvmrc
6
+ .yardoc
7
+ coverage
8
+ doc/
9
+ Gemfile.lock
10
+ gemfiles/*.lock
11
+ InstalledFiles
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ _yardoc
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.2
4
+ - 1.9.3
5
+ gemfile:
6
+ - gemfiles/activerecord_3_0.gemfile
7
+ - gemfiles/activerecord_3_1.gemfile
8
+ - gemfiles/activerecord_3_2.gemfile
9
+ script: rspec
@@ -0,0 +1,25 @@
1
+ HEAD
2
+ * Added a new class level 'skip_version' that works on one model at
3
+ a time
4
+ * Internal refactoring and tidying up by Alex Crichton
5
+
6
+ Version 1.2.2
7
+ * Fix conflicts with other Tagging systems
8
+ * Adding a bundler Gemfile and updating the gemspec for the development
9
+ * Adding an ':intial_version' option that will create a versions row on
10
+ initial create
11
+ * A couple internal refactors to remove alias_method_chain calls
12
+
13
+
14
+ Version 1.2.1
15
+ * Soft Delete tracking including
16
+ * Adding revert tracking (version table change)
17
+ * Adding 'original_number' to the version class
18
+
19
+ Version 1.1.0
20
+ * Ruby 1.9.2 compatibility
21
+ * Initial Rails 3 compatibility
22
+
23
+ Version 1.0.2
24
+
25
+ * Original Version import from Laserlemon - thanks!
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'rake', '>= 0.9'
6
+ gem 'rspec', '~> 2.0'
7
+ gem 'sqlite3', '~> 1.0'
8
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Steve Richert
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.
@@ -0,0 +1,200 @@
1
+ = Vestal Versions {<img src="https://secure.travis-ci.org/laserlemon/vestal_versions.png?branch=rails_3" alt="Build Status" />}[https://travis-ci.org/laserlemon/vestal_versions]
2
+
3
+ Finally, DRY ActiveRecord versioning!
4
+
5
+ <tt>acts_as_versioned</tt>[http://github.com/technoweenie/acts_as_versioned] by technoweenie[http://github.com/technoweenie] was a great start, but it failed to keep up with ActiveRecord's introduction of dirty objects in version 2.1. Additionally, each versioned model needs its own versions table that duplicates most of the original table's columns. The versions table is then populated with records that often duplicate most of the original record's attributes. All in all, not very DRY.
6
+
7
+ <tt>vestal_versions</tt>[http://github.com/laserlemon/vestal_versions] requires only one versions table (polymorphically associated with its parent models) and no changes whatsoever to existing tables. But it goes one step DRYer by storing a serialized hash of _only_ the models' changes. Think modern version control systems. By traversing the record of changes, the models can be reverted to any point in time.
8
+
9
+ And that's just what <tt>vestal_versions</tt> does. Not only can a model be reverted to a previous version number but also to a date or time!
10
+
11
+ == Compatibility
12
+
13
+ Tested with Active Record 3.2.8 with Ruby 1.9.3 and 1.9.2.
14
+
15
+ == Installation
16
+
17
+ In the Gemfile:
18
+
19
+ ** Note: I am giving this project some much needed love to keep her relevant in a post Rails 3 world. I will be finalizing a version to support 1.9.2+ and Rails 3.2+ soon and pushing the gem, till then, use the git repo:
20
+ ~dreamr
21
+
22
+ gem 'vestal_versions', :git => 'git://github.com/laserlemon/vestal_versions'
23
+
24
+
25
+ Next, generate and run the first and last versioning migration you'll ever need:
26
+
27
+ $ rails generate vestal_versions:migration
28
+ $ rake db:migrate
29
+
30
+ == Example
31
+
32
+ To version an ActiveRecord model, simply add <tt>versioned</tt> to your class like so:
33
+
34
+ class User < ActiveRecord::Base
35
+ versioned
36
+
37
+ validates_presence_of :first_name, :last_name
38
+
39
+ def name
40
+ "#{first_name} #{last_name}"
41
+ end
42
+ end
43
+
44
+ It's that easy! Now watch it in action...
45
+
46
+ >> u = User.create(:first_name => "Steve", :last_name => "Richert")
47
+ => #<User first_name: "Steve", last_name: "Richert">
48
+ >> u.version
49
+ => 1
50
+ >> u.update_attribute(:first_name, "Stephen")
51
+ => true
52
+ >> u.name
53
+ => "Stephen Richert"
54
+ >> u.version
55
+ => 2
56
+ >> u.revert_to(10.seconds.ago)
57
+ => 1
58
+ >> u.name
59
+ => "Steve Richert"
60
+ >> u.version
61
+ => 1
62
+ >> u.save
63
+ => true
64
+ >> u.version
65
+ => 3
66
+ >> u.update_attribute(:last_name, "Jobs")
67
+ => true
68
+ >> u.name
69
+ => "Steve Jobs"
70
+ >> u.version
71
+ => 4
72
+ >> u.revert_to!(2)
73
+ => true
74
+ >> u.name
75
+ => "Stephen Richert"
76
+ >> u.version
77
+ => 5
78
+
79
+ == Upgrading to 1.0
80
+
81
+ For the most part, version 1.0 of <tt>vestal_versions</tt> is backwards compatible, with just a few notable changes:
82
+
83
+ * The versions table has been beefed up. You'll need to add the following columns (and indexes, if you feel so inclined):
84
+
85
+ change_table :versions do |t|
86
+ t.belongs_to :user, :polymorphic => true
87
+ t.string :user_name
88
+ t.string :tag
89
+ end
90
+
91
+ change_table :versions do |t|
92
+ t.index [:user_id, :user_type]
93
+ t.index :user_name
94
+ t.index :tag
95
+ end
96
+
97
+ * When a model is created (or updated the first time after being versioned), an initial version record with a number of 1 is no longer created. These aren't used during reversion and so they end up just being dead weight. Feel free to scrap all your versions where <tt>number == 1</tt> after the upgrade if you'd like to free up some room in your database (but you don't have to).
98
+
99
+ * Models that have no version records in the database will return a <tt>@user.version</tt> of 1. In the past, this would have returned <tt>nil</tt> instead.
100
+
101
+ * <tt>Version</tt> has moved to <tt>VestalVersions::Version</tt> to make way for custom version classes.
102
+
103
+ * <tt>Version#version</tt> did not survive the move to <tt>VestalVersions::Version#version</tt>. That alias was dropped (too confusing). Use <tt>VestalVersions::Version#number</tt>.
104
+
105
+ == New to 1.0
106
+
107
+ There are a handful of exciting new additions in version 1.0 of <tt>vestal_versions</tt>. A lot has changed in the code: much better documentation, more modular organization of features, and a more exhaustive test suite. But there are also a number of new features that are available in this release of <tt>vestal_versions</tt>:
108
+
109
+ * The ability to completely skip versioning within a new <tt>skip_version</tt> block:
110
+
111
+ @user.version # => 1
112
+ @user.skip_version do
113
+ @user.update_attribute(:first_name, "Stephen")
114
+ @user.first_name = "Steve"
115
+ @user.save
116
+ @user.update_attributes(:last_name => "Jobs")
117
+ end
118
+ @user.version # => 1
119
+
120
+ Also available, are <tt>merge_version</tt> and <tt>append_version</tt> blocks. The <tt>merge_version</tt> block will compile the possibly multiple versions that would result from the updates inside the block into one summary version. The single resulting version is then tacked onto the version history as usual. The <tt>append_version</tt> block works similarly except that the resulting single version is combined with the most recent version in the history and saved.
121
+
122
+ * Version tagging. Any version can have a tag attached to it (must be unique within the scope of the versioned parent) and that tag can be used for reversion.
123
+
124
+ @user.name # => "Steve Richert"
125
+ @user.update_attribute(:last_name, "Jobs")
126
+ @user.name # => "Steve Jobs"
127
+ @user.tag_version("apple")
128
+ @user.update_attribute(:last_name, "Richert")
129
+ @user.name # => "Steve Richert"
130
+ @user.revert_to("apple")
131
+ @user.name # => "Steve Jobs"
132
+
133
+ So if you're not big on version numbers, you could just tag your versions and avoid the numbers altogether.
134
+
135
+ * Resetting. This is basically a hard revert. The new <tt>reset_to!</tt> instance method behaves just like the <tt>revert_to!</tt> method except that after the reversion, it will also scrap all the versions that came after that target version.
136
+
137
+ @user.name # => "Steve Richert"
138
+ @user.version # => 1
139
+ @user.versions.count # => 0
140
+ @user.update_attribute(:last_name, "Jobs")
141
+ @user.name # => "Steve Jobs"
142
+ @user.version # => 2
143
+ @user.versions.count # => 1
144
+ @user.reset_to!(1)
145
+ @user.name # => "Steve Richert"
146
+ @user.version # => 1
147
+ @user.versions.count # => 0
148
+
149
+ * Storing which user is responsible for a revision. Rather than introduce a lot of controller magic to guess what to store, you can simply update an additional attribute on your versioned model: <tt>updated_by</tt>.
150
+
151
+ @user.update_attributes(:last_name => "Jobs", :updated_by => "Tyler")
152
+ @user.versions.last.user # => "Tyler"
153
+
154
+ Instead of passing a simple string to the <tt>updated_by</tt> setter, you can pass a model instance, such as an ActiveRecord user or administrator. The association will be saved polymorphically alongside the version.
155
+
156
+ @user.update_attributes(:last_name => "Jobs", :updated_by => current_user)
157
+ @user.versions.last.user # => #<User first_name: "Steven", last_name: "Tyler">
158
+
159
+ * Global configuration. The new <tt>vestal_versions</tt> Rails generator also writes an initializer with instructions on how to set application-wide options for the <tt>versioned</tt> method.
160
+
161
+ * Conditional version creation. The <tt>versioned</tt> method now accepts <tt>:if</tt> and <tt>:unless</tt> options. Each expects a symbol representing an instance method or a proc that will be evaluated to determine whether or not to create a new version after an update. An array containing any combination of symbols and procs can also be given.
162
+
163
+ class User < ActiveRecord::Base
164
+ versioned :if => :really_create_a_version?
165
+ end
166
+
167
+ * Custom version classes. By passing a <tt>:class_name</tt> option to the <tt>versioned</tt> method, you can specify your own ActiveRecord version model. <tt>VestalVersions::Version</tt> is the default, but feel free to stray from that. I recommend that your custom model inherit from <tt>VestalVersions::Version</tt>, but that's up to you!
168
+
169
+ * A <tt>versioned?</tt> convenience class method. If your user model is versioned, <tt>User.versioned?</tt> will let you know.
170
+
171
+ * Soft Deletes & Restoration. By setting <tt>:dependent</tt> to <tt>:tracking</tt> destroys will be tracked. On destroy a new version will be created storing the complete details of the object with a tag of 'deleted'. The object can later be restored using the <tt>restore!</tt> method on the VestalVersions::Version record. The attributes of the restored object will be set using the attribute writer methods. After a restore! is performed the version record with the 'deleted' tag is removed from the history.
172
+
173
+ class User < ActiveRecord::Base
174
+ versioned :dependent => :tracking
175
+ end
176
+
177
+ >> @user.version
178
+ => 2
179
+ >> @user.destroy
180
+ => <User id: 2, first_name: "Steve", last_name: "Jobs", ... >
181
+ >> User.find(2)
182
+ => ActiveRecord::RecordNotFound: Couldn't find User with ID=2
183
+ >> VestalVersions::Version.last
184
+ => <VestalVersions::Version id: 4, versioned_id: 2, versioned_type: "User", user_id: nil, user_type: nil, user_name: nil, modifications: {"created_at"=>Sun Aug 01 18:39:57 UTC 2010, "updated_at"=>Sun Aug 01 18:42:28 UTC 2010, "id"=>2, "last_name"=>"Jobs", "first_name"=>"Stephen"}, number: 3, tag: "deleted", created_at: "2010-08-01 18:42:43", updated_at: "2010-08-01 18:42:43">
185
+ >> VestalVersions::Version.last.restore!
186
+ => <User id: 2, first_name => "Steven", last_name: "Jobs", ... >
187
+ >> @user = User.find(2)
188
+ => <User id: 2, first_name => "Steven", last_name: "Jobs", ... >
189
+ >> @user.version
190
+ => 2
191
+
192
+ == Thanks!
193
+
194
+ Thank you to all those who post {issues and suggestions}[http://github.com/laserlemon/vestal_versions/issues]. And special thanks to:
195
+
196
+ * splattael[http://github.com/splattael], who first bugged (and helped) me to write some tests for this thing
197
+ * snaury[http://github.com/snaury], who helped out early on with the <tt>between</tt> association method, the <tt>:dependent</tt> option and a conflict from using a method called <tt>changes</tt>
198
+ * sthapit[http://github.com/sthapit], who was responsible for the <tt>:only</tt> and <tt>:except</tt> options as well as showing me that I'm a dummy for storing a useless first version
199
+
200
+ To contribute to <tt>vestal_versions</tt>, please fork, hack away in the integration[http://github.com/laserlemon/vestal_versions/tree/integration] branch and send me a pull request. Remember your tests!
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 3.0'
4
+
5
+ gemspec :path => '../'
6
+
7
+ gem 'rake', '>= 0.9'
8
+ gem 'rspec', '~> 2.0'
9
+ gem 'sqlite3', '~> 1.0'
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 3.1'
4
+
5
+ gemspec :path => '../'
6
+
7
+ gem 'rake', '>= 0.9'
8
+ gem 'rspec', '~> 2.0'
9
+ gem 'sqlite3', '~> 1.0'
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 3.2'
4
+
5
+ gemspec :path => '../'
6
+
7
+ gem 'rake', '>= 0.9'
8
+ gem 'rspec', '~> 2.0'
9
+ gem 'sqlite3', '~> 1.0'
@@ -0,0 +1,11 @@
1
+ require 'rails/generators/named_base'
2
+
3
+ module VestalVersions
4
+ module Generators
5
+ module Base
6
+ def source_root
7
+ @_vestal_versions_source_root ||= File.expand_path(File.join('../vestal_versions', generator_name, 'templates'), __FILE__)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ require 'generators/vestal_versions'
2
+ require 'rails/generators/active_record'
3
+
4
+ module VestalVersions
5
+ module Generators
6
+ class MigrationGenerator < ActiveRecord::Generators::Base
7
+ extend Base
8
+
9
+ argument :name, :type => :string, :default => 'create_vestal_versions'
10
+
11
+ def generate_files
12
+ migration_template 'migration.rb', "db/migrate/#{name}"
13
+ template 'initializer.rb', 'config/initializers/vestal_versions.rb'
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ VestalVersions.configure do |config|
2
+ # Place any global options here. For example, in order to specify your own version model to use
3
+ # throughout the application, simply specify:
4
+ #
5
+ # config.class_name = "MyCustomVersion"
6
+ #
7
+ # Any options passed to the "versioned" method in the model itself will override this global
8
+ # configuration.
9
+ end
@@ -0,0 +1,28 @@
1
+ class CreateVestalVersions < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :versions do |t|
4
+ t.belongs_to :versioned, :polymorphic => true
5
+ t.belongs_to :user, :polymorphic => true
6
+ t.string :user_name
7
+ t.text :modifications
8
+ t.integer :number
9
+ t.integer :reverted_from
10
+ t.string :tag
11
+
12
+ t.timestamps
13
+ end
14
+
15
+ change_table :versions do |t|
16
+ t.index [:versioned_id, :versioned_type]
17
+ t.index [:user_id, :user_type]
18
+ t.index :user_name
19
+ t.index :number
20
+ t.index :tag
21
+ t.index :created_at
22
+ end
23
+ end
24
+
25
+ def self.down
26
+ drop_table :versions
27
+ end
28
+ end
@@ -0,0 +1,126 @@
1
+ require 'active_support/concern'
2
+ require 'active_support/dependencies/autoload'
3
+ require 'active_support/core_ext/module/delegation'
4
+ require 'active_record'
5
+
6
+ # +vestal_versions+ keeps track of updates to ActiveRecord models, leveraging the introduction of
7
+ # dirty attributes in Rails 2.1. By storing only the updated attributes in a serialized column of a
8
+ # single version model, the history is kept DRY and no additional schema changes are necessary.
9
+ #
10
+ # Author:: Steve Richert
11
+ # Copyright:: Copyright (c) 2009 Steve Richert
12
+ # License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
13
+ #
14
+ # To enable versioning on a model, simply use the +versioned+ method:
15
+ #
16
+ # class User < ActiveRecord::Base
17
+ # versioned
18
+ # end
19
+ #
20
+ # user = User.create(:name => "Steve Richert")
21
+ # user.version # => 1
22
+ # user.update_attribute(:name, "Steve Jobs")
23
+ # user.version # => 2
24
+ # user.revert_to(1)
25
+ # user.name # => "Steve Richert"
26
+ #
27
+ # See the +versioned+ documentation for more details.
28
+
29
+ # The base module that gets included in ActiveRecord::Base. See the documentation for
30
+ # VestalVersions::ClassMethods for more useful information.
31
+ module VestalVersions
32
+ extend ActiveSupport::Concern
33
+ extend ActiveSupport::Autoload
34
+
35
+ autoload :Changes
36
+ autoload :Conditions
37
+ autoload :Control
38
+ autoload :Creation
39
+ autoload :Deletion
40
+ autoload :Options
41
+ autoload :Reload
42
+ autoload :Reset
43
+ autoload :Reversion
44
+ autoload :Users
45
+ autoload :Version
46
+ autoload :VERSION, 'vestal_versions/version_num'
47
+ autoload :VersionTagging
48
+ autoload :Versioned
49
+ autoload :Versions
50
+
51
+ class << self
52
+ delegate :config, :configure, :to => Version
53
+ end
54
+
55
+ included do
56
+ include Versioned
57
+ end
58
+
59
+ module ClassMethods
60
+ # +versioned+ associates an ActiveRecord model with many versions. When the object is updated,
61
+ # a new version containing the changes is created. There are several options available to the
62
+ # +versioned+ method, most of which are passed to the +has_many+ association itself:
63
+ # * <tt>:class_name</tt>: The class name of the version model to use for the association. By
64
+ # default, this is set to "VestalVersions::Version", representing the built-in version class.
65
+ # By specifying this option, you can override the version class, to include custom version
66
+ # behavior. It's recommended that a custom version inherit from VestalVersions::Version.
67
+ # * <tt>:dependent</tt>: Also common to +has_many+ associations, this describes the behavior of
68
+ # version records when the parent object is destroyed. This defaults to :delete_all, which
69
+ # will permanently remove all associated versions *without* triggering any destroy callbacks.
70
+ # Other options are :destroy which removes the associated versions *with* callbacks, or
71
+ # :nullify which leaves the version records in the database, but dissociates them from the
72
+ # parent object by setting the foreign key columns to +nil+ values. Setting this option to
73
+ # :tracking will perform a soft delete on destroy and create a new version record preserving
74
+ # details of this record for later restoration.
75
+ # * <tt>:except</tt>: An update will trigger version creation as long as at least one column
76
+ # outside those specified here was updated. Also, upon version creation, the columns
77
+ # specified here will be excluded from the change history. This is useful when dealing with
78
+ # unimportant, constantly changing, or sensitive information. This option accepts a symbol,
79
+ # string or an array of either, representing column names to exclude. It is completely
80
+ # optional and defaults to +nil+, allowing all columns to be versioned. This option is also
81
+ # ignored if the +only+ option is used.
82
+ # * <tt>:extend</tt>: This option allows you to extend the +has_many+ association proxy with a
83
+ # module or an array of modules. Any methods defined in those modules become available on the
84
+ # +versions+ association. The VestalVersions::Versions module is essential to the
85
+ # functionality of +vestal_versions+ and so is prepended to any additional modules that you
86
+ # might specify here.
87
+ # * <tt>:if</tt>: Accepts a symbol, a proc or an array of either to be evaluated when the parent
88
+ # object is updated to determine whether a new version should be created. +to_proc+ is called
89
+ # on any symbols given and the resulting procs are called, passing in the object itself. If
90
+ # an array is given, all must be evaluate to +true+ in order for a version to be created.
91
+ # * <tt>:initial_version</tt>: When set to true, an initial version is always created when the
92
+ # parent object is created. This initial version will have nil changes however it can be
93
+ # used to store who created the original version.
94
+ # * <tt>:only</tt>: An update will trigger version creation as long as at least one updated
95
+ # column falls within those specified here. Also, upon version creation, only the columns
96
+ # specified here will be included in the change history. This option accepts a symbol, string
97
+ # or an array of either, representing column names to include. It is completely optional and
98
+ # defaults to +nil+, allowing all columns to be versioned. This option takes precedence over
99
+ # the +except+ option if both are specified.
100
+ # * <tt>:unless</tt>: Accepts a symbol, a proc or an array of either to be evaluated when the
101
+ # parent object is updated to determine whether version creation should be skipped. +to_proc+
102
+ # is called on any symbols given and the resulting procs are called, passing in the object
103
+ # itself. If an array is given and any element evaluates as +true+, the version creation will
104
+ # be skipped.
105
+ def versioned(options = {}, &block)
106
+ return if versioned?
107
+
108
+ include Options
109
+ include Changes
110
+ include Creation
111
+ include Users
112
+ include Reversion
113
+ include Reset
114
+ include Conditions
115
+ include Control
116
+ include VersionTagging
117
+ include Reload
118
+ include Deletion
119
+
120
+ prepare_versioned_options(options)
121
+ has_many :versions, options, &block
122
+ end
123
+ end
124
+ end
125
+
126
+ ActiveRecord::Base.class_eval{ include VestalVersions }