tonkapark-clearance 0.6.9.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. data/CHANGELOG.textile +167 -0
  2. data/LICENSE +21 -0
  3. data/README.textile +123 -0
  4. data/Rakefile +76 -0
  5. data/TODO.textile +6 -0
  6. data/app/controllers/clearance/confirmations_controller.rb +52 -0
  7. data/app/controllers/clearance/passwords_controller.rb +81 -0
  8. data/app/controllers/clearance/sessions_controller.rb +67 -0
  9. data/app/controllers/clearance/users_controller.rb +35 -0
  10. data/app/models/clearance_mailer.rb +23 -0
  11. data/app/views/clearance_mailer/change_password.html.erb +7 -0
  12. data/app/views/clearance_mailer/confirmation.html.erb +2 -0
  13. data/app/views/passwords/edit.html.erb +23 -0
  14. data/app/views/passwords/new.html.erb +15 -0
  15. data/app/views/sessions/new.html.erb +28 -0
  16. data/app/views/users/_form.html.erb +13 -0
  17. data/app/views/users/new.html.erb +6 -0
  18. data/clearance.gemspec +140 -0
  19. data/config/clearance_routes.rb +30 -0
  20. data/generators/clearance/USAGE +1 -0
  21. data/generators/clearance/clearance_generator.rb +41 -0
  22. data/generators/clearance/lib/insert_commands.rb +33 -0
  23. data/generators/clearance/lib/rake_commands.rb +22 -0
  24. data/generators/clearance/templates/README +22 -0
  25. data/generators/clearance/templates/factories.rb +13 -0
  26. data/generators/clearance/templates/migrations/create_users.rb +21 -0
  27. data/generators/clearance/templates/migrations/update_users.rb +41 -0
  28. data/generators/clearance/templates/user.rb +3 -0
  29. data/generators/clearance_features/USAGE +1 -0
  30. data/generators/clearance_features/clearance_features_generator.rb +20 -0
  31. data/generators/clearance_features/templates/features/password_reset.feature +33 -0
  32. data/generators/clearance_features/templates/features/sign_in.feature +42 -0
  33. data/generators/clearance_features/templates/features/sign_out.feature +23 -0
  34. data/generators/clearance_features/templates/features/sign_up.feature +28 -0
  35. data/generators/clearance_features/templates/features/step_definitions/clearance_steps.rb +110 -0
  36. data/generators/clearance_features/templates/features/step_definitions/factory_girl_steps.rb +5 -0
  37. data/generators/clearance_features/templates/features/support/paths.rb +22 -0
  38. data/lib/clearance/authentication.rb +100 -0
  39. data/lib/clearance/extensions/errors.rb +6 -0
  40. data/lib/clearance/extensions/rescue.rb +3 -0
  41. data/lib/clearance/extensions/routes.rb +14 -0
  42. data/lib/clearance/user.rb +143 -0
  43. data/lib/clearance.rb +6 -0
  44. data/rails/init.rb +1 -0
  45. data/shoulda_macros/clearance.rb +262 -0
  46. metadata +138 -0
@@ -0,0 +1,110 @@
1
+ # General
2
+
3
+ Then /^I should see error messages$/ do
4
+ assert_match /error(s)? prohibited/m, response.body
5
+ end
6
+
7
+ # Database
8
+
9
+ Given /^no user exists with an email of "(.*)"$/ do |email|
10
+ assert_nil User.find_by_email(email)
11
+ end
12
+
13
+ Given /^I signed up with "(.*)\/(.*)"$/ do |email, password|
14
+ user = Factory :user,
15
+ :email => email,
16
+ :password => password,
17
+ :password_confirmation => password
18
+ end
19
+
20
+ Given /^I am signed up and confirmed as "(.*)\/(.*)"$/ do |email, password|
21
+ user = Factory :email_confirmed_user,
22
+ :email => email,
23
+ :password => password,
24
+ :password_confirmation => password
25
+ end
26
+
27
+ # Session
28
+
29
+ Then /^I should be signed in$/ do
30
+ assert controller.signed_in?
31
+ end
32
+
33
+ Then /^I should be signed out$/ do
34
+ assert ! controller.signed_in?
35
+ end
36
+
37
+ When /^session is cleared$/ do
38
+ request.reset_session
39
+ controller.instance_variable_set(:@_current_user, nil)
40
+ end
41
+
42
+ # Emails
43
+
44
+ Then /^a confirmation message should be sent to "(.*)"$/ do |email|
45
+ user = User.find_by_email(email)
46
+ sent = ActionMailer::Base.deliveries.first
47
+ assert_equal [user.email], sent.to
48
+ assert_match /confirm/i, sent.subject
49
+ assert !user.token.blank?
50
+ assert_match /#{user.token}/, sent.body
51
+ end
52
+
53
+ When /^I follow the confirmation link sent to "(.*)"$/ do |email|
54
+ user = User.find_by_email(email)
55
+ visit new_user_confirmation_path(:user_id => user, :token => user.token)
56
+ end
57
+
58
+ Then /^a password reset message should be sent to "(.*)"$/ do |email|
59
+ user = User.find_by_email(email)
60
+ sent = ActionMailer::Base.deliveries.first
61
+ assert_equal [user.email], sent.to
62
+ assert_match /password/i, sent.subject
63
+ assert !user.token.blank?
64
+ assert_match /#{user.token}/, sent.body
65
+ end
66
+
67
+ When /^I follow the password reset link sent to "(.*)"$/ do |email|
68
+ user = User.find_by_email(email)
69
+ visit edit_user_password_path(:user_id => user, :token => user.token)
70
+ end
71
+
72
+ When /^I try to change the password of "(.*)" without token$/ do |email|
73
+ user = User.find_by_email(email)
74
+ visit edit_user_password_path(:user_id => user)
75
+ end
76
+
77
+ Then /^I should be forbidden$/ do
78
+ assert_response :forbidden
79
+ end
80
+
81
+ # Actions
82
+
83
+ When /^I sign in( with "remember me")? as "(.*)\/(.*)"$/ do |remember, email, password|
84
+ When %{I go to the sign in page}
85
+ And %{I fill in "Email" with "#{email}"}
86
+ And %{I fill in "Password" with "#{password}"}
87
+ And %{I check "Remember me"} if remember
88
+ And %{I press "Sign In"}
89
+ end
90
+
91
+ When /^I sign out$/ do
92
+ visit '/session', :delete
93
+ end
94
+
95
+ When /^I request password reset link to be sent to "(.*)"$/ do |email|
96
+ When %{I go to the password reset request page}
97
+ And %{I fill in "Email address" with "#{email}"}
98
+ And %{I press "Reset password"}
99
+ end
100
+
101
+ When /^I update my password with "(.*)\/(.*)"$/ do |password, confirmation|
102
+ And %{I fill in "Choose password" with "#{password}"}
103
+ And %{I fill in "Confirm password" with "#{confirmation}"}
104
+ And %{I press "Save this password"}
105
+ end
106
+
107
+ When /^I return next time$/ do
108
+ When %{session is cleared}
109
+ And %{I go to the homepage}
110
+ end
@@ -0,0 +1,5 @@
1
+ Factory.factories.each do |name, factory|
2
+ Given /^an? #{name} exists with an? (.*) of "([^"]*)"$/ do |attr, value|
3
+ Factory(name, attr.gsub(' ', '_') => value)
4
+ end
5
+ end
@@ -0,0 +1,22 @@
1
+ module NavigationHelpers
2
+ def path_to(page_name)
3
+ case page_name
4
+
5
+ when /the homepage/i
6
+ root_path
7
+ when /the sign up page/i
8
+ new_user_path
9
+ when /the sign in page/i
10
+ new_session_path
11
+ when /the password reset request page/i
12
+ new_password_path
13
+
14
+ # Add more page name => path mappings here
15
+
16
+ else
17
+ raise "Can't find mapping from \"#{page_name}\" to a path."
18
+ end
19
+ end
20
+ end
21
+
22
+ World(NavigationHelpers)
@@ -0,0 +1,100 @@
1
+ module Clearance
2
+ module Authentication
3
+
4
+ def self.included(controller)
5
+ controller.send(:include, InstanceMethods)
6
+
7
+ controller.class_eval do
8
+ helper_method :current_user
9
+ helper_method :signed_in?
10
+
11
+ hide_action :current_user, :signed_in?
12
+ end
13
+ end
14
+
15
+ module InstanceMethods
16
+ def current_user
17
+ @_current_user ||= (user_from_cookie || user_from_session)
18
+ end
19
+
20
+ def signed_in?
21
+ ! current_user.nil?
22
+ end
23
+
24
+ protected
25
+
26
+ def authenticate
27
+ deny_access unless signed_in?
28
+ end
29
+
30
+ def user_from_session
31
+ if session[:user_id]
32
+ return nil unless user = ::User.find_by_id(session[:user_id])
33
+ return user if user.email_confirmed?
34
+ end
35
+ end
36
+
37
+ def user_from_cookie
38
+ if token = cookies[:remember_token]
39
+ return nil unless user = ::User.find_by_token(token)
40
+ return user if user.remember?
41
+ end
42
+ end
43
+
44
+ def sign_user_in(user)
45
+ warn "[DEPRECATION] sign_user_in: unnecessary. use sign_in(user) instead."
46
+ sign_in(user)
47
+ end
48
+
49
+ def sign_in(user)
50
+ if user
51
+ session[:user_id] = user.id
52
+ end
53
+ end
54
+
55
+ def remember?
56
+ params[:session] && params[:session][:remember_me] == "1"
57
+ end
58
+
59
+ def remember(user)
60
+ user.remember_me!
61
+ cookies[:remember_token] = { :value => user.token,
62
+ :expires => user.token_expires_at }
63
+ end
64
+
65
+ def forget(user)
66
+ user.forget_me! if user
67
+ cookies.delete(:remember_token)
68
+ reset_session
69
+ end
70
+
71
+ def redirect_back_or(default)
72
+ redirect_to(return_to || default)
73
+ clear_return_to
74
+ end
75
+
76
+ def return_to
77
+ session[:return_to] || params[:return_to]
78
+ end
79
+
80
+ def clear_return_to
81
+ session[:return_to] = nil
82
+ end
83
+
84
+ def redirect_to_root
85
+ redirect_to(root_url)
86
+ end
87
+
88
+ def store_location
89
+ session[:return_to] = request.request_uri if request.get?
90
+ end
91
+
92
+ def deny_access(flash_message = nil, opts = {})
93
+ store_location
94
+ flash[:failure] = flash_message if flash_message
95
+ redirect_to(new_session_url)
96
+ end
97
+ end
98
+
99
+ end
100
+ end
@@ -0,0 +1,6 @@
1
+ if defined?(ActionController)
2
+ module ActionController
3
+ class Forbidden < StandardError
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ if defined?(ActionController::Base)
2
+ ActionController::Base.rescue_responses.update('ActionController::Forbidden' => :forbidden)
3
+ end
@@ -0,0 +1,14 @@
1
+ if defined?(ActionController::Routing::RouteSet)
2
+ class ActionController::Routing::RouteSet
3
+ def load_routes_with_clearance!
4
+ lib_path = File.dirname(__FILE__)
5
+ clearance_routes = File.join(lib_path, *%w[.. .. .. config clearance_routes.rb])
6
+ unless configuration_files.include?(clearance_routes)
7
+ add_configuration_file(clearance_routes)
8
+ end
9
+ load_routes_without_clearance!
10
+ end
11
+
12
+ alias_method_chain :load_routes!, :clearance
13
+ end
14
+ end
@@ -0,0 +1,143 @@
1
+ require 'digest/sha1'
2
+
3
+ module Clearance
4
+ module User
5
+
6
+ def self.included(model)
7
+ model.extend(ClassMethods)
8
+
9
+ model.send(:include, InstanceMethods)
10
+ model.send(:include, AttrAccessible)
11
+ model.send(:include, AttrAccessor)
12
+ model.send(:include, Validations)
13
+ model.send(:include, Callbacks)
14
+ end
15
+
16
+ module AttrAccessible
17
+ def self.included(model)
18
+ model.class_eval do
19
+ attr_accessible :email, :password, :password_confirmation
20
+ end
21
+ end
22
+ end
23
+
24
+ module AttrAccessor
25
+ def self.included(model)
26
+ model.class_eval do
27
+ attr_accessor :password, :password_confirmation
28
+ end
29
+ end
30
+ end
31
+
32
+ module Validations
33
+ def self.included(model)
34
+ model.class_eval do
35
+ validates_presence_of :email
36
+ validates_uniqueness_of :email, :case_sensitive => false
37
+ validates_format_of :email, :with => %r{.+@.+\..+}
38
+
39
+ validates_presence_of :password, :if => :password_required?
40
+ validates_confirmation_of :password, :if => :password_required?
41
+ end
42
+ end
43
+ end
44
+
45
+ module Callbacks
46
+ def self.included(model)
47
+ model.class_eval do
48
+ before_save :initialize_salt, :encrypt_password, :initialize_token
49
+ end
50
+ end
51
+ end
52
+
53
+ module InstanceMethods
54
+ def authenticated?(password)
55
+ encrypted_password == encrypt(password)
56
+ end
57
+
58
+ def encrypt(string)
59
+ generate_hash("--#{salt}--#{string}--")
60
+ end
61
+
62
+ def remember?
63
+ token_expires_at && Time.now.utc < token_expires_at
64
+ end
65
+
66
+ def remember_me!
67
+ remember_me_until! 2.weeks.from_now.utc
68
+ end
69
+
70
+ def forget_me!
71
+ clear_token
72
+ save(false)
73
+ end
74
+
75
+ def confirm_email!
76
+ self.email_confirmed = true
77
+ self.token = nil
78
+ save(false)
79
+ end
80
+
81
+ def forgot_password!
82
+ generate_token
83
+ save(false)
84
+ end
85
+
86
+ def update_password(new_password, new_password_confirmation)
87
+ self.password = new_password
88
+ self.password_confirmation = new_password_confirmation
89
+ clear_token if valid?
90
+ save
91
+ end
92
+
93
+ protected
94
+
95
+ def generate_hash(string)
96
+ Digest::SHA1.hexdigest(string)
97
+ end
98
+
99
+ def initialize_salt
100
+ if new_record?
101
+ self.salt = generate_hash("--#{Time.now.utc.to_s}--#{password}--")
102
+ end
103
+ end
104
+
105
+ def encrypt_password
106
+ return if password.blank?
107
+ self.encrypted_password = encrypt(password)
108
+ end
109
+
110
+ def generate_token
111
+ self.token = encrypt("--#{Time.now.utc.to_s}--#{password}--")
112
+ self.token_expires_at = nil
113
+ end
114
+
115
+ def clear_token
116
+ self.token = nil
117
+ self.token_expires_at = nil
118
+ end
119
+
120
+ def initialize_token
121
+ generate_token if new_record?
122
+ end
123
+
124
+ def password_required?
125
+ encrypted_password.blank? || !password.blank?
126
+ end
127
+
128
+ def remember_me_until!(time)
129
+ self.token_expires_at = time
130
+ self.token = encrypt("--#{token_expires_at}--#{password}--")
131
+ save(false)
132
+ end
133
+ end
134
+
135
+ module ClassMethods
136
+ def authenticate(email, password)
137
+ return nil unless user = find_by_email(email)
138
+ return user if user.authenticated?(password)
139
+ end
140
+ end
141
+
142
+ end
143
+ end
data/lib/clearance.rb ADDED
@@ -0,0 +1,6 @@
1
+ require 'clearance/extensions/errors'
2
+ require 'clearance/extensions/rescue'
3
+ require 'clearance/extensions/routes'
4
+
5
+ require 'clearance/authentication'
6
+ require 'clearance/user'
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'clearance'
@@ -0,0 +1,262 @@
1
+ module Clearance
2
+ module Shoulda
3
+
4
+ # STATE OF AUTHENTICATION
5
+
6
+ def should_be_signed_in_as(&block)
7
+ should "be signed in as #{block.bind(self).call}" do
8
+ user = block.bind(self).call
9
+ assert_not_nil user,
10
+ "please pass a User. try: should_be_signed_in_as { @user }"
11
+ assert_equal user, @controller.send(:current_user),
12
+ "#{user.inspect} is not the current_user, " <<
13
+ "which is #{@controller.send(:current_user).inspect}"
14
+ end
15
+ end
16
+
17
+ def should_be_signed_in_and_email_confirmed_as(&block)
18
+ warn "[DEPRECATION] should_be_signed_in_and_email_confirmed_as: questionable usefulness"
19
+ should_be_signed_in_as &block
20
+
21
+ should "have confirmed email" do
22
+ user = block.bind(self).call
23
+
24
+ assert_not_nil user
25
+ assert_equal user, assigns(:user)
26
+ assert assigns(:user).email_confirmed?
27
+ end
28
+ end
29
+
30
+ def should_not_be_signed_in
31
+ should "not be signed in" do
32
+ assert_nil session[:user_id]
33
+ end
34
+ end
35
+
36
+ def should_deny_access_on(http_method, action, opts = {})
37
+ warn "[DEPRECATION] should_deny_access_on: use a setup & should_deny_access(:flash => ?)"
38
+ flash_message = opts.delete(:flash)
39
+ context "on #{http_method} to #{action}" do
40
+ setup do
41
+ send(http_method, action, opts)
42
+ end
43
+
44
+ should_deny_access(:flash => flash_message)
45
+ end
46
+ end
47
+
48
+ def should_deny_access(opts = {})
49
+ if opts[:flash]
50
+ should_set_the_flash_to opts[:flash]
51
+ else
52
+ should_not_set_the_flash
53
+ end
54
+
55
+ should_redirect_to('new_session_url') { new_session_url }
56
+ end
57
+
58
+ # HTTP FLUENCY
59
+
60
+ def should_forbid(description, &block)
61
+ should "forbid #{description}" do
62
+ assert_raises ActionController::Forbidden do
63
+ instance_eval(&block)
64
+ end
65
+ end
66
+ end
67
+
68
+ # CONTEXTS
69
+
70
+ def signed_in_user_context(&blk)
71
+ warn "[DEPRECATION] signed_in_user_context: creates a Mystery Guest, causes Obscure Test"
72
+ context "A signed in user" do
73
+ setup do
74
+ @user = Factory(:user)
75
+ @user.confirm_email!
76
+ sign_in_as @user
77
+ end
78
+ merge_block(&blk)
79
+ end
80
+ end
81
+
82
+ def public_context(&blk)
83
+ warn "[DEPRECATION] public_context: common case is no-op. call sign_out otherwise"
84
+ context "The public" do
85
+ setup { sign_out }
86
+ merge_block(&blk)
87
+ end
88
+ end
89
+
90
+ # CREATING USERS
91
+
92
+ def should_create_user_successfully
93
+ warn "[DEPRECATION] should_create_user_successfully: not meant to be public, no longer used internally"
94
+ should_assign_to :user
95
+ should_change 'User.count', :by => 1
96
+
97
+ should "send the confirmation email" do
98
+ assert_sent_email do |email|
99
+ email.subject =~ /account confirmation/i
100
+ end
101
+ end
102
+
103
+ should_set_the_flash_to /confirm/i
104
+ should_redirect_to_url_after_create
105
+ end
106
+
107
+ # RENDERING
108
+
109
+ def should_render_nothing
110
+ should "render nothing" do
111
+ assert @response.body.blank?
112
+ end
113
+ end
114
+
115
+ # REDIRECTS
116
+
117
+ def should_redirect_to_url_after_create
118
+ should_redirect_to("the post-create url") do
119
+ @controller.send(:url_after_create)
120
+ end
121
+ end
122
+
123
+ def should_redirect_to_url_after_update
124
+ should_redirect_to("the post-update url") do
125
+ @controller.send(:url_after_update)
126
+ end
127
+ end
128
+
129
+ def should_redirect_to_url_after_destroy
130
+ should_redirect_to("the post-destroy url") do
131
+ @controller.send(:url_after_destroy)
132
+ end
133
+ end
134
+
135
+ # VALIDATIONS
136
+
137
+ def should_validate_confirmation_of(attribute, opts = {})
138
+ warn "[DEPRECATION] should_validate_confirmation_of: not meant to be public, no longer used internally"
139
+ raise ArgumentError if opts[:factory].nil?
140
+
141
+ context "on save" do
142
+ should_validate_confirmation_is_not_blank opts[:factory], attribute
143
+ should_validate_confirmation_is_not_bad opts[:factory], attribute
144
+ end
145
+ end
146
+
147
+ def should_validate_confirmation_is_not_blank(factory, attribute, opts = {})
148
+ warn "[DEPRECATION] should_validate_confirmation_is_not_blank: not meant to be public, no longer used internally"
149
+ should "validate #{attribute}_confirmation is not blank" do
150
+ model = Factory.build(factory, blank_confirmation_options(attribute))
151
+ model.save
152
+ assert_confirmation_error(model, attribute,
153
+ "#{attribute}_confirmation cannot be blank")
154
+ end
155
+ end
156
+
157
+ def should_validate_confirmation_is_not_bad(factory, attribute, opts = {})
158
+ warn "[DEPRECATION] should_validate_confirmation_is_not_bad: not meant to be public, no longer used internally"
159
+ should "validate #{attribute}_confirmation is different than #{attribute}" do
160
+ model = Factory.build(factory, bad_confirmation_options(attribute))
161
+ model.save
162
+ assert_confirmation_error(model, attribute,
163
+ "#{attribute}_confirmation cannot be different than #{attribute}")
164
+ end
165
+ end
166
+
167
+ # FORMS
168
+
169
+ def should_display_a_password_update_form
170
+ warn "[DEPRECATION] should_display_a_password_update_form: not meant to be public, no longer used internally"
171
+ should "have a form for the user's token, password, and password confirm" do
172
+ update_path = ERB::Util.h(
173
+ user_password_path(@user, :token => @user.token)
174
+ )
175
+
176
+ assert_select 'form[action=?]', update_path do
177
+ assert_select 'input[name=_method][value=?]', 'put'
178
+ assert_select 'input[name=?]', 'user[password]'
179
+ assert_select 'input[name=?]', 'user[password_confirmation]'
180
+ end
181
+ end
182
+ end
183
+
184
+ def should_display_a_sign_up_form
185
+ warn "[DEPRECATION] should_display_a_sign_up_form: not meant to be public, no longer used internally"
186
+ should "display a form to sign up" do
187
+ assert_select "form[action=#{users_path}][method=post]",
188
+ true, "There must be a form to sign up" do
189
+ assert_select "input[type=text][name=?]",
190
+ "user[email]", true, "There must be an email field"
191
+ assert_select "input[type=password][name=?]",
192
+ "user[password]", true, "There must be a password field"
193
+ assert_select "input[type=password][name=?]",
194
+ "user[password_confirmation]", true, "There must be a password confirmation field"
195
+ assert_select "input[type=submit]", true,
196
+ "There must be a submit button"
197
+ end
198
+ end
199
+ end
200
+
201
+ def should_display_a_sign_in_form
202
+ warn "[DEPRECATION] should_display_a_sign_in_form: not meant to be public, no longer used internally"
203
+ should 'display a "sign in" form' do
204
+ assert_select "form[action=#{session_path}][method=post]",
205
+ true, "There must be a form to sign in" do
206
+ assert_select "input[type=text][name=?]",
207
+ "session[email]", true, "There must be an email field"
208
+ assert_select "input[type=password][name=?]",
209
+ "session[password]", true, "There must be a password field"
210
+ assert_select "input[type=checkbox][name=?]",
211
+ "session[remember_me]", true, "There must be a 'remember me' check box"
212
+ assert_select "input[type=submit]", true,
213
+ "There must be a submit button"
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end
219
+
220
+ module Clearance
221
+ module Shoulda
222
+ module Helpers
223
+ def sign_in_as(user)
224
+ @controller.class_eval { attr_accessor :current_user }
225
+ @controller.current_user = user
226
+ return user
227
+ end
228
+
229
+ def sign_in
230
+ sign_in_as Factory(:email_confirmed_user)
231
+ end
232
+
233
+ def sign_out
234
+ @controller.class_eval { attr_accessor :current_user }
235
+ @controller.current_user = nil
236
+ end
237
+
238
+ def blank_confirmation_options(attribute)
239
+ warn "[DEPRECATION] blank_confirmation_options: not meant to be public, no longer used internally"
240
+ opts = { attribute => attribute.to_s }
241
+ opts.merge("#{attribute}_confirmation".to_sym => "")
242
+ end
243
+
244
+ def bad_confirmation_options(attribute)
245
+ warn "[DEPRECATION] bad_confirmation_options: not meant to be public, no longer used internally"
246
+ opts = { attribute => attribute.to_s }
247
+ opts.merge("#{attribute}_confirmation".to_sym => "not_#{attribute}")
248
+ end
249
+
250
+ def assert_confirmation_error(model, attribute, message = "confirmation error")
251
+ warn "[DEPRECATION] assert_confirmation_error: not meant to be public, no longer used internally"
252
+ assert model.errors.on(attribute).include?("doesn't match confirmation"),
253
+ message
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ class Test::Unit::TestCase
260
+ include Clearance::Shoulda::Helpers
261
+ end
262
+ Test::Unit::TestCase.extend(Clearance::Shoulda)