Cru*_*nez 7 ruby-on-rails actionmailer devise
我有一个可以处理许多子域的 rails 应用程序,并且我有多个使用不同域名运行的实时版本。这会导致 URL 介于
mywebsite.comcompany1.mywebsite.comcompany1.mytestwebsite.comDevise有使用链接重置密码等的邮件程序。这些链接有时不正确并将我发送到不正确的网站,因为主机名有时与以下默认 URL 的主机名不同/config/environments/production.rb:
config.action_mailer.default_url_options = { host: 'mywebsite.com' }
Run Code Online (Sandbox Code Playgroud)
如何request.host从控制器传递到设计邮件程序?
如果我有主机,我可以创建将用户发送到正确网站的链接
只需添加一个文件/config/initializers并覆盖设备控制器
文件:
# config/initializers/devise_monkeypatch.rb
module Devise
module Models
module Recoverable
module ClassMethods
# extract data from attributes hash and pass it to the next method
def send_reset_password_instructions(attributes = {})
data = attributes.delete(:data).to_unsafe_h
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions(data) if recoverable.persisted?
recoverable
end
end
# adjust so it accepts data parameter and sends it to next method
def send_reset_password_instructions(data)
token = set_reset_password_token
send_reset_password_instructions_notification(token, data)
token
end
# adjust so it accepts data parameter and sends to next method
protected def send_reset_password_instructions_notification(token, data)
send_devise_notification(:reset_password_instructions, token, data: data)
end
end
end
Mailer.class_eval do
# extract data from options and set it as instance variable
def reset_password_instructions(record, token, opts={})
@token = token
@data = opts.delete :data
devise_mail(record, :reset_password_instructions, opts)
end
end
end
Run Code Online (Sandbox Code Playgroud)
生成控制器和视图
rails g devise:controllers users -c=passwords
rails g devise:views
Run Code Online (Sandbox Code Playgroud)
编辑路线
# config/routes.rb
devise_for :users, controllers: {
passwords: 'users/passwords'
}
Run Code Online (Sandbox Code Playgroud)
编辑创建操作
class Users::PasswordsController < Devise::PasswordsController
def create
params[:user][:data] = { host: request.url.remove(request.path) }
super
end
end
Run Code Online (Sandbox Code Playgroud)
编辑视图
<p>
<%= link_to 'Change my password',
edit_password_url(
@resource, reset_password_token: @token, host: @data[:host]
)
%>
</p>
Run Code Online (Sandbox Code Playgroud)
解释如下:
查看源代码,这些是用于从控制器到邮件程序的方法。
# the controller calls send_reset_pasword_instructions on class
Devise::PasswordsController#create
resource_class.send_reset_password_instructions(resource_params)
# class finds instance and calls send_reset_password_instructions on instance
Devise::Models::Recoverable::ClassMethods
def send_reset_password_instructions(attributes = {})
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions if recoverable.persisted?
recoverable
end
Devise::Models::Recoverable
# instance calls send_reset_password_instructions_notification
def send_reset_password_instructions
token = set_reset_password_token
send_reset_password_instructions_notification(token)
token
end
# instance calls send_devise_notification
protected def send_reset_password_instructions_notification(token)
send_devise_notification(:reset_password_instructions, token, {})
end
Devise::Models::Authenticatable
# instance calls mailer
protected def send_devise_notification(notification, *args)
message = devise_mailer.send(notification, self, *args)
# Remove once we move to Rails 4.2+ only.
if message.respond_to?(:deliver_now)
message.deliver_now
else
message.deliver
end
end
# mailer
protected def devise_mailer
Devise.mailer
end
class Devise::Mailer
# mailer sets @token
def reset_password_instructions(record, token, opts={})
@token = token
devise_mail(record, :reset_password_instructions, opts)
end
Run Code Online (Sandbox Code Playgroud)
您只需要在最后一个方法中设置另一个实例变量,但您必须编辑其他方法才能成功传递数据。
这是原始与所需更改的比较:
Devise::PasswordsController
# original, will stay the same
def create
self.resource = resource_class.send_reset_password_instructions(resource_params)
yield resource if block_given?
if successfully_sent?(resource)
respond_with({}, location: after_sending_reset_password_instructions_path_for(resource_name))
else
respond_with(resource)
end
end
# override to add data
def create
params[:user][:data] = request.url
super
end
Devise::Models::Recoverable::ClassMethods
# original, will be overwritten
def send_reset_password_instructions(attributes = {})
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions if recoverable.persisted?
recoverable
end
# extract data from attributes hash and pass it to the next method
def send_reset_password_instructions(attributes = {})
data = attributes.delete :data
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions(data) if recoverable.persisted?
recoverable
end
Devise::Models::Recoverable
# original, will be overwritten
def send_reset_password_instructions
token = set_reset_password_token
send_reset_password_instructions_notification(token)
token
end
# adjust so it accepts data parameter and sends it to next method
def send_reset_password_instructions(data)
token = set_reset_password_token
send_reset_password_instructions_notification(token, data)
token
end
# original, will be overwritten
protected def send_reset_password_instructions_notification(token)
send_devise_notification(:reset_password_instructions, token, {})
end
# adjust so it accepts data parameter and sends to next method
protected def send_reset_password_instructions_notification(token, data)
send_devise_notification(:reset_password_instructions, token, data: data)
end
Devise::Models::Authenticatable
# original, stays the same
protected def send_devise_notification(notification, *args)
message = devise_mailer.send(notification, self, *args)
# Remove once we move to Rails 4.2+ only.
if message.respond_to?(:deliver_now)
message.deliver_now
else
message.deliver
end
end
# original, stays the same
protected def devise_mailer
Devise.mailer
end
class Devise::Mailer
# extract data from options and set it as instance variable
def reset_password_instructions(record, token, opts={})
@token = token
@data = opts.delete[:data]
devise_mail(record, :reset_password_instructions, opts)
end
Run Code Online (Sandbox Code Playgroud)
删除保持不变的代码以及控制器代码,因为我们将在另一个文件中更改它。将它包装成一个整洁的小模块并对其进行调整,以便我们向类添加方法并传递Hash对象而不是Parameter对象。
这是最终版本
module Devise
module Models
module Recoverable
module ClassMethods
# extract data from attributes paramater object and convert it to hash
# and pass it to the next method
def send_reset_password_instructions(attributes = {})
data = attributes.delete(:data).to_unsafe_h
recoverable = find_or_initialize_with_errors(reset_password_keys, attributes, :not_found)
recoverable.send_reset_password_instructions(data) if recoverable.persisted?
recoverable
end
end
# adjust so it accepts data parameter and sends it to next method
def send_reset_password_instructions(data)
token = set_reset_password_token
send_reset_password_instructions_notification(token, data)
token
end
# adjust so it accepts data parameter and sends to next method
protected def send_reset_password_instructions_notification(token, data)
send_devise_notification(:reset_password_instructions, token, data: data)
end
end
end
Mailer.class_eval do
# extract data from options and set it as instance variable
def reset_password_instructions(record, token, opts={})
@token = token
@data = opts.delete :data
devise_mail(record, :reset_password_instructions, opts)
end
end
end
Run Code Online (Sandbox Code Playgroud)
我自己回答了这个问题,因为我无法在网上找到解决方案,即使还有其他人也问过这个问题。将此发布在 Stack Overflow 上以最大限度地帮助其他人解决相同的问题。
| 归档时间: |
|
| 查看次数: |
1500 次 |
| 最近记录: |