accounts 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.rvmrc +53 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +167 -0
- data/README.mkd +105 -0
- data/Rakefile +24 -0
- data/accounts.gemspec +52 -0
- data/demo/views/change_email.haml +14 -0
- data/demo/views/change_password.haml +36 -0
- data/demo/views/forgot_password.haml +21 -0
- data/demo/views/logon.haml +24 -0
- data/demo/views/register.haml +15 -0
- data/demo/web_app.rb +99 -0
- data/features/change-email.feature +51 -0
- data/features/change-password.feature +89 -0
- data/features/register.feature +42 -0
- data/features/step_definitions/steps.rb +102 -0
- data/features/step_definitions/web_steps.rb +228 -0
- data/features/support/env.rb +18 -0
- data/features/support/paths.rb +29 -0
- data/features/support/unused.rb +29 -0
- data/lib/accounts.rb +79 -0
- data/lib/accounts/configure.rb +122 -0
- data/lib/accounts/helpers.rb +90 -0
- data/lib/accounts/model.rb +126 -0
- data/lib/accounts/version.rb +5 -0
- data/spec/user_model_spec.rb +105 -0
- metadata +263 -0
@@ -0,0 +1,228 @@
|
|
1
|
+
# Taken from the cucumber-rails project.
|
2
|
+
# IMPORTANT: This file is generated by cucumber-sinatra - edit at your own peril.
|
3
|
+
# It is recommended to regenerate this file in the future when you upgrade to a
|
4
|
+
# newer version of cucumber-sinatra. Consider adding your own code to a new file
|
5
|
+
# instead of editing this one. Cucumber will automatically load all features/**/*.rb
|
6
|
+
# files.
|
7
|
+
|
8
|
+
require 'uri'
|
9
|
+
require 'cgi'
|
10
|
+
require File.expand_path(File.join(File.dirname(__FILE__), "..", "support", "paths"))
|
11
|
+
|
12
|
+
module WithinHelpers
|
13
|
+
def with_scope(locator)
|
14
|
+
locator ? within(locator) { yield } : yield
|
15
|
+
end
|
16
|
+
end
|
17
|
+
World(WithinHelpers)
|
18
|
+
|
19
|
+
Given /^(?:\w+ )am on (.+)$/ do |page_name|
|
20
|
+
visit path_to(page_name)
|
21
|
+
end
|
22
|
+
|
23
|
+
When /^(?:\w+ )go to (.+)$/ do |page_name|
|
24
|
+
visit path_to(page_name)
|
25
|
+
end
|
26
|
+
|
27
|
+
When /^(?:\w+ )press(?:es)? "([^\"]*)"(?: within "([^\"]*)")?$/ do |button, selector|
|
28
|
+
with_scope(selector) do
|
29
|
+
click_button(button)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
When /^(?:\w+ )follows? "([^\"]*)"(?: within "([^\"]*)")?$/ do |link, selector|
|
34
|
+
with_scope(selector) do
|
35
|
+
click_link(link)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
When /^(?:\w+ )fills? in "([^\"]*)" with "([^\"]*)"(?: within "([^\"]*)")?$/ do |field, value, selector|
|
40
|
+
with_scope(selector) do
|
41
|
+
fill_in(field, :with => value)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
When /^(?:\w+ )fills? in "([^\"]*)" for "([^\"]*)"(?: within "([^\"]*)")?$/ do |value, field, selector|
|
46
|
+
with_scope(selector) do
|
47
|
+
fill_in(field, :with => value)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Use this to fill in an entire form with data from a table. Example:
|
52
|
+
#
|
53
|
+
# When I fill in the following:
|
54
|
+
# | Account Number | 5002 |
|
55
|
+
# | Expiry date | 2009-11-01 |
|
56
|
+
# | Note | Nice guy |
|
57
|
+
# | Wants Email? | |
|
58
|
+
#
|
59
|
+
# TODO: Add support for checkbox, select og option
|
60
|
+
# based on naming conventions.
|
61
|
+
#
|
62
|
+
When /^(?:\w+ )fills? in the following(?: within "([^\"]*)")?:$/ do |selector, fields|
|
63
|
+
with_scope(selector) do
|
64
|
+
fields.rows_hash.each do |name, value|
|
65
|
+
When %{I fill in "#{name}" with "#{value}"}
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
When /^(?:\w+ )select "([^\"]*)" from "([^\"]*)"(?: within "([^\"]*)")?$/ do |value, field, selector|
|
71
|
+
with_scope(selector) do
|
72
|
+
select(value, :from => field)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
When /^(?:\w+ )check "([^\"]*)"(?: within "([^\"]*)")?$/ do |field, selector|
|
77
|
+
with_scope(selector) do
|
78
|
+
check(field)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
When /^(?:\w+ )uncheck "([^\"]*)"(?: within "([^\"]*)")?$/ do |field, selector|
|
83
|
+
with_scope(selector) do
|
84
|
+
uncheck(field)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
When /^(?:\w+ )choose "([^\"]*)"(?: within "([^\"]*)")?$/ do |field, selector|
|
89
|
+
with_scope(selector) do
|
90
|
+
choose(field)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
When /^(?:\w+ )attach the file "([^\"]*)" to "([^\"]*)"(?: within "([^\"]*)")?$/ do |path, field, selector|
|
95
|
+
with_scope(selector) do
|
96
|
+
attach_file(field, path)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
Then /^(?:\w+ )should see JSON:$/ do |expected_json|
|
101
|
+
require 'json'
|
102
|
+
expected = JSON.pretty_generate(JSON.parse(expected_json))
|
103
|
+
actual = JSON.pretty_generate(JSON.parse(response.body))
|
104
|
+
expected.should == actual
|
105
|
+
end
|
106
|
+
|
107
|
+
Then /^(?:\w+ )should see "([^\"]*)"(?: within "([^\"]*)")?$/ do |text, selector|
|
108
|
+
with_scope(selector) do
|
109
|
+
if page.respond_to? :should
|
110
|
+
page.should have_content(text)
|
111
|
+
else
|
112
|
+
assert page.has_content?(text)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
Then /^(?:\w+ )should see \/([^\/]*)\/(?: within "([^\"]*)")?$/ do |regexp, selector|
|
118
|
+
regexp = Regexp.new(regexp)
|
119
|
+
with_scope(selector) do
|
120
|
+
if page.respond_to? :should
|
121
|
+
page.should have_xpath('//*', :text => regexp)
|
122
|
+
else
|
123
|
+
assert page.has_xpath?('//*', :text => regexp)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
Then /^(?:\w+ )should not see "([^\"]*)"(?: within "([^\"]*)")?$/ do |text, selector|
|
129
|
+
with_scope(selector) do
|
130
|
+
if page.respond_to? :should
|
131
|
+
page.should have_no_content(text)
|
132
|
+
else
|
133
|
+
assert page.has_no_content?(text)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
Then /^(?:\w+ )should not see \/([^\/]*)\/(?: within "([^\"]*)")?$/ do |regexp, selector|
|
139
|
+
regexp = Regexp.new(regexp)
|
140
|
+
with_scope(selector) do
|
141
|
+
if page.respond_to? :should
|
142
|
+
page.should have_no_xpath('//*', :text => regexp)
|
143
|
+
else
|
144
|
+
assert page.has_no_xpath?('//*', :text => regexp)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
Then /^the "([^\"]*)" field(?: within "([^\"]*)")? should contain "([^\"]*)"$/ do |field, selector, value|
|
150
|
+
with_scope(selector) do
|
151
|
+
field = find_field(field)
|
152
|
+
field_value = (field.tag_name == 'textarea') ? field.text : field.value
|
153
|
+
if field_value.respond_to? :should
|
154
|
+
field_value.should =~ /#{value}/
|
155
|
+
else
|
156
|
+
assert_match(/#{value}/, field_value)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
Then /^the "([^\"]*)" field(?: within "([^\"]*)")? should not contain "([^\"]*)"$/ do |field, selector, value|
|
162
|
+
with_scope(selector) do
|
163
|
+
field = find_field(field)
|
164
|
+
field_value = (field.tag_name == 'textarea') ? field.text : field.value
|
165
|
+
if field_value.respond_to? :should_not
|
166
|
+
field_value.should_not =~ /#{value}/
|
167
|
+
else
|
168
|
+
assert_no_match(/#{value}/, field_value)
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
Then /^the "([^\"]*)" checkbox(?: within "([^\"]*)")? should be checked$/ do |label, selector|
|
174
|
+
with_scope(selector) do
|
175
|
+
field_checked = find_field(label)['checked']
|
176
|
+
if field_checked.respond_to? :should
|
177
|
+
field_checked.should == 'checked'
|
178
|
+
else
|
179
|
+
assert_equal 'checked', field_checked
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
Then /^the "([^\"]*)" checkbox(?: within "([^\"]*)")? should not be checked$/ do |label, selector|
|
185
|
+
with_scope(selector) do
|
186
|
+
field_checked = find_field(label)['checked']
|
187
|
+
if field_checked.respond_to? :should_not
|
188
|
+
field_checked.should_not == 'checked'
|
189
|
+
else
|
190
|
+
assert_not_equal 'checked', field_checked
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
Then /^(?:\w+ )should be on "([^\"]*)"$/ do |pat|
|
196
|
+
current_path = URI.parse(current_url).path
|
197
|
+
if current_path.respond_to? :should
|
198
|
+
current_path.should match pat
|
199
|
+
else
|
200
|
+
assert_true current_path.match(pat)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
Then /^(?:\w+ )should be on (.+)$ page$/ do |page_name|
|
205
|
+
current_path = URI.parse(current_url).path
|
206
|
+
if current_path.respond_to? :should
|
207
|
+
current_path.should == path_to(page_name)
|
208
|
+
else
|
209
|
+
assert_equal path_to(page_name), current_path
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
Then /^(?:\w+ )should have the following query string:$/ do |expected_pairs|
|
214
|
+
query = URI.parse(current_url).query
|
215
|
+
actual_params = query ? CGI.parse(query) : {}
|
216
|
+
expected_params = {}
|
217
|
+
expected_pairs.rows_hash.each_pair{|k,v| expected_params[k] = v.split(',')}
|
218
|
+
|
219
|
+
if actual_params.respond_to? :should
|
220
|
+
actual_params.should == expected_params
|
221
|
+
else
|
222
|
+
assert_equal expected_params, actual_params
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
Then /^show me the page$/ do
|
227
|
+
save_and_open_page
|
228
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
2
|
+
|
3
|
+
require './demo/web_app'
|
4
|
+
require 'capybara/cucumber'
|
5
|
+
|
6
|
+
Capybara.app = MyWebApp
|
7
|
+
|
8
|
+
class AccountsWorld
|
9
|
+
include Capybara::DSL
|
10
|
+
include RSpec::Expectations
|
11
|
+
include RSpec::Matchers
|
12
|
+
end
|
13
|
+
|
14
|
+
World do |;world|
|
15
|
+
world = AccountsWorld.new
|
16
|
+
#STDERR.puts world.class.included_modules
|
17
|
+
#File.open("/tmp/methods.txt", "w").puts world.methods
|
18
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# Taken from the cucumber-rails project.
|
2
|
+
|
3
|
+
module NavigationHelpers
|
4
|
+
# Maps a name to a path. Used by the
|
5
|
+
#
|
6
|
+
# When /^I go to (.+)$/ do |page_name|
|
7
|
+
#
|
8
|
+
# step definition in web_steps.rb
|
9
|
+
#
|
10
|
+
def path_to(page_name)
|
11
|
+
case page_name
|
12
|
+
|
13
|
+
when /the home\s?page/
|
14
|
+
'/'
|
15
|
+
|
16
|
+
# Add more mappings here.
|
17
|
+
# Here is an example that pulls values out of the Regexp:
|
18
|
+
#
|
19
|
+
# when /^(.*)'s profile page$/i
|
20
|
+
# user_profile_path(User.find_by_login($1))
|
21
|
+
|
22
|
+
else
|
23
|
+
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
|
24
|
+
"Now, go and add a mapping in #{__FILE__}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
World(NavigationHelpers)
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# From http://www.natontesting.com/2011/04/05/cucumber-formatter-for-unused-steps/
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010, Nathaniel Ritmeyer. All rights reserved.
|
4
|
+
#
|
5
|
+
# http://www.natontesting.com
|
6
|
+
#
|
7
|
+
# Save this in a file called 'unused.rb' in your 'features/support' directory. Then, to list
|
8
|
+
# all the unused steps in your project, run the following command:
|
9
|
+
#
|
10
|
+
# cucumber -d -f Cucumber::Formatter::Unused
|
11
|
+
#
|
12
|
+
# or...
|
13
|
+
#
|
14
|
+
# cucumber -d -f Unused
|
15
|
+
|
16
|
+
require 'cucumber/formatter/stepdefs'
|
17
|
+
|
18
|
+
class Unused < Cucumber::Formatter::Stepdefs
|
19
|
+
def print_summary(features)
|
20
|
+
add_unused_stepdefs
|
21
|
+
keys = @stepdef_to_match.keys.sort {|a,b| a.regexp_source <=> b.regexp_source}
|
22
|
+
puts "The following steps are unused...\n---------"
|
23
|
+
keys.each do |stepdef_key|
|
24
|
+
if @stepdef_to_match[stepdef_key].none?
|
25
|
+
puts "#{stepdef_key.regexp_source}\n#{stepdef_key.file_colon_line}\n---"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
data/lib/accounts.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
# Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
2
|
+
|
3
|
+
require 'accounts/version'
|
4
|
+
require 'accounts/model'
|
5
|
+
require 'accounts/configure'
|
6
|
+
require 'accounts/helpers'
|
7
|
+
|
8
|
+
module Accounts
|
9
|
+
|
10
|
+
class Server < ::Sinatra::Base
|
11
|
+
|
12
|
+
helpers do
|
13
|
+
include ::Accounts::Helpers
|
14
|
+
end
|
15
|
+
|
16
|
+
error ::Accounts::AccountsError do
|
17
|
+
env['sinatra.error'].return_error_page
|
18
|
+
end
|
19
|
+
|
20
|
+
post '/logon' do
|
21
|
+
email = params[:email]
|
22
|
+
password = params[:password]
|
23
|
+
|
24
|
+
if authenticate! email, password then
|
25
|
+
redirect to Accounts.redirect_after_logon
|
26
|
+
else
|
27
|
+
session[:account_id] = nil
|
28
|
+
halt 403
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
post '/register' do
|
33
|
+
email = params[:email]
|
34
|
+
account = ::Accounts::Account.first({ :email => email })
|
35
|
+
case
|
36
|
+
when !account
|
37
|
+
register_new_account email
|
38
|
+
halt Accounts.post_register_new_account_response
|
39
|
+
when account.status.include?(:email_confirmed)
|
40
|
+
halt Accounts.post_register_already_registered_response[email]
|
41
|
+
end
|
42
|
+
send_change_password_link account
|
43
|
+
Accounts.post_register_new_account_response
|
44
|
+
end
|
45
|
+
|
46
|
+
post '/forgot-password' do
|
47
|
+
email = params[:email]
|
48
|
+
account = ::Accounts::Account.first({ :email => email }) \
|
49
|
+
or halt Accounts.post_forgot_password_email_does_not_match_response[email]
|
50
|
+
send_change_password_link account
|
51
|
+
Accounts.post_forgot_password_response
|
52
|
+
end
|
53
|
+
|
54
|
+
post '/change-password' do
|
55
|
+
halt 403 unless session[:account_id]
|
56
|
+
account = ::Accounts::Account.get(session[:account_id]) \
|
57
|
+
or halt 403
|
58
|
+
account.set_password params[:password]
|
59
|
+
Accounts.deliver_change_password_confirmation[account.email]
|
60
|
+
Accounts.changed_your_password_response
|
61
|
+
end
|
62
|
+
|
63
|
+
post '/change-email' do
|
64
|
+
halt 403 unless session[:account_id]
|
65
|
+
account = ::Accounts::Account.get(session[:account_id]) \
|
66
|
+
or halt 403
|
67
|
+
new_email = params[:email]
|
68
|
+
::Accounts::Account.count(:email => new_email) == 0 \
|
69
|
+
or halt "#{new_email} is already taken"
|
70
|
+
send_change_email_confirmation account, new_email
|
71
|
+
Accounts.deliver_change_email_notification[account.email, new_email]
|
72
|
+
Accounts.post_change_email_response
|
73
|
+
end
|
74
|
+
|
75
|
+
get '/response-token/:token' do
|
76
|
+
respond_to_token params[:token]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# Copyright Westside Consulting LLC, Ann Arbor, MI, USA, 2012
|
2
|
+
|
3
|
+
module Accounts
|
4
|
+
class << self
|
5
|
+
attr_accessor :page_not_found # String
|
6
|
+
attr_accessor :redirect_after_logon # String
|
7
|
+
attr_accessor :post_forgot_password_response # String
|
8
|
+
attr_accessor :changed_your_password_response # String
|
9
|
+
attr_accessor :post_register_new_account_response # String
|
10
|
+
attr_accessor :post_register_already_registered_response # Proc(email)
|
11
|
+
attr_accessor :post_forgot_password_email_does_not_match_response # Proc(email)
|
12
|
+
attr_accessor :post_change_email_response # String
|
13
|
+
attr_accessor :admin_email # String
|
14
|
+
attr_accessor :deliver_registration_confirmation # Proc(email, link)
|
15
|
+
attr_accessor :deliver_change_password_link # Proc(email, link)
|
16
|
+
attr_accessor :deliver_change_password_confirmation # Proc(email)
|
17
|
+
attr_accessor :deliver_change_email_confirmation # Proc(old_email, new_email, link)
|
18
|
+
attr_accessor :deliver_new_account_admin_notification #Proc(email)
|
19
|
+
attr_accessor :deliver_change_email_notification #Proc(old_email, new_email)
|
20
|
+
|
21
|
+
##
|
22
|
+
# Configure Accounts with your custom pages and e-mails.
|
23
|
+
#
|
24
|
+
# Accounts.configure do |config|
|
25
|
+
# config.changed_your_password_response = "You have changed your password."
|
26
|
+
# config.redirect_after_logon = "/welcome"
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# See lib/accounts/configure.rb for complete example.
|
30
|
+
#
|
31
|
+
# All config params that are strings may be replaced with Procs.
|
32
|
+
def configure
|
33
|
+
yield self
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Set defaults - can be overiden
|
39
|
+
Accounts.configure do |config|
|
40
|
+
|
41
|
+
config.changed_your_password_response = "You have changed your password."
|
42
|
+
config.redirect_after_logon = "/welcome"
|
43
|
+
config.post_register_new_account_response = "Check your e-mail."
|
44
|
+
config.post_forgot_password_response = "Check your e-mail to change your password."
|
45
|
+
config.post_change_email_response = "Check your e-mail."
|
46
|
+
config.admin_email = 'admin@accounts.test'
|
47
|
+
|
48
|
+
config.post_forgot_password_email_does_not_match_response = ->(email) {
|
49
|
+
%Q{#{email} does not match any account. You may <a href="/register">register here</a>.}
|
50
|
+
}
|
51
|
+
|
52
|
+
config.post_register_already_registered_response = ->(email) {
|
53
|
+
%Q{#{email} is already registered. You may <a href="/logon?email=#{email}"log on</a>.}
|
54
|
+
}
|
55
|
+
|
56
|
+
config.deliver_registration_confirmation = ->(email, link) {
|
57
|
+
Mail.deliver do
|
58
|
+
from Accounts.admin_email
|
59
|
+
to email
|
60
|
+
subject 'Your e-mail is confirmed'
|
61
|
+
body %Q{You have registered for accounts.test.
|
62
|
+
|
63
|
+
Follow this link to confirm your e-mail address and set your password: #{link}
|
64
|
+
}
|
65
|
+
end
|
66
|
+
}
|
67
|
+
|
68
|
+
config.deliver_change_password_link = ->(email, link) {
|
69
|
+
Mail.deliver do
|
70
|
+
from Accounts.admin_email
|
71
|
+
to email
|
72
|
+
subject 'How to change your password'
|
73
|
+
body "Follow this link to change your password: #{link}"
|
74
|
+
end
|
75
|
+
}
|
76
|
+
|
77
|
+
config.deliver_change_password_confirmation = ->(email) {
|
78
|
+
Mail.deliver do
|
79
|
+
from Accounts.admin_email
|
80
|
+
to email
|
81
|
+
subject 'Your password has changed'
|
82
|
+
body "The password for #{email} has changed."
|
83
|
+
end
|
84
|
+
}
|
85
|
+
|
86
|
+
config.deliver_change_email_confirmation = ->(old_email, new_email, link) {
|
87
|
+
Mail.deliver do
|
88
|
+
from Accounts.admin_email
|
89
|
+
to new_email
|
90
|
+
subject 'You requested to change your e-mail'
|
91
|
+
body %Q{You have requested to change your e-mail to #{new_email}."
|
92
|
+
|
93
|
+
You must visit this link to confirm: #{link}
|
94
|
+
|
95
|
+
#{old_email} has also been sent a notification e-mail.
|
96
|
+
}
|
97
|
+
end
|
98
|
+
}
|
99
|
+
config.deliver_new_account_admin_notification = ->(email) {
|
100
|
+
Mail.deliver do
|
101
|
+
from Accounts.admin_email
|
102
|
+
to Accounts.admin_email
|
103
|
+
subject 'new account has confirmed e-mail'
|
104
|
+
body "#{email} has registered and confirmed"
|
105
|
+
end
|
106
|
+
}
|
107
|
+
|
108
|
+
config.deliver_change_email_notification = ->(old_email, new_email) {
|
109
|
+
Mail.deliver do
|
110
|
+
from Accounts.admin_email
|
111
|
+
to old_email
|
112
|
+
subject 'You requested to change your e-mail'
|
113
|
+
body %Q{You have requested to change your e-mail to #{new_email}."
|
114
|
+
|
115
|
+
An e-mail has been sent to #{new_email}.
|
116
|
+
|
117
|
+
Please open that mail and follow the instructions.
|
118
|
+
}
|
119
|
+
end
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|