dm-actionstamps-jwr 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -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.
data/README.rdoc ADDED
@@ -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
+
data/Rakefile ADDED
@@ -0,0 +1,89 @@
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 few fixes to kematzy's dm-actionstamps 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 = "joelwreed@gmail.com"
11
+ gem.homepage = "http://github.com/joelwreed/dm-actionstamps"
12
+ gem.authors = ["joelwreed", "kematzy"]
13
+ gem.add_dependency "dm-core", ">= 1.0"
14
+ gem.add_development_dependency "rspec", ">= 1.3.0"
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_opts = ["--color", "--format", "nested", "--require", "spec/spec_helper.rb"]
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ namespace :spec do
36
+
37
+ desc "Run all specifications verbosely"
38
+ Spec::Rake::SpecTask.new(:quiet) do |t|
39
+ t.libs << "lib"
40
+ t.spec_opts = ["--color", "--require", "spec/spec_helper.rb"]
41
+ t.spec_files = FileList['spec/**/*_spec.rb']
42
+ end
43
+
44
+ desc "Run specific spec verbosely (SPEC=/path/2/file)"
45
+ Spec::Rake::SpecTask.new(:select) do |t|
46
+ t.libs << "lib"
47
+ t.spec_opts = ["--color", "--format", "specdoc", "--require", "spec/spec_helper.rb"]
48
+ t.spec_files = [ENV["SPEC"]]
49
+ end
50
+
51
+ end
52
+
53
+ task :spec => :check_dependencies
54
+
55
+ task :default => :spec
56
+
57
+ require 'rake/rdoctask'
58
+ Rake::RDocTask.new do |rdoc|
59
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
60
+
61
+ rdoc.rdoc_dir = 'rdoc'
62
+ rdoc.title = "DM::ActionStamps #{version}"
63
+ rdoc.rdoc_files.include('README*')
64
+ rdoc.rdoc_files.include('lib/**/*.rb')
65
+ end
66
+
67
+ desc 'Build the rdoc HTML Files'
68
+ task :docs do
69
+ version = File.exist?('VERSION') ? IO.read('VERSION').chomp : "[Unknown]"
70
+
71
+ sh "sdoc -N --title 'DM::ActionStamps v#{version}' lib/ README.rdoc"
72
+ end
73
+
74
+
75
+ namespace :docs do
76
+
77
+ # desc 'Remove rdoc products'
78
+ # task :remove => [:clobber_rdoc]
79
+ #
80
+ # desc 'Force a rebuild of the RDOC files'
81
+ # task :rebuild => [:rerdoc]
82
+
83
+ desc 'Build docs, and open in browser for viewing (specify BROWSER)'
84
+ task :open => [:docs] do
85
+ browser = ENV["BROWSER"] || "safari"
86
+ sh "open -a #{browser} doc/index.html"
87
+ end
88
+
89
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.2
@@ -0,0 +1,59 @@
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-jwr}
8
+ s.version = "0.0.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["kematzy", "jreed"]
12
+ s.date = %q{2013-05-30}
13
+ s.description = %q{A few fixes to kematzy's 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{joelwreed@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/joelwreed/dm-actionstamps}
34
+ s.rdoc_options = ["--charset=UTF-8"]
35
+ s.require_paths = ["lib"]
36
+ s.rubygems_version = %q{1.3.7}
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::VERSION) >= Gem::Version.new('1.2.0') then
48
+ s.add_runtime_dependency(%q<dm-core>, [">= 1.0"])
49
+ s.add_development_dependency(%q<rspec>, [">= 1.3.0"])
50
+ else
51
+ s.add_dependency(%q<dm-core>, [">= 1.0"])
52
+ s.add_dependency(%q<rspec>, [">= 1.3.0"])
53
+ end
54
+ else
55
+ s.add_dependency(%q<dm-core>, [">= 1.0"])
56
+ s.add_dependency(%q<rspec>, [">= 1.3.0"])
57
+ end
58
+ end
59
+
@@ -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 'dm-actionstamps/actionstamps.rb'
12
+
13
+ DataMapper::Model.append_extensions(DataMapper::Actionstamps)
14
+ # DataMapper::Model.append_inclusions(DataMapper::Actionstamps)
15
+
16
+
@@ -0,0 +1,151 @@
1
+
2
+ require 'dm-core'
3
+
4
+ module DataMapper
5
+ module Actionstamps
6
+
7
+ ##
8
+ # Provider method, used to setup the +current_user+ information.
9
+ #
10
+ # ==== Examples
11
+ #
12
+ # class User
13
+ # <snip...>
14
+ #
15
+ # provides_actionstamps
16
+ #
17
+ # end
18
+ #
19
+ # => creates the :current_user & :current_user= methods
20
+ #
21
+ # class Client
22
+ # <snip...>
23
+ #
24
+ # provides_actionstamps
25
+ #
26
+ # end
27
+ #
28
+ # => creates the :current_client & :current_client= methods
29
+ #
30
+ #
31
+ # @api public
32
+ def provides_actionstamps
33
+ @actionstamps_class = self
34
+
35
+ extend DataMapper::Actionstamps::ClassMethods
36
+
37
+ class_eval(<<-END, __FILE__, __LINE__)
38
+ def self.current_#{name.downcase}=(user)
39
+ Thread.current["#{name.downcase}_#{self.object_id}_actionstamp"] = user
40
+ end
41
+ def self.current_#{name.downcase}
42
+ Thread.current["#{name.downcase}_#{self.object_id}_actionstamp"]
43
+ end
44
+ END
45
+ end
46
+
47
+
48
+ ##
49
+ # The Receiver Model receives the actionstamps and defines the
50
+ #
51
+ # ==== Examples
52
+ #
53
+ # class Bill
54
+ # include DataMapper::Resource
55
+ # property :id, Serial
56
+ # property :amount, Integer
57
+ # ...<snip>
58
+ #
59
+ # actionstamps :by, Client
60
+ #
61
+ # end
62
+ #
63
+ # => creates the :created_by and :updated_by fields.
64
+ #
65
+ # actionstamps :by_id, Author
66
+ #
67
+ #
68
+ # @api public/private
69
+ def actionstamps(*args)
70
+ # set default args if none passed in
71
+ args = [:by, ::User ] if args.empty?
72
+ # if invalid args, just bail out
73
+ if ( args[0].is_a?(Hash) || nil ) || ( args[1].is_a?(Hash) || args[1].is_a?( Symbol) || args[1].is_a?(String) || nil )
74
+ raise ArgumentError, "Invalid arguments passed: syntax: actionstamps :by, ModelName"
75
+ end
76
+
77
+ configs = { :suffix => args[0], :model => args[1] }
78
+
79
+ # do we have the fields :created_? / :updated_? declared, if not we declare them
80
+ if properties.any? { |p| p.name.to_s =~ /^(created|updated)_#{Regexp.escape(configs[:suffix].to_s)}$/ }
81
+ # if they are declared then we use them
82
+ 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."
83
+ else
84
+ property "created_#{configs[:suffix]}".to_sym, Integer#, :default => 1
85
+ property "updated_#{configs[:suffix]}".to_sym, Integer#, :default => 1
86
+ end
87
+
88
+ @actionstamps_receiver_properties = ["created_#{configs[:suffix]}".to_sym, "updated_#{configs[:suffix]}".to_sym ]
89
+ @actionstamps_model_class = configs[:model].to_s.capitalize
90
+
91
+ extend DataMapper::Actionstamps::ClassMethods
92
+ include DataMapper::Actionstamps::ReceiverMethods
93
+
94
+
95
+ class_eval(<<-END, __FILE__, __LINE__)
96
+ def set_actionstamps
97
+ 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?
98
+ self.updated_#{configs[:suffix].to_s} = #{configs[:model]}.current_#{configs[:model].to_s.downcase}.id if #{configs[:model]}.current_#{configs[:model].to_s.downcase}
99
+ end
100
+ END
101
+
102
+ end #/def actionstamps
103
+
104
+ module ClassMethods
105
+ attr_reader :actionstamps_class
106
+ end #/module ClassMethods
107
+
108
+
109
+ module ReceiverMethods
110
+
111
+ def self.included(model)
112
+ model.before :save, :set_actionstamps_on_save
113
+ model.extend DataMapper::Actionstamps::ClassMethods
114
+ end
115
+
116
+ ##
117
+ # Saves the record with the created_? / updated_? attributes set to the current time.
118
+ #
119
+ # ==== Examples
120
+ #
121
+ # @model.touch
122
+ #
123
+ # @api public
124
+ def touch
125
+ set_actionstamps
126
+ save
127
+ end
128
+
129
+
130
+ private
131
+
132
+ ##
133
+ # Callback method
134
+ #
135
+ # ==== Examples
136
+ #
137
+ # before :save, :set_actionstamps_on_save
138
+ #
139
+ #
140
+ # @api public
141
+ def set_actionstamps_on_save
142
+ return unless dirty?
143
+ set_actionstamps
144
+ end
145
+
146
+
147
+ end #/module ReceiverMethods
148
+
149
+ end #/module Actionstamps
150
+
151
+ end #/module DataMapper
@@ -0,0 +1,5 @@
1
+ module DataMapper
2
+ module Actionstamps
3
+ VERSION = "0.0.3"
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,30 @@
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
+ require 'dm-migrations'
8
+
9
+ def load_driver(name, default_uri)
10
+ return false if ENV['ADAPTER'] != name.to_s
11
+
12
+ lib = "dm-#{name}-adapter"
13
+
14
+ begin
15
+ gem lib, '>=1.0'
16
+ require lib
17
+ DataMapper.setup(name, ENV["#{name.to_s.upcase}_SPEC_URI"] || default_uri)
18
+ DataMapper::Repository.adapters[:default] = DataMapper::Repository.adapters[name]
19
+ true
20
+ rescue Gem::LoadError => e
21
+ warn "Could not load #{lib}: #{e}"
22
+ false
23
+ end
24
+ end
25
+
26
+ ENV['ADAPTER'] ||= 'sqlite'
27
+
28
+ HAS_SQLITE3 = load_driver(:sqlite, 'sqlite3::memory:')
29
+ HAS_MYSQL = load_driver(:mysql, 'mysql://localhost/dm_core_test')
30
+ HAS_POSTGRES = load_driver(:postgres, 'postgres://postgres@localhost/dm_core_test')
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dm-actionstamps-jwr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - kematzy
9
+ - jreed
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-05-30 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: dm-core
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '1.0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '1.0'
31
+ - !ruby/object:Gem::Dependency
32
+ name: rspec
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: 1.3.0
39
+ type: :development
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: 1.3.0
47
+ description: A few fixes to kematzy's DataMapper plugin that works similar to the
48
+ dm-timestamps in that it 'automagically' adds/updates the created_?, updated_? fields
49
+ of your models.
50
+ email: joelwreed@gmail.com
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files:
54
+ - LICENSE
55
+ - README.rdoc
56
+ files:
57
+ - .document
58
+ - .gitignore
59
+ - LICENSE
60
+ - README.rdoc
61
+ - Rakefile
62
+ - VERSION
63
+ - dm-actionstamps.gemspec
64
+ - lib/dm-actionstamps.rb
65
+ - lib/dm-actionstamps/actionstamps.rb
66
+ - lib/dm-actionstamps/version.rb
67
+ - spec/integration/actionstamps_spec.rb
68
+ - spec/spec_helper.rb
69
+ homepage: http://github.com/joelwreed/dm-actionstamps
70
+ licenses: []
71
+ post_install_message:
72
+ rdoc_options:
73
+ - --charset=UTF-8
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 1.8.23
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: A DataMapper plugin that automatically adds/updates the created_?, updated_?
94
+ fields of your models.
95
+ test_files:
96
+ - spec/integration/actionstamps_spec.rb
97
+ - spec/spec_helper.rb