Как избежать 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 в базу данных разработки, например. вместо этого используйте тестовую базу данных? Или я просто делаю это неправильно?
Ответы
Ответ 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.