dm-is-versioned 0.9.7

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/History.txt ADDED
@@ -0,0 +1 @@
1
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2007 Timothy Bennett
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/Manifest.txt ADDED
@@ -0,0 +1,12 @@
1
+ History.txt
2
+ LICENSE
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ TODO
7
+ lib/dm-is-versioned.rb
8
+ lib/dm-is-versioned/is/version.rb
9
+ lib/dm-is-versioned/is/versioned.rb
10
+ spec/spec.opts
11
+ spec/spec_helper.rb
12
+ spec/versioned_spec.rb
data/README.txt ADDED
@@ -0,0 +1,3 @@
1
+ = dm-is-versioned
2
+
3
+ DataMapper plugin enabling simple versioning of models.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+ require 'spec/rake/spectask'
4
+ require 'pathname'
5
+
6
+ ROOT = Pathname(__FILE__).dirname.expand_path
7
+ require ROOT + 'lib/dm-is-versioned/is/version'
8
+
9
+ AUTHOR = "Bernerd Schaefer"
10
+ EMAIL = "bj.schaefer@gmail.com"
11
+ GEM_NAME = "dm-is-versioned"
12
+ GEM_VERSION = DataMapper::Is::Versioned::VERSION
13
+ GEM_DEPENDENCIES = [["dm-core", GEM_VERSION]]
14
+ GEM_CLEAN = ["log", "pkg"]
15
+ GEM_EXTRAS = { :has_rdoc => true, :extra_rdoc_files => %w[ README.txt LICENSE TODO ] }
16
+
17
+ PROJECT_NAME = "datamapper"
18
+ PROJECT_URL = "http://github.com/sam/dm-more/tree/master/dm-is-versioned"
19
+ PROJECT_DESCRIPTION = PROJECT_SUMMARY = "DataMapper plugin enabling simple versioning of models"
20
+
21
+ require ROOT.parent + 'tasks/hoe'
22
+
23
+ task :default => [ :spec ]
24
+
25
+ WIN32 = (RUBY_PLATFORM =~ /win32|mingw|cygwin/) rescue nil
26
+ SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
27
+
28
+ desc "Install #{GEM_NAME} #{GEM_VERSION} (default ruby)"
29
+ task :install => [ :package ] do
30
+ sh "#{SUDO} gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources", :verbose => false
31
+ end
32
+
33
+ desc "Uninstall #{GEM_NAME} #{GEM_VERSION} (default ruby)"
34
+ task :uninstall => [ :clobber ] do
35
+ sh "#{SUDO} gem uninstall #{GEM_NAME} -v#{GEM_VERSION} -I -x", :verbose => false
36
+ end
37
+
38
+ namespace :jruby do
39
+ desc "Install #{GEM_NAME} #{GEM_VERSION} with JRuby"
40
+ task :install => [ :package ] do
41
+ sh %{#{SUDO} jruby -S gem install --local pkg/#{GEM_NAME}-#{GEM_VERSION} --no-update-sources}, :verbose => false
42
+ end
43
+ end
44
+
45
+ desc 'Run specifications'
46
+ Spec::Rake::SpecTask.new(:spec) do |t|
47
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
48
+ t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s)
49
+
50
+ begin
51
+ t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
52
+ t.rcov_opts << '--exclude' << 'spec'
53
+ t.rcov_opts << '--text-summary'
54
+ t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
55
+ rescue Exception
56
+ # rcov not installed
57
+ end
58
+ end
data/TODO ADDED
File without changes
@@ -0,0 +1,7 @@
1
+ module DataMapper
2
+ module Is
3
+ module Versioned
4
+ VERSION = "0.9.7"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,127 @@
1
+ module DataMapper
2
+ module Is
3
+ ##
4
+ # = Is Versioned
5
+ # The Versioned module will configure a model to be versioned.
6
+ #
7
+ # The is-versioned plugin functions differently from other versioning
8
+ # solutions (such as acts_as_versioned), but can be configured to
9
+ # function like it if you so desire.
10
+ #
11
+ # The biggest difference is that there is not an incrementing 'version'
12
+ # field, but rather, any field of your choosing which will be unique
13
+ # on update.
14
+ #
15
+ # == Setup
16
+ # For simplicity, I will assume that you have loaded dm-timestamps to
17
+ # automatically update your :updated_at field. See versioned_spec for
18
+ # and example of updating the versioned field yourself.
19
+ #
20
+ # class Story
21
+ # include DataMapper::Resource
22
+ # property :id, Serial
23
+ # property :title, String
24
+ # property :updated_at, DateTime
25
+ #
26
+ # is_versioned :on => [:updated_at]
27
+ # end
28
+ #
29
+ # == Auto Upgrading and Auto Migrating
30
+ #
31
+ # Story.auto_migrate! # => will run auto_migrate! on Story::Version, too
32
+ # Story.auto_upgrade! # => will run auto_upgrade! on Story::Version, too
33
+ #
34
+ # == Usage
35
+ #
36
+ # story = Story.get(1)
37
+ # story.title = "New Title"
38
+ # story.save # => Saves this story and creates a new version with the
39
+ # # original values.
40
+ # story.versions.size # => 1
41
+ #
42
+ # story.title = "A Different New Title"
43
+ # story.save
44
+ # story.versions.size # => 2
45
+ #
46
+ # TODO: enable replacing a current version with an old version.
47
+ module Versioned
48
+
49
+ def is_versioned(options = {})
50
+ on = options[:on]
51
+
52
+ class << self; self end.class_eval do
53
+ define_method :const_missing do |name|
54
+ storage_name = Extlib::Inflection.tableize(self.name + "Version")
55
+ model = DataMapper::Model.new(storage_name)
56
+
57
+ if name == :Version
58
+ properties.each do |property|
59
+ options = property.options
60
+ options[:key] = true if property.name == on || options[:serial] == true
61
+ options[:serial] = false
62
+ model.property property.name, property.type, options
63
+ end
64
+
65
+ self.const_set("Version", model)
66
+ else
67
+ super(name)
68
+ end
69
+ end
70
+ end
71
+
72
+ self.after_class_method :auto_migrate! do
73
+ self::Version.auto_migrate!
74
+ end
75
+
76
+ self.after_class_method :auto_upgrade! do
77
+ self::Version.auto_upgrade!
78
+ end
79
+
80
+ self.before :attribute_set do |property, value|
81
+ pending_version_attributes[property] ||= self.attribute_get(property)
82
+ end
83
+
84
+ self.after :update do |result|
85
+ if result && dirty_attributes.has_key?(properties[on])
86
+ self.class::Version.create(self.attributes.merge(pending_version_attributes))
87
+ self.pending_version_attributes.clear
88
+ end
89
+
90
+ result
91
+ end
92
+
93
+ include DataMapper::Is::Versioned::InstanceMethods
94
+ end
95
+
96
+
97
+ module InstanceMethods
98
+ ##
99
+ # Returns a hash of original values to be stored in the
100
+ # versions table when a new version is created. It is
101
+ # cleared after a version model is created.
102
+ #
103
+ # --
104
+ # @return <Hash>
105
+ def pending_version_attributes
106
+ @pending_version_attributes ||= {}
107
+ end
108
+
109
+ ##
110
+ # Returns a collection of other versions of this resource.
111
+ # The versions are related on the models keys, and ordered
112
+ # by the version field.
113
+ #
114
+ # --
115
+ # @return <Collection>
116
+ def versions
117
+ query = {}
118
+ version = self.class.const_get("Version")
119
+ self.class.key.zip(self.key) { |property, value| query[property.name] = value }
120
+ query.merge(:order => version.key.collect { |key| key.name.desc })
121
+ version.all(query)
122
+ end
123
+ end
124
+
125
+ end # Versioned
126
+ end # Is
127
+ end # DataMapper
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'pathname'
3
+
4
+ gem 'dm-core', '~>0.9.7'
5
+ require 'dm-core'
6
+
7
+ require Pathname(__FILE__).dirname.expand_path / 'dm-is-versioned' / 'is' / 'versioned.rb'
8
+
9
+ # Include the plugin in Resource
10
+ module DataMapper
11
+ module Resource
12
+ module ClassMethods
13
+ include DataMapper::Is::Versioned
14
+ end # module ClassMethods
15
+ end # module Resource
16
+ end # module DataMapper
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --format specdoc
2
+ --colour
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ gem 'rspec', '>=1.1.3'
3
+ require 'spec'
4
+ require 'pathname'
5
+ require Pathname(__FILE__).dirname.expand_path.parent + 'lib/dm-is-versioned'
6
+
7
+ def load_driver(name, default_uri)
8
+ return false if ENV['ADAPTER'] != name.to_s
9
+
10
+ lib = "do_#{name}"
11
+
12
+ begin
13
+ gem lib, '~>0.9.7'
14
+ require lib
15
+ DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
16
+ DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
17
+ true
18
+ rescue Gem::LoadError => e
19
+ warn "Could not load #{lib}: #{e}"
20
+ false
21
+ end
22
+ end
23
+
24
+ ENV['ADAPTER'] ||= 'sqlite3'
25
+
26
+ HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
27
+ HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
28
+ HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
@@ -0,0 +1,155 @@
1
+ require File.dirname(__FILE__) + "/spec_helper"
2
+
3
+ class Story
4
+ include DataMapper::Resource
5
+
6
+ property :id, Integer, :serial => true
7
+ property :title, String
8
+ property :updated_at, DateTime
9
+
10
+ before :save do
11
+ # For the sake of testing, make sure the updated_at is always unique
12
+ time = self.updated_at ? self.updated_at + 1 : Time.now
13
+ self.updated_at = time if self.dirty?
14
+ end
15
+
16
+ is_versioned :on => :updated_at
17
+
18
+ end
19
+
20
+ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
21
+ describe 'DataMapper::Is::Versioned' do
22
+ describe "inner class" do
23
+ it "should be present" do
24
+ Story::Version.should be_a_kind_of(Class)
25
+ end
26
+
27
+ it "should have a default storage name" do
28
+ Story::Version.storage_name.should == "story_versions"
29
+ end
30
+
31
+ it "should have its parent's properties" do
32
+ Story.properties.each do |property|
33
+ Story::Version.properties.should have_property(property.name)
34
+ end
35
+ end
36
+ end # inner class
37
+
38
+ describe "#auto_migrate!" do
39
+ before do
40
+ Story::Version.should_receive(:auto_migrate!)
41
+ end
42
+ it "should get called on the inner class" do
43
+ Story.auto_migrate!
44
+ end
45
+ end # #auto_migrate!
46
+
47
+ describe "#auto_upgrade!" do
48
+ before do
49
+ Story::Version.should_receive(:auto_upgrade!)
50
+ end
51
+ it "should get called on the inner class" do
52
+ Story.auto_upgrade!
53
+ end
54
+ end # #auto_upgrade!
55
+
56
+ describe "#create" do
57
+ before do
58
+ Story.auto_migrate!
59
+ Story.create(:title => "A Very Interesting Article")
60
+ end
61
+ it "should not create a versioned copy" do
62
+ Story::Version.all.size.should == 0
63
+ end
64
+ end # #create
65
+
66
+ describe "#save" do
67
+ before do
68
+ Story.auto_migrate!
69
+ end
70
+
71
+ describe "(with new resource)" do
72
+ before do
73
+ @story = Story.new(:title => "A Story")
74
+ @story.save
75
+ end
76
+ it "should not create a versioned copy" do
77
+ Story::Version.all.size.should == 0
78
+ end
79
+ end
80
+
81
+ describe "(with a clean existing resource)" do
82
+ before do
83
+ @story = Story.create(:title => "A Story")
84
+ @story.save
85
+ end
86
+
87
+ it "should not create a versioned copy" do
88
+ Story::Version.all.size.should == 0
89
+ end
90
+ end
91
+
92
+ describe "(with a dirty existing resource)" do
93
+ before do
94
+ @story = Story.create(:title => "A Story")
95
+ @story.title = "An Inner Update"
96
+ @story.title = "An Updated Story"
97
+ @story.save
98
+ end
99
+
100
+ it "should create a versioned copy" do
101
+ Story::Version.all.size.should == 1
102
+ end
103
+
104
+ it "should not have the same value for the versioned field" do
105
+ @story.updated_at.should_not == Story::Version.first.updated_at
106
+ end
107
+
108
+ it "should save the original value, not the inner update" do
109
+ # changes to the story between saves shouldn't be updated.
110
+ @story.versions.last.title.should == "A Story"
111
+ end
112
+ end
113
+
114
+ end # #save
115
+
116
+ describe "#pending_version_attributes" do
117
+ before do
118
+ @story = Story.create(:title => "A Story")
119
+ end
120
+
121
+ it "should be updated when a property changes" do
122
+ @story.title = "A New Title"
123
+ @story.pending_version_attributes[:title].should == "A Story"
124
+ end
125
+
126
+ it "should be cleared when a resource is saved" do
127
+ @story.title = "A New Title"
128
+ @story.save
129
+ @story.pending_version_attributes.should be_empty
130
+ end
131
+ end # #pending_version_attributes
132
+
133
+ describe "#versions" do
134
+ before do
135
+ Story.auto_migrate!
136
+ @story = Story.create(:title => "A Story")
137
+ end
138
+
139
+ it "should return an empty array when there are no versions" do
140
+ @story.versions.should == []
141
+ end
142
+
143
+ it "should return a collection when there are versions" do
144
+ @story.versions.should == Story::Version.all(:id => @story.id)
145
+ end
146
+
147
+ it "should not return another object's versions" do
148
+ @story2 = Story.create(:title => "A Different Story")
149
+ @story2.title = "A Different Title"
150
+ @story2.save
151
+ @story.versions.should == Story::Version.all(:id => @story.id)
152
+ end
153
+ end # #versions
154
+ end
155
+ end
metadata ADDED
@@ -0,0 +1,87 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-is-versioned
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.7
5
+ platform: ruby
6
+ authors:
7
+ - Bernerd Schaefer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-11-18 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: dm-core
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - "="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.9.7
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hoe
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.8.2
34
+ version:
35
+ description: DataMapper plugin enabling simple versioning of models
36
+ email:
37
+ - bj.schaefer@gmail.com
38
+ executables: []
39
+
40
+ extensions: []
41
+
42
+ extra_rdoc_files:
43
+ - README.txt
44
+ - LICENSE
45
+ - TODO
46
+ files:
47
+ - History.txt
48
+ - LICENSE
49
+ - Manifest.txt
50
+ - README.txt
51
+ - Rakefile
52
+ - TODO
53
+ - lib/dm-is-versioned.rb
54
+ - lib/dm-is-versioned/is/version.rb
55
+ - lib/dm-is-versioned/is/versioned.rb
56
+ - spec/spec.opts
57
+ - spec/spec_helper.rb
58
+ - spec/versioned_spec.rb
59
+ has_rdoc: true
60
+ homepage: http://github.com/sam/dm-more/tree/master/dm-is-versioned
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --main
64
+ - README.txt
65
+ require_paths:
66
+ - lib
67
+ required_ruby_version: !ruby/object:Gem::Requirement
68
+ requirements:
69
+ - - ">="
70
+ - !ruby/object:Gem::Version
71
+ version: "0"
72
+ version:
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: "0"
78
+ version:
79
+ requirements: []
80
+
81
+ rubyforge_project: datamapper
82
+ rubygems_version: 1.3.1
83
+ signing_key:
84
+ specification_version: 2
85
+ summary: DataMapper plugin enabling simple versioning of models
86
+ test_files: []
87
+