用户在创建帖子时自动向其发送电子邮件

时间:2015-10-07 19:50:13

标签: ruby-on-rails ruby rspec

我是一名实习生全栈开发人员,他在轨道上学习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

1 个答案:

答案 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

该模式称为服务对象。拥有一个处理单个任务的简单对象很容易测试,并且可以更容易地实现在后台作业中发送电子邮件。我会把那部分留给你。

进一步阅读: