userstamp 2.0.0

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.
Files changed (56) hide show
  1. data/.gitignore +2 -0
  2. data/CHANGELOG +26 -0
  3. data/LICENSE +20 -0
  4. data/README +177 -0
  5. data/Rakefile +38 -0
  6. data/VERSION +1 -0
  7. data/init.rb +1 -0
  8. data/lib/migration_helper.rb +19 -0
  9. data/lib/stampable.rb +151 -0
  10. data/lib/stamper.rb +43 -0
  11. data/lib/userstamp.rb +48 -0
  12. data/rdoc/classes/Ddb/Controller.html +111 -0
  13. data/rdoc/classes/Ddb/Controller/Userstamp.html +125 -0
  14. data/rdoc/classes/Ddb/Controller/Userstamp/InstanceMethods.html +105 -0
  15. data/rdoc/classes/Ddb/Userstamp.html +121 -0
  16. data/rdoc/classes/Ddb/Userstamp/MigrationHelper.html +111 -0
  17. data/rdoc/classes/Ddb/Userstamp/MigrationHelper/InstanceMethods.html +142 -0
  18. data/rdoc/classes/Ddb/Userstamp/Stampable.html +128 -0
  19. data/rdoc/classes/Ddb/Userstamp/Stampable/ClassMethods.html +222 -0
  20. data/rdoc/classes/Ddb/Userstamp/Stamper.html +112 -0
  21. data/rdoc/classes/Ddb/Userstamp/Stamper/ClassMethods.html +142 -0
  22. data/rdoc/classes/Ddb/Userstamp/Stamper/InstanceMethods.html +207 -0
  23. data/rdoc/created.rid +1 -0
  24. data/rdoc/files/CHANGELOG.html +137 -0
  25. data/rdoc/files/LICENSE.html +129 -0
  26. data/rdoc/files/README.html +341 -0
  27. data/rdoc/files/lib/migration_helper_rb.html +101 -0
  28. data/rdoc/files/lib/stampable_rb.html +101 -0
  29. data/rdoc/files/lib/stamper_rb.html +101 -0
  30. data/rdoc/files/lib/userstamp_rb.html +101 -0
  31. data/rdoc/fr_class_index.html +37 -0
  32. data/rdoc/fr_file_index.html +33 -0
  33. data/rdoc/fr_method_index.html +33 -0
  34. data/rdoc/index.html +24 -0
  35. data/rdoc/rdoc-style.css +208 -0
  36. data/test/compatibility_stamping_test.rb +63 -0
  37. data/test/controllers/posts_controller.rb +26 -0
  38. data/test/controllers/users_controller.rb +12 -0
  39. data/test/controllers/userstamp_controller.rb +9 -0
  40. data/test/database.yml +4 -0
  41. data/test/fixtures/comments.yml +16 -0
  42. data/test/fixtures/people.yml +11 -0
  43. data/test/fixtures/posts.yml +9 -0
  44. data/test/fixtures/users.yml +7 -0
  45. data/test/helpers/functional_test_helper.rb +37 -0
  46. data/test/helpers/unit_test_helper.rb +29 -0
  47. data/test/models/comment.rb +4 -0
  48. data/test/models/person.rb +3 -0
  49. data/test/models/ping.rb +7 -0
  50. data/test/models/post.rb +4 -0
  51. data/test/models/user.rb +3 -0
  52. data/test/schema.rb +56 -0
  53. data/test/stamping_test.rb +110 -0
  54. data/test/userstamp_controller_test.rb +118 -0
  55. data/userstamp.gemspec +107 -0
  56. metadata +123 -0
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.log
2
+ *.sqlite3
data/CHANGELOG ADDED
@@ -0,0 +1,26 @@
1
+ 2.0 (2-17-2008)
2
+ * [Ben Wyrosdick] - Added a migration helper that gives migration scripts a <tt>userstamps</tt>
3
+ method.
4
+ * [Marshall Roch] - Stamping can be temporarily turned off using the 'without_stamps' class
5
+ method.
6
+ Example:
7
+ Post.without_stamps do
8
+ post = Post.find(params[:id])
9
+ post.update_attributes(params[:post])
10
+ post.save
11
+ end
12
+
13
+ * Models that should receive updates made by 'stampers' now use the acts_as_stampable class
14
+ method. This sets up the belongs_to relationships and also injects private methods for use by
15
+ the individual callback filter methods.
16
+
17
+ * Models that are responsible for updating now use the acts_as_stamper class method. This
18
+ injects the stamper= and stamper methods that are thread safe and should be updated per
19
+ request by a controller.
20
+
21
+ * The Userstamp module is now meant to be included with one of your project's controllers (the
22
+ Application Controller is recommended). It creates a before filter called 'set_stampers' that
23
+ is responsible for setting all the current Stampers.
24
+
25
+ 1.0 (01-18-2006)
26
+ * Initial Release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-2008 DeLynn Berry
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 ADDED
@@ -0,0 +1,177 @@
1
+ = Userstamp Plugin (v 2.0)
2
+
3
+ == Overview
4
+
5
+ The Userstamp Plugin extends ActiveRecord::Base[http://api.rubyonrails.com/classes/ActiveRecord/Base.html] to add automatic updating of 'creator',
6
+ 'updater', and 'deleter' attributes. It is based loosely on the ActiveRecord::Timestamp[http://api.rubyonrails.com/classes/ActiveRecord/Timestamp.html] module.
7
+
8
+ Two class methods (<tt>model_stamper</tt> and <tt>stampable</tt>) are implemented in this plugin.
9
+ The <tt>model_stamper</tt> method is used in models that are responsible for creating, updating, or
10
+ deleting other objects. The <tt>stampable</tt> method is used in models that are subject to being
11
+ created, updated, or deleted by 'stampers'.
12
+
13
+
14
+ == Installation
15
+ - As Rails plugin: `script/plugin install git://github.com/delynn/userstamp.git `
16
+ - As gem: ` sudo gem install userstamp -s http://gemcutter.org `
17
+
18
+ Once installed you will need to restart your application for the plugin to be loaded into the Rails
19
+ environment.
20
+
21
+ You might also be interested in using Piston[http://piston.rubyforge.org/index.html] to manage the
22
+ importing and future updating of this plugin.
23
+
24
+ == Usage
25
+ In this new version of the Userstamp plug-in, the assumption is that you have two different
26
+ categories of objects; those that mani˝pulate, and those that are manipulated. For those objects
27
+ that are being manipulated there's the Stampable module and for the manipulators there's the
28
+ Stamper module. There's also the actual Userstamp module for your controllers that assists in
29
+ setting up your environment on a per request basis.
30
+
31
+ To better understand how all this works, I think an example is in order. For this example we will
32
+ assume that a weblog application is comprised of User and Post objects. The first thing we need to
33
+ do is create the migrations for these objects, and the plug-in gives you a <tt>userstamps</tt>
34
+ method for very easily doing this:
35
+
36
+ class CreateUsers < ActiveRecord::Migration
37
+ def self.up
38
+ create_table :users, :force => true do |t|
39
+ t.timestamps
40
+ t.userstamps
41
+ t.name
42
+ end
43
+ end
44
+
45
+ def self.down
46
+ drop_table :users
47
+ end
48
+ end
49
+
50
+ class CreatePosts < ActiveRecord::Migration
51
+ def self.up
52
+ create_table :posts, :force => true do |t|
53
+ t.timestamps
54
+ t.userstamps
55
+ t.title
56
+ end
57
+ end
58
+
59
+ def self.down
60
+ drop_table :posts
61
+ end
62
+ end
63
+
64
+ Second, since Users are going to manipulate other objects in our project, we'll use the
65
+ <tt>model_stamper</tt> method in our User class:
66
+
67
+ class User < ActiveRecord::Base
68
+ model_stamper
69
+ end
70
+
71
+ Finally, we need to setup a controller to set the current user of the application. It's
72
+ recommended that you do this in your ApplicationController:
73
+
74
+ class ApplicationController < ActionController::Base
75
+ include Userstamp
76
+ end
77
+
78
+ If all you are interested in is making sure all tables that have the proper columns are stamped
79
+ by the currently logged in user you can stop right here. More than likely you want all your
80
+ associations setup on your stamped objects, and that's where the <tt>stampable</tt> class method
81
+ comes in. So in our example we'll want to use this method in both our User and Post classes:
82
+
83
+ class User < ActiveRecord::Base
84
+ model_stamper
85
+ stampable
86
+ end
87
+
88
+ class Post < ActiveRecord::Base
89
+ stampable
90
+ end
91
+
92
+ Okay, so what all have we done? The <tt>model_stamper</tt> class method injects two methods into the
93
+ User class. They are #stamper= and #stamper and look like this:
94
+
95
+ def stamper=(object)
96
+ object_stamper = if object.is_a?(ActiveRecord::Base)
97
+ object.send("#{object.class.primary_key}".to_sym)
98
+ else
99
+ object
100
+ end
101
+
102
+ Thread.current["#{self.to_s.downcase}_#{self.object_id}_stamper"] = object_stamper
103
+ end
104
+
105
+ def stamper
106
+ Thread.current["#{self.to_s.downcase}_#{self.object_id}_stamper"]
107
+ end
108
+
109
+ The big change with this new version is that we are now using Thread.current to save the current
110
+ stamper so as to avoid conflict with concurrent requests.
111
+
112
+ The <tt>stampable</tt> method allows you to customize what columns will get stamped, and also
113
+ creates the +creator+, +updater+, and +deleter+ associations.
114
+
115
+ The Userstamp module that we included into our ApplicationController uses the setter method to
116
+ set which user is currently making the request. By default the 'set_stampers' method works perfectly
117
+ with the RestfulAuthentication[http://svn.techno-weenie.net/projects/plugins/restful_authentication] plug-in:
118
+
119
+ def set_stampers
120
+ User.stamper = self.current_user
121
+ end
122
+
123
+ If you aren't using ActsAsAuthenticated, then you need to create your own version of the
124
+ <tt>set_stampers</tt> method in the controller where you've included the Userstamp module.
125
+
126
+ Now, let's get back to the Stampable module (since it really is the interesting one). The Stampable
127
+ module sets up before_* filters that are responsible for setting those attributes at the appropriate
128
+ times. It also creates the belongs_to relationships for you.
129
+
130
+ If you need to customize the columns that are stamped, the <tt>stampable</tt> method can be
131
+ completely customized. Here's an quick example:
132
+
133
+ class Post < ActiveRecord::Base
134
+ acts_as_stampable :stamper_class_name => :person,
135
+ :creator_attribute => :create_user,
136
+ :updater_attribute => :update_user,
137
+ :deleter_attribute => :delete_user
138
+ end
139
+
140
+ If you are upgrading your application from the old version of Userstamp, there is a compatibility
141
+ mode to have the plug-in use the old "_by" columns by default. To enable this mode, add the
142
+ following line to the RAILS_ROOT/config/environment.rb file:
143
+
144
+ Ddb::Userstamp.compatibility_mode = true
145
+
146
+ If you are having a difficult time getting the Userstamp plug-in to work, I recommend you checkout
147
+ the sample application that I created. You can find this application on GitHub[http://github.com/delynn/userstamp_sample]
148
+
149
+ == Uninstall
150
+ Uninstalling the plugin can be done using the built in Rails plugin script. Issue the following
151
+ command from the root of your application:
152
+
153
+ script/plugin remove userstamp
154
+
155
+
156
+ == Documentation
157
+ RDoc has been run on the plugin directory and is available in the doc directory.
158
+
159
+
160
+ == Running Unit Tests
161
+ There are extensive unit tests in the "test" directory of the plugin. These test can be run
162
+ individually by executing the following command from the userstamp directory:
163
+
164
+ ruby test/compatibility_stamping_test.rb
165
+ ruby test/stamping_test.rb
166
+ ruby test/userstamp_controller_test.rb
167
+
168
+
169
+ == Bugs & Feedback
170
+ Bug reports and feedback are welcome via my delynn+userstamp@gmail.com email address. I also
171
+ encouraged everyone to clone the git repository and make modifications--I'll be more than happy
172
+ to merge any changes from other people's branches that would be beneficial to the whole project.
173
+
174
+
175
+ == Credits and Special Thanks
176
+ The original idea for this plugin came from the Rails Wiki article entitled
177
+ {Extending ActiveRecord}[http://wiki.rubyonrails.com/rails/pages/ExtendingActiveRecordExample].
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ desc 'Default: run unit tests.'
6
+ task :default => :test
7
+
8
+ desc 'Test the userstamp plugin.'
9
+ Rake::TestTask.new(:test) do |t|
10
+ t.libs << 'lib'
11
+ t.pattern = 'test/**/*_test.rb'
12
+ t.verbose = true
13
+ end
14
+
15
+ desc 'Generate documentation for the userstamp plugin.'
16
+ Rake::RDocTask.new(:rdoc) do |rdoc|
17
+ rdoc.rdoc_dir = 'rdoc'
18
+ rdoc.title = 'Userstamp'
19
+ rdoc.options << '--line-numbers' << '--inline-source'
20
+ rdoc.rdoc_files.include('README', 'CHANGELOG', 'LICENSE')
21
+ rdoc.rdoc_files.include('lib/**/*.rb')
22
+ end
23
+
24
+ begin
25
+ require 'jeweler'
26
+ project_name = 'userstamp'
27
+ Jeweler::Tasks.new do |gem|
28
+ gem.name = project_name
29
+ gem.summary = "This Rails plugin extends ActiveRecord::Base to add automatic updating of created_by and updated_by attributes of your models in much the same way that the ActiveRecord::Timestamp module updates created_(at/on) and updated_(at/on) attributes."
30
+ gem.email = "delynn@gmail.com"
31
+ gem.homepage = "http://github.com/delynn/#{project_name}"
32
+ gem.authors = ["DeLynn Berry"]
33
+ end
34
+
35
+ Jeweler::GemcutterTasks.new
36
+ rescue LoadError
37
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
38
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'userstamp'
@@ -0,0 +1,19 @@
1
+ module Ddb
2
+ module Userstamp
3
+ module MigrationHelper
4
+ def self.included(base) # :nodoc:
5
+ base.send(:include, InstanceMethods)
6
+ end
7
+
8
+ module InstanceMethods
9
+ def userstamps(include_deleted_by = false)
10
+ column(Ddb::Userstamp.compatibility_mode ? :created_by : :creator_id, :integer)
11
+ column(Ddb::Userstamp.compatibility_mode ? :updated_by : :updater_id, :integer)
12
+ column(Ddb::Userstamp.compatibility_mode ? :deleted_by : :deleter_id, :integer) if include_deleted_by
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Ddb::Userstamp::MigrationHelper)
data/lib/stampable.rb ADDED
@@ -0,0 +1,151 @@
1
+ module Ddb #:nodoc:
2
+ module Userstamp
3
+ # Determines what default columns to use for recording the current stamper.
4
+ # By default this is set to false, so the plug-in will use columns named
5
+ # <tt>creator_id</tt>, <tt>updater_id</tt>, and <tt>deleter_id</tt>.
6
+ #
7
+ # To turn compatibility mode on, place the following line in your environment.rb
8
+ # file:
9
+ #
10
+ # Ddb::Userstamp.compatibility_mode = true
11
+ #
12
+ # This will cause the plug-in to use columns named <tt>created_by</tt>,
13
+ # <tt>updated_by</tt>, and <tt>deleted_by</tt>.
14
+ mattr_accessor :compatibility_mode
15
+ @@compatibility_mode = false
16
+
17
+ # Extends the stamping functionality of ActiveRecord by automatically recording the model
18
+ # responsible for creating, updating, and deleting the current object. See the Stamper
19
+ # and Userstamp modules for further documentation on how the entire process works.
20
+ module Stampable
21
+ def self.included(base) #:nodoc:
22
+ super
23
+
24
+ base.extend(ClassMethods)
25
+ base.class_eval do
26
+ include InstanceMethods
27
+
28
+ # Should ActiveRecord record userstamps? Defaults to true.
29
+ class_inheritable_accessor :record_userstamp
30
+ self.record_userstamp = true
31
+
32
+ # Which class is responsible for stamping? Defaults to :user.
33
+ class_inheritable_accessor :stamper_class_name
34
+
35
+ # What column should be used for the creator stamp?
36
+ # Defaults to :creator_id when compatibility mode is off
37
+ # Defaults to :created_by when compatibility mode is on
38
+ class_inheritable_accessor :creator_attribute
39
+
40
+ # What column should be used for the updater stamp?
41
+ # Defaults to :updater_id when compatibility mode is off
42
+ # Defaults to :updated_by when compatibility mode is on
43
+ class_inheritable_accessor :updater_attribute
44
+
45
+ # What column should be used for the deleter stamp?
46
+ # Defaults to :deleter_id when compatibility mode is off
47
+ # Defaults to :deleted_by when compatibility mode is on
48
+ class_inheritable_accessor :deleter_attribute
49
+
50
+ self.stampable
51
+ end
52
+ end
53
+
54
+ module ClassMethods
55
+ # This method is automatically called on for all classes that inherit from
56
+ # ActiveRecord, but if you need to customize how the plug-in functions, this is the
57
+ # method to use. Here's an example:
58
+ #
59
+ # class Post < ActiveRecord::Base
60
+ # stampable :stamper_class_name => :person,
61
+ # :creator_attribute => :create_user,
62
+ # :updater_attribute => :update_user,
63
+ # :deleter_attribute => :delete_user
64
+ # end
65
+ #
66
+ # The method will automatically setup all the associations, and create <tt>before_save</tt>
67
+ # and <tt>before_create</tt> filters for doing the stamping.
68
+ def stampable(options = {})
69
+ defaults = {
70
+ :stamper_class_name => :user,
71
+ :creator_attribute => Ddb::Userstamp.compatibility_mode ? :created_by : :creator_id,
72
+ :updater_attribute => Ddb::Userstamp.compatibility_mode ? :updated_by : :updater_id,
73
+ :deleter_attribute => Ddb::Userstamp.compatibility_mode ? :deleted_by : :deleter_id
74
+ }.merge(options)
75
+
76
+ self.stamper_class_name = defaults[:stamper_class_name].to_sym
77
+ self.creator_attribute = defaults[:creator_attribute].to_sym
78
+ self.updater_attribute = defaults[:updater_attribute].to_sym
79
+ self.deleter_attribute = defaults[:deleter_attribute].to_sym
80
+
81
+ class_eval do
82
+ belongs_to :creator, :class_name => self.stamper_class_name.to_s.singularize.camelize,
83
+ :foreign_key => self.creator_attribute
84
+
85
+ belongs_to :updater, :class_name => self.stamper_class_name.to_s.singularize.camelize,
86
+ :foreign_key => self.updater_attribute
87
+
88
+ before_save :set_updater_attribute
89
+ before_create :set_creator_attribute
90
+
91
+ if defined?(Caboose::Acts::Paranoid)
92
+ belongs_to :deleter, :class_name => self.stamper_class_name.to_s.singularize.camelize,
93
+ :foreign_key => self.deleter_attribute
94
+ before_destroy :set_deleter_attribute
95
+ end
96
+ end
97
+ end
98
+
99
+ # Temporarily allows you to turn stamping off. For example:
100
+ #
101
+ # Post.without_stamps do
102
+ # post = Post.find(params[:id])
103
+ # post.update_attributes(params[:post])
104
+ # post.save
105
+ # end
106
+ def without_stamps
107
+ original_value = self.record_userstamp
108
+ self.record_userstamp = false
109
+ yield
110
+ self.record_userstamp = original_value
111
+ end
112
+
113
+ def stamper_class #:nodoc:
114
+ stamper_class_name.to_s.capitalize.constantize rescue nil
115
+ end
116
+ end
117
+
118
+ module InstanceMethods #:nodoc:
119
+ private
120
+ def has_stamper?
121
+ !self.class.stamper_class.nil? && !self.class.stamper_class.stamper.nil? rescue false
122
+ end
123
+
124
+ def set_creator_attribute
125
+ return unless self.record_userstamp
126
+ if respond_to?(self.creator_attribute.to_sym) && has_stamper?
127
+ self.send("#{self.creator_attribute}=".to_sym, self.class.stamper_class.stamper)
128
+ end
129
+ end
130
+
131
+ def set_updater_attribute
132
+ return unless self.record_userstamp
133
+ if respond_to?(self.updater_attribute.to_sym) && has_stamper?
134
+ self.send("#{self.updater_attribute}=".to_sym, self.class.stamper_class.stamper)
135
+ end
136
+ end
137
+
138
+ def set_deleter_attribute
139
+ return unless self.record_userstamp
140
+ if respond_to?(self.deleter_attribute.to_sym) && has_stamper?
141
+ self.send("#{self.deleter_attribute}=".to_sym, self.class.stamper_class.stamper)
142
+ save
143
+ end
144
+ end
145
+ #end private
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ ActiveRecord::Base.send(:include, Ddb::Userstamp::Stampable) if defined?(ActiveRecord)