Написать активный декоратор DRY - PullRequest
1 голос
/ 02 июля 2019

Я использую ActiveDecorator: https://github.com/amatsuda/active_decorator

При написании моего UserDecorator я обнаружил, что он сильно перекрывается.Просто украсьте datetime, но мне приходится много раз повторять написание декоратора.

# frozen_string_literal: true

module UserDecorator
  def created_at_datetime
    created_at&.strftime '%Y/%m/%d %H:%M:%S'
  end

  def confirmed_at_datetime
    confirmed_at&.strftime '%Y/%m/%d %H:%M:%S'
  end

  def locked_at_datetime
    locked_at&.strftime '%Y/%m/%d %H:%M:%S'
  end

  def current_sign_in_at_datetime
    current_sign_in_at&.strftime '%Y/%m/%d %H:%M:%S'
  end

  def last_sign_in_at_datetime
    last_sign_in_at&.strftime '%Y/%m/%d %H:%M:%S'
  end
end

И угадайте, что на моем AdminDecorator у меня точно такие же поля.Нужно ли мне снова копировать все это в AdminDecorator?Любой совет, ребята?

Ссылка: https://github.com/amatsuda/active_decorator/issues/104

Ответы [ 3 ]

5 голосов
/ 02 июля 2019

Я не использую active_decorator, но, думаю, у меня возникнет соблазн создать DatetimeDecorator, что-то вроде:

module DatetimeDecorator

  %i(
    created_at
    confirmed_at
    locked_at
    current_sign_in_at
    last_sign_in_at
  ).each do |attr_sym|
    define_method("#{attr_sym}_datetime") do 
      send(attr_sym)&.strftime '%Y/%m/%m %H:%M:%S'
    end
  end

end

Когда вы включаете этот модуль, вы получаете пять методов, которые вы ранее определили в UserDecorator, каждый из которых использует один и тот же код форматирования.

Теперь, чтобы включить этот модуль, используйте included hook. Что-то вроде:

module UserDecorator

  self.included(base)
    base.class_eval do 
      include DatetimeDecorator
    end
  end

end

module AdminDecorator

  self.included(base)
    base.class_eval do 
      include DatetimeDecorator
    end
  end

end

Теперь ваш UserDecorator и ваш AdminDecorator имеют пять методов, которые вы ранее определили в UserDecorator.

Это не проверено, поэтому вам, возможно, придется немного поиграть с ним.

2 голосов
/ 02 июля 2019

Это очень похоже на ответ jvillian и ответ truongnm .

Я обычно пишу маленькие методы DSL для добавления повторяющихся методов, что-то вроде этого:

module DateFormatter
  DEFAULT_FORMAT = '%Y/%m/%d %H:%M:%S'

  def add_formatter(attribute, format: DEFAULT_FORMAT)
    define_method("#{attribute}_datetime") do
      public_send(attribute)&.strftime(format)
    end
  end
end

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

Как метод класса, add_format может быть вызван прямо внутри тела класса следующим образом:

module UserDecorator
  extend DateFormatter

  add_formatter :created_at
  add_formatter :confirmed_at, format: '%A %-d, %Y'
  add_formatter :locked_at
  add_formatter :current_sign_in_at
  add_formatter :last_sign_in_at
end

В модуле UserDecorator появляется новый метод:

UserDecorator.instance_methods
#=> [
#     :confirmed_at_datetime,
#     :locked_at_datetime,
#     :current_sign_in_at_datetime,
#     :last_sign_in_at_datetime,
#     :created_at_datetime
#   ]

Их можно называть как обычно:

class TestUser
  include UserDecorator

  def created_at
    Time.new(2019, 7, 2, 11, 53)
  end

  def confirmed_at
    Time.new(2019, 7, 2, 11, 53)
  end
end

u = TestUser.new
u.created_at_datetime
#=> "2019/07/02 11:53:00"

u.confirmed_at_datetime
#=> "Tuesday 2, 2019"

Поскольку вы используете Rails, вы можете включить его ActiveSupport::Concern шаблон.

1 голос
/ 02 июля 2019

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

module UserDecorator
  DATETIME_DECORATOR_FIELDS = %w[created_at confirmed_at locked_at current_sign_in last_sign_in_at].freeze

  DATETIME_DECORATOR_FIELDS.each do |field|
    define_method("#{field}_datetime") do |format = nil|
      return send(field)&.strftime(format) if format

      send(field)&.strftime '%Y/%m/%m %H:%M:%S'
    end
  end
end

Это создаст метод, принимающий формат параметров или нет:

User.first.created_at_datetime #=> "2019/06/06 16:18:02"

User.first.created_at_datetime "%Y/%m/%m" #=> "2019/06/06"
...