我正致力于创建一个支持数据库的电子邮件审核系统,以便我可以跟踪电子邮件。棘手的部分是我希望能够通过邮件程序类组织这些并且还能够存储邮件程序方法的名称。
创建邮件拦截器或观察者从Mail::Message
实例收集数据并不困难,但我很好奇是否有方法来捕获类和方法创建该消息实例的名称。
如果可能的话,我宁愿不使用回调。
有什么想法吗?
答案 0 :(得分:9)
这就是我最终的目标......我希望能够以这种方式对这种做法的利弊进行一些反馈。感觉有点难看,但很容易。基本上,我包括在我的邮件程序中使用回调的功能,将类和方法名称元数据附加到Mail::Message
对象,以便在我的观察者中可以访问它。我通过在Mail::Message
对象上设置实例变量来附加它,然后将attr_reader
发送到Mail::Message
类,这样我就可以调用mail.mailer_klass
和mail.mailer_action
。< / p>
我是这样做的,因为我想在交付后记录Mail::Message
对象,这样我就可以得到它发送的确切日期,并知道记录的电子邮件应该已成功发送。
邮件:
class MyMailer < ActionMailer::Base
default from: "noreply@theapp.com"
include AbstractController::Callbacks
# Where I attach the class and method
after_action :attach_metadata
def welcome_note(user)
@user = user
mail(subject: "Thanks for signing up!", to: @user.email)
end
private
def attach_metadata
mailer_klass = self.class.to_s
mailer_action = self.action_name
self.message.instance_variable_set(:@mailer_klass, mailer_klass)
self.message.instance_variable_set(:@mailer_action, mailer_action)
self.message.class.send(:attr_reader, :mailer_klass)
self.message.class.send(:attr_reader, :mailer_action)
end
end
观察员:
class MailAuditor
def self.delivered_email(mail)
if mail.multipart?
body = mail.html_part.decoded
else
body = mail.body.raw_source
end
Email.create!(
sender: mail.from,
recipient: mail.to,
bcc: mail.bcc,
cc: mail.cc,
subject: mail.subject,
body: body,
mailer_klass: mail.mailer_klass,
mailer_action: mail.mailer_action,
sent_at: mail.date
)
end
end
配置/初始化/ mail.rb
ActionMailer::Base.register_observer(MailAuditor)
思想?
答案 1 :(得分:3)
不确定你在这里问的是什么......你想跟踪Mailer的使用时间或使用地点?
如果是前者,您可以使用类似:https://gist.github.com/ridiculous/783cf3686c51341ba32f
的方式挂钩方法调用如果是后者,那么我能想到的唯一方法是使用__callee__
来获取该信息。
希望有所帮助!
答案 2 :(得分:0)
我可能会使用alias_method_chain
类型的方法 - 类似于Ryan的方法,但不使用eval
。
https://gist.github.com/kenmazaika/cd80b0379655d39690d8
从未成为观察家的忠实粉丝。
答案 3 :(得分:0)
为了阐明Mark Murphy第一次评论所暗示的一个更简单的答案,我采用了一种非常简单的方法:
class ApplicationMailer < ActionMailer::Base
default from: "noreply@theapp.com"
after_action :log_email
private
def log_email
mailer_class = self.class.to_s
mailer_action = self.action_name
EmailLog.log("#{mailer_class}##{mailer_action}", message)
end
end
使用简单模型EmailLog来保存记录
class EmailLog < ApplicationRecord
def self.log(email_type, message)
EmailLog.create(
email_type: email_type,
from: self.comma_separated(message.from),
to: self.comma_separated(message.to),
cc: self.comma_separated(message.cc),
subject: message.subject,
body: message.body)
end
private
def self.comma_separated(arr)
if !arr.nil? && !arr.empty?
arr.join(",")
else
""
end
end
end
如果您的所有邮件程序都来自ApplicationMailer,那么您已经完成了设置。
答案 4 :(得分:0)
您可以使用process_action
callback(为什么?)来拦截邮件参数,例如:
class BaseMailer < ActionMailer::Base
private
# see https://api.rubyonrails.org/classes/AbstractController/Callbacks.html#method-i-process_action
def process_action(action_name, *action_args)
super
track_email_status(action_name, action_args)
end
# move these methods to the concern, copied here for the sake of simplicity!
def track_email_status(action_name, action_args)
email_status = EmailStatus.create!(
user: (action_args.first if action_args.first.is_a?(User)),
email: message.to.first,
mailer_kind: "#{self.class.name}##{action_name}",
mailer_args: tracked_mailer_args(action_name, action_args)
)
message.instance_variable_set(:@email_status, email_status)
end
def tracked_mailer_args(action_name, action_args)
args_map = method(action_name).parameters.map(&:second).zip(action_args).to_h
args_map = self.class.parameter_filter.filter(args_map)
simplify_tracked_arg(args_map.values)
end
def simplify_tracked_arg(argument)
case argument
when Hash then argument.transform_values { |v| simplify_tracked_arg(v) }
when Array then argument.map { |arg| simplify_tracked_arg(arg) }
when ActiveRecord::Base then "#{argument.class.name}##{argument.id}"
else argument
end
end
def self.parameter_filter
@parameter_filter ||= ActionDispatch::Http::ParameterFilter.new(Rails.application.config.filter_parameters)
end
end
这样,您可以跟踪邮件标题/类/ action_name /参数,并构建复杂的电子邮件跟踪后端。您还可以使用观察者来跟踪电子邮件的发送时间:
class EmailStatusObserver
def self.delivered_email(mail)
mail.instance_variable_get(:@email_status)&.touch(:sent_at)
end
end
# config/initializers/email_status_observer.rb
ActionMailer::Base.register_observer(EmailStatusObserver)
RSpec测试:
describe BaseMailer do
context 'track email status' do
let(:school) { create(:school) }
let(:teacher) { create(:teacher, school: school) }
let(:password) { 'May the Force be with you' }
let(:current_time) { Time.current.change(usec: 0) }
around { |example| travel_to(current_time, &example) }
class TestBaseMailer < BaseMailer
def test_email(user, password)
mail to: user.email, body: password
end
end
subject { TestBaseMailer.test_email(teacher, password) }
it 'creates EmailStatus with tracking data' do
expect { subject.deliver_now }.to change { EmailStatus.count }.by(1)
email_status = EmailStatus.last
expect(email_status.user_id).to eq(teacher.id)
expect(email_status.email).to eq(teacher.email)
expect(email_status.sent_at).to eq(current_time)
expect(email_status.status).to eq(:sent)
expect(email_status.mailer_kind).to eq('TestBaseMailer#test_email')
expect(email_status.mailer_args).to eq(["Teacher##{teacher.id}", '[FILTERED]'])
end
end
end