mm-versionable 0.2

Sign up to get free protection for your applications and to get access to all the features.
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