dm-actionstamps 0.0.1

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