Понимание обратных вызовов сохранения в рельсах: вставка строки базы данных откатывается после сбоя, который должен произойти позже - PullRequest
0 голосов
/ 05 января 2019

Я унаследовал приложение Rails с проблемой: создание пользователя откатывается, если не удается отправить приветственное письмо. (Но мы всегда хотим, чтобы пользователь был создан).

create вызывает метод new , затем save , и шаг электронной почты вызывается в after_create , поэтому проблема с электронной почтой не должна не влияет на сохранение, верно? Но в соответствии с трассировкой стека мы терпим неудачу в:

  • send_email метод ( delivery_now строка). Этот метод был вызван:
  • метод create в users_controller (строка if @ user.save )

Примечание: единственное место в коде, которое вызывается send_email , находится в user.rb:

after_create :send_email

И, конечно, мы ожидаем, что транзакция будет отменена, если какой-либо из обратных вызовов 'before' вернет false, но есть только два, которые просто обновляют данные объекта и кажутся не связанными с отправкой по электронной почте:

  before_save :combine_dates
  before_save :set_timestamps

См. Ниже два метода, которые терпят неудачу, а затем два метода «до», которые кажутся несвязанными. Интересно, что когда я ломаю электронную почту, чтобы она не отправлялась, мы не регистрируем ни одну из этих строк logger.error в CAPS - означает ли это, что @ user.save дает сбой так, как мы не можем восстановить?

Вот метод создания:

  def create
    @user = User.new(user_params_with_races)

    existing_user = User.find_by_email(@user.email)
    if existing_user
        @user.is_dupe = true
    end

    if @user.save
      Rails.logger.error "USER.SAVE WAS TRUE"
      respond_to do |format|
        format.html do 
          if @user.contact?
            redirect_to mailing_list_user_path(@user)
            return
          end
          redirect_to edit_user_path(@user), notice: 'User was successfully created.'
        end
        format.js do 
          if @user.contact?
            render json: {redirect: mailing_list_user_path(@user)}
          else
            render json: {action: user_path(@user), email: @user.email}  
          end
        end
      end      

    else
      Rails.logger.error "USER.SAVE WAS FALSE"
      respond_to do |format|
        format.html { render :new }
        format.js { head :not_acceptable }
      end
    end
  end

Вот send_email:

  def send_email
    UserMailer.mailing_list(self).deliver_now if contact?
    UserMailer.welcome(self).deliver_now if diagnosed?
  end

Вот два метода «до»:

  def set_timestamps
    if email == email_confirmation and registered_at.blank?
      self.registered_at = Time.new
    end
  end

  def combine_dates
    if birthday_year_year.present? && birthday_day.present? && birthday_month.present?
      begin
        self.send('birthday=', Date.new(birthday_year_year.to_i, birthday_month.to_i, birthday_day.to_i))
      rescue
        self.send('birthday=', nil)
      end
    end
    if bd_birthday_year.present? && bd_birthday_day.present? && bd_birthday_month.present?
      begin
        self.send('bd_birthday=', Date.new(bd_birthday_year.to_i, bd_birthday_month.to_i, bd_birthday_day.to_i))
      rescue
        self.send('bd_birthday=', nil)
      end
    end
  end

Ответы [ 2 ]

0 голосов
/ 05 января 2019

Если вы возвращаете false или у вас есть исключение в before_save или если у вас есть исключение в after_save, транзакция откатывается и ничего не сохраняется.

Один из способов справиться с этим (если вы всегда хотите отправлять сообщения даже при сбое электронной почты) - перехватить исключение в методе, используя rescue ...

Но даже лучше, использовать after_commit вместо after_save

after_commit :send_email

Что существует именно по этой причине.

0 голосов
/ 05 января 2019
  1. after_save вызывается до совершения транзакции , поэтому, если возникает исключение, запись не сохраняется в БД.
  2. Обратный вызов в Rails по умолчанию является синхронным, поэтому, если один из них вызывает необработанное исключение, «основной поток» прерывается и #save вызывает ошибку.
  3. Как отметил @jvillian в комментарии, отправлять электронные письма из обратных вызовов ActiveRercord - плохая идея.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...