jacqui-versioned 0.1.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/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +51 -0
- data/Rakefile +26 -0
- data/VERSION +1 -0
- data/init.rb +2 -0
- data/lib/version.rb +36 -0
- data/lib/versioned.rb +225 -0
- data/test/between_test.rb +58 -0
- data/test/changes_test.rb +28 -0
- data/test/comparable_test.rb +35 -0
- data/test/creation_test.rb +79 -0
- data/test/helper.rb +10 -0
- data/test/latest_changes_test.rb +42 -0
- data/test/lock_test.rb +91 -0
- data/test/revert_test.rb +79 -0
- data/test/schema.rb +55 -0
- data/test/specified_version_key_test.rb +50 -0
- data/test/test_helper.rb +10 -0
- data/test/test_versioned.rb +80 -0
- data/versioned.gemspec +60 -0
- metadata +92 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2009 Jacqui Maher
|
|
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.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
= versioned
|
|
2
|
+
|
|
3
|
+
Simple versioning for MongoMapper
|
|
4
|
+
|
|
5
|
+
= installation
|
|
6
|
+
|
|
7
|
+
The versioned gem is hosted on gemcutter.org:
|
|
8
|
+
|
|
9
|
+
* gem install versioned
|
|
10
|
+
|
|
11
|
+
= usage
|
|
12
|
+
|
|
13
|
+
class Doc
|
|
14
|
+
include MongoMapper::Document
|
|
15
|
+
include Versioned
|
|
16
|
+
key :title, String
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
@doc = Doc.create(:title=>"v1")
|
|
20
|
+
@doc.title = "v2"
|
|
21
|
+
@doc.save
|
|
22
|
+
|
|
23
|
+
@doc.revert
|
|
24
|
+
|
|
25
|
+
puts @doc.title
|
|
26
|
+
=> v1
|
|
27
|
+
|
|
28
|
+
@doc.title = "v3"
|
|
29
|
+
@doc.save
|
|
30
|
+
@doc.version
|
|
31
|
+
=> 3
|
|
32
|
+
|
|
33
|
+
@doc.retrieve_version 2
|
|
34
|
+
puts @doc.title
|
|
35
|
+
=> "v2"
|
|
36
|
+
|
|
37
|
+
@doc = Doc.find(@doc.id)
|
|
38
|
+
@doc.title
|
|
39
|
+
=> "v3"
|
|
40
|
+
@doc.version
|
|
41
|
+
=> 3
|
|
42
|
+
|
|
43
|
+
== Note on Patches/Pull Requests
|
|
44
|
+
|
|
45
|
+
* Fork the project.
|
|
46
|
+
* Make your feature addition or bug fix.
|
|
47
|
+
* Add tests for it. This is important so I don't break it in a
|
|
48
|
+
future version unintentionally.
|
|
49
|
+
* Commit, do not mess with rakefile, version, or history.
|
|
50
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
|
51
|
+
* Send me a pull request. Bonus points for topic branches.
|
data/Rakefile
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'rake/testtask'
|
|
4
|
+
require 'rake/rdoctask'
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
require 'jeweler'
|
|
8
|
+
Jeweler::Tasks.new do |g|
|
|
9
|
+
g.name = 'jacqui-versioned'
|
|
10
|
+
g.summary = %(Versioning for MongoMapper)
|
|
11
|
+
g.description = %(Versioning for MongoMapper)
|
|
12
|
+
g.email = 'jacqui@brighter.net'
|
|
13
|
+
g.homepage = 'http://github.com/jacqui/versioned'
|
|
14
|
+
g.authors = %w(twoism toastyapps jacqui mrkurt)
|
|
15
|
+
end
|
|
16
|
+
Jeweler::GemcutterTasks.new
|
|
17
|
+
rescue LoadError
|
|
18
|
+
puts 'Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
Rake::TestTask.new do |t|
|
|
22
|
+
t.libs = %w(test)
|
|
23
|
+
t.pattern = 'test/**/*_test.rb'
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
task :default => :test
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.1
|
data/init.rb
ADDED
data/lib/version.rb
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
class Version
|
|
2
|
+
include MongoMapper::Document
|
|
3
|
+
include Comparable
|
|
4
|
+
|
|
5
|
+
key :number, Integer
|
|
6
|
+
key :versioned_type, String
|
|
7
|
+
key :versioned_id, ObjectId
|
|
8
|
+
key :changes, Hash
|
|
9
|
+
timestamps!
|
|
10
|
+
|
|
11
|
+
belongs_to :versioned, :polymorphic => true
|
|
12
|
+
def changes
|
|
13
|
+
read_attribute(:changes)
|
|
14
|
+
end
|
|
15
|
+
alias_attribute :version, :number
|
|
16
|
+
|
|
17
|
+
def <=>(other)
|
|
18
|
+
number <=> other.number
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def previous
|
|
22
|
+
find_related(:first, :number => {:$lt => number}, :order => 'number.desc')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def next
|
|
26
|
+
find_related(:first, :number => {:$gt => number}, :order => 'number.asc')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
protected
|
|
30
|
+
|
|
31
|
+
def find_related(*args)
|
|
32
|
+
options = args.extract_options!
|
|
33
|
+
params = options.merge(:versioned_id => versioned_id, :versioned_type => versioned_type)
|
|
34
|
+
self.class.find(args.first, params)
|
|
35
|
+
end
|
|
36
|
+
end
|
data/lib/versioned.rb
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
require 'version'
|
|
2
|
+
|
|
3
|
+
module Versioned
|
|
4
|
+
class StaleDocumentError < MongoMapper::MongoMapperError; end
|
|
5
|
+
def self.included(base)
|
|
6
|
+
base.extend ClassMethods
|
|
7
|
+
base.class_eval do
|
|
8
|
+
versioned
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module LockingInstanceMethods
|
|
13
|
+
private
|
|
14
|
+
#new? isn't working
|
|
15
|
+
def is_new_document?
|
|
16
|
+
(read_attribute(self.version_lock_key).blank? && changes[self.version_lock_key.to_s].blank?) ||
|
|
17
|
+
(changes[self.version_lock_key.to_s] && changes[self.version_lock_key.to_s].first.blank?)
|
|
18
|
+
end
|
|
19
|
+
def prep_lock_version
|
|
20
|
+
old = read_attribute(self.version_lock_key)
|
|
21
|
+
if !is_new_document? || old.blank?
|
|
22
|
+
v = (Time.now.to_f * 1000).ceil.to_s
|
|
23
|
+
write_attribute self.version_lock_key, v
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
old
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def save_to_collection(options = {})
|
|
30
|
+
current_version = prep_lock_version
|
|
31
|
+
if is_new_document?
|
|
32
|
+
collection.insert(to_mongo, :safe => true)
|
|
33
|
+
else
|
|
34
|
+
selector = { :_id => read_attribute(:_id), self.version_lock_key => current_version }
|
|
35
|
+
#can't upsert, safe must be true for this to work
|
|
36
|
+
result = collection.update(selector, to_mongo, :upsert => false, :safe => true)
|
|
37
|
+
|
|
38
|
+
if result.is_a?(Array) && result[0][0]['updatedExisting'] == false
|
|
39
|
+
write_attribute self.version_lock_key, current_version
|
|
40
|
+
raise StaleDocumentError.new
|
|
41
|
+
elsif !result.is_a?(Array)
|
|
42
|
+
#wtf?
|
|
43
|
+
write_attribute self.version_lock_key, current_version
|
|
44
|
+
raise "Unexpected result from mongo"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
selector[:_id]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
module ClassMethods
|
|
52
|
+
def locking!(options = {})
|
|
53
|
+
include(LockingInstanceMethods)
|
|
54
|
+
class_inheritable_accessor :version_lock_key
|
|
55
|
+
self.version_lock_key = options[:key] || :lock_version
|
|
56
|
+
key self.version_lock_key, Integer
|
|
57
|
+
|
|
58
|
+
if self.respond_to?(:version_use_key)
|
|
59
|
+
self.version_use_key = self.version_lock_key
|
|
60
|
+
(self.version_except_columns ||= []) << self.version_lock_key.to_s #don't version the lock key
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def versioned(options = {})
|
|
65
|
+
class_inheritable_accessor :version_only_columns
|
|
66
|
+
self.version_only_columns = Array(options[:only]).map(&:to_s).uniq if options[:only]
|
|
67
|
+
class_inheritable_accessor :version_except_columns
|
|
68
|
+
self.version_except_columns = Array(options[:except]).map(&:to_s).uniq if options[:except]
|
|
69
|
+
|
|
70
|
+
class_inheritable_accessor :version_use_key
|
|
71
|
+
self.version_use_key = options[:use_key]
|
|
72
|
+
|
|
73
|
+
many :versions, :as => :versioned, :order => 'number ASC', :dependent => :delete_all do
|
|
74
|
+
def between(from, to)
|
|
75
|
+
from_number, to_number = number_at(from), number_at(to)
|
|
76
|
+
return [] if from_number.nil? || to_number.nil?
|
|
77
|
+
condition = (from_number == to_number) ? to_number : Range.new(*[from_number, to_number].sort)
|
|
78
|
+
if condition.is_a?(Range)
|
|
79
|
+
conditions = {'$gte' => condition.first, '$lte' => condition.last}
|
|
80
|
+
else
|
|
81
|
+
conditions = condition
|
|
82
|
+
end
|
|
83
|
+
find(:all,
|
|
84
|
+
:number => conditions,
|
|
85
|
+
:order => "number #{(from_number > to_number) ? 'DESC' : 'ASC'}"
|
|
86
|
+
)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def at(value)
|
|
90
|
+
case value
|
|
91
|
+
when Version then value
|
|
92
|
+
when Numeric then find_by_number(value.floor)
|
|
93
|
+
when Symbol then respond_to?(value) ? send(value) : nil
|
|
94
|
+
when Date, Time then last(:created_at => {'$lte' => value.to_time})
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def number_at(value)
|
|
99
|
+
case value
|
|
100
|
+
when Version then value.number
|
|
101
|
+
when Numeric then value.floor
|
|
102
|
+
when Symbol, Date, Time then at(value).try(:number)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
after_create :create_initial_version
|
|
108
|
+
after_update :create_initial_version, :if => :needs_initial_version?
|
|
109
|
+
after_update :create_version, :if => :needs_version?
|
|
110
|
+
|
|
111
|
+
include InstanceMethods
|
|
112
|
+
alias_method_chain :reload, :versions
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
module InstanceMethods
|
|
118
|
+
private
|
|
119
|
+
def versioned_columns
|
|
120
|
+
case
|
|
121
|
+
when version_only_columns then self.class.keys.keys & version_only_columns
|
|
122
|
+
when version_except_columns then self.class.keys.keys - version_except_columns
|
|
123
|
+
else self.class.keys.keys
|
|
124
|
+
end - %w(created_at created_on updated_at updated_on)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def needs_initial_version?
|
|
128
|
+
versions.empty?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def needs_version?
|
|
132
|
+
!(versioned_columns & changed).empty?
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def reset_version(new_version = nil)
|
|
136
|
+
@last_version = nil if new_version.nil?
|
|
137
|
+
@version = new_version
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def next_version_number(initial = false)
|
|
141
|
+
v = read_attribute self.version_use_key unless self.version_use_key.nil?
|
|
142
|
+
v = 1 if v.nil? && initial
|
|
143
|
+
v = last_version + 1 if v.nil? && !initial
|
|
144
|
+
v
|
|
145
|
+
end
|
|
146
|
+
def create_initial_version
|
|
147
|
+
versions.create(:changes => nil, :number => next_version_number(true))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def create_version
|
|
151
|
+
versions << Version.create(:changes => changes.slice(*versioned_columns), :number => next_version_number)
|
|
152
|
+
reset_version
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
public
|
|
156
|
+
def version
|
|
157
|
+
@version ||= last_version
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def last_version
|
|
161
|
+
@last_version ||= versions.inject(1){|max, version| version.number > max ? version.number : max}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def reverted?
|
|
165
|
+
version != last_version
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def reload_with_versions(*args)
|
|
169
|
+
reset_version
|
|
170
|
+
reload_without_versions(*args)
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def changes_between(from, to)
|
|
174
|
+
from_number, to_number = versions.number_at(from), versions.number_at(to)
|
|
175
|
+
return {} if from_number == to_number
|
|
176
|
+
chain = versions.between(from_number, to_number)
|
|
177
|
+
return {} if chain.empty?
|
|
178
|
+
|
|
179
|
+
backward = chain.first > chain.last
|
|
180
|
+
backward ? chain.pop : chain.shift
|
|
181
|
+
|
|
182
|
+
chain.inject({}) do |changes, version|
|
|
183
|
+
version.changes.each do |attribute, change|
|
|
184
|
+
change.reverse! if backward
|
|
185
|
+
new_change = [changes.fetch(attribute, change).first, change.last]
|
|
186
|
+
changes.update(attribute => new_change)
|
|
187
|
+
end
|
|
188
|
+
changes
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def revert
|
|
193
|
+
revert_to self.versions.at(self.version).previous
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def retrieve_version n
|
|
197
|
+
versions.find_by_number(n).changes.each do |n,v|
|
|
198
|
+
self.send("#{n.to_sym}=",v.first)
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def revert_to(value)
|
|
203
|
+
to_number = versions.number_at(value)
|
|
204
|
+
changes = changes_between(version, to_number)
|
|
205
|
+
return version if changes.empty?
|
|
206
|
+
|
|
207
|
+
changes.each do |attribute, change|
|
|
208
|
+
write_attribute(attribute, change.last)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
reset_version(to_number)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def revert_to!(value)
|
|
215
|
+
revert_to(value)
|
|
216
|
+
reset_version if saved = save
|
|
217
|
+
saved
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def latest_changes
|
|
221
|
+
return {} if version.nil?
|
|
222
|
+
versions.at(version).changes
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class BetweenTest < Test::Unit::TestCase
|
|
4
|
+
context 'The number of versions between' do
|
|
5
|
+
setup do
|
|
6
|
+
@user = User.create(:name => 'Steve Richert')
|
|
7
|
+
@version = @user.version
|
|
8
|
+
@valid = [@version, 0, 1_000_000, :first, :last, 1.day.since(@user.created_at), @user.versions.first]
|
|
9
|
+
@invalid = [nil, :bogus, 'bogus', Date.parse('0001-12-25')]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context 'the current version and the current version' do
|
|
13
|
+
should 'equal one' do
|
|
14
|
+
assert_equal 1, @user.versions.between(@version, @version).size
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
context 'the current version and a valid value' do
|
|
19
|
+
should 'not equal zero' do
|
|
20
|
+
@valid.each do |valid|
|
|
21
|
+
assert_not_equal 0, @user.versions.between(@version, valid).size
|
|
22
|
+
assert_not_equal 0, @user.versions.between(valid, @version).size
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'the current version and an invalid value' do
|
|
28
|
+
should 'equal zero' do
|
|
29
|
+
@invalid.each do |invalid|
|
|
30
|
+
assert_equal 0, @user.versions.between(@version, invalid).size
|
|
31
|
+
assert_equal 0, @user.versions.between(invalid, @version).size
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
context 'two invalid values' do
|
|
37
|
+
should 'equal zero' do
|
|
38
|
+
@invalid.each do |first|
|
|
39
|
+
@invalid.each do |second|
|
|
40
|
+
assert_equal 0, @user.versions.between(first, second).size
|
|
41
|
+
assert_equal 0, @user.versions.between(second, first).size
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
context 'a valid value and an invalid value' do
|
|
48
|
+
should 'equal zero' do
|
|
49
|
+
@valid.each do |valid|
|
|
50
|
+
@invalid.each do |invalid|
|
|
51
|
+
assert_equal 0, @user.versions.between(valid, invalid).size
|
|
52
|
+
assert_equal 0, @user.versions.between(invalid, valid).size
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class ChangesTest < Test::Unit::TestCase
|
|
4
|
+
context "A version's changes" do
|
|
5
|
+
setup do
|
|
6
|
+
@user = User.create(:name => 'Steve Richert')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
should "initially be blank" do
|
|
10
|
+
assert @user.versions.first.changes.blank?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
should 'contain all changed attributes' do
|
|
14
|
+
@user.name = 'Steve Jobs'
|
|
15
|
+
changes = @user.changes
|
|
16
|
+
@user.save
|
|
17
|
+
assert_equal changes, @user.versions.last.changes.slice(*changes.keys)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
should 'contain no more than the changed attributes and timestamps' do
|
|
21
|
+
timestamps = %w(created_at created_on updated_at updated_on)
|
|
22
|
+
@user.name = 'Steve Jobs'
|
|
23
|
+
changes = @user.changes
|
|
24
|
+
@user.save
|
|
25
|
+
assert_equal changes, @user.versions.last.changes.except(*timestamps)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class ComparableTest < Test::Unit::TestCase
|
|
4
|
+
context 'A comparable version' do
|
|
5
|
+
setup do
|
|
6
|
+
@version_1 = Version.new(:number => 1)
|
|
7
|
+
@version_2 = Version.new(:number => 2)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
should 'equal itself' do
|
|
11
|
+
assert @version_1 == @version_1
|
|
12
|
+
assert @version_2 == @version_2
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context 'with version number 1' do
|
|
16
|
+
should 'not equal a version with version number 2' do
|
|
17
|
+
assert @version_1 != @version_2
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
should 'be less than a version with version number 2' do
|
|
21
|
+
assert @version_1 < @version_2
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'with version number 2' do
|
|
26
|
+
should 'not equal a version with version number 1' do
|
|
27
|
+
assert @version_2 != @version_1
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
should 'be greater than a version with version number 1' do
|
|
31
|
+
assert @version_2 > @version_1
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class CreationTest < Test::Unit::TestCase
|
|
4
|
+
context 'The number of versions' do
|
|
5
|
+
setup do
|
|
6
|
+
@name = 'Steve Richert'
|
|
7
|
+
@user = User.create(:name => @name)
|
|
8
|
+
@count = @user.versions.count
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
should 'initially equal one' do
|
|
12
|
+
assert_equal 1, @count
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
should 'not increase when no changes are made in an update' do
|
|
16
|
+
@user.name = @name
|
|
17
|
+
assert_equal @count, @user.versions.count
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
should 'not increase when no changes are made before a save' do
|
|
21
|
+
@user.save
|
|
22
|
+
assert_equal @count, @user.versions.count
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
should 'not increase when reverting to the current version' do
|
|
26
|
+
@user.revert_to!(@user.version)
|
|
27
|
+
assert_equal @count, @user.versions.count
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
context 'after an update' do
|
|
31
|
+
setup do
|
|
32
|
+
@initial_count = @count
|
|
33
|
+
@name = 'Steve Jobs'
|
|
34
|
+
@user.name = @name
|
|
35
|
+
@user.save
|
|
36
|
+
@count = @user.versions.count
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
should 'increase by one' do
|
|
40
|
+
assert_equal @initial_count + 1, @count
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
should 'increase by one when reverted' do
|
|
44
|
+
@user.revert_to!(:first)
|
|
45
|
+
assert_equal @count + 1, @user.versions.count
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
should 'not increase until a revert is saved' do
|
|
49
|
+
@user.revert_to(:first)
|
|
50
|
+
assert_equal @count, @user.versions.count
|
|
51
|
+
@user.save
|
|
52
|
+
assert_not_equal @count, @user.versions.count
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
should "retrieve a specific version without reverting it" do
|
|
57
|
+
@user.name = "Hoge"
|
|
58
|
+
@user.save
|
|
59
|
+
version_count = @user.versions.size
|
|
60
|
+
@user.retrieve_version 2
|
|
61
|
+
assert_equal version_count, @user.versions.size
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
context 'after multiple updates' do
|
|
65
|
+
setup do
|
|
66
|
+
@initial_count = @count
|
|
67
|
+
@new_name = 'Steve Jobs'
|
|
68
|
+
@user.name = @new_name
|
|
69
|
+
@user.name = @name
|
|
70
|
+
@count = @user.versions.count
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
should 'not increase when reverting to an identical version' do
|
|
74
|
+
@user.revert_to!(:first)
|
|
75
|
+
assert_equal @count, @user.versions.count
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/test/helper.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class LatestChangesTest < Test::Unit::TestCase
|
|
4
|
+
context "A created model's last changes" do
|
|
5
|
+
setup do
|
|
6
|
+
@user = User.create(:name => 'Steve Richert')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
should 'be blank' do
|
|
10
|
+
assert @user.latest_changes.blank?
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "An updated model's last changes" do
|
|
15
|
+
setup do
|
|
16
|
+
@user = User.create(:name => 'Steve Richert')
|
|
17
|
+
@previous_attributes = @user.attributes
|
|
18
|
+
@user.name = 'Steve Jobs'
|
|
19
|
+
@current_attributes = @user.attributes
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
should 'values of two-element arrays with unique values' do
|
|
23
|
+
@user.latest_changes.values.each do |value|
|
|
24
|
+
assert_kind_of Array, value
|
|
25
|
+
assert_equal 2, value.size
|
|
26
|
+
assert_equal value, value.uniq
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
should 'begin with the previous attribute values' do
|
|
31
|
+
changes = @user.latest_changes.inject({}){|h,(k,v)| h.update(k => v.first) }
|
|
32
|
+
previous = @previous_attributes.slice(*@user.latest_changes.keys)
|
|
33
|
+
assert_equal previous, changes
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
should 'end with the current attribute values' do
|
|
37
|
+
changes = @user.latest_changes.inject({}){|h,(k,v)| h.update(k => v.last) }
|
|
38
|
+
current = @current_attributes.slice(*@user.latest_changes.keys)
|
|
39
|
+
assert_equal current, changes
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/test/lock_test.rb
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class LockTest < Test::Unit::TestCase
|
|
4
|
+
context 'An unversioned model with locks' do
|
|
5
|
+
setup do
|
|
6
|
+
@user = UnversionedLockableUser.create(:name => 'Kurt')
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
should 'have a lock_version field' do
|
|
10
|
+
assert_not_nil @user.lock_version
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
should 'save just fine when no conflicts' do
|
|
14
|
+
@user.name = 'Bob'
|
|
15
|
+
assert @user.save
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
should 'save just fine when given the proper lock version' do
|
|
19
|
+
u = UnversionedLockableUser.find(@user.id)
|
|
20
|
+
@user.update_attributes(:name => 'Bill')
|
|
21
|
+
|
|
22
|
+
assert u.update_attributes(:name => 'Jose', :lock_version => @user.lock_version)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
should 'not save when given the wrong version' do
|
|
26
|
+
assert_raise Versioned::StaleDocumentError do
|
|
27
|
+
@user.update_attributes(:name => 'Bob', :lock_version => 1111)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# lock_version should match what we passed in
|
|
31
|
+
assert_equal 1111, @user.lock_version
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
context 'A versioned model with locks' do
|
|
35
|
+
setup do
|
|
36
|
+
@user = LockableUser.create(:name => 'Kurt', :required_field => 'woo!')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
should 'have a lock_version field' do
|
|
40
|
+
assert_not_nil @user.lock_version
|
|
41
|
+
assert_equal @user.version, @user.lock_version
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
should 'save just fine when no conflicts' do
|
|
45
|
+
@user.name = 'Bob'
|
|
46
|
+
assert @user.save
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
should 'save just fine when given the proper lock version' do
|
|
50
|
+
u = LockableUser.find(@user.id)
|
|
51
|
+
@user.update_attributes(:name => 'Bill')
|
|
52
|
+
|
|
53
|
+
assert u.update_attributes(:name => 'Jose', :lock_version => @user.lock_version)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
should 'not save when given the wrong version' do
|
|
57
|
+
assert_raise Versioned::StaleDocumentError do
|
|
58
|
+
@user.update_attributes(:name => 'Bob', :lock_version => 1111)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# lock_version should match what we passed in
|
|
62
|
+
assert_equal 1111, @user.lock_version
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
should 'be revertable with the lock version' do
|
|
66
|
+
v = @user.lock_version
|
|
67
|
+
name = @user.name
|
|
68
|
+
@user.update_attributes(:name => "Schlub")
|
|
69
|
+
|
|
70
|
+
assert_not_equal v, @user.lock_version
|
|
71
|
+
|
|
72
|
+
@user.revert_to(v)
|
|
73
|
+
|
|
74
|
+
assert_equal name, @user.name
|
|
75
|
+
assert_not_equal v, @user.lock_version #shouldn't have reverted version
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
should 'accept a specified version on create' do
|
|
79
|
+
u = LockableUser.create(:name => 'Burt', :required_field => 'woo!', :lock_version => 1111)
|
|
80
|
+
assert_equal 1111, u.lock_version
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
should "have same lock_version when validation fails" do
|
|
84
|
+
@user.required_field = nil
|
|
85
|
+
v = @user.lock_version
|
|
86
|
+
result = @user.save
|
|
87
|
+
assert !result
|
|
88
|
+
assert_equal v, @user.lock_version
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
data/test/revert_test.rb
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class RevertTest < Test::Unit::TestCase
|
|
4
|
+
context 'A model reversion' do
|
|
5
|
+
setup do
|
|
6
|
+
@user, @attributes, @times = User.new, {}, {}
|
|
7
|
+
names = ['Steve Richert', 'Stephen Richert', 'Stephen Jobs', 'Steve Jobs']
|
|
8
|
+
time = names.size.hours.ago
|
|
9
|
+
names.each do |name|
|
|
10
|
+
@user.update_attributes({:name => name})
|
|
11
|
+
@attributes[@user.version] = @user.attributes
|
|
12
|
+
time += 1.hour
|
|
13
|
+
@user.versions.last.update_attributes({:created_at => time})
|
|
14
|
+
@times[@user.version] = time
|
|
15
|
+
end
|
|
16
|
+
@first_version, @last_version = @attributes.keys.min, @attributes.keys.max
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
should 'do nothing for a non-existent version' do
|
|
20
|
+
attributes = @user.attributes
|
|
21
|
+
@user.revert_to!(nil)
|
|
22
|
+
assert_equal attributes, @user.attributes
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
should 'return the new version number' do
|
|
26
|
+
new_version = @user.revert_to(@first_version)
|
|
27
|
+
assert_equal @first_version, new_version
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
should 'change the version number when saved' do
|
|
31
|
+
current_version = @user.version
|
|
32
|
+
@user.revert_to!(@first_version)
|
|
33
|
+
assert_not_equal current_version, @user.version
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
should 'be able to target the first version' do
|
|
37
|
+
@user.revert_to(:first)
|
|
38
|
+
assert_equal @first_version, @user.version
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
should 'be able to target the last version' do
|
|
42
|
+
@user.revert_to(:last)
|
|
43
|
+
assert_equal @last_version, @user.version
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
should 'do nothing for a non-existent method name' do
|
|
47
|
+
current_version = @user.version
|
|
48
|
+
@user.revert_to(:bogus)
|
|
49
|
+
assert_equal current_version, @user.version
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
should 'be able to target a version number' do
|
|
53
|
+
@user.revert_to(1)
|
|
54
|
+
assert 1, @user.version
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
should 'be able to target a date and time' do
|
|
58
|
+
@times.each do |version, time|
|
|
59
|
+
@user.revert_to(time + 1.second)
|
|
60
|
+
assert_equal version, @user.version
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
should 'be able to target a version object' do
|
|
65
|
+
@user.versions.each do |version|
|
|
66
|
+
@user.revert_to(version)
|
|
67
|
+
assert_equal version.number, @user.version
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
should "correctly roll back the model's attributes" do
|
|
72
|
+
timestamps = %w(created_at created_on updated_at updated_on)
|
|
73
|
+
@attributes.each do |version, attributes|
|
|
74
|
+
@user.revert_to!(version)
|
|
75
|
+
assert_equal attributes.except(*timestamps), @user.attributes.except(*timestamps)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
data/test/schema.rb
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
MongoMapper.connection = Mongo::Connection.new('127.0.0.1')
|
|
2
|
+
MongoMapper.database = "testing_versioned"
|
|
3
|
+
|
|
4
|
+
class User
|
|
5
|
+
include MongoMapper::Document
|
|
6
|
+
include Versioned
|
|
7
|
+
key :first_name, String
|
|
8
|
+
key :last_name, String
|
|
9
|
+
timestamps!
|
|
10
|
+
|
|
11
|
+
def name
|
|
12
|
+
[first_name, last_name].compact.join(' ')
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def name=(names)
|
|
16
|
+
self[:first_name], self[:last_name] = names.split(' ', 2)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class Loser
|
|
21
|
+
include MongoMapper::Document
|
|
22
|
+
extend Versioned::ClassMethods
|
|
23
|
+
versioned :use_key => :revision
|
|
24
|
+
key :revision, Integer
|
|
25
|
+
key :name, String
|
|
26
|
+
timestamps!
|
|
27
|
+
|
|
28
|
+
before_save :set_revision
|
|
29
|
+
|
|
30
|
+
def set_revision
|
|
31
|
+
write_attribute :revision, (Time.now.to_f * 1000).ceil
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class LockableUser
|
|
36
|
+
include MongoMapper::Document
|
|
37
|
+
include Versioned
|
|
38
|
+
locking!
|
|
39
|
+
|
|
40
|
+
key :name, String
|
|
41
|
+
key :required_field, String
|
|
42
|
+
validates_presence_of :required_field
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
class UnversionedLockableUser
|
|
46
|
+
include MongoMapper::Document
|
|
47
|
+
extend Versioned::ClassMethods
|
|
48
|
+
locking!
|
|
49
|
+
|
|
50
|
+
key :name, String
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
User.destroy_all
|
|
54
|
+
Loser.destroy_all
|
|
55
|
+
Version.destroy_all
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
|
|
3
|
+
class SpecifiedVersionKey < Test::Unit::TestCase
|
|
4
|
+
context 'A model with a specified version key' do
|
|
5
|
+
setup do
|
|
6
|
+
@name = "Blah"
|
|
7
|
+
@loser = Loser.create(:name => @name)
|
|
8
|
+
@version = @loser.version
|
|
9
|
+
end
|
|
10
|
+
should 'be used for the initial version' do
|
|
11
|
+
assert_equal @loser.revision, @loser.version
|
|
12
|
+
assert_equal @loser.revision, @loser.versions.first.number
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
context 'after an update' do
|
|
16
|
+
setup do
|
|
17
|
+
@initial_version = @loser.version
|
|
18
|
+
@initial_count = @loser.versions.count
|
|
19
|
+
@initial_name = @loser.name
|
|
20
|
+
@name = 'Blip'
|
|
21
|
+
@loser.name = @name
|
|
22
|
+
@loser.save
|
|
23
|
+
@version = @loser.version
|
|
24
|
+
@count = @loser.versions.count
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
should 'have a different version number' do
|
|
28
|
+
assert_not_equal @initial_version, @loser.version
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
should 'still be using the specified key' do
|
|
32
|
+
assert_equal @loser.revision, @loser.version
|
|
33
|
+
assert_equal @loser.revision, @loser.versions.last.number
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
should 'version count should have increased by one' do
|
|
37
|
+
assert_equal @initial_count + 1, @count
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
should 'revert properly' do
|
|
41
|
+
@loser.revert
|
|
42
|
+
@loser.save!
|
|
43
|
+
assert_equal @initial_name, @loser.name
|
|
44
|
+
assert_equal @count + 1, @loser.versions.count
|
|
45
|
+
assert_not_equal @version, @loser.version
|
|
46
|
+
assert_not_equal @initial_version, @loser.version
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
$: << File.join(File.dirname(__FILE__), '..', 'lib')
|
|
2
|
+
$: << File.dirname(__FILE__)
|
|
3
|
+
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
require 'test/unit'
|
|
6
|
+
require 'shoulda'
|
|
7
|
+
require 'mongo_mapper'
|
|
8
|
+
require 'versioned'
|
|
9
|
+
require 'schema'
|
|
10
|
+
begin; require 'redgreen'; rescue LoadError; end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
require "test_helper"
|
|
2
|
+
|
|
3
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/versioned')
|
|
4
|
+
require File.expand_path(File.dirname(__FILE__) + '/../lib/version')
|
|
5
|
+
|
|
6
|
+
class Doc
|
|
7
|
+
include MongoMapper::Document
|
|
8
|
+
include Versioned
|
|
9
|
+
key :title, String
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
MongoMapper.connection.drop_database DB_NAME
|
|
13
|
+
MongoMapper.database = DB_NAME
|
|
14
|
+
|
|
15
|
+
class VersionTest < Test::Unit::TestCase
|
|
16
|
+
|
|
17
|
+
context "A Content Instance" do
|
|
18
|
+
|
|
19
|
+
setup do
|
|
20
|
+
@doc = Doc.create(:title=>"Foo")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
should "respond to versions" do
|
|
24
|
+
assert @doc.respond_to?(:versions)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
should "have before_save callback" do
|
|
28
|
+
assert Doc.before_update.collect(&:method).include?(:save_version)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
context "after update" do
|
|
32
|
+
setup do
|
|
33
|
+
@doc.title = "Version 2"
|
|
34
|
+
@doc.save
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
should "create a version after update" do
|
|
38
|
+
assert_equal(1, @doc.versions.count)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
should "have the correct version number" do
|
|
42
|
+
assert_equal(1, @doc.versions.first.version_number)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
should "update the model's version key" do
|
|
46
|
+
assert_equal(1, @doc.version)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
should "have :title in changes" do
|
|
50
|
+
assert_contains(@doc.versions.first.changed_attrs.keys, "title")
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
should "revert to last version" do
|
|
54
|
+
@doc.revert
|
|
55
|
+
assert_equal(@doc.title, "Foo")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
should "revert by version #" do
|
|
59
|
+
@doc.title = "Version 3"
|
|
60
|
+
@doc.save
|
|
61
|
+
@doc.revert_to_version 2
|
|
62
|
+
assert_equal("Version 2", @doc.title)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
should "retrieve a specific version without reverting it" do
|
|
66
|
+
@doc.title = "Version 3"
|
|
67
|
+
@doc.save
|
|
68
|
+
version_count = @doc.versions.size
|
|
69
|
+
@doc.retrieve_version 2
|
|
70
|
+
assert_equal version_count, @doc.versions.size
|
|
71
|
+
end
|
|
72
|
+
should "cleanup versions on destroy" do
|
|
73
|
+
@doc.destroy
|
|
74
|
+
assert_equal(0, Version.count)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
end
|
|
80
|
+
end
|
data/versioned.gemspec
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# Generated by jeweler
|
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
|
4
|
+
# -*- encoding: utf-8 -*-
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |s|
|
|
7
|
+
s.name = %q{versioned}
|
|
8
|
+
s.version = "0.1.0"
|
|
9
|
+
|
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
|
11
|
+
s.authors = ["twoism", "toastyapps", "jacqui", "mrkurt"]
|
|
12
|
+
s.date = %q{2009-12-17}
|
|
13
|
+
s.description = %q{Versioning for MongoMapper}
|
|
14
|
+
s.email = %q{signalstatic@gmail.com}
|
|
15
|
+
s.extra_rdoc_files = [
|
|
16
|
+
"README.markdown"
|
|
17
|
+
]
|
|
18
|
+
s.files = [
|
|
19
|
+
"README.markdown",
|
|
20
|
+
"Rakefile",
|
|
21
|
+
"VERSION",
|
|
22
|
+
"lib/version.rb",
|
|
23
|
+
"lib/versioned.rb",
|
|
24
|
+
"test/between_test.rb",
|
|
25
|
+
"test/changes_test.rb",
|
|
26
|
+
"test/comparable_test.rb",
|
|
27
|
+
"test/creation_test.rb",
|
|
28
|
+
"test/latest_changes_test.rb",
|
|
29
|
+
"test/revert_test.rb",
|
|
30
|
+
"test/schema.rb",
|
|
31
|
+
"test/test_helper.rb",
|
|
32
|
+
"versioned.gemspec"
|
|
33
|
+
]
|
|
34
|
+
s.homepage = %q{http://github.com/twoism/versioned}
|
|
35
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
|
36
|
+
s.require_paths = ["lib"]
|
|
37
|
+
s.rubygems_version = %q{1.3.5}
|
|
38
|
+
s.summary = %q{Versioning for MongoMapper}
|
|
39
|
+
s.test_files = [
|
|
40
|
+
"test/between_test.rb",
|
|
41
|
+
"test/changes_test.rb",
|
|
42
|
+
"test/comparable_test.rb",
|
|
43
|
+
"test/creation_test.rb",
|
|
44
|
+
"test/latest_changes_test.rb",
|
|
45
|
+
"test/revert_test.rb",
|
|
46
|
+
"test/schema.rb",
|
|
47
|
+
"test/test_helper.rb"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
if s.respond_to? :specification_version then
|
|
51
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
|
52
|
+
s.specification_version = 3
|
|
53
|
+
|
|
54
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
|
55
|
+
else
|
|
56
|
+
end
|
|
57
|
+
else
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
metadata
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: jacqui-versioned
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- twoism
|
|
8
|
+
- toastyapps
|
|
9
|
+
- jacqui
|
|
10
|
+
- mrkurt
|
|
11
|
+
autorequire:
|
|
12
|
+
bindir: bin
|
|
13
|
+
cert_chain: []
|
|
14
|
+
|
|
15
|
+
date: 2010-01-18 00:00:00 -05:00
|
|
16
|
+
default_executable:
|
|
17
|
+
dependencies: []
|
|
18
|
+
|
|
19
|
+
description: Versioning for MongoMapper
|
|
20
|
+
email: jacqui@brighter.net
|
|
21
|
+
executables: []
|
|
22
|
+
|
|
23
|
+
extensions: []
|
|
24
|
+
|
|
25
|
+
extra_rdoc_files:
|
|
26
|
+
- LICENSE
|
|
27
|
+
- README.rdoc
|
|
28
|
+
files:
|
|
29
|
+
- .document
|
|
30
|
+
- .gitignore
|
|
31
|
+
- LICENSE
|
|
32
|
+
- README.rdoc
|
|
33
|
+
- Rakefile
|
|
34
|
+
- VERSION
|
|
35
|
+
- init.rb
|
|
36
|
+
- lib/version.rb
|
|
37
|
+
- lib/versioned.rb
|
|
38
|
+
- pkg/versioned-0.1.0.gem
|
|
39
|
+
- test/between_test.rb
|
|
40
|
+
- test/changes_test.rb
|
|
41
|
+
- test/comparable_test.rb
|
|
42
|
+
- test/creation_test.rb
|
|
43
|
+
- test/helper.rb
|
|
44
|
+
- test/latest_changes_test.rb
|
|
45
|
+
- test/lock_test.rb
|
|
46
|
+
- test/revert_test.rb
|
|
47
|
+
- test/schema.rb
|
|
48
|
+
- test/specified_version_key_test.rb
|
|
49
|
+
- test/test_helper.rb
|
|
50
|
+
- test/test_versioned.rb
|
|
51
|
+
- versioned.gemspec
|
|
52
|
+
has_rdoc: true
|
|
53
|
+
homepage: http://github.com/jacqui/versioned
|
|
54
|
+
licenses: []
|
|
55
|
+
|
|
56
|
+
post_install_message:
|
|
57
|
+
rdoc_options:
|
|
58
|
+
- --charset=UTF-8
|
|
59
|
+
require_paths:
|
|
60
|
+
- lib
|
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
|
+
requirements:
|
|
63
|
+
- - ">="
|
|
64
|
+
- !ruby/object:Gem::Version
|
|
65
|
+
version: "0"
|
|
66
|
+
version:
|
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
version: "0"
|
|
72
|
+
version:
|
|
73
|
+
requirements: []
|
|
74
|
+
|
|
75
|
+
rubyforge_project:
|
|
76
|
+
rubygems_version: 1.3.5
|
|
77
|
+
signing_key:
|
|
78
|
+
specification_version: 3
|
|
79
|
+
summary: Versioning for MongoMapper
|
|
80
|
+
test_files:
|
|
81
|
+
- test/between_test.rb
|
|
82
|
+
- test/changes_test.rb
|
|
83
|
+
- test/comparable_test.rb
|
|
84
|
+
- test/creation_test.rb
|
|
85
|
+
- test/helper.rb
|
|
86
|
+
- test/latest_changes_test.rb
|
|
87
|
+
- test/lock_test.rb
|
|
88
|
+
- test/revert_test.rb
|
|
89
|
+
- test/schema.rb
|
|
90
|
+
- test/specified_version_key_test.rb
|
|
91
|
+
- test/test_helper.rb
|
|
92
|
+
- test/test_versioned.rb
|