versioned 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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 = 'versioned'
10
+ g.summary = %(Versioning for MongoMapper)
11
+ g.description = %(Versioning for MongoMapper)
12
+ g.email = 'signalstatic@gmail.com'
13
+ g.homepage = 'http://github.com/twoism/versioned'
14
+ g.authors = %w(twoism toastyapps jacqui)
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.0
data/lib/version.rb ADDED
@@ -0,0 +1,20 @@
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
+ end
data/lib/versioned.rb ADDED
@@ -0,0 +1,163 @@
1
+ require 'version'
2
+
3
+ module Versioned
4
+ def self.included(base)
5
+ base.extend ClassMethods
6
+ base.class_eval do
7
+ versioned
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ def versioned(options = {})
13
+ class_inheritable_accessor :version_only_columns
14
+ self.version_only_columns = Array(options[:only]).map(&:to_s).uniq if options[:only]
15
+ class_inheritable_accessor :version_except_columns
16
+ self.version_except_columns = Array(options[:except]).map(&:to_s).uniq if options[:except]
17
+
18
+ many :versions, :as => :versioned, :order => 'number ASC', :dependent => :delete_all do
19
+ def between(from, to)
20
+ from_number, to_number = number_at(from), number_at(to)
21
+ return [] if from_number.nil? || to_number.nil?
22
+ condition = (from_number == to_number) ? to_number : Range.new(*[from_number, to_number].sort)
23
+ if condition.is_a?(Range)
24
+ conditions = {'$gte' => condition.first, '$lte' => condition.last}
25
+ else
26
+ conditions = condition
27
+ end
28
+ find(:all,
29
+ :number => conditions,
30
+ :order => "number #{(from_number > to_number) ? 'DESC' : 'ASC'}"
31
+ )
32
+ end
33
+
34
+ def at(value)
35
+ case value
36
+ when Version then value
37
+ when Numeric then find_by_number(value.floor)
38
+ when Symbol then respond_to?(value) ? send(value) : nil
39
+ when Date, Time then last(:created_at => {'$lte' => value.to_time})
40
+ end
41
+ end
42
+
43
+ def number_at(value)
44
+ case value
45
+ when Version then value.number
46
+ when Numeric then value.floor
47
+ when Symbol, Date, Time then at(value).try(:number)
48
+ end
49
+ end
50
+ end
51
+
52
+ after_create :create_initial_version
53
+ after_update :create_initial_version, :if => :needs_initial_version?
54
+ after_update :create_version, :if => :needs_version?
55
+
56
+ include InstanceMethods
57
+ alias_method_chain :reload, :versions
58
+ end
59
+ end
60
+
61
+ module InstanceMethods
62
+ private
63
+ def versioned_columns
64
+ case
65
+ when version_only_columns then self.class.keys.keys & version_only_columns
66
+ when version_except_columns then self.class.keys.keys - version_except_columns
67
+ else self.class.keys.keys
68
+ end - %w(created_at created_on updated_at updated_on)
69
+ end
70
+
71
+ def needs_initial_version?
72
+ versions.empty?
73
+ end
74
+
75
+ def needs_version?
76
+ !(versioned_columns & changed).empty?
77
+ end
78
+
79
+ def reset_version(new_version = nil)
80
+ @last_version = nil if new_version.nil?
81
+ @version = new_version
82
+ end
83
+
84
+ def create_initial_version
85
+ versions.create(:changes => nil, :number => 1)
86
+ end
87
+
88
+ def create_version
89
+ versions << Version.create(:changes => changes.slice(*versioned_columns), :number => (last_version + 1))
90
+ reset_version
91
+ end
92
+
93
+ public
94
+ def version
95
+ @version ||= last_version
96
+ end
97
+
98
+ def last_version
99
+ @last_version ||= versions.inject(1){|max, version| version.number > max ? version.number : max}
100
+ end
101
+
102
+ def reverted?
103
+ version != last_version
104
+ end
105
+
106
+ def reload_with_versions(*args)
107
+ reset_version
108
+ reload_without_versions(*args)
109
+ end
110
+
111
+ def changes_between(from, to)
112
+ from_number, to_number = versions.number_at(from), versions.number_at(to)
113
+ return {} if from_number == to_number
114
+ chain = versions.between(from_number, to_number)
115
+ return {} if chain.empty?
116
+
117
+ backward = chain.first > chain.last
118
+ backward ? chain.pop : chain.shift
119
+
120
+ chain.inject({}) do |changes, version|
121
+ version.changes.each do |attribute, change|
122
+ change.reverse! if backward
123
+ new_change = [changes.fetch(attribute, change).first, change.last]
124
+ changes.update(attribute => new_change)
125
+ end
126
+ changes
127
+ end
128
+ end
129
+
130
+ def revert
131
+ revert_to self.version -1
132
+ end
133
+
134
+ def retrieve_version n
135
+ versions.find_by_number(n).changes.each do |n,v|
136
+ self.send("#{n.to_sym}=",v.first)
137
+ end
138
+ end
139
+
140
+ def revert_to(value)
141
+ to_number = versions.number_at(value)
142
+ changes = changes_between(version, to_number)
143
+ return version if changes.empty?
144
+
145
+ changes.each do |attribute, change|
146
+ write_attribute(attribute, change.last)
147
+ end
148
+
149
+ reset_version(to_number)
150
+ end
151
+
152
+ def revert_to!(value)
153
+ revert_to(value)
154
+ reset_version if saved = save
155
+ saved
156
+ end
157
+
158
+ def latest_changes
159
+ return {} if version.nil? || version == 1
160
+ versions.at(version).changes
161
+ end
162
+ end
163
+ end
Binary file
@@ -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
@@ -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
@@ -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,21 @@
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
+ User.destroy_all
21
+ Version.destroy_all
@@ -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
data/versioned.gemspec ADDED
@@ -0,0 +1,61 @@
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"]
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.rdoc"
17
+ ]
18
+ s.files = [
19
+ "README.rdoc",
20
+ "Rakefile",
21
+ "VERSION",
22
+ "lib/version.rb",
23
+ "lib/versioned.rb",
24
+ "pkg/versioned-0.1.0.gem",
25
+ "test/between_test.rb",
26
+ "test/changes_test.rb",
27
+ "test/comparable_test.rb",
28
+ "test/creation_test.rb",
29
+ "test/latest_changes_test.rb",
30
+ "test/revert_test.rb",
31
+ "test/schema.rb",
32
+ "test/test_helper.rb",
33
+ "versioned.gemspec"
34
+ ]
35
+ s.homepage = %q{http://github.com/twoism/versioned}
36
+ s.rdoc_options = ["--charset=UTF-8"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = %q{1.3.5}
39
+ s.summary = %q{Versioning for MongoMapper}
40
+ s.test_files = [
41
+ "test/between_test.rb",
42
+ "test/changes_test.rb",
43
+ "test/comparable_test.rb",
44
+ "test/creation_test.rb",
45
+ "test/latest_changes_test.rb",
46
+ "test/revert_test.rb",
47
+ "test/schema.rb",
48
+ "test/test_helper.rb"
49
+ ]
50
+
51
+ if s.respond_to? :specification_version then
52
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
53
+ s.specification_version = 3
54
+
55
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
56
+ else
57
+ end
58
+ else
59
+ end
60
+ end
61
+
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: versioned
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - twoism
8
+ - toastyapps
9
+ - jacqui
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+
14
+ date: 2009-12-17 00:00:00 -05:00
15
+ default_executable:
16
+ dependencies: []
17
+
18
+ description: Versioning for MongoMapper
19
+ email: signalstatic@gmail.com
20
+ executables: []
21
+
22
+ extensions: []
23
+
24
+ extra_rdoc_files:
25
+ - README.rdoc
26
+ files:
27
+ - README.rdoc
28
+ - Rakefile
29
+ - VERSION
30
+ - lib/version.rb
31
+ - lib/versioned.rb
32
+ - pkg/versioned-0.1.0.gem
33
+ - test/between_test.rb
34
+ - test/changes_test.rb
35
+ - test/comparable_test.rb
36
+ - test/creation_test.rb
37
+ - test/latest_changes_test.rb
38
+ - test/revert_test.rb
39
+ - test/schema.rb
40
+ - test/test_helper.rb
41
+ - versioned.gemspec
42
+ has_rdoc: true
43
+ homepage: http://github.com/twoism/versioned
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --charset=UTF-8
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.5
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Versioning for MongoMapper
70
+ test_files:
71
+ - test/between_test.rb
72
+ - test/changes_test.rb
73
+ - test/comparable_test.rb
74
+ - test/creation_test.rb
75
+ - test/latest_changes_test.rb
76
+ - test/revert_test.rb
77
+ - test/schema.rb
78
+ - test/test_helper.rb