userstamper 4.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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +35 -0
- data/CHANGELOG.md +116 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +222 -0
- data/LICENSE +22 -0
- data/README.md +142 -0
- data/lib/userstamper.rb +23 -0
- data/lib/userstamper/configuration.rb +40 -0
- data/lib/userstamper/controller_concern.rb +44 -0
- data/lib/userstamper/migration_concern.rb +9 -0
- data/lib/userstamper/model_concern.rb +6 -0
- data/lib/userstamper/railtie.rb +15 -0
- data/lib/userstamper/stampable.rb +106 -0
- data/lib/userstamper/stamper.rb +54 -0
- data/lib/userstamper/utilities.rb +57 -0
- data/spec/controllers/posts_controller_spec.rb +44 -0
- data/spec/controllers/users_controller_spec.rb +50 -0
- data/spec/coverage_helper.rb +58 -0
- data/spec/dummy/README.rdoc +28 -0
- data/spec/dummy/Rakefile +6 -0
- data/spec/dummy/app/assets/config/manifest.js +0 -0
- data/spec/dummy/app/assets/images/.keep +0 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +15 -0
- data/spec/dummy/app/controllers/application_controller.rb +13 -0
- data/spec/dummy/app/controllers/concerns/.keep +0 -0
- data/spec/dummy/app/controllers/posts_controller.rb +36 -0
- data/spec/dummy/app/controllers/users_controller.rb +22 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.keep +0 -0
- data/spec/dummy/app/models/comment.rb +5 -0
- data/spec/dummy/app/models/concerns/.keep +0 -0
- data/spec/dummy/app/models/person.rb +3 -0
- data/spec/dummy/app/models/post.rb +6 -0
- data/spec/dummy/app/models/tag.rb +3 -0
- data/spec/dummy/app/models/user.rb +3 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/bin/bundle +3 -0
- data/spec/dummy/bin/rails +4 -0
- data/spec/dummy/bin/rake +4 -0
- data/spec/dummy/bin/setup +29 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +12 -0
- data/spec/dummy/config/boot.rb +5 -0
- data/spec/dummy/config/database.yml +23 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +41 -0
- data/spec/dummy/config/environments/production.rb +79 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/assets.rb +11 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/spec/dummy/config/initializers/inflections.rb +16 -0
- data/spec/dummy/config/initializers/mime_types.rb +4 -0
- data/spec/dummy/config/initializers/session_store.rb +3 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +23 -0
- data/spec/dummy/config/routes.rb +56 -0
- data/spec/dummy/config/secrets.yml +22 -0
- data/spec/dummy/lib/assets/.keep +0 -0
- data/spec/dummy/log/.keep +0 -0
- data/spec/dummy/public/404.html +67 -0
- data/spec/dummy/public/422.html +67 -0
- data/spec/dummy/public/500.html +66 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/lib/configuration_spec.rb +20 -0
- data/spec/lib/migration_spec.rb +60 -0
- data/spec/lib/stamper_spec.rb +66 -0
- data/spec/lib/stamping_spec.rb +235 -0
- data/spec/lib/userstamp_spec.rb +4 -0
- data/spec/rails_helper.rb +7 -0
- data/spec/spec_helper.rb +98 -0
- data/spec/support/database_helpers.rb +73 -0
- data/spec/support/with_temporary_table.rb +51 -0
- data/userstamper.gemspec +46 -0
- metadata +279 -0
data/lib/userstamper.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/rails"
|
3
|
+
require "active_support/concern"
|
4
|
+
|
5
|
+
require "zeitwerk"
|
6
|
+
loader = Zeitwerk::Loader.for_gem
|
7
|
+
loader.setup
|
8
|
+
|
9
|
+
module Userstamper
|
10
|
+
# Retrieves the configuration for the Userstamper gem.
|
11
|
+
# @return [Userstamper::Configuration]
|
12
|
+
def self.config
|
13
|
+
Configuration
|
14
|
+
end
|
15
|
+
|
16
|
+
# Configures the gem.
|
17
|
+
# @yield [Userstamper::Configuration] The configuration for the gem.
|
18
|
+
def self.configure
|
19
|
+
yield config
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
require "userstamper/railtie" if defined?(Rails::Railtie)
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Userstamper::Configuration
|
2
|
+
# !@attribute [r] default_stamper
|
3
|
+
# Determines the default model used to stamp other models.
|
4
|
+
#
|
5
|
+
# By default, this is set to +'User'+.
|
6
|
+
def self.default_stamper
|
7
|
+
ActiveRecord::Base.stamper_class_name
|
8
|
+
end
|
9
|
+
|
10
|
+
# !@attribute [rw] default_stamper
|
11
|
+
# @see {.default_stamper}
|
12
|
+
def self.default_stamper=(stamper)
|
13
|
+
ActiveRecord::Base.stamper_class_name = stamper
|
14
|
+
end
|
15
|
+
self.default_stamper = 'User'.freeze
|
16
|
+
|
17
|
+
# @!attribute [r] default_stamper_class
|
18
|
+
# Determines the default model used to stamp other models.
|
19
|
+
def self.default_stamper_class
|
20
|
+
ActiveRecord::Base.stamper_class
|
21
|
+
end
|
22
|
+
|
23
|
+
# !@attribute [rw] creator_attribute
|
24
|
+
# Determines the name of the column in the database which stores the name of the creator.
|
25
|
+
#
|
26
|
+
# Override the attribute by using the stampable class method within a model.
|
27
|
+
#
|
28
|
+
# By default, this is set to +:creator_id+.
|
29
|
+
mattr_accessor :creator_attribute
|
30
|
+
self.creator_attribute = :creator_id
|
31
|
+
|
32
|
+
# !@attribute [rw] updater_attribute
|
33
|
+
# Determines the name of the column in the database which stores the name of the updater.
|
34
|
+
#
|
35
|
+
# Override the attribute by using the stampable class method within a model.
|
36
|
+
#
|
37
|
+
# By default, this is set to +:updater_id+.
|
38
|
+
mattr_accessor :updater_attribute
|
39
|
+
self.updater_attribute = :updater_id
|
40
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# The +Userstamper::Controller+ module, when included into a controller, adds an +before_action+
|
2
|
+
# callback (named +set_stamper+) and an +after_action+ callback (named +reset_stamper+). These
|
3
|
+
# methods assume a couple of things, but can be re-implemented in your controller to better suit
|
4
|
+
# your application.
|
5
|
+
#
|
6
|
+
# See the documentation for `set_stamper` and `reset_stamper` for specific implementation details.
|
7
|
+
module Userstamper::ControllerConcern
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
around_action :with_stamper
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
# This {#with_stamper} method sets the stamper for the duration of the action. This ensures
|
17
|
+
# that exceptions raised within the controller action would properly restore the previous stamper.
|
18
|
+
#
|
19
|
+
# TODO: Remove set_stamper/reset_stamper
|
20
|
+
def with_stamper
|
21
|
+
set_stamper
|
22
|
+
yield
|
23
|
+
ensure
|
24
|
+
reset_stamper
|
25
|
+
end
|
26
|
+
|
27
|
+
# The {#set_stamper} method as implemented here assumes a couple of things. First, that you are
|
28
|
+
# using a +User+ model as the stamper and second that your controller has a +current_user+
|
29
|
+
# method that contains the currently logged in stamper. If either of these are not the case in
|
30
|
+
# your application you will want to manually add your own implementation of this method to the
|
31
|
+
# private section of your +ApplicationController+
|
32
|
+
def set_stamper
|
33
|
+
@_userstamp_stamper = Userstamper.config.default_stamper_class.stamper
|
34
|
+
Userstamper.config.default_stamper_class.stamper = current_user
|
35
|
+
end
|
36
|
+
|
37
|
+
# The {#reset_stamper} method as implemented here assumes that a +User+ model is being used as
|
38
|
+
# the stamper. If this is not the case then you will need to manually add your own
|
39
|
+
# implementation of this method to the private section of your +ApplicationController+
|
40
|
+
def reset_stamper
|
41
|
+
Userstamper.config.default_stamper_class.stamper = @_userstamp_stamper
|
42
|
+
@_userstamp_stamper = nil
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Userstamper
|
2
|
+
class Railtie < Rails::Railtie
|
3
|
+
initializer "userstamper.action_controller" do
|
4
|
+
ActiveSupport.on_load(:action_controller_base) do
|
5
|
+
include Userstamper::ControllerConcern
|
6
|
+
end
|
7
|
+
|
8
|
+
ActiveSupport.on_load(:active_record) do
|
9
|
+
include Userstamper::ModelConcern
|
10
|
+
|
11
|
+
ActiveRecord::ConnectionAdapters::TableDefinition.send(:include, Userstamper::MigrationConcern)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Extends the stamping functionality of ActiveRecord by automatically recording the model
|
2
|
+
# responsible for creating, updating the current object. See the +Stamper+ and
|
3
|
+
# +ControllerAdditions+ modules for further documentation on how the entire process works.
|
4
|
+
module Userstamper::Stampable
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
# Should ActiveRecord record userstamps? Defaults to true.
|
9
|
+
class_attribute :record_userstamp
|
10
|
+
self.record_userstamp = true
|
11
|
+
|
12
|
+
class_attribute :stamper_class_name
|
13
|
+
|
14
|
+
before_validation :set_updater_attribute, if: :record_userstamp
|
15
|
+
before_validation :set_creator_attribute, on: :create, if: :record_userstamp
|
16
|
+
before_save :set_updater_attribute, if: :record_userstamp
|
17
|
+
before_create :set_creator_attribute, if: :record_userstamp
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
def columns(*)
|
22
|
+
columns = super
|
23
|
+
return columns if defined?(@stamper_initialized) && @stamper_initialized
|
24
|
+
|
25
|
+
add_userstamp_associations({})
|
26
|
+
columns
|
27
|
+
end
|
28
|
+
|
29
|
+
# This method customizes how the gem functions. For example:
|
30
|
+
#
|
31
|
+
# class Post < ActiveRecord::Base
|
32
|
+
# stampable stamper_class_name: Person.name
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# the gem configuration.
|
36
|
+
def stampable(options = {})
|
37
|
+
self.stamper_class_name = options.delete(:stamper_class_name) if options.key?(:stamper_class_name)
|
38
|
+
|
39
|
+
add_userstamp_associations(options)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Temporarily allows you to turn stamping off. For example:
|
43
|
+
#
|
44
|
+
# Post.without_stamps do
|
45
|
+
# post = Post.find(params[:id])
|
46
|
+
# post.update_attributes(params[:post])
|
47
|
+
# post.save
|
48
|
+
# end
|
49
|
+
def without_stamps
|
50
|
+
original_value = self.record_userstamp
|
51
|
+
self.record_userstamp = false
|
52
|
+
yield
|
53
|
+
ensure
|
54
|
+
self.record_userstamp = original_value
|
55
|
+
end
|
56
|
+
|
57
|
+
def stamper_class #:nodoc:
|
58
|
+
stamper_class_name.to_s.camelize.constantize rescue nil
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Defines the associations for Userstamper.
|
64
|
+
def add_userstamp_associations(options)
|
65
|
+
@stamper_initialized = true
|
66
|
+
Userstamper::Utilities.remove_association(self, :creator)
|
67
|
+
Userstamper::Utilities.remove_association(self, :updater)
|
68
|
+
|
69
|
+
associations = Userstamper::Utilities.available_association_columns(self)
|
70
|
+
return if associations.nil?
|
71
|
+
|
72
|
+
config = Userstamper.config
|
73
|
+
klass = stamper_class.try(:name)
|
74
|
+
relation_options = options.reverse_merge(class_name: klass)
|
75
|
+
|
76
|
+
belongs_to :creator, relation_options.reverse_merge(foreign_key: config.creator_attribute, required: false) if associations.first
|
77
|
+
belongs_to :updater, relation_options.reverse_merge(foreign_key: config.updater_attribute, required: false) if associations.second
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
private
|
82
|
+
|
83
|
+
def has_stamper?
|
84
|
+
!self.class.stamper_class.nil? && !self.class.stamper_class.stamper.nil?
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_creator_attribute
|
88
|
+
return unless has_stamper?
|
89
|
+
|
90
|
+
creator_association = self.class.reflect_on_association(:creator)
|
91
|
+
return unless creator_association
|
92
|
+
return if creator.present?
|
93
|
+
|
94
|
+
Userstamper::Utilities.assign_stamper(self, creator_association)
|
95
|
+
end
|
96
|
+
|
97
|
+
def set_updater_attribute
|
98
|
+
return unless has_stamper?
|
99
|
+
|
100
|
+
updater_association = self.class.reflect_on_association(:updater)
|
101
|
+
return unless updater_association
|
102
|
+
return if !new_record? && !changed?
|
103
|
+
|
104
|
+
Userstamper::Utilities.assign_stamper(self, updater_association)
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Userstamper::Stamper
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
module ClassMethods
|
5
|
+
def model_stamper
|
6
|
+
# don't allow multiple calls
|
7
|
+
return if singleton_class.included_modules.include?(InstanceMethods)
|
8
|
+
extend Userstamper::Stamper::InstanceMethods
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
# Used to set the current stamper for this model.
|
14
|
+
#
|
15
|
+
# @overload stamper=(object_id)
|
16
|
+
# @param [Fixnum] object_id The ID of the stamper.
|
17
|
+
# @return [Fixnum]
|
18
|
+
# @overload stamper=(object)
|
19
|
+
# @param [ActiveRecord::Base] object The stamper object.
|
20
|
+
# @return [ActiveRecord::Base]
|
21
|
+
def stamper=(object)
|
22
|
+
Thread.current[stamper_identifier] = object
|
23
|
+
end
|
24
|
+
|
25
|
+
# Retrieves the existing stamper.
|
26
|
+
def stamper
|
27
|
+
Thread.current[stamper_identifier]
|
28
|
+
end
|
29
|
+
|
30
|
+
# Sets the stamper back to +nil+ to prepare for the next request.
|
31
|
+
def reset_stamper
|
32
|
+
self.stamper = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# For the duration that execution is within the provided block, the stamper for this class
|
36
|
+
# would be the specified value.
|
37
|
+
#
|
38
|
+
# This replaces the {#stamper=} and {#reset_stamper} pair because this guarantees exception
|
39
|
+
# safety.
|
40
|
+
def with_stamper(stamper)
|
41
|
+
old_stamper = self.stamper
|
42
|
+
self.stamper = stamper
|
43
|
+
yield
|
44
|
+
ensure
|
45
|
+
self.stamper = old_stamper
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def stamper_identifier
|
51
|
+
"#{self.to_s.downcase}_#{self.object_id}_stamper"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Userstamper::Utilities
|
2
|
+
# Removes the association methods from the model.
|
3
|
+
#
|
4
|
+
# @param [Class] model The model to remove methods from.
|
5
|
+
# @param [Symbol] association The name of the association to remove.
|
6
|
+
# @return [void]
|
7
|
+
def self.remove_association(model, association)
|
8
|
+
methods = [
|
9
|
+
association,
|
10
|
+
"#{association}=",
|
11
|
+
"build_#{association}",
|
12
|
+
"create_#{association}",
|
13
|
+
"create_#{association}!"
|
14
|
+
]
|
15
|
+
|
16
|
+
model.generated_association_methods.module_eval do
|
17
|
+
methods.each do |method|
|
18
|
+
remove_possible_method(method)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Obtains the creator/updater columns which are present in the model.
|
24
|
+
#
|
25
|
+
# @param [Class] model The model to query.
|
26
|
+
# @return [nil|Array<(bool, bool, bool)>] Nil if the model does not have a table defined.
|
27
|
+
# Otherwise, a tuple of booleans indicating the presence of the created, updated columns.
|
28
|
+
def self.available_association_columns(model)
|
29
|
+
return nil if model.name.nil? || model.table_name.empty?
|
30
|
+
columns = Set[*model.column_names]
|
31
|
+
config = Userstamper.config
|
32
|
+
|
33
|
+
[config.creator_attribute.present? && columns.include?(config.creator_attribute.to_s),
|
34
|
+
config.updater_attribute.present? && columns.include?(config.updater_attribute.to_s)]
|
35
|
+
rescue ActiveRecord::StatementInvalid => _
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
|
39
|
+
# Assigns the stamper to the given association reflection in the record.
|
40
|
+
#
|
41
|
+
# If the stamper is a record, then it is assigned to the association; if it is a number, then it
|
42
|
+
# is assigned to the foreign key.
|
43
|
+
#
|
44
|
+
# @param [ActiveRecord::Base] record The record to assign.
|
45
|
+
# @param [ActiveRecord::Reflection] association The association to assign
|
46
|
+
def self.assign_stamper(record, association)
|
47
|
+
stamp_value = record.class.stamper_class.stamper
|
48
|
+
attribute =
|
49
|
+
if stamp_value.is_a?(ActiveRecord::Base)
|
50
|
+
association.name
|
51
|
+
else
|
52
|
+
association.foreign_key
|
53
|
+
end
|
54
|
+
|
55
|
+
record.send("#{attribute}=", stamp_value)
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe PostsController, type: :controller do
|
4
|
+
controller do
|
5
|
+
end
|
6
|
+
|
7
|
+
before(:each) { define_first_post }
|
8
|
+
|
9
|
+
context 'when updating a Post' do
|
10
|
+
it 'sets the correct updater' do
|
11
|
+
request.session = { person_id: @delynn.id }
|
12
|
+
post :update, params: { id: @first_post.id, post: { title: 'Different' } }
|
13
|
+
|
14
|
+
expect(response.status).to eq(200)
|
15
|
+
expect(controller.instance_variable_get(:@post).title).to eq('Different')
|
16
|
+
expect(controller.instance_variable_get(:@post).updater).to eq(@delynn)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when handling multiple requests' do
|
21
|
+
def simulate_second_request
|
22
|
+
old_request_session = request.session
|
23
|
+
request.session = { person_id: @nicole.id }
|
24
|
+
|
25
|
+
post :update, params: { id: @first_post.id, post: { title: 'Different Second'} }
|
26
|
+
expect(controller.instance_variable_get(:@post).updater).to eq(@nicole)
|
27
|
+
ensure
|
28
|
+
request.session = old_request_session
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'sets the correct updater' do
|
32
|
+
request.session = { person_id: @delynn.id }
|
33
|
+
get :edit, params: { id: @first_post.id }
|
34
|
+
expect(response.status).to eq(200)
|
35
|
+
|
36
|
+
simulate_second_request
|
37
|
+
|
38
|
+
post :update, params: { id: @first_post.id, post: { title: 'Different' } }
|
39
|
+
expect(response.status).to eq(200)
|
40
|
+
expect(controller.instance_variable_get(:@post).title).to eq('Different')
|
41
|
+
expect(controller.instance_variable_get(:@post).updater).to eq(@delynn)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'rails_helper'
|
2
|
+
|
3
|
+
RSpec.describe UsersController, type: :controller do
|
4
|
+
controller do
|
5
|
+
end
|
6
|
+
|
7
|
+
context 'when updating a User' do
|
8
|
+
it 'sets the correct updater' do
|
9
|
+
request.session = { user_id: @hera.id }
|
10
|
+
patch :update, params: { id: @hera.id, user: { name: 'Different'} }
|
11
|
+
|
12
|
+
expect(response.status).to eq(200)
|
13
|
+
expect(controller.instance_variable_get(:@user).name).to eq('Different')
|
14
|
+
expect(controller.instance_variable_get(:@user).updater).to eq(@hera)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'when handling multiple requests' do
|
19
|
+
def simulate_second_request
|
20
|
+
old_request_session = request.session
|
21
|
+
request.session = { user_id: @zeus.id }
|
22
|
+
|
23
|
+
post :update, params: { id: @hera.id, user: { name: 'Different Second' } }
|
24
|
+
expect(controller.instance_variable_get(:@user).updater).to eq(@zeus)
|
25
|
+
ensure
|
26
|
+
request.session = old_request_session
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'sets the correct updater' do
|
30
|
+
request.session = { user_id: @hera.id }
|
31
|
+
get :edit, params: { id: @hera.id }
|
32
|
+
expect(response.status).to eq(200)
|
33
|
+
|
34
|
+
simulate_second_request
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when the handler raises an exception' do
|
39
|
+
before { @stamper = User.stamper }
|
40
|
+
it 'restores the correct stamper' do
|
41
|
+
begin
|
42
|
+
request.session = { user_id: @zeus.id }
|
43
|
+
post :create
|
44
|
+
rescue
|
45
|
+
end
|
46
|
+
|
47
|
+
expect(User.stamper).to be(@stamper)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|