如何在 aasm 回调中调用 ActionMailer 方法?
How to call ActionMailer method in aasm callback?
我在 rails 上学习 ruby,但在使用 aasm 回调和 actionmailer 时遇到了问题。
我有一个酒店模型。这是一个代码:
class Hotel < ActiveRecord::Base
include AASM
scope :approved_hotels, -> { where(aasm_state: "approved") }
has_many :comments
belongs_to :user, :counter_cache => true
has_many :ratings
belongs_to :address
aasm do
state :pending, initial: true
state :approved
state :rejected
event :approve, :after => :send_email do
transitions from: :pending, to: :approved
end
event :reject, :after => :send_email do
transitions from: :pending, to: :rejected
end
end
def send_email
end
end
如您所见,当用户添加的酒店状态发生变化时,他必须收到电子邮件。这是我写的,但它不是解决方案,因为每次管理员更新酒店 "pending" 状态时,用户都会收到电子邮件。
class HotelsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show, :top5hotels]
def update
@hotel = Hotel.find(params[:id])
if @hotel.aasm_state == "pending"
@hotel.aasm_state = params[:state]
UserMailer.changed_state_email(current_user, @hotel.name,
@hotel.aasm_state).deliver
end
if @hotel.update_attributes!(params[:hotel])
redirect_to admin_hotel_path(@hotel), notice: "Hotel was successfully updated."
else
render "edit"
end
end
end
所以我想我需要使用回调,但我不知道如何调用
UserMailer.changed_state_email(current_user, @hotel.name,
@hotel.aasm_state).deliver
来自模型。
我试过了
UserMailer.changed_state_email(User.find(:id), Hotel.find(:name),
Hotel.find(aasm_state)).deliver
但这不起作用。
我真的别无选择,正在寻求任何帮助。
谢谢!
您不能使用回调,因为您有 current_user
它是控制器上下文的一部分,并且您无法访问模型上下文中的请求信息。
无论如何,即使您可以使用回调,在这种情况下我强烈建议您采用不同的方法。应该很少使用 ActiveRecord 回调,特别是对于涉及与其他对象或资源(例如邮件程序或级联更新)交互的任何代码,您应该避免使用它们。风险是即使你不需要它(例如测试)也会触发回调增加开销,或者当项目的复杂性增加时它会与其他回调冲突。
在这种情况下,解决方案非常简单。在模型中定义一个新方法(我现在不向您介绍服务对象...),您可以使用它来更改状态和发送电子邮件。
class Hotel
def user_state_change(user, new_state)
return unless pending? && new_state.present?
if update_attribute(:aasm_state, new_state)
UserMailer.changed_state_email(user, name, aasm_state).deliver
end
end
end
您的控制器将变成
class HotelsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show, :top5hotels]
def update
@hotel = Hotel.find(params[:id])
@hotel.user_state_change(current_user, params[:state])
if @hotel.update_attributes!(params[:hotel])
redirect_to admin_hotel_path(@hotel), notice: "Hotel was successfully updated."
else
render "edit"
end
end
end
附带说明一下,您可能希望使用状态机转换方法,而不是更改状态属性。事实上,使用状态机转换将确保触发转换验证。
我在 rails 上学习 ruby,但在使用 aasm 回调和 actionmailer 时遇到了问题。 我有一个酒店模型。这是一个代码:
class Hotel < ActiveRecord::Base
include AASM
scope :approved_hotels, -> { where(aasm_state: "approved") }
has_many :comments
belongs_to :user, :counter_cache => true
has_many :ratings
belongs_to :address
aasm do
state :pending, initial: true
state :approved
state :rejected
event :approve, :after => :send_email do
transitions from: :pending, to: :approved
end
event :reject, :after => :send_email do
transitions from: :pending, to: :rejected
end
end
def send_email
end
end
如您所见,当用户添加的酒店状态发生变化时,他必须收到电子邮件。这是我写的,但它不是解决方案,因为每次管理员更新酒店 "pending" 状态时,用户都会收到电子邮件。
class HotelsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show, :top5hotels]
def update
@hotel = Hotel.find(params[:id])
if @hotel.aasm_state == "pending"
@hotel.aasm_state = params[:state]
UserMailer.changed_state_email(current_user, @hotel.name,
@hotel.aasm_state).deliver
end
if @hotel.update_attributes!(params[:hotel])
redirect_to admin_hotel_path(@hotel), notice: "Hotel was successfully updated."
else
render "edit"
end
end
end
所以我想我需要使用回调,但我不知道如何调用
UserMailer.changed_state_email(current_user, @hotel.name,
@hotel.aasm_state).deliver
来自模型。 我试过了
UserMailer.changed_state_email(User.find(:id), Hotel.find(:name),
Hotel.find(aasm_state)).deliver
但这不起作用。 我真的别无选择,正在寻求任何帮助。 谢谢!
您不能使用回调,因为您有 current_user
它是控制器上下文的一部分,并且您无法访问模型上下文中的请求信息。
无论如何,即使您可以使用回调,在这种情况下我强烈建议您采用不同的方法。应该很少使用 ActiveRecord 回调,特别是对于涉及与其他对象或资源(例如邮件程序或级联更新)交互的任何代码,您应该避免使用它们。风险是即使你不需要它(例如测试)也会触发回调增加开销,或者当项目的复杂性增加时它会与其他回调冲突。
在这种情况下,解决方案非常简单。在模型中定义一个新方法(我现在不向您介绍服务对象...),您可以使用它来更改状态和发送电子邮件。
class Hotel
def user_state_change(user, new_state)
return unless pending? && new_state.present?
if update_attribute(:aasm_state, new_state)
UserMailer.changed_state_email(user, name, aasm_state).deliver
end
end
end
您的控制器将变成
class HotelsController < ApplicationController
before_filter :authenticate_user!, except: [:index, :show, :top5hotels]
def update
@hotel = Hotel.find(params[:id])
@hotel.user_state_change(current_user, params[:state])
if @hotel.update_attributes!(params[:hotel])
redirect_to admin_hotel_path(@hotel), notice: "Hotel was successfully updated."
else
render "edit"
end
end
end
附带说明一下,您可能希望使用状态机转换方法,而不是更改状态属性。事实上,使用状态机转换将确保触发转换验证。