Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 197 lines (143 sloc) 10.152 kb
c65bb36 @lailsonbm Updated README.
lailsonbm authored
1 = vestal_versions for Rails 3
8e022b4 Replaced placeholder README with an temporary rdoc version.
laserlemon authored
2
0707988 Populated README.rdoc.
laserlemon authored
3 Finally, DRY ActiveRecord versioning!
4
4fddd55 Just fixed a couple README typos.
laserlemon authored
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.
0707988 Populated README.rdoc.
laserlemon authored
6
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
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.
0707988 Populated README.rdoc.
laserlemon authored
8
4fddd55 Just fixed a couple README typos.
laserlemon authored
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!
0707988 Populated README.rdoc.
laserlemon authored
10
c65bb36 @lailsonbm Updated README.
lailsonbm authored
11 == Compatibility
bca79b6 @alexcrichton Whitespace in the README
authored
12
6aabc69 @alexcrichton Not a few changes in the README
authored
13 Tested with Active Record 3.0.3 with Ruby 1.8.7 and 1.9.2.
c65bb36 @lailsonbm Updated README.
lailsonbm authored
14
8e022b4 Replaced placeholder README with an temporary rdoc version.
laserlemon authored
15 == Installation
16
9df232a @lailsonbm Tweaking README.
lailsonbm authored
17 In the Gemfile:
0707988 Populated README.rdoc.
laserlemon authored
18
5f5c083 @adamcooper updating the install instructions
adamcooper authored
19 gem 'vestal_versions', :git => 'git://github.com/adamcooper/vestal_versions'
2796cb5 @laserlemon Cleaned up README whitespace.
laserlemon authored
20
0707988 Populated README.rdoc.
laserlemon authored
21 Next, generate and run the first and last versioning migration you'll ever need:
22
6aabc69 @alexcrichton Not a few changes in the README
authored
23 $ rails generate vestal_versions:migration
0707988 Populated README.rdoc.
laserlemon authored
24 $ rake db:migrate
8e022b4 Replaced placeholder README with an temporary rdoc version.
laserlemon authored
25
26 == Example
27
4fbae79 Fixed a README typo.
laserlemon authored
28 To version an ActiveRecord model, simply add <tt>versioned</tt> to your class like so:
0707988 Populated README.rdoc.
laserlemon authored
29
30 class User < ActiveRecord::Base
31 versioned
bca79b6 @alexcrichton Whitespace in the README
authored
32
0707988 Populated README.rdoc.
laserlemon authored
33 validates_presence_of :first_name, :last_name
bca79b6 @alexcrichton Whitespace in the README
authored
34
0707988 Populated README.rdoc.
laserlemon authored
35 def name
36 "#{first_name} #{last_name}"
37 end
38 end
8e022b4 Replaced placeholder README with an temporary rdoc version.
laserlemon authored
39
0707988 Populated README.rdoc.
laserlemon authored
40 It's that easy! Now watch it in action...
8e022b4 Replaced placeholder README with an temporary rdoc version.
laserlemon authored
41
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
42 >> u = User.create(:first_name => "Steve", :last_name => "Richert")
0707988 Populated README.rdoc.
laserlemon authored
43 => #<User first_name: "Steve", last_name: "Richert">
44 >> u.version
45 => 1
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
46 >> u.update_attribute(:first_name, "Stephen")
0707988 Populated README.rdoc.
laserlemon authored
47 => true
48 >> u.name
49 => "Stephen Richert"
50 >> u.version
51 => 2
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
52 >> u.revert_to(10.seconds.ago)
0707988 Populated README.rdoc.
laserlemon authored
53 => 1
54 >> u.name
55 => "Steve Richert"
56 >> u.version
57 => 1
58 >> u.save
59 => true
60 >> u.version
61 => 3
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
62 >> u.update_attribute(:last_name, "Jobs")
0707988 Populated README.rdoc.
laserlemon authored
63 => true
64 >> u.name
65 => "Steve Jobs"
66 >> u.version
67 => 4
68 >> u.revert_to!(2)
69 => true
70 >> u.name
71 => "Stephen Richert"
72 >> u.version
73 => 5
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
74
75 == Upgrading to 1.0
76
77 For the most part, version 1.0 of <tt>vestal_versions</tt> is backwards compatible, with just a few notable changes:
78
79 * The versions table has been beefed up. You'll need to add the following columns (and indexes, if you feel so inclined):
bca79b6 @alexcrichton Whitespace in the README
authored
80
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
81 change_table :versions do |t|
82 t.belongs_to :user, :polymorphic => true
83 t.string :user_name
84 t.string :tag
85 end
bca79b6 @alexcrichton Whitespace in the README
authored
86
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
87 change_table :versions do |t|
88 t.index [:user_id, :user_type]
89 t.index :user_name
90 t.index :tag
91 end
bca79b6 @alexcrichton Whitespace in the README
authored
92
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
93 * 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).
bca79b6 @alexcrichton Whitespace in the README
authored
94
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
95 * 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.
bca79b6 @alexcrichton Whitespace in the README
authored
96
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
97 * <tt>Version</tt> has moved to <tt>VestalVersions::Version</tt> to make way for custom version classes.
bca79b6 @alexcrichton Whitespace in the README
authored
98
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
99 * <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>.
100
101 == New to 1.0
102
103 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>:
104
105 * The ability to completely skip versioning within a new <tt>skip_version</tt> block:
bca79b6 @alexcrichton Whitespace in the README
authored
106
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
107 @user.version # => 1
108 @user.skip_version do
109 @user.update_attribute(:first_name, "Stephen")
110 @user.first_name = "Steve"
111 @user.save
112 @user.update_attributes(:last_name => "Jobs")
113 end
114 @user.version # => 1
bca79b6 @alexcrichton Whitespace in the README
authored
115
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
116 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.
bca79b6 @alexcrichton Whitespace in the README
authored
117
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
118 * 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.
bca79b6 @alexcrichton Whitespace in the README
authored
119
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
120 @user.name # => "Steve Richert"
121 @user.update_attribute(:last_name, "Jobs")
122 @user.name # => "Steve Jobs"
123 @user.tag_version("apple")
124 @user.update_attribute(:last_name, "Richert")
125 @user.name # => "Steve Richert"
126 @user.revert_to("apple")
127 @user.name # => "Steve Jobs"
bca79b6 @alexcrichton Whitespace in the README
authored
128
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
129 So if you're not big on version numbers, you could just tag your versions and avoid the numbers altogether.
bca79b6 @alexcrichton Whitespace in the README
authored
130
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
131 * 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.
bca79b6 @alexcrichton Whitespace in the README
authored
132
0c94215 @laserlemon Fixed some README code indentation.
laserlemon authored
133 @user.name # => "Steve Richert"
134 @user.version # => 1
135 @user.versions.count # => 0
136 @user.update_attribute(:last_name, "Jobs")
137 @user.name # => "Steve Jobs"
138 @user.version # => 2
139 @user.versions.count # => 1
140 @user.reset_to!(1)
141 @user.name # => "Steve Richert"
142 @user.version # => 1
143 @user.versions.count # => 0
bca79b6 @alexcrichton Whitespace in the README
authored
144
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
145 * 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>.
bca79b6 @alexcrichton Whitespace in the README
authored
146
0c94215 @laserlemon Fixed some README code indentation.
laserlemon authored
147 @user.update_attributes(:last_name => "Jobs", :updated_by => "Tyler")
148 @user.versions.last.user # => "Tyler"
bca79b6 @alexcrichton Whitespace in the README
authored
149
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
150 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.
bca79b6 @alexcrichton Whitespace in the README
authored
151
0c94215 @laserlemon Fixed some README code indentation.
laserlemon authored
152 @user.update_attributes(:last_name => "Jobs", :updated_by => current_user)
153 @user.versions.last.user # => #<User first_name: "Steven", last_name: "Tyler">
bca79b6 @alexcrichton Whitespace in the README
authored
154
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
155 * 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.
bca79b6 @alexcrichton Whitespace in the README
authored
156
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
157 * 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.
bca79b6 @alexcrichton Whitespace in the README
authored
158
0c94215 @laserlemon Fixed some README code indentation.
laserlemon authored
159 class User < ActiveRecord::Base
160 versioned :if => :really_create_a_version?
161 end
bca79b6 @alexcrichton Whitespace in the README
authored
162
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
163 * 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!
bca79b6 @alexcrichton Whitespace in the README
authored
164
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
165 * A <tt>versioned?</tt> convenience class method. If your user model is versioned, <tt>User.versioned?</tt> will let you know.
e59d61c @adamcooper Updating the documentation for the soft deletes.
adamcooper authored
166
167 * 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.
168
169 class User < ActiveRecord::Base
170 versioned :dependent => :tracking
171 end
172
173 >> @user.version
174 => 2
175 >> @user.destroy
bca79b6 @alexcrichton Whitespace in the README
authored
176 => <User id: 2, first_name: "Steve", last_name: "Jobs", ... >
e59d61c @adamcooper Updating the documentation for the soft deletes.
adamcooper authored
177 >> User.find(2)
178 => ActiveRecord::RecordNotFound: Couldn't find User with ID=2
179 >> VestalVersions::Version.last
bca79b6 @alexcrichton Whitespace in the README
authored
180 => <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">
e59d61c @adamcooper Updating the documentation for the soft deletes.
adamcooper authored
181 >> VestalVersions::Version.last.restore!
182 => <User id: 2, first_name => "Steven", last_name: "Jobs", ... >
183 >> @user = User.find(2)
184 => <User id: 2, first_name => "Steven", last_name: "Jobs", ... >
185 >> @user.version
186 => 2
187
7614a0b @laserlemon Updated the README to reflect usage changes and to outline the upgrade p...
laserlemon authored
188 == Thanks!
189
190 Thank you to all those who post {issues and suggestions}[http://github.com/laserlemon/vestal_versions/issues]. And special thanks to:
191
192 * splattael[http://github.com/splattael], who first bugged (and helped) me to write some tests for this thing
193 * 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>
194 * 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
b9ea70d @laserlemon Added short contribution instructions to the README.
laserlemon authored
195
196 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!
Something went wrong with that request. Please try again.