Подтвердить что ты не робот

Как избежать ActionMailer:: Предварительный просмотр данных в базу данных разработки?

Я использую предварительный просмотр Rails 4.1.0.beta1 новых Action Mailer и имею следующий код:

class EventInvitationPreview < ActionMailer::Preview
  def invitation_email
    invite = FactoryGirl.create :event_invitation, :for_match, :from_user, :to_user
    EventInvitationMailer.invitation_email(invite)
  end
end

Это все хорошо, пока я на самом деле не попытаюсь просмотреть мою электронную почту и получить сообщение о том, что проверка на объекте пользователя не удалась из-за дублирования адресов электронной почты. Оказывается, что ActionMailer:: Preview записывает в мою базу данных разработки.

Хотя я мог бы работать с отказом валидации или использовать приборы вместо фабрик, есть ли способ избежать записи ActionMailer:: Preview в базу данных разработки, например. вместо этого используйте тестовую базу данных? Или я просто делаю это неправильно?

4b9b3361

Ответ 1

TL; DR. Оригинальный автор функции предварительного просмотра ActionMailer (с помощью MailView) предоставляет три примеры различных поддерживаемых подходов:

  • Вытяните данные из существующих светильников: Account.first
  • Factory -образный шаблон: user = User.create!, за которым следует user.destroy
  • Stub-like: Struct.new(:email, :name).new('[email protected]', 'Jill Smith')

~ ~ ~ ~ ~ ~ ~ ~ ~

Выяснить проблему, с которой сталкивается OP...

Другим проявлением этой проблемы является попытка использовать FactoryGirl.build (а не создавать) для генерации непостоянных данных. Этот подход предлагается одним из лучших результатов Google для "Rails 4.1" - http://brewhouse.io/blog/2013/12/17/whats-new-in-rails-4-1.html?brewPubStart=1 - в том, как использовать этот новый особенность ". Этот подход представляется разумным, однако, если вы пытаетесь создать URL-адрес на основе этих данных, это приводит к ошибке в следующих строках:

ActionController::UrlGenerationError in Rails::Mailers#preview

No route matches {:action=>"edit", :controller=>"password_resets", :format=>nil, :id=>nil} missing required keys: [:id]

Использование FactoryGirl.create (а не сборка) решит эту проблему, но, как отмечает OP, приведет к загрязнению базы данных разработки.

Если вы проверите документы для оригинальной MailView, которая стала этой функцией Rails 4.1, оригинальный автор дает немного больше информации о своих намерений в этой ситуации. А именно, исходный автор предоставляет следующие три примера, все из которых направлены на повторное использование/очистку/ненасыщенность данных, а не на предоставление средств для использования другой базы данных:

# app/mailers/mail_preview.rb or lib/mail_preview.rb
class MailPreview < MailView
  # Pull data from existing fixtures
  def invitation
    account = Account.first
    inviter, invitee = account.users[0, 2]
    Notifier.invitation(inviter, invitee) 
  end

  # Factory-like pattern
  def welcome
    user = User.create!
    mail = Notifier.welcome(user)
    user.destroy
    mail
  end

  # Stub-like
  def forgot_password
    user = Struct.new(:email, :name).new('[email protected]', 'Jill Smith')
    mail = UserMailer.forgot_password(user)
  end
end

Ответ 2

Вы можете просто использовать транзакцию вокруг предварительного просмотра электронной почты, просто поместите ее в свой lib/monkey_mailers_controller.rb (и требуйте):

# lib/monkey_mailers_controller.rb
class Rails::MailersController
  alias_method :preview_orig, :preview

  def preview
    ActiveRecord::Base.transaction do
      preview_orig
      raise ActiveRecord::Rollback
    end
  end
end

Затем вы можете вызвать .create и т.д. в своих почтовых превью, но ничего не будет сохранено в базе данных. Работает в Rails 4.2.3.

Ответ 3

Если у вас сложная иерархия объектов, вы можете использовать транзакционную семантику для отката состояния базы данных, как в тестовой среде (при условии, что ваша БД поддерживает транзакции). Например:

# spec/mailers/previews/price_change_preview.rb
class PriceChangeMailerPreview < ActionMailer::Preview
  #transactional strategy
  def price_decrease
    User.transaction do
      user = FactoryGirl.create(:user, :with_favorited_products) #creates a bunch of nested objects
      mail = PriceChange.price_decrease(user, user.favorited_products.first)
      raise ActiveRecord::Rollback, "Don't really want these objects committed to the db!"
    end
    mail
  end
end

#spec/factories/user.rb
FactoryGirl.define do
  factory :user do
    ...
    trait :with_favorited_products do
      after(:create) do |user|
         user.favorited_products << create(:product)
         user.save!
      end
    end
  end
end

Мы не можем использовать user.destroy с зависимым:: destroy в этом случае, потому что уничтожение связанных продуктов обычно не имеет смысла (если Amazon удаляет меня как клиента, они не удаляют все продукты, которые я предпочитаю с рынка).

Обратите внимание, что транзакции поддерживаются предыдущими реализациями функциональности предварительного просмотра. Не уверены, почему они не поддерживаются ActionMailer:: Preview.