dm-actionstamps 0.0.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.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 kematzy
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.
@@ -0,0 +1,180 @@
1
+ = dm-actionstamps
2
+
3
+ A DataMapper plugin that works similar to the dm-timestamps in that it 'automagically' adds/updates the
4
+ created_?, updated_? fields of your models.
5
+
6
+ This is a completely rewritten version of the "rlivesey's"[http://github.com/rlivsey/] <tt>"dm-userstamp"[http://github.com/rlivsey/dm-userstamp]</tt> gem.
7
+
8
+ It was rewritten and given a new new when I made more dynamic in nature than dm-userstamp.
9
+
10
+
11
+ == Installation
12
+
13
+ $ (sudo)? gem install dm-actionstamps
14
+
15
+ <b>NB! Depends upon the whole DataMapper suite being installed, and has ONLY been tested with DM 0.10.2.</b>
16
+
17
+
18
+ == Getting Started
19
+
20
+
21
+
22
+ Require <tt>dm-actionstamps</tt> in your app.
23
+
24
+ require 'dm-core' # must be required first
25
+ require 'dm-actionstamps'
26
+
27
+
28
+ Lets say we have a User class and we want to track the user actions in our other models. Just do as follows:
29
+
30
+ class User
31
+ include DataMapper::Resource
32
+ property :id, Serial
33
+ property :name, String
34
+
35
+ provides_actionstamps
36
+
37
+ end
38
+
39
+
40
+ By declaring <tt>:provides_actionstamps</tt> in your User model, you get the following functionality free.
41
+
42
+ User.current_user = new user...
43
+
44
+ User.current_user => retrieves the current_user
45
+
46
+
47
+ You can then use it as follows:
48
+
49
+ @user = User.create(:id => 99, :name => "Joe")
50
+
51
+ User.current_user = @user
52
+
53
+
54
+ # retrieve the current user info.
55
+ User.current_user.id => returns 99
56
+
57
+
58
+ OK, so far not so exciting, but lets say we have an Article class, and we want to track the User that
59
+ created the Article and the last User that updated the Article. It's easy too:
60
+
61
+ class Article
62
+ include DataMapper::Resource
63
+ property :id, Serial
64
+ property :title, String
65
+ ...<snip>
66
+
67
+ actionstamps :by, User
68
+
69
+ end
70
+
71
+ Once you have your Article model we can create our Articles just as normal:
72
+
73
+ @article = Article.create(:title => 'Example 1')
74
+
75
+
76
+ The instance of <tt>Article.get(1)</tt> now has the following things for free:
77
+
78
+ * a <tt>:created_by</tt> attribute with the value of <tt>User.current_user.id</tt> if set. Default value is <tt>nil</tt>.
79
+
80
+ * a <tt>:updated_by</tt> attribute with the value of <tt>User.current_user.id</tt> if set. Default value is <tt>nil</tt>.
81
+
82
+
83
+ So given this usage scenario:
84
+
85
+ @user = User.create(:id => 99, :name => "Joe")
86
+ User.current_user = @user
87
+
88
+ @article = Article.create(:title => 'Example 2')
89
+
90
+
91
+ The Article instance should have the following:
92
+
93
+ @article.created_by => 99
94
+ @article.updated_by => 99
95
+
96
+
97
+ == Customisations
98
+
99
+ <tt>dm-actionstamps</tt> plugin/gem was created because I needed to track the actions of Client, Staff and other 'User' models,
100
+ and the <tt>"dm-userstamp"[http://github.com/rlivsey/dm-userstamp]</tt> gem was hardcoded to the <tt>User</tt> model.
101
+
102
+ To support more flexible declarations, I have separated out the functionality into two parts:
103
+
104
+ * Provider Model
105
+ * Receiver Model
106
+
107
+
108
+ The Provider Model provides the actionstamps, and can be declared like this:
109
+
110
+ class Client
111
+ include DataMapper::Resource
112
+ property :id, Serial
113
+ property :name, String
114
+
115
+ provides_actionstamps
116
+
117
+ end
118
+
119
+ In difference to the examples above, the <tt>current_?</tt> methods takes the name of the Model that was given the provider status (via the <tt>:provides_actionstamps</tt> method):
120
+
121
+ Client.current_client = Client.create(:id => 88, :name => "Happy")
122
+
123
+ Client.current_client => returns the Client
124
+
125
+
126
+
127
+ The Receiver Model receives the actionstamps and can be declared like this:
128
+
129
+
130
+ class Bill
131
+ include DataMapper::Resource
132
+ property :id, Serial
133
+ property :amount, Integer
134
+ ...<snip>
135
+
136
+ actionstamps :by, Client
137
+
138
+ end
139
+
140
+
141
+ So when we create a new instance of Bill, we get the values from the Client.current_client method:
142
+
143
+ @bill = Bill.create(:amount => "100")
144
+
145
+ @bill.created_by => 88
146
+
147
+
148
+ == TODO
149
+
150
+ * Contemplate if there is ever a scenario where you need to track two different models updating the same record? (Seller vs Buyers ?)
151
+
152
+ * Think of some clever way to automagically set "User.current_user = @user" when you load or create a new User in your enclosing application.
153
+
154
+ * Any other improvements that you can think of?
155
+
156
+
157
+ == RTFM
158
+
159
+ For a better understanding of this gem/plugin, make sure you study the '<tt>dm-actionstamps/spec/integration/actionstamps_spec.rb</tt>' file.
160
+
161
+
162
+ == Errors / Bugs
163
+
164
+ If something is not behaving intuitively, it is a bug, and should be reported.
165
+ Report it here: http://github.com/kematzy/dm-actionstamps/issues
166
+
167
+ == Note on Patches/Pull Requests
168
+
169
+ * Fork the project.
170
+ * Make your feature addition or bug fix.
171
+ * Add tests for it. This is important so I don't break it in a
172
+ future version unintentionally.
173
+ * Commit, do not mess with rakefile, version, or history.
174
+ * (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)
175
+ * Send me a pull request. Bonus points for topic branches.
176
+
177
+ == Copyright
178
+
179
+ Copyright (c) 2010 kematzy. Released under the MIT license.
180
+
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "dm-actionstamps"
8
+ gem.summary = %Q{A DataMapper plugin that automatically adds/updates the created_?, updated_? fields of your models.}
9
+ gem.description = %Q{A DataMapper plugin that works similar to the dm-timestamps in that it 'automagically' adds/updates the created_?, updated_? fields of your models.}
10
+ gem.email = "kematzy@gmail.com"
11
+ gem.homepage = "http://github.com/kematzy/dm-actionstamps"
12
+ gem.authors = ["kematzy"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_opts = ["--color", "--format", "nested", "--require", "spec/spec_helper.rb"]
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+ task :spec => :check_dependencies
35
+
36
+ task :default => :spec
37
+
38
+ require 'rake/rdoctask'
39
+ Rake::RDocTask.new do |rdoc|
40
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
41
+
42
+ rdoc.rdoc_dir = 'rdoc'
43
+ rdoc.title = "dm-actionstamps #{version}"
44
+ rdoc.rdoc_files.include('README*')
45
+ rdoc.rdoc_files.include('lib/**/*.rb')
46
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,56 @@
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{dm-actionstamps}
8
+ s.version = "0.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["kematzy"]
12
+ s.date = %q{2010-05-17}
13
+ s.description = %q{A DataMapper plugin that works similar to the dm-timestamps in that it 'automagically' adds/updates the created_?, updated_? fields of your models.}
14
+ s.email = %q{kematzy@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "dm-actionstamps.gemspec",
27
+ "lib/dm-actionstamps.rb",
28
+ "lib/dm-actionstamps/actionstamps.rb",
29
+ "lib/dm-actionstamps/version.rb",
30
+ "spec/integration/actionstamps_spec.rb",
31
+ "spec/spec_helper.rb"
32
+ ]
33
+ s.homepage = %q{http://github.com/kematzy/dm-actionstamps}
34
+ s.rdoc_options = ["--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.6}
37
+ s.summary = %q{A DataMapper plugin that automatically adds/updates the created_?, updated_? fields of your models.}
38
+ s.test_files = [
39
+ "spec/integration/actionstamps_spec.rb",
40
+ "spec/spec_helper.rb"
41
+ ]
42
+
43
+ if s.respond_to? :specification_version then
44
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
45
+ s.specification_version = 3
46
+
47
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
48
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
49
+ else
50
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
51
+ end
52
+ else
53
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
54
+ end
55
+ end
56
+
@@ -0,0 +1,16 @@
1
+
2
+ # Need to import datamapper and other gems
3
+ # require 'rubygems' # read [ http://gist.github.com/54177 ] to understand why this line is commented out
4
+ require 'pathname'
5
+
6
+ # Add all external dependencies for the plugin here
7
+ # gem 'dm-core', '~> 0.10.2'
8
+ require 'dm-core'
9
+
10
+ # Require plugin-files
11
+ require Pathname(__FILE__).dirname.expand_path / 'dm-actionstamps' / 'actionstamps.rb'
12
+
13
+ DataMapper::Model.append_extensions(DataMapper::Actionstamps)
14
+ # DataMapper::Model.append_inclusions(DataMapper::Actionstamps)
15
+
16
+
@@ -0,0 +1,152 @@
1
+
2
+ require 'dm-core'
3
+ require 'extlib/inflection'
4
+
5
+ module DataMapper
6
+ module Actionstamps
7
+
8
+ ##
9
+ # Provider method, used to setup the +current_user+ information.
10
+ #
11
+ # ==== Examples
12
+ #
13
+ # class User
14
+ # <snip...>
15
+ #
16
+ # provides_actionstamps
17
+ #
18
+ # end
19
+ #
20
+ # => creates the :current_user & :current_user= methods
21
+ #
22
+ # class Client
23
+ # <snip...>
24
+ #
25
+ # provides_actionstamps
26
+ #
27
+ # end
28
+ #
29
+ # => creates the :current_client & :current_client= methods
30
+ #
31
+ #
32
+ # @api public
33
+ def provides_actionstamps
34
+ @actionstamps_class = self
35
+
36
+ extend DataMapper::Actionstamps::ClassMethods
37
+
38
+ class_eval(<<-END, __FILE__, __LINE__)
39
+ def self.current_#{name.downcase}=(user)
40
+ Thread.current["#{name.downcase}_#{self.object_id}_actionstamp"] = user
41
+ end
42
+ def self.current_#{name.downcase}
43
+ Thread.current["#{name.downcase}_#{self.object_id}_actionstamp"]
44
+ end
45
+ END
46
+ end
47
+
48
+
49
+ ##
50
+ # The Receiver Model receives the actionstamps and defines the
51
+ #
52
+ # ==== Examples
53
+ #
54
+ # class Bill
55
+ # include DataMapper::Resource
56
+ # property :id, Serial
57
+ # property :amount, Integer
58
+ # ...<snip>
59
+ #
60
+ # actionstamps :by, Client
61
+ #
62
+ # end
63
+ #
64
+ # => creates the :created_by and :updated_by fields.
65
+ #
66
+ # actionstamps :by_id, Author
67
+ #
68
+ #
69
+ # @api public/private
70
+ def actionstamps(*args)
71
+ # set default args if none passed in
72
+ args = [:by, ::User ] if args.empty?
73
+ # if invalid args, just bail out
74
+ if ( args[0].is_a?(Hash) || nil ) || ( args[1].is_a?(Hash) || args[1].is_a?( Symbol) || args[1].is_a?(String) || nil )
75
+ raise ArgumentError, "Invalid arguments passed: syntax: actionstamps :by, ModelName"
76
+ end
77
+
78
+ configs = { :suffix => args[0], :model => args[1] }
79
+
80
+ # do we have the fields :created_? / :updated_? declared, if not we declare them
81
+ if properties.any? { |p| p.name.to_s =~ /^(created|updated)_#{Regexp.escape(configs[:suffix].to_s)}$/ }
82
+ # if they are declared then we use them
83
+ raise ArgumentError, "Full functionality NOT implemented yet. Please DO NOT declare your created_#{configs[:suffix]} / updated_#{configs[:suffix]} properties, they will be automatically declared by this plugin."
84
+ else
85
+ property "created_#{configs[:suffix]}".to_sym, Integer#, :default => 1
86
+ property "updated_#{configs[:suffix]}".to_sym, Integer#, :default => 1
87
+ end
88
+
89
+ @actionstamps_receiver_properties = ["created_#{configs[:suffix]}".to_sym, "updated_#{configs[:suffix]}".to_sym ]
90
+ @actionstamps_model_class = configs[:model].to_s.capitalize
91
+
92
+ extend DataMapper::Actionstamps::ClassMethods
93
+ include DataMapper::Actionstamps::ReceiverMethods
94
+
95
+
96
+ class_eval(<<-END, __FILE__, __LINE__)
97
+ def set_actionstamps
98
+ self.created_#{configs[:suffix].to_s} = #{configs[:model]}.current_#{configs[:model].to_s.downcase}.id if #{configs[:model]}.current_#{configs[:model].to_s.downcase} && self.new? && self.created_#{configs[:suffix].to_s}.nil?
99
+ self.updated_#{configs[:suffix].to_s} = #{configs[:model]}.current_#{configs[:model].to_s.downcase}.id if #{configs[:model]}.current_#{configs[:model].to_s.downcase}
100
+ end
101
+ END
102
+
103
+ end #/def actionstamps
104
+
105
+ module ClassMethods
106
+ attr_reader :actionstamps_class
107
+ end #/module ClassMethods
108
+
109
+
110
+ module ReceiverMethods
111
+
112
+ def self.included(model)
113
+ model.before :save, :set_actionstamps_on_save
114
+ model.extend DataMapper::Actionstamps::ClassMethods
115
+ end
116
+
117
+ ##
118
+ # Saves the record with the created_? / updated_? attributes set to the current time.
119
+ #
120
+ # ==== Examples
121
+ #
122
+ # @model.touch
123
+ #
124
+ # @api public
125
+ def touch
126
+ set_actionstamps
127
+ save
128
+ end
129
+
130
+
131
+ private
132
+
133
+ ##
134
+ # Callback method
135
+ #
136
+ # ==== Examples
137
+ #
138
+ # before :save, :set_actionstamps_on_save
139
+ #
140
+ #
141
+ # @api public
142
+ def set_actionstamps_on_save
143
+ return unless dirty?
144
+ set_actionstamps
145
+ end
146
+
147
+
148
+ end #/module ReceiverMethods
149
+
150
+ end #/module Actionstamps
151
+
152
+ end #/module DataMapper
@@ -0,0 +1,5 @@
1
+ module DataMapper
2
+ module Actionstamps
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,576 @@
1
+ require 'pathname'
2
+ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
+
4
+
5
+ return unless HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
6
+
7
+ describe "DataMapper::Actionstamps" do
8
+
9
+ describe "Default behaviours" do
10
+
11
+ before(:all) do
12
+ class User
13
+ include DataMapper::Resource
14
+ property :id, Serial
15
+ property :name, String
16
+
17
+ provides_actionstamps
18
+ end
19
+
20
+ class Article
21
+ include DataMapper::Resource
22
+ property :id, Serial
23
+ property :title, String
24
+
25
+ actionstamps #:by, User
26
+ end
27
+
28
+ DataMapper.auto_migrate!
29
+ end
30
+
31
+ before(:each) do
32
+ @user = User.create(:name => "Joe", :id => 99)
33
+ # User.current_user = @user
34
+ end
35
+
36
+ after do
37
+ User.all.destroy!
38
+ Article.all.destroy!
39
+ end
40
+
41
+ describe "Provider Model" do
42
+
43
+ describe "#self.actionstamps_class" do
44
+
45
+ it "should return the constant of the Actionstamps provider class" do
46
+ User.actionstamps_class.should == User
47
+
48
+ class Donkey
49
+ include DataMapper::Resource
50
+ property :id, Serial
51
+ property :name, String
52
+
53
+ provides_actionstamps
54
+ end
55
+ Donkey.actionstamps_class.should == Donkey
56
+ end
57
+
58
+ end #/ #self.actionstamps_class
59
+
60
+ describe "#self.current_user=(user)" do
61
+
62
+ it "should respond to :current_user=" do
63
+ User.should respond_to(:current_user=)
64
+ end
65
+
66
+ it "should allow setting a new user" do
67
+ lambda {
68
+ User.current_user = User.create(:id => 77, :name => "Adam")
69
+ }.should_not raise_error(Exception)
70
+ end
71
+
72
+ end #/ #self.current_user=(user)
73
+
74
+
75
+ describe "#self.current_user" do
76
+
77
+ it "should respond to :current_user" do
78
+ User.should respond_to(:current_user)
79
+ end
80
+
81
+ it "should return the currently assigned User" do
82
+ User.current_user.id.should == 77
83
+ end
84
+
85
+ end #/ #self.current_user
86
+
87
+ end #/ Provider Model
88
+
89
+ describe "Receiver Model" do
90
+
91
+ describe "when there is NO current_user" do
92
+
93
+ before(:each) do
94
+ User.current_user = nil
95
+ end
96
+
97
+ it "should not set the created_by" do
98
+ User.current_user.should == nil
99
+ a = Article.create(:title => "This also works")
100
+ a.created_by.should == nil
101
+ a.updated_by.should == nil
102
+ end
103
+
104
+ it "should not set the updated_by" do
105
+ User.current_user.should == nil
106
+ a = Article.create(:title => "This also works")
107
+ a.updated_by.should == nil
108
+ end
109
+
110
+ it "should set :updated_by and NOT set :created_by when touched" do
111
+ User.current_user.should == nil # sanity
112
+ a = Article.create(:title => "Absolutely Amazing, it all works" )
113
+
114
+ a.touch
115
+
116
+ a.updated_by.should == nil
117
+ a.created_by.should == nil
118
+ end
119
+
120
+ end #/ when there is NO current_user
121
+
122
+ describe "when there is a current_user" do
123
+
124
+ before(:each) do
125
+ User.current_user = @user
126
+ end
127
+
128
+ it "should NOT set if :created_by is already set" do
129
+ User.current_user.id.should == 99 # sanity
130
+ a = Article.new(:title => "Hell, this works too" )
131
+ a.created_by = 5
132
+ a.save
133
+ a.created_by.should == 5
134
+ a.created_by.should be_a_kind_of(Integer)
135
+ end
136
+
137
+ it "should set :created_by on creation" do
138
+ User.current_user.id.should == 99 # sanity
139
+ a = Article.new(:title => "Hell, this works as well!" )
140
+ a.created_by.should == nil
141
+ a.save
142
+ a.created_by.should be_a_kind_of(Integer)
143
+ a.created_by.should == User.current_user.id
144
+ end
145
+
146
+ it "should NOT alter :created_by on model updates" do
147
+ User.current_user.id.should == 99 # sanity
148
+ a = Article.new(:title => "Even this works" )
149
+ a.created_by.should == nil
150
+ a.save
151
+ a.created_by.should be_a_kind_of(Integer)
152
+ a.created_by.should == User.current_user.id
153
+
154
+ u = User.create(:name => "Eve", :id => 88)
155
+ User.current_user = u
156
+ a.title = "Updating things works as well"
157
+ a.save
158
+ a.created_by.should_not == User.current_user.id
159
+ a.updated_by.should == User.current_user.id
160
+ end
161
+
162
+ it "should set :updated_by on creation and on update" do
163
+ User.current_user.id.should == 99 # sanity
164
+ a = Article.new(:title => "This is just great, it all works" )
165
+ a.updated_by.should == nil
166
+ a.save
167
+ a.updated_by.should be_a_kind_of(Integer)
168
+ a.updated_by.should == User.current_user.id
169
+
170
+ u = User.create(:name => "Eve", :id => 88)
171
+ User.current_user = u
172
+ a.title = "Updating things works as well"
173
+ a.save
174
+ a.updated_by.should == User.current_user.id
175
+ a.updated_by.should_not == @user.id
176
+ end
177
+
178
+ it "should set :updated_by and NOT set :created_by when touched" do
179
+ User.current_user.id.should == 99 # sanity
180
+ a = Article.create(:title => "Absolutely Amazing, it all works" )
181
+ a.created_by.should be_a_kind_of(Integer)
182
+ a.created_by.should == User.current_user.id
183
+ a.updated_by.should be_a_kind_of(Integer)
184
+ a.updated_by.should == User.current_user.id
185
+
186
+ User.current_user = User.create(:name => "Eve", :id => 88)
187
+ User.current_user.id.should == 88
188
+
189
+ a.touch
190
+
191
+ a.updated_by.should == User.current_user.id
192
+ a.created_by.should_not == User.current_user.id
193
+ a.created_by.should == @user.id
194
+ end
195
+
196
+ end #/ when there is a current_user
197
+
198
+ end #/ Receiver Model
199
+
200
+ end #/ Default behaviours
201
+
202
+ describe "Error Handling" do
203
+
204
+ describe "Provider declarations" do
205
+
206
+ it "should raise an ArgumentError when passed an arg" do
207
+ lambda {
208
+ class Client
209
+ include DataMapper::Resource
210
+ property :id, Serial
211
+ property :name, String
212
+
213
+ provides_actionstamps :some_value
214
+
215
+ # actionstamps :by, User
216
+ end
217
+
218
+ }.should raise_error(ArgumentError)
219
+ end
220
+
221
+ end #/ Provider declarations
222
+
223
+ describe "Receiver declarations" do
224
+
225
+ it "should raise an ArgumentError when passed a Hash as the 1st arg" do
226
+ lambda {
227
+ class Post
228
+ include DataMapper::Resource
229
+ property :id, Serial
230
+ property :title, String
231
+
232
+ actionstamps :model => :user
233
+ end
234
+
235
+ }.should raise_error(ArgumentError)
236
+ end
237
+
238
+ it "should raise an ArgumentError when passed a Hash as the 2nd args" do
239
+ lambda {
240
+ class Post
241
+ include DataMapper::Resource
242
+ property :id, Serial
243
+ property :title, String
244
+
245
+ actionstamps :by, :model => :user
246
+ end
247
+ }.should raise_error(ArgumentError)
248
+ end
249
+
250
+ it "should raise an ArgumentError when passed a Symbol as the 2nd args" do
251
+ lambda {
252
+ class Post
253
+ include DataMapper::Resource
254
+ property :id, Serial
255
+ property :title, String
256
+
257
+ actionstamps :by, :user
258
+ end
259
+ }.should raise_error(ArgumentError)
260
+ end
261
+
262
+ it "should raise an ArgumentError when passed a String as the 2nd args" do
263
+ lambda {
264
+ class Post
265
+ include DataMapper::Resource
266
+ property :id, Serial
267
+ property :title, String
268
+
269
+ actionstamps :by, "User"
270
+ end
271
+ }.should raise_error(ArgumentError)
272
+ end
273
+
274
+ it "should raise an ArgumentError when passed a non-existant model as the 2nd args" do
275
+ lambda {
276
+ class Post
277
+ include DataMapper::Resource
278
+ property :id, Serial
279
+ property :title, String
280
+
281
+ actionstamps :by, ::DoesNotExist
282
+ end
283
+ }.should raise_error(NameError)
284
+ end
285
+
286
+ it "should raise an ArgumentError when declaring a property by the same name" do
287
+ lambda {
288
+ class Post
289
+ include DataMapper::Resource
290
+ property :id, Serial
291
+ property :title, String
292
+
293
+ property :created_by_id, Integer
294
+
295
+ actionstamps :by_id, ::User
296
+ end
297
+ }.should raise_error(ArgumentError)
298
+ end
299
+
300
+ end #/ Receiver declarations
301
+
302
+ end #/ Error Handling
303
+
304
+ describe "Associations" do
305
+
306
+ describe "when using :created_by_id" do
307
+
308
+ before(:each) do
309
+ class Author
310
+ include DataMapper::Resource
311
+ property :id, Serial
312
+ property :name, String
313
+
314
+ provides_actionstamps
315
+
316
+ has n, :articles, 'Article', :parent_key => [:id], :child_key => [:created_by_id]
317
+ has n, :authored_articles, 'Article', :parent_key => [:id], :child_key => [:created_by_id]
318
+ has n, :updated_articles, 'Article', :parent_key => [:id], :child_key => [:updated_by_id]
319
+ end
320
+
321
+ class Article
322
+ include DataMapper::Resource
323
+ property :id, Serial
324
+ property :title, String
325
+
326
+ actionstamps :by_id, Author
327
+
328
+ belongs_to :author, 'Author', :parent_key => [:id], :child_key => [:created_by_id]
329
+ belongs_to :updater, 'Author', :parent_key => [:id], :child_key => [:updated_by_id]
330
+ end
331
+
332
+ DataMapper.auto_migrate!
333
+
334
+ @author_joe = Author.create(:id => 22, :name => "Joe")
335
+ @author_jane = Author.create(:id => 33, :name => "Jane")
336
+
337
+ Author.current_author = @author_joe
338
+
339
+ @article1 = Article.create(:title => "Article 1")
340
+ @article2 = Article.create(:title => "Article 2")
341
+ @article3 = Article.create(:title => "Article 3")
342
+
343
+ Author.current_author = @author_jane
344
+ @article4 = Article.create(:title => "Article 4")
345
+ @article5 = Article.create(:title => "Article 5")
346
+ @article6 = Article.create(:title => "Article 6")
347
+
348
+ # update
349
+ @article3.title = "Article 3 updated" # set updater to Jane
350
+ @article3.save
351
+
352
+ end
353
+
354
+ describe "Author" do
355
+
356
+ describe "has n :authored_articles" do
357
+
358
+ it "should respond to :authored_articles" do
359
+ @author_joe.should respond_to(:authored_articles)
360
+ end
361
+
362
+ it "should return all the articles created by the author" do
363
+ @author_joe.authored_articles.map(&:id).should == [1,2,3]
364
+ @author_jane.authored_articles.map(&:id).should == [4,5,6]
365
+ end
366
+
367
+ end #/ has n :authored_articles
368
+
369
+ describe "has n :updated_articles" do
370
+
371
+ it "should respond to :updated_articles" do
372
+ @author_joe.should respond_to(:updated_articles)
373
+ end
374
+
375
+ it "should return all the articles updated by author" do
376
+ @author_joe.updated_articles.map(&:id).should == [1,2]
377
+ @author_jane.updated_articles.map(&:id).should == [3,4,5,6]
378
+ end
379
+
380
+ end #/ has n :updated_articles
381
+
382
+ describe "has n :articles" do
383
+
384
+ it "should respond to :articles" do
385
+ @author_joe.should respond_to(:articles)
386
+ end
387
+
388
+ it "should return all the articles created by the author" do
389
+ @author_joe.articles.map(&:id).should == [1,2,3]
390
+ @author_jane.articles.map(&:id).should == [4,5,6]
391
+ end
392
+
393
+ end #/ has n :articles
394
+
395
+ end #/ Author
396
+
397
+ describe "Article" do
398
+
399
+ describe "belongs_to :author" do
400
+
401
+ it "should respond to :author" do
402
+ @article1.should respond_to(:author)
403
+ end
404
+
405
+ it "should return the author" do
406
+ @article1.author.should == @author_joe
407
+ @article4.author.should == @author_jane
408
+ end
409
+
410
+ end #/ belongs_to :author
411
+
412
+ describe "belongs_to :updater" do
413
+
414
+ it "should respond to :updater" do
415
+ @article1.should respond_to(:updater)
416
+ end
417
+
418
+ it "should return the updater" do
419
+ @article1.updater.should == @author_joe
420
+ @article3.updater.should == @author_jane
421
+ end
422
+
423
+ end #/ belongs_to :author
424
+
425
+ end #/ Article
426
+
427
+ end #/ when using :created_by_id
428
+
429
+ describe "when using :created_by" do
430
+
431
+ before(:each) do
432
+ class Author
433
+ include DataMapper::Resource
434
+ property :id, Serial
435
+ property :name, String
436
+
437
+ provides_actionstamps
438
+
439
+ has n, :articles, 'Article', :parent_key => [:id], :child_key => [:created_by]
440
+ has n, :authored_articles, 'Article', :parent_key => [:id], :child_key => [:created_by]
441
+ has n, :updated_articles, 'Article', :parent_key => [:id], :child_key => [:updated_by]
442
+ end
443
+
444
+ class Article
445
+ include DataMapper::Resource
446
+ property :id, Serial
447
+ property :title, String
448
+
449
+ actionstamps :by, Author
450
+
451
+ belongs_to :author, 'Author', :parent_key => [:id], :child_key => [:created_by]
452
+ belongs_to :updater, 'Author', :parent_key => [:id], :child_key => [:updated_by]
453
+ end
454
+
455
+ DataMapper.auto_migrate!
456
+
457
+ @author_joe = Author.create(:id => 22, :name => "Joe")
458
+ @author_jane = Author.create(:id => 33, :name => "Jane")
459
+
460
+ Author.current_author = @author_joe
461
+
462
+ @article1 = Article.create(:title => "Article 1")
463
+ @article2 = Article.create(:title => "Article 2")
464
+ @article3 = Article.create(:title => "Article 3")
465
+
466
+ Author.current_author = @author_jane
467
+ @article4 = Article.create(:title => "Article 4")
468
+ @article5 = Article.create(:title => "Article 5")
469
+ @article6 = Article.create(:title => "Article 6")
470
+
471
+ # update
472
+ @article3.title = "Article 3 updated" # set updater to Jane
473
+ @article3.save
474
+
475
+ end
476
+
477
+ describe "Author" do
478
+
479
+ describe "has n :authored_articles" do
480
+
481
+ it "should respond to :authored_articles" do
482
+ @author_joe.should respond_to(:authored_articles)
483
+ end
484
+
485
+ it "should return all the articles created by the author" do
486
+ @author_joe.authored_articles.map(&:id).should == [1,2,3]
487
+ @author_jane.authored_articles.map(&:id).should == [4,5,6]
488
+ end
489
+
490
+ end #/ has n :authored_articles
491
+
492
+ describe "has n :updated_articles" do
493
+
494
+ it "should respond to :updated_articles" do
495
+ @author_joe.should respond_to(:updated_articles)
496
+ end
497
+
498
+ it "should return all the articles updated by author" do
499
+ @author_joe.updated_articles.map(&:id).should == [1,2]
500
+ @author_jane.updated_articles.map(&:id).should == [3,4,5,6]
501
+ end
502
+
503
+ end #/ has n :updated_articles
504
+
505
+ describe "has n :articles" do
506
+
507
+ it "should respond to :articles" do
508
+ @author_joe.should respond_to(:articles)
509
+ end
510
+
511
+ it "should return all the articles created by the author" do
512
+ @author_joe.articles.map(&:id).should == [1,2,3]
513
+ @author_jane.articles.map(&:id).should == [4,5,6]
514
+ end
515
+
516
+ end #/ has n :articles
517
+
518
+ end #/ Author
519
+
520
+ describe "Article" do
521
+
522
+ describe "belongs_to :author" do
523
+
524
+ it "should respond to :author" do
525
+ @article1.should respond_to(:author)
526
+ end
527
+
528
+ it "should return the author" do
529
+ @article1.author.should == @author_joe
530
+ @article4.author.should == @author_jane
531
+ end
532
+
533
+ end #/ belongs_to :author
534
+
535
+ describe "belongs_to :updater" do
536
+
537
+ it "should respond to :updater" do
538
+ @article1.should respond_to(:updater)
539
+ end
540
+
541
+ it "should return the updater" do
542
+ @article1.updater.should == @author_joe
543
+ @article3.updater.should == @author_jane
544
+ end
545
+
546
+ end #/ belongs_to :author
547
+
548
+ end #/ Article
549
+
550
+ end #/ when using :created_by
551
+
552
+ end #/ Associations
553
+
554
+
555
+ describe "Client Model" do
556
+
557
+ before(:each) do
558
+ class Client
559
+ include DataMapper::Resource
560
+ property :id, Serial
561
+ property :name, String
562
+
563
+ provides_actionstamps
564
+ end
565
+
566
+ end
567
+ %w(current_client current_client=).each do |m|
568
+ it "should respond to :#{m}" do
569
+ Client.should respond_to(m.to_sym)
570
+ end
571
+ end
572
+
573
+ end #/ Client Model
574
+
575
+
576
+ end #/ DataMapper::Actionstamps
@@ -0,0 +1,29 @@
1
+
2
+ require 'rubygems'
3
+ require 'spec'
4
+ require 'pathname'
5
+ require Pathname(__FILE__).dirname.parent.expand_path + 'lib/dm-actionstamps'
6
+ require 'dm-core'
7
+
8
+ def load_driver(name, default_uri)
9
+ return false if ENV['ADAPTER'] != name.to_s
10
+
11
+ lib = "do_#{name}"
12
+
13
+ begin
14
+ gem lib, '>=0.10.1'
15
+ require lib
16
+ DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
17
+ DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
18
+ true
19
+ rescue Gem::LoadError => e
20
+ warn "Could not load #{lib}: #{e}"
21
+ false
22
+ end
23
+ end
24
+
25
+ ENV['ADAPTER'] ||= 'sqlite3'
26
+
27
+ HAS_SQLITE3 = load_driver(:sqlite3, 'sqlite3::memory:')
28
+ HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
29
+ HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-actionstamps
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - kematzy
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-05-17 00:00:00 +08:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 9
31
+ version: 1.2.9
32
+ type: :development
33
+ version_requirements: *id001
34
+ description: A DataMapper plugin that works similar to the dm-timestamps in that it 'automagically' adds/updates the created_?, updated_? fields of your models.
35
+ email: kematzy@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - LICENSE
42
+ - README.rdoc
43
+ files:
44
+ - .document
45
+ - .gitignore
46
+ - LICENSE
47
+ - README.rdoc
48
+ - Rakefile
49
+ - VERSION
50
+ - dm-actionstamps.gemspec
51
+ - lib/dm-actionstamps.rb
52
+ - lib/dm-actionstamps/actionstamps.rb
53
+ - lib/dm-actionstamps/version.rb
54
+ - spec/integration/actionstamps_spec.rb
55
+ - spec/spec_helper.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/kematzy/dm-actionstamps
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ segments:
70
+ - 0
71
+ version: "0"
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ requirements: []
80
+
81
+ rubyforge_project:
82
+ rubygems_version: 1.3.6
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: A DataMapper plugin that automatically adds/updates the created_?, updated_? fields of your models.
86
+ test_files:
87
+ - spec/integration/actionstamps_spec.rb
88
+ - spec/spec_helper.rb