我是一名实习生全栈开发人员,他在轨道上学习ruby,并且是在为期6个月的强化课程的第1个月。
我正在制作一个' reddit'样式应用,用户可以在其中创建主题,帖子和评论。
我正在尝试在用户创建新帖子时自动向其发送电子邮件。
我正在使用ActionMailer。
我正在我的帖子模型中进行after_create回调,并在邮件程序中使用名为“favorite_mailer”的邮件。
我面临的问题是,我无法成功实现after_create回调,这会触发电子邮件在创建帖子后自动发送给用户。
我在我的邮件程序中定义了一个名为new_post的方法,该方法应该接收2个参数(user,post)。
我在Post模型中定义了一个名为send_new_post email的回调方法,但不能让它通过我的Rspec测试。
任何帮助将不胜感激。
我创建了以下Post模型规范:
describe "send_new_post_email" do
it "triggers an after_create callback called send_new_post_email" do
expect(post).to receive(:send_new_post_email).at_least(:once)
post.send(:send_new_post_email)
end
it "sends an email to users when they create a new post" do
expect(FavoriteMailer).to receive(:new_post).with(user, post).and_return(double(deliver_now: true))
post.save
end
end
这是我的帖子模型(相关位是send_new_post_email回调):
class Post < ActiveRecord::Base
belongs_to :topic
belongs_to :user
has_many :comments, dependent: :destroy
has_many :votes, dependent: :destroy
has_many :favorites, dependent: :destroy
has_many :labelings, as: :labelable
has_many :labels, through: :labelings
after_create :create_vote
after_create :create_favorite
after_create :send_new_post_email
default_scope { order('rank DESC') }
validates :title, length: { minimum: 5 }, presence: true
validates :body, length: { minimum: 20 }, presence: true
validates :topic, presence: true
validates :user, presence: true
def up_votes
votes.where(value: 1).count
end
def down_votes
votes.where(value: -1).count
end
def points
votes.sum(:value)
end
def update_rank
age_in_days = (created_at - Time.new(1970,1,1)) / 1.day.seconds
new_rank = points + age_in_days
update_attribute(:rank, new_rank)
end
private
def create_vote
user.votes.create(value: 1, post: self)
end
def create_favorite
user.favorites.create(post: self)
end
def send_new_post_email
FavoriteMailer.new_post(self.user, self)
end
end
最后,这是我的邮件:
class FavoriteMailer < ApplicationMailer
default from: "charlietarr1@gmail.com"
def new_comment(user, post, comment)
# #18
headers["Message-ID"] = "<comments/#{comment.id}@your-app-name.example>"
headers["In-Reply-To"] = "<post/#{post.id}@your-app-name.example>"
headers["References"] = "<post/#{post.id}@your-app-name.example>"
@user = user
@post = post
@comment = comment
# #19
mail(to: user.email, subject: "New comment on #{post.title}")
end
def new_post(user, post)
# #18
headers["Message-ID"] = "<post/#{post.id}@your-app-name.example>"
headers["In-Reply-To"] = "<post/#{post.id}@your-app-name.example>"
headers["References"] = "<post/#{post.id}@your-app-name.example>"
@user = user
@post = post
# #19
mail(to: user.email, subject: "You have favorited #{post.title}")
end
end
答案 0 :(得分:0)
我不会使用模型回调来处理这类生命周期事件。
因为无论何时创建记录都会调用它,这意味着您必须在测试中覆盖它。 ActiveRecord模型很容易变得像神一样臃肿,并且通知用户有点超出维护数据和业务逻辑的模型工作。
它还会妨碍将通知委派给后台作业,如果您需要发送多封电子邮件,这非常重要。
好吧,我们可以把它塞进控制器中。但这可能不是最佳的,因为控制器是PITA测试,我们喜欢瘦。
因此,让我们创建一个对象,其任务是通知用户:
module PostCreationNotifier
def self.call(post)
FavoriteMailer.new_post(post.user, post)
end
end
然后我们将其添加到控制器操作:
def create
@post = Post.new(post_params)
if @post.save
redirect_to @post
PostCreationNotifier.call(@post)
else
render :new
end
end
但是 - 这可能不是你想要的! Doh!它只会通知创作者她刚创建了一个帖子 - 她已经知道了!
如果我们想要通知所有参与者的回复,我们可能需要查看该主题并向所有参与者发送电子邮件:
module PostCreationNotifier
def self.call(post)
post.thread.followers.map do |f|
FavoriteMailer.new_post(f, post)
end
end
end
describe PostCreationNotifier do
let(:followers) { 2.times.map { create(:user) } }
let(:post){ create(:post, thread: create(:thread, followers: followers)) }
let(:mails) { PostCreationNotifier.call(post) }
it "sends an email to each of the followers" do
expect(mails.first.to).to eq followers.first.email
expect(mails.last.to).to eq followers.last.email
end
end
该模式称为服务对象。拥有一个处理单个任务的简单对象很容易测试,并且可以更容易地实现在后台作业中发送电子邮件。我会把那部分留给你。
进一步阅读: