mm-versionable 0.2

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.md ADDED
@@ -0,0 +1,28 @@
1
+ MongoMapper Versioning
2
+ ======================
3
+ A MongoMapper plugin which enables document versioning.
4
+
5
+ Install
6
+ -------
7
+ $ gem install mm-versionable
8
+
9
+ Note on Patches/Pull Requests
10
+ -----------------------------
11
+ *Fork the project
12
+ *Make your feature addition or bug fix.
13
+ *Add tests for it. This is critical so that things dont break unintentionally.
14
+ *Commit, do not make any changes in the rakefile, version, or history. (If you want to have your own version, that is fine but bump the version in a commit by itself so I can ignore it when I pull)
15
+ *Send me a pull requests.
16
+
17
+ Development
18
+ -----------
19
+
20
+ Problems or Questions?
21
+ ----------------------
22
+ Hit up on the mongomapper google group: http://groups.google.com/group/mongomapper
23
+ Hop on IRC: irc://chat.freenode.net/#mongomapper
24
+ I am available on IRC with the name dhruvasagar, you could alternatively also contact me directly on dhruva.sagar@gmail.com
25
+
26
+ Copyright
27
+ ---------
28
+ See LICENCE for details.
data/Rakefile ADDED
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'jeweler'
4
+
5
+ require File.join(File.dirname(__FILE__), '/lib/versionable/version')
6
+
7
+ require 'rake/testtask'
8
+ namespace :test do
9
+ Rake::TestTask.new(:units) do |test|
10
+ test.libs << 'test'
11
+ test.ruby_opts << '-rubygems'
12
+ test.pattern = 'test/unit/**/test_*.rb'
13
+ test.verbose = true
14
+ end
15
+
16
+ Rake::TestTask.new(:performance) do |test|
17
+ test.libs << 'test'
18
+ test.ruby_opts << '-rubygems'
19
+ test.pattern = 'test/performance/**/*.rb'
20
+ test.verbose = true
21
+ end
22
+ end
23
+
24
+ task :default => 'test:units'
25
+
26
+ Jeweler::Tasks.new do |gem|
27
+ gem.name = 'mm-versionable'
28
+ gem.summary = 'A MongoMapper extension for document versioning'
29
+ gem.description = 'A MongoMapper extension that enables document versionable'
30
+ gem.email = 'dhruva.sagar@gmail.com'
31
+ gem.homepage = 'https://github.com/artha42/mm-versionable/'
32
+ gem.authors = ['Dhruva Sagar']
33
+ gem.version = Versionable::Version
34
+
35
+ gem.add_dependency('differ')
36
+ gem.add_dependency('mongo_mapper')
37
+ end
38
+
39
+ Jeweler::GemcutterTasks.new
data/config.ru ADDED
@@ -0,0 +1,11 @@
1
+ $LOAD_PATH.unshift(".") unless $LOAD_PATH.include?(".")
2
+
3
+ require 'pp'
4
+ require 'rubygems'
5
+
6
+ $:.unshift(::File.join(::File.expand_path('.'), '/lib'))
7
+ require 'versionable.rb'
8
+
9
+ require 'test/config/config'
10
+ require 'test/models/post'
11
+ require 'test/models/user'
@@ -0,0 +1,19 @@
1
+ class Version
2
+ include MongoMapper::Document
3
+
4
+ key :data, Hash
5
+ key :date, Time
6
+ key :pos, Integer
7
+ key :doc_id, String
8
+ key :message, String
9
+ key :updater_id, String
10
+
11
+ def content(key)
12
+ cdata = self.data[key]
13
+ if cdata.respond_to?(:join)
14
+ cdata.join(" ")
15
+ else
16
+ cdata
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,111 @@
1
+ require 'versionable/models/version'
2
+
3
+ module Versionable
4
+ def self.configure(model)
5
+ model.enable_versioning
6
+ end
7
+
8
+ module InstanceMethods
9
+ def save(options={})
10
+ save_version(options[:updater_id]) if self.respond_to?(:rolling_back) && !rolling_back
11
+ super
12
+ end
13
+
14
+ private
15
+ def save_version(updater_id=nil)
16
+ if self.respond_to?(:versions)
17
+ version = self.current_version
18
+ version.message = self.version_message
19
+ if self.versions.empty?
20
+ version.pos = 0
21
+ else
22
+ version.pos = self.versions.last.pos + 1
23
+ end
24
+ version.updater_id = updater_id
25
+ version.save
26
+
27
+ self.versions.shift
28
+ self.versions << version
29
+
30
+ @versions_count = @versions_count.to_i + 1
31
+ end
32
+ end
33
+ end
34
+
35
+ module ClassMethods
36
+ def enable_versioning(opts={})
37
+ attr_accessor :rolling_back
38
+
39
+ key :version_message, String
40
+ key :version_number, Integer
41
+
42
+ define_method(:versions_count) do
43
+ @versions_count ||= Version.count(:doc_id => self._id.to_s)
44
+ end
45
+
46
+ define_method(:versions) do
47
+ @versions ||= Version.all(:doc_id => self._id.to_s, :order => 'pos desc', :limit => (opts[:limit] || 10)).reverse
48
+ end
49
+
50
+ define_method(:all_versions) do
51
+ Version.where(:doc_id => self._id.to_s).sort(:pos.desc)
52
+ end
53
+
54
+ define_method(:rollback) do |pos = nil|
55
+ #The last version is always same as the current version, so -2 instead of -1
56
+ pos = self.versions.count-2 if pos.nil?
57
+ version = self.version_at(pos)
58
+
59
+ if version
60
+ self.attributes = version.data
61
+ end
62
+
63
+ self.version_number = version.pos
64
+ self
65
+ end
66
+
67
+ define_method(:rollback!) do |pos = nil|
68
+ self.rollback(pos)
69
+
70
+ @rolling_back = true
71
+ save!
72
+ @rolling_back = false
73
+
74
+ self
75
+ end
76
+
77
+ define_method(:diff) do |key, pos1, pos2, format = :html|
78
+ version1 = self.version_at(pos1)
79
+ version2 = self.version_at(pos2)
80
+
81
+ Differ.diff_by_word(version1.content(key), version2.content(key)).format_as(format)
82
+ end
83
+
84
+ define_method(:current_version) do
85
+ Version.new(:data => self.attributes, :date => Time.now, :doc_id => self._id.to_s)
86
+ end
87
+
88
+ define_method(:version_at) do |pos|
89
+ case pos
90
+ when :current
91
+ current_version
92
+ when :first
93
+ index = self.versions.index {|v| v.pos == 0}
94
+ version = self.versions[index] if index
95
+ version ||= Version.first(:user_id => self._id.to_s, :pos => 0)
96
+ version
97
+ when :last
98
+ #The last version is always same as the current version, so -2 instead of -1
99
+ self.versions[self.versions.count-2]
100
+ when :latest
101
+ self.versions.last
102
+ else
103
+ index = self.versions.index {|v| v.pos == pos}
104
+ version = self.versions[index] if index
105
+ version ||= Version.first(:user_id => self._id.to_s, :pos => pos)
106
+ version
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,3 @@
1
+ module Versionable
2
+ Version = '0.2'
3
+ end
@@ -0,0 +1,2 @@
1
+ require 'mongo_mapper'
2
+ require 'versionable/plugins/versionable'
@@ -0,0 +1,64 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mm-versionable}
8
+ s.version = "0.2"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Dhruva Sagar"]
12
+ s.date = %q{2010-11-26}
13
+ s.description = %q{A MongoMapper extension that enables document versionable}
14
+ s.email = %q{dhruva.sagar@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.md"
17
+ ]
18
+ s.files = [
19
+ "README.md",
20
+ "Rakefile",
21
+ "config.ru",
22
+ "lib/versionable.rb",
23
+ "lib/versionable/models/version.rb",
24
+ "lib/versionable/plugins/versionable.rb",
25
+ "lib/versionable/version.rb",
26
+ "mm-versionable.gemspec",
27
+ "test/config/config.rb",
28
+ "test/config/database.yml",
29
+ "test/models/post.rb",
30
+ "test/models/user.rb",
31
+ "test/performance/read_write.rb",
32
+ "test/test_helper.rb",
33
+ "test/unit/test_versioning.rb"
34
+ ]
35
+ s.homepage = %q{https://github.com/artha42/mm-versionable/}
36
+ s.require_paths = ["lib"]
37
+ s.rubygems_version = %q{1.3.7}
38
+ s.summary = %q{A MongoMapper extension for document versioning}
39
+ s.test_files = [
40
+ "test/config/config.rb",
41
+ "test/models/post.rb",
42
+ "test/models/user.rb",
43
+ "test/performance/read_write.rb",
44
+ "test/test_helper.rb",
45
+ "test/unit/test_versioning.rb"
46
+ ]
47
+
48
+ if s.respond_to? :specification_version then
49
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
50
+ s.specification_version = 3
51
+
52
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
53
+ s.add_runtime_dependency(%q<differ>, [">= 0"])
54
+ s.add_runtime_dependency(%q<mongo_mapper>, [">= 0"])
55
+ else
56
+ s.add_dependency(%q<differ>, [">= 0"])
57
+ s.add_dependency(%q<mongo_mapper>, [">= 0"])
58
+ end
59
+ else
60
+ s.add_dependency(%q<differ>, [">= 0"])
61
+ s.add_dependency(%q<mongo_mapper>, [">= 0"])
62
+ end
63
+ end
64
+
@@ -0,0 +1 @@
1
+ MongoMapper.database = YAML.load(File.read('config/database.yml'))[ENV['RACK_ENV']]['database'] rescue 'versionable_test'
@@ -0,0 +1,5 @@
1
+ development:
2
+ database: versionable_dev
3
+
4
+ test:
5
+ database: versionable_test
@@ -0,0 +1,8 @@
1
+ class Post
2
+ include MongoMapper::EmbeddedDocument
3
+
4
+ key :date, Time
5
+ key :body, String
6
+ key :title, String
7
+
8
+ end
@@ -0,0 +1,11 @@
1
+ class User
2
+ include MongoMapper::Document
3
+ plugin Versionable
4
+
5
+ key :fname, String
6
+ key :lname, String
7
+ key :email, String
8
+ key :address, String
9
+
10
+ many :posts
11
+ end
@@ -0,0 +1,29 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../../lib/versionable')
2
+
3
+ require 'benchmark'
4
+
5
+ MongoMapper.database = 'versionable_performance'
6
+
7
+ class Foo
8
+ include MongoMapper::Document
9
+ plugin Versionable
10
+
11
+ enable_versioning
12
+
13
+ key :approved, Boolean
14
+ key :count, Integer
15
+ key :approved_at, Time
16
+ key :expire_on, Date
17
+ end
18
+ Foo.collection.remove
19
+
20
+ Benchmark.bm(5) do |x|
21
+ ids = []
22
+ hhids = []
23
+ x.report("write ") do
24
+ 1000.times { |i| ids << Foo.create(:count => 0, :approved => true, :approved_at => Time.now, :expire_on => Date.today).id }
25
+ end
26
+ x.report("read ") do
27
+ ids.each { |id| Foo.first(:id => id) }
28
+ end
29
+ end
@@ -0,0 +1,20 @@
1
+ $LOAD_PATH.unshift('.') unless $LOAD_PATH.include?('.')
2
+
3
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/versionable')
4
+
5
+ require 'pp'
6
+ require 'shoulda'
7
+
8
+ require 'test/models/post'
9
+ require 'test/models/user'
10
+
11
+ require 'config/config'
12
+
13
+ User.delete_all
14
+ u = User.create(:fname => 'dhruva', :lname => 'sagar', :email => 'dhruva.sagar@gmail.com')
15
+ u.fname = 'Dhruva'
16
+ u.lname = 'Sagar'
17
+ u.save
18
+
19
+ u.posts << Post.new(:title => 'Dummy title', :body => 'Dummy post body', :date => Time.now)
20
+ u.save
@@ -0,0 +1,84 @@
1
+ require 'test_helper'
2
+
3
+ class VersioningTest < Test::Unit::TestCase
4
+ context 'Versioning enabled' do
5
+ setup do
6
+ @user = User.first
7
+ end
8
+ should 'respond to method version_message' do
9
+ assert @user.respond_to?(:version_message)
10
+ end
11
+ should 'respond to method version_number' do
12
+ assert @user.respond_to?(:version_number)
13
+ end
14
+ should 'respond to method versions' do
15
+ assert @user.respond_to?(:versions)
16
+ end
17
+ should 'respond to method all_versions' do
18
+ assert @user.respond_to?(:all_versions)
19
+ end
20
+ should 'respond to method rollback' do
21
+ assert @user.respond_to?(:rollback)
22
+ end
23
+ should 'respond to method rollback!' do
24
+ assert @user.respond_to?(:rollback!)
25
+ end
26
+ should 'respond to method diff' do
27
+ assert @user.respond_to?(:diff)
28
+ end
29
+ should 'respond to method current_version' do
30
+ assert @user.respond_to?(:current_version)
31
+ end
32
+ should 'respond to method version_at' do
33
+ assert @user.respond_to?(:version_at)
34
+ end
35
+ end
36
+
37
+ context 'Version manipulations' do
38
+ setup do
39
+ @user = User.first
40
+ end
41
+ should 'return the total versions_count' do
42
+ assert @user.versions_count
43
+ end
44
+ should 'return last 10 or lesser versions' do
45
+ assert @user.versions
46
+ assert @user.versions.count <= 10
47
+ end
48
+ should 'return the plucky query for all versions' do
49
+ assert @user.all_versions
50
+ assert @user.all_versions.is_a?(Plucky::Query)
51
+ end
52
+ should 'load first version on rollback :first' do
53
+ assert @user.rollback(:first).fname == 'dhruva'
54
+ assert @user.version_number == 0
55
+ end
56
+ should 'load last version on rollback :last' do
57
+ assert @user.rollback(:last).fname == 'Dhruva'
58
+ assert @user.posts.empty?
59
+ assert @user.version_number == (@user.versions_count - 2)
60
+ end
61
+ should 'load latest version on rollback :latest' do
62
+ assert @user.rollback(:latest).fname == 'Dhruva'
63
+ assert !@user.posts.empty?
64
+ assert @user.version_number == (@user.versions_count - 1)
65
+ end
66
+ should 'revert to first version on rollback!' do
67
+ @user.rollback!(:first)
68
+ assert @user.fname == 'dhruva'
69
+ assert @user.version_number == 0
70
+ end
71
+ should 'revert to last version on rollback!' do
72
+ @user.rollback!(:last)
73
+ assert @user.fname == 'Dhruva'
74
+ assert @user.posts.empty?
75
+ assert @user.version_number == (@user.versions_count - 2)
76
+ end
77
+ should 'revert to latest version on rollback!' do
78
+ @user.rollback!(:latest)
79
+ assert @user.fname == 'Dhruva'
80
+ assert !@user.posts.empty?
81
+ assert @user.version_number == (@user.versions_count - 1)
82
+ end
83
+ end
84
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mm-versionable
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ version: "0.2"
9
+ platform: ruby
10
+ authors:
11
+ - Dhruva Sagar
12
+ autorequire:
13
+ bindir: bin
14
+ cert_chain: []
15
+
16
+ date: 2010-11-26 00:00:00 +05:30
17
+ default_executable:
18
+ dependencies:
19
+ - !ruby/object:Gem::Dependency
20
+ name: differ
21
+ prerelease: false
22
+ requirement: &id001 !ruby/object:Gem::Requirement
23
+ none: false
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :runtime
31
+ version_requirements: *id001
32
+ - !ruby/object:Gem::Dependency
33
+ name: mongo_mapper
34
+ prerelease: false
35
+ requirement: &id002 !ruby/object:Gem::Requirement
36
+ none: false
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ segments:
41
+ - 0
42
+ version: "0"
43
+ type: :runtime
44
+ version_requirements: *id002
45
+ description: A MongoMapper extension that enables document versionable
46
+ email: dhruva.sagar@gmail.com
47
+ executables: []
48
+
49
+ extensions: []
50
+
51
+ extra_rdoc_files:
52
+ - README.md
53
+ files:
54
+ - README.md
55
+ - Rakefile
56
+ - config.ru
57
+ - lib/versionable.rb
58
+ - lib/versionable/models/version.rb
59
+ - lib/versionable/plugins/versionable.rb
60
+ - lib/versionable/version.rb
61
+ - mm-versionable.gemspec
62
+ - test/config/config.rb
63
+ - test/config/database.yml
64
+ - test/models/post.rb
65
+ - test/models/user.rb
66
+ - test/performance/read_write.rb
67
+ - test/test_helper.rb
68
+ - test/unit/test_versioning.rb
69
+ has_rdoc: true
70
+ homepage: https://github.com/artha42/mm-versionable/
71
+ licenses: []
72
+
73
+ post_install_message:
74
+ rdoc_options: []
75
+
76
+ require_paths:
77
+ - lib
78
+ required_ruby_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ segments:
92
+ - 0
93
+ version: "0"
94
+ requirements: []
95
+
96
+ rubyforge_project:
97
+ rubygems_version: 1.3.7
98
+ signing_key:
99
+ specification_version: 3
100
+ summary: A MongoMapper extension for document versioning
101
+ test_files:
102
+ - test/config/config.rb
103
+ - test/models/post.rb
104
+ - test/models/user.rb
105
+ - test/performance/read_write.rb
106
+ - test/test_helper.rb
107
+ - test/unit/test_versioning.rb