acts_as_versionable 0.5.0 → 0.5.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/README.rdoc +53 -21
- data/lib/acts_as_versionable.rb +16 -6
- data/lib/acts_as_versionable/version.rb +1 -1
- data/test/acts_as_versionable_test.rb +264 -0
- metadata +4 -2
data/README.rdoc
CHANGED
@@ -1,28 +1,29 @@
|
|
1
1
|
= Acts As Versionable
|
2
2
|
|
3
|
-
|
4
|
-
Maintains versions in same table, just adding two new fields
|
3
|
+
Minimalist versionable engine for rails > 3
|
5
4
|
|
6
|
-
|
5
|
+
Maintains versions in same table, just adding two new fields
|
7
6
|
|
8
7
|
=== Installation
|
9
8
|
|
10
9
|
gem 'acts_as_versionable'
|
11
10
|
|
12
|
-
|
11
|
+
add two fields to your model
|
13
12
|
|
14
13
|
version_number:integer
|
15
14
|
version_id:integer
|
16
15
|
|
16
|
+
**Sample migration**
|
17
|
+
|
17
18
|
class ActsAsVersionableDocuments < ActiveRecord::Migration
|
18
19
|
def self.up
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
20
|
+
add_column :documents, :version_number, :default => 0
|
21
|
+
add_column :documents, :version_id, :default => null
|
22
|
+
|
23
|
+
# optional indexes
|
24
|
+
add_index :documents, :version_number, :name => "index_documents_on_version_number"
|
25
|
+
add_index :documents, :version_id, :name => "index_documents_on_version_id"
|
26
|
+
# optional unique :id, :version_number
|
26
27
|
end
|
27
28
|
|
28
29
|
def self.down
|
@@ -30,17 +31,17 @@
|
|
30
31
|
remove_column :documents, :version_id
|
31
32
|
end
|
32
33
|
end
|
33
|
-
|
34
|
+
|
34
35
|
=== Example
|
35
36
|
|
36
37
|
class Document < ActiveRecord::Base
|
37
38
|
acts_as_versionable :limit => 3
|
38
39
|
end
|
39
40
|
|
40
|
-
# use scope to get top versions
|
41
|
-
documents = Document.last_versions
|
41
|
+
# use scope to get top versions
|
42
|
+
documents = Document.last_versions => scope that return most recent versions
|
42
43
|
|
43
|
-
#
|
44
|
+
# create first version
|
44
45
|
document = Document.create(:title => 'title', :body => 'body')
|
45
46
|
document.version => 1
|
46
47
|
document.last_version => 1
|
@@ -56,14 +57,45 @@
|
|
56
57
|
|
57
58
|
# revert to version 1
|
58
59
|
document.revert_to_version(1) => #<Document ...>
|
59
|
-
document.version => 3
|
60
|
+
document.version => 3 # after revert a new version is created with content of version 1
|
60
61
|
document.title => 'title'
|
61
|
-
|
62
|
-
# get a version
|
63
|
-
version2 = document.get_version 2 => #<Document ... version_number => 2>
|
64
|
-
version2.version => 2
|
65
|
-
version2.title => 'new title"
|
62
|
+
|
63
|
+
# get a version
|
64
|
+
version2 = document.get_version 2 => #<Document ... version_number => 2>
|
65
|
+
version2.version => 2
|
66
|
+
version2.title => 'new title"
|
66
67
|
version2.last_version => 4
|
67
68
|
|
69
|
+
# you can't edit a version
|
70
|
+
version2 = document.get_version 2
|
71
|
+
version2.title = "new version"
|
72
|
+
version2.save => raise NonEditableVersionError
|
73
|
+
|
74
|
+
version2.versionable? => false
|
75
|
+
|
76
|
+
# to edit a version you need get the editable_version
|
77
|
+
editable = version2.editable_version
|
78
|
+
editable.versionable? => true
|
79
|
+
editable.title = "new"
|
80
|
+
editable.save
|
81
|
+
editable.last_version => 5
|
82
|
+
|
83
|
+
== How to contribute
|
84
|
+
|
85
|
+
If you find what you might think is a bug:
|
86
|
+
|
87
|
+
1. Check the GitHub issue tracker to see if anyone else has had the same issue.
|
88
|
+
http://github.com/csegura/acts_as_versionable/issues/
|
89
|
+
2. If you don't see anything, create an issue with information on how to reproduce it.
|
90
|
+
|
91
|
+
If you want to contribute an enhancement or a fix:
|
92
|
+
|
93
|
+
1. Fork the project on github.
|
94
|
+
http://github.com/csegura/acts_as_versionable/
|
95
|
+
2. Make your changes with tests.
|
96
|
+
3. Commit the changes without making changes to the Rakefile, VERSION, or any other files that aren't related to your enhancement or fix
|
97
|
+
4. Send a pull request.
|
98
|
+
|
99
|
+
Copyright ©2012 Carlos Segura, released under the MIT license
|
68
100
|
|
69
101
|
|
data/lib/acts_as_versionable.rb
CHANGED
@@ -4,6 +4,8 @@ module ActsAsVersionable
|
|
4
4
|
|
5
5
|
class NoSuchVersionError < Exception
|
6
6
|
end
|
7
|
+
class NonEditableVersionError < Exception
|
8
|
+
end
|
7
9
|
|
8
10
|
extend ActiveSupport::Concern
|
9
11
|
|
@@ -16,6 +18,7 @@ module ActsAsVersionable
|
|
16
18
|
self.max_versions = (options[:max_versions] || 10)
|
17
19
|
|
18
20
|
after_save :create_new_version
|
21
|
+
before_validation :versionable_validation
|
19
22
|
|
20
23
|
has_many :internal_versions,
|
21
24
|
:class_name => self.name,
|
@@ -35,7 +38,7 @@ module ActsAsVersionable
|
|
35
38
|
module InstanceMethods
|
36
39
|
def revert_to_version(number)
|
37
40
|
version = get_version number
|
38
|
-
editable =
|
41
|
+
editable = editable_version
|
39
42
|
copy_version_values version, editable
|
40
43
|
editable.version_number = nil
|
41
44
|
editable.version_id = nil
|
@@ -75,13 +78,13 @@ module ActsAsVersionable
|
|
75
78
|
return 0 if versions.count == 0
|
76
79
|
versions.first.version_number
|
77
80
|
end
|
78
|
-
|
79
|
-
private
|
80
|
-
|
81
|
+
|
81
82
|
# return the last version editable
|
82
|
-
def
|
83
|
+
def editable_version
|
83
84
|
parent_version.nil? ? self : parent_version
|
84
|
-
end
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
85
88
|
|
86
89
|
# callback after save
|
87
90
|
def create_new_version
|
@@ -105,6 +108,13 @@ module ActsAsVersionable
|
|
105
108
|
columns.each {|c| to[c.name] = from[c.name] }
|
106
109
|
to
|
107
110
|
end
|
111
|
+
|
112
|
+
def versionable_validation
|
113
|
+
if !new_record? && !versionable?
|
114
|
+
raise NonEditableVersionError, "Can't modify versioned record. Use version.editable_version!!"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
108
118
|
end
|
109
119
|
|
110
120
|
end
|
@@ -0,0 +1,264 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'activerecord'
|
3
|
+
require 'active_record'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
#require "#{File.dirname(__FILE__)}/../init"
|
7
|
+
require File.join(File.dirname(__FILE__), '../lib', 'acts_as_versionable')
|
8
|
+
|
9
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
10
|
+
|
11
|
+
def setup_db
|
12
|
+
ActiveRecord::Schema.define(:version => 1) do
|
13
|
+
create_table :documents do |t|
|
14
|
+
t.string :title
|
15
|
+
t.text :body
|
16
|
+
t.integer :user_id
|
17
|
+
t.integer :version_number
|
18
|
+
t.integer :version_id
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def teardown_db
|
25
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
26
|
+
ActiveRecord::Base.connection.drop_table(table)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Document < ActiveRecord::Base
|
31
|
+
acts_as_versionable
|
32
|
+
end
|
33
|
+
|
34
|
+
class VersionableTest < Test::Unit::TestCase
|
35
|
+
def setup
|
36
|
+
setup_db
|
37
|
+
|
38
|
+
(1..3).each do |m|
|
39
|
+
document = Document.create!(:title => m.to_s, :body => m.to_s)
|
40
|
+
(1..m*2).each do |v|
|
41
|
+
document.title = v.to_s
|
42
|
+
document.body = v.to_s
|
43
|
+
document.save
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def teardown
|
49
|
+
teardown_db
|
50
|
+
end
|
51
|
+
|
52
|
+
def test_documents
|
53
|
+
assert_equal 18, Document.all.count
|
54
|
+
#Document.last_versions.each do |d|
|
55
|
+
# p "title: #{d.title} versions: #{d.last_version}"
|
56
|
+
# p "#{d.versions.map &:title}"
|
57
|
+
#end
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_if_class_methods_present
|
61
|
+
[:last_versions, :get_versionable].each do |method|
|
62
|
+
assert_equal true, Document.respond_to?(method)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_if_mixed_methods_present
|
67
|
+
document = Document.first
|
68
|
+
[:versions, :get_version, :revert_to_version,
|
69
|
+
:last_version, :versionable?, :editable_version,
|
70
|
+
:internal_versions, :parent_version].each do |method|
|
71
|
+
assert_equal true, document.respond_to?(method)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_last_versions
|
76
|
+
documents = Document.last_versions
|
77
|
+
assert_equal 3, documents.count
|
78
|
+
end
|
79
|
+
|
80
|
+
def test_initial_versions_of_the_documents
|
81
|
+
documents = Document.last_versions
|
82
|
+
|
83
|
+
documents.each do |d|
|
84
|
+
assert_equal nil, d.version_number
|
85
|
+
assert_equal nil, d.version_id
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_get_versionable
|
90
|
+
document = Document.get_versionable(1,2).first
|
91
|
+
assert_equal 2, document.version_number
|
92
|
+
assert_equal '1', document.title
|
93
|
+
end
|
94
|
+
|
95
|
+
def test_versions_created
|
96
|
+
document = Document.create(:title => "A", :body => "AAAA")
|
97
|
+
assert_equal 1, document.last_version
|
98
|
+
assert_equal 1, document.version
|
99
|
+
document.title = "B"
|
100
|
+
document.save
|
101
|
+
assert_equal 2, document.version
|
102
|
+
end
|
103
|
+
|
104
|
+
def test_last_version
|
105
|
+
documents = Document.last_versions
|
106
|
+
|
107
|
+
assert_equal 3, documents[0].last_version
|
108
|
+
assert_equal 5, documents[1].last_version
|
109
|
+
assert_equal 7, documents[2].last_version
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_version_change_to_2
|
113
|
+
document = Document.last_versions.first
|
114
|
+
assert_not_nil document
|
115
|
+
|
116
|
+
assert_equal '2', document.title
|
117
|
+
assert_equal 3, document.last_version
|
118
|
+
|
119
|
+
document.update_attributes(:title => '4', :body => '4')
|
120
|
+
|
121
|
+
assert_equal 4, document.last_version
|
122
|
+
assert_equal '4', document.title
|
123
|
+
assert_equal '4', document.body
|
124
|
+
end
|
125
|
+
|
126
|
+
def test_revert_to_version
|
127
|
+
document = Document.last_versions.first
|
128
|
+
assert_not_nil document
|
129
|
+
|
130
|
+
document.update_attributes(:title => '4', :body => '4')
|
131
|
+
assert_equal 4, document.last_version
|
132
|
+
|
133
|
+
document = document.revert_to_version(1)
|
134
|
+
|
135
|
+
assert_equal 5, document.last_version
|
136
|
+
assert_equal '1', document.title
|
137
|
+
assert_equal '1', document.body
|
138
|
+
|
139
|
+
document = document.revert_to_version(4)
|
140
|
+
|
141
|
+
assert_equal 6, document.last_version
|
142
|
+
assert_equal '4', document.title
|
143
|
+
assert_equal '4', document.body
|
144
|
+
|
145
|
+
assert_raise ActsAsVersionable::NoSuchVersionError do
|
146
|
+
document.revert_to_version(10)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
# def test_new_from_version
|
152
|
+
# article = Article.first
|
153
|
+
# assert_not_nil article
|
154
|
+
#
|
155
|
+
# dummy = article.new_from_version 1
|
156
|
+
#
|
157
|
+
# article.revert_to(1)
|
158
|
+
# assert_equal dummy.title, article.title
|
159
|
+
# assert_equal dummy.body, article.body
|
160
|
+
# end
|
161
|
+
|
162
|
+
def test_versions
|
163
|
+
document = Document.last_versions.first
|
164
|
+
assert_not_nil document
|
165
|
+
|
166
|
+
assert_equal 3, document.versions.count
|
167
|
+
|
168
|
+
document = document.get_version(2)
|
169
|
+
assert_equal 3, document.versions.count
|
170
|
+
end
|
171
|
+
|
172
|
+
def test_get_version
|
173
|
+
document = Document.last_versions.first
|
174
|
+
assert_not_nil document
|
175
|
+
|
176
|
+
first = document.versions[1]
|
177
|
+
one = document.get_version(2)
|
178
|
+
|
179
|
+
assert_equal first.title, one.title
|
180
|
+
assert_equal first.body, one.body
|
181
|
+
end
|
182
|
+
|
183
|
+
def test_get_versions
|
184
|
+
document = Document.last_versions.first
|
185
|
+
assert_not_nil document
|
186
|
+
|
187
|
+
version2 = document.get_version 2
|
188
|
+
assert_equal 2, version2.version
|
189
|
+
end
|
190
|
+
|
191
|
+
def test_max_versions
|
192
|
+
document = Document.last_versions.last
|
193
|
+
(1..10).each do |v|
|
194
|
+
document.title = v.to_s
|
195
|
+
document.save
|
196
|
+
end
|
197
|
+
assert_equal 10, document.versions.count
|
198
|
+
end
|
199
|
+
|
200
|
+
def test_dependent_destroy
|
201
|
+
Document.last_versions.destroy_all
|
202
|
+
assert_equal [], Document.all
|
203
|
+
end
|
204
|
+
|
205
|
+
def test_modify_versioned
|
206
|
+
document = Document.last_versions.first
|
207
|
+
assert_not_nil document
|
208
|
+
|
209
|
+
assert_equal 3, document.last_version
|
210
|
+
|
211
|
+
version2 = document.get_version 2
|
212
|
+
version2.title = "version2 new"
|
213
|
+
|
214
|
+
assert_equal false, version2.versionable?
|
215
|
+
|
216
|
+
assert_raise ActsAsVersionable::NonEditableVersionError do
|
217
|
+
version2.save
|
218
|
+
end
|
219
|
+
|
220
|
+
assert_equal 2, version2.version
|
221
|
+
assert_equal "version2 new", version2.title
|
222
|
+
|
223
|
+
assert_equal 3, document.last_version
|
224
|
+
end
|
225
|
+
|
226
|
+
def test_readme
|
227
|
+
# cerate first version
|
228
|
+
document = Document.create(:title => 'title', :body => 'body')
|
229
|
+
assert_equal 1, document.version
|
230
|
+
assert_equal 1, document.last_version
|
231
|
+
|
232
|
+
# modify
|
233
|
+
document.title = 'new title'
|
234
|
+
document.save
|
235
|
+
|
236
|
+
# second version was created
|
237
|
+
assert_equal 2, document.version
|
238
|
+
assert_equal 2, document.last_version
|
239
|
+
assert_equal 2, document.versions.count
|
240
|
+
|
241
|
+
# revert to previous version
|
242
|
+
document.revert_to_version(1)
|
243
|
+
assert_equal "title", document.title
|
244
|
+
# a new version also is created
|
245
|
+
assert_equal 3, document.version
|
246
|
+
|
247
|
+
# get a version
|
248
|
+
document = document.get_version(2)
|
249
|
+
assert_equal 2, document.version
|
250
|
+
assert_equal "new title", document.title
|
251
|
+
|
252
|
+
# revert to a version
|
253
|
+
document = document.revert_to_version(2)
|
254
|
+
assert_equal "new title", document.title
|
255
|
+
assert_equal 4, document.version
|
256
|
+
|
257
|
+
version1 = document.get_version(1)
|
258
|
+
assert_equal 1, version1.version
|
259
|
+
assert_equal 4, version1.last_version
|
260
|
+
|
261
|
+
end
|
262
|
+
|
263
|
+
end
|
264
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: acts_as_versionable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -26,6 +26,7 @@ files:
|
|
26
26
|
- acts_as_versionable.gemspec
|
27
27
|
- lib/acts_as_versionable.rb
|
28
28
|
- lib/acts_as_versionable/version.rb
|
29
|
+
- test/acts_as_versionable_test.rb
|
29
30
|
homepage: ''
|
30
31
|
licenses: []
|
31
32
|
post_install_message:
|
@@ -50,4 +51,5 @@ rubygems_version: 1.8.10
|
|
50
51
|
signing_key:
|
51
52
|
specification_version: 3
|
52
53
|
summary: Minimalist engine for versions
|
53
|
-
test_files:
|
54
|
+
test_files:
|
55
|
+
- test/acts_as_versionable_test.rb
|