Лучший способ обработки данных в сервисе Rails - PullRequest
0 голосов
/ 24 октября 2019

У меня есть служба напоминаний, которая принимает некоторые «запросы на изменения», а затем отправляет их в рабочую очередь для последующего выполнения. Каждый «запрос на изменение» может иметь напоминание «за десять минут до» или «один час после напоминания». В будущем у него также будут различные типы напоминаний.

Я ищу самый отточенный рубиновый способ обработки и отправки каждого из напоминаний рабочему, если утверждения не подходят, ноЯ могу ошибаться!

class ReminderService

  # @data
  # ten_minute_before: data[:ten_minutes_before_reminder] bool,
  # one_hour_after: data[:one_hour_after_reminder] bool,
  # user: user,
  # price_change_ids: data[:change_requests],
  # user_hash: user_hash(user)

  attr_accessor :data, :user

  def initialize(user, data)
    {data[:price_change_ids]}
    @user = user
    @data = data
  end

  def run
    change_requests
  end

  def send
    reminderWorker.perform_at(action_reminder_at, price_change_id, reminder_type, user, user_hash)
  end

  def change_requests
    data[:price_change_ids]
  end

  def user_hash
    data[:user_hash]
  end

  def action_reminder_at
    DateTime.parse(price_change_at) - 11.minutes
  end

  def price_change_id
    data[:price_change_ids].first.id
  end

  def price_change_at
    data[:user_hash][:price_change_at_time]
  end
end

Работник службы:

class PriceReminderWorker
  include Sidekiq::Worker

  sidekiq_options queue: 'price_reminders', retry: true

  def perform(price_change_id, reminder, user, data)
    id = price_change_id
    price_change = priceChangeRequest.find(id)

    if reminder[:ten_minutes_before]
      reminderMailer.notify(user, data).deliver_later unless price_change.cancelled_at
    end

    if reminder[:one_hour_after]
      Sms::priceChangeSms.new(build_hash(user)).submit unless price_change.cancelled_at
    end
  end

  def build_hash(user)
    {
        to: Phonelib.parse(user.mobile_number).full_e164,
        company_id: user&.company&.id,
        user_name: user.first_name,
        admin_user_name: data[:from_admin_user],
        body: Sms::Message::priceChangeMessage.new(data).build
    }
  end
end

1 Ответ

1 голос
/ 24 октября 2019

Если вы действительно хотите придерживаться своей текущей PriceChangeRequest структуры, то я думаю, что я бы сделал:

class PriceReminderWorker
  include Sidekiq::Worker
  sidekiq_options queue: 'price_reminders', retry: true

  attr_accessor *%w( price_change_id user data ).freeze

  def perform(price_change_id, reminder, user, data)
    @price_change_id  = price_change_id
    @user             = user
    @data             = data 

    %i(
      ten_minutes_before
      one_hour_after
    ).each do |time_sym|
      send("send_#{time_sym}_reminder") if price_change_request.send("#{time_sym}_reminder")
    end if price_change_request

  end

private 

  def user_hash
    {
      to:               Phonelib.parse(user.mobile_number).full_e164,
      company_id:       user&.company&.id,
      user_name:        user.first_name,
      admin_user_name:  data[:from_admin_user],
      body:             Sms::Message::priceChangeMessage.new(data).build
    }
  end

  def send_ten_minutes_before_reminder
    reminderMailer.notify(user, data).deliver_later unless price_change.cancelled_at
  end

  def send_one_hour_after_reminder
    Sms::priceChangeSms.new(user_hash).submit unless price_change.cancelled_at
  end

  def price_change_request
    @price_change_request ||= PriceChangeRequest.find(price_change_id)
  end

end

Но я думаю, что это не очень хорошая идея иметьгруппа booleans, как ten_minutes_before_reminder и one_hour_after_reminder в вашем PriceChangeRequest классе. Особенно, если вы начинаете иметь много типов напоминаний. Скорее всего, вы получите малонаселенные логические поля, что не так уж и велико. И вам придется запускать новые миграции каждый раз, когда вы хотите добавить новый тип напоминания. Бла, бла, бла.

На вашем месте, я думаю, у меня возник бы соблазн иметь класс ReminderTime, например:

# == Schema Information
#
# Table name: reminder_times
#
#  id           :bigint           not null, primary key
#  system_name  :string           not null
#  created_at   :datetime         not null
#  updated_at   :datetime         not null
#
class ReminderTime < ApplicationRecord
  validates :system_name, presence: true
  has_many :price_change_request_reminder_times
  has_many :price_change_requests, through: :price_change_request_reminder_times
end

и PriceChangeRequestReminderTime что-то вроде:

# == Schema Information
#
# Table name: price_change_request_reminder_times
#
#  id                       :bigint           not null, primary key
#  reminder_time_id         :integer          not null
#  price_change_request_id  :integer          not null
#  created_at               :datetime         not null
#  updated_at               :datetime         not null
#
class PriceChangeRequestReminderTime < ApplicationRecord
  belongs_to :reminder_time
  belongs_to :price_change_request
end

Затем в вашем PriceChangeRequest сделайте что-то вроде:

class PriceChangeRequest < ApplicationRecord
  has_many :price_change_request_reminder_times
  has_many :reminder_times, through: :price_change_request_reminder_times

  def set_reminder_times(*time_syms)
    time_syms.each do |time_sym|
      reminder_times << ReminderTime.find_or_create_by!(system_name: time_sym)
    end
  end

  def remove_reminder_times(*type_syms)
    price_change_request_reminder_times.
      where(reminder_time: ReminderTime.find_by(system_name: type_syms)).
      destroy_all
  end

  def reminder_time_names
    reminder_times.pluck(:system_name)
  end

end

Чтобы установить время напоминания, вы должны сделать что-то вроде:

@price_change_request.set_reminder_times :ten_minutes_before 

Или

@price_change_request.set_reminder_times :ten_minutes_before, :one_hour_after

Чтобы удалить время напоминания, вы должны сделать что-то вроде:

@price_change_request.remove_reminder_times :ten_minutes_before

Или

@price_change_request.remove_reminder_times :one_hour_after, :ten_minutes_before

... чтобы вы могли сделать что-то вроде:

class PriceReminderWorker
  include Sidekiq::Worker
  sidekiq_options queue: 'price_reminders', retry: true

  attr_accessor *%w( 
    price_change_id 
    user 
    data 
  ).freeze

  delegate *%w(
    reminder_time_names
  ), to: :price_change_request

  def perform(price_change_id, reminder, user, data)
    @price_change_id  = price_change_id
    @user             = user
    @data             = data 
    reminder_time_names.each do |reminder_time_name|
      send("send_#{reminder_time_name}_reminder")
    end if price_change_request
  end

private 

  def user_hash
    {
      to:               Phonelib.parse(user.mobile_number).full_e164,
      company_id:       user&.company&.id,
      user_name:        user.first_name,
      admin_user_name:  data[:from_admin_user],
      body:             Sms::Message::priceChangeMessage.new(data).build
    }
  end

  def send_ten_minutes_before_reminder
    reminderMailer.notify(user, data).deliver_later unless price_change.cancelled_at
  end

  def send_one_hour_after_reminder
    Sms::priceChangeSms.new(user_hash).submit unless price_change.cancelled_at
  end

  def price_change_request
    @price_change_request ||= PriceChangeRequest.find(price_change_id)
  end

end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...