Шаблон декоратора для функциональности модели в Rails 5 - PullRequest
0 голосов
/ 31 мая 2018

Я занимаюсь разработкой приложения на Rails 5 по шаблону «толстая модель / тонкий контроллер».По мере того, как я добавляю такие вещи, как регистрация и проверка, я обнаруживаю, что мои модели становятся слишком толстыми.Например, вот набросок того, как выглядит метод подписки на список ...

class SubscriberList < ApplicationRecord
  # relationships and validations

  def subscribe!(args)
    log that a subscription is attempted

    begin
      do the subscription
    rescue errors
      log the failure and reason
      rethrow
    end

    log successful subscription
    log other details about the subscription

    SubscriptionValidationJob.perform_later( new_subscriber )

    return new_subscriber
  end
end

Все более усложняется то, что регистрация и валидация сводятся к подписке.Я понимаю, что должен решить эту проблему путем перемещения регистрации и проверки в декораторы , вероятно, используя draper.

У меня нет большого опыта работы с декораторами.Моя главная проблема - ошибки из-за того, что в коде используется недекорированная модель, когда она должна использовать декорированную модель.Или наоборот.Интерфейсы одинаковы, изменения являются побочными эффектами, поэтому их будет трудно обнаружить.

Я бы соблазнительно использовал decorates_association и decorates_finders, чтобы избежать этого, но документация Draper гласит:чтобы избежать этого ...

Предполагается, что декораторы ведут себя очень похоже на модели, которые они украшают, и по этой причине очень заманчиво просто декорировать ваши объекты в начале действия вашего контроллера, а затемиспользовать декораторы во всем.Не.

Однако Draper (и большинство статей о Rails + Decorator, которые я смог найти), похоже, сосредоточены на функциональности представления ...

Поскольку декораторы разработанычтобы быть поглощенным видом, вы должны иметь к ним доступ только там.Манипулируйте вашими моделями, чтобы подготовить вещи, а затем украсьте в последнюю минуту, прямо перед визуализацией.Это позволяет избежать многих распространенных ошибок, возникающих при попытке модифицировать декораторы (в частности, декораторы коллекций) после их создания.

В отличие от функциональности представления, где у вас есть контроллер, обеспечивающий передачу моделей моделям.взгляд украшен, мои декораторы для функциональности модели.Декоратор в основном для организации кода и простоты тестирования, почти все должны использовать декорированную модель.

Каковы наилучшие практики использования декораторов для добавления к функциональности модели?Всегда использовать декорированные модели?Что-то более радикальное, например перенос подписки и отписки в другой класс?

1 Ответ

0 голосов
/ 31 мая 2018

Я не думаю, что это хорошо подходит для декораторов.В рельсах декораторы в основном обертывают объекты модели логикой представления, используемой в представлении.Они работают как расширение одного объекта, что позволяет вам логически разделить различные задачи объекта.

Например:

class User
  def born_on
    Date.new(1989, 9, 10)
  end
end

class UserDecorator < SimpleDelegator
  def birth_year
    born_on.year
  end
end

Декораторы не подходят, когда дело доходит до продолжениякак операции, где несколько объектов взаимодействуют.

Скорее всего, вам следует взглянуть на шаблон сервисных объектов, в котором вы создаете объекты одного назначения, выполняющие одну задачу:

class SubscriptionService

  attr_accessor :user, :list

  def initialize(user, list)
    @user = user
    @list = list
  end

  def self.perform(user, list)
    self.new(user, list).perform
  end

  def perform
     @subscription = Subscription.new(user: @user, list: @list)
     log_subscription_attempted
     if @subscription.create
       send_welcome_email
       # ...
     else
       log_failure_reason
       # ...
     end

     @subscription
  end

  private

    def send_welcome_email
      # ...
    end

    def log_subscription_attempted
      # ...
    end

    def log_failure_reason
      # ...
    end
end

Но вы также должны учитывать, что вы создаетеваши модели правильно.В этом примере вы хотели бы, чтобы три модели были соединены следующим образом:

class User
  has_many :subscriptions
  has_many :subscription_lists, through: :subscriptions
end

class Subscription
  belongs_to :user
  belongs_to :subscription_list
  validates_uniqueness_of :user_id, scope: :subscription_list_id
end

# or topic
class SubscriptionList 
  has_many :subscriptions
  has_many :users, through: :subscriptions
end

Каждая модель должна обрабатывать один отдельный объект / ресурс в приложении.Так, например, модель SubscriptionList не должна напрямую участвовать в подписке одного пользователя.Если ваши модели набирают вес, это может быть признаком того, что вы слишком много вкладываете в слишком малый набор объектов бизнес-логики или что конфигурация базы данных плохо настроена.

...