Comments 25

К сожалению, trailblazer хорош только до тех пора, пока не возникает необходимость смотреть в его код.

Я хотел попробовать trailblazer. Можете рассказать за ваш опыт его использования?

Я трогал его более полугода назад. Тогда он мне показался очень сырым, АПИ постоянно менялся. При попытке разобраться с проблемами, приходится продираться через довольно небрежные исходники, написанные в очень специфичном стиле. Я такой код могу понимать только в дебагере, да и то с трудом. Судя по отсутствию других core-контрибьютеров, такие сложности не только у меня.


Вообще, так можно охарактеризовать весь код этого автора (apotonick) — reform, cells. Эти гемы предполагается использовать вместе с trailblazer.


Еще один момент. Мне показалось, что trailblazer больше ориентирован на работу с формами, вьюхами и т.д.,


В итоге, trailblazer, я не использую, но использую reform вместе с ActiveAdmin. Я от этого не в восторге, но хорошей альтернативы нет.

плюс trailblazer в том, что его не обязательно использовать целиком. Я, например, часто использую Cells, и больше ничего

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

Естественно, если гуи нет — то там только Операции разве что можно использовать

Вообще, операции это основная суть trailblazer'а… Эта та штука которая объединяет уровни авторизацию, форм-обжекты и репрезентеры (классы отвечающие как за сериализацию данных в различные форматы такие как xml, json и тп), так и десериализацию из тех же форматов). Все это вместе позволяет в кратчайшие сроки реализовать красивый API приложения со всеми необходимыми вещами. К примеру, из коробки можно отдавать данные по стандарту JSON-API.
Да, можно использовать trailblazer по частям (я так и начинал: cells -> reform -> representable -> disposable и тд), к тому же это позволяет довольно быстро понять как все это должно работать.
Потом наступает период когда ты смотришь на свой разросшийся контроллер из-за таких вещей как авторизация и препопуляция данных и понимаешь что в нем этого быть не должно… Вот тут то и понимаешь всю прелесть trailblazer-операций=)
Что особенно приятно, операции не делают за тебя никакой магии, к примеру, для авторизации можно использовать собственные policy-классы не имеющие ничего общего с trailblazer'ом.

Что касается cells, то последние обновления данного гема намекают на то, что в скором времени его можно будет использовать в качестве полноценной замены ActionView.

Я делаю API в формате JSON, специфика в основном такова, что данные отдаются и редко пишутся. Trailblazer мне никакой пользы не приносит, только проблемы так как приходится ковыряться в этом disposable. Вместо representable использую AMS. Никакой лапши в контроллерах нет. Типичный тест на контроллер выглядит так — проверка кода ответа, проверка что ответ не пустой и проверка что ответ соответствует JSON Schema.


Я хочу сказать, что Trailblazer — это очередная штука которая помогает организовать код тем, что его не в состоянии организовать сам.

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

Не всегда это возможно, Например, в rails action нельзя передать зависимость явно.

Но зачем нужна зависимость в Rails Action? Хотя для этих случаев есть before_action callbacks.

Например, в контроллере используется CreateArticle, у которого есть зависимость persist_article, которая делает что-то тяжелое (например, внешний вызов).


Тестируя контроллер, я хочу тестировать только его зону отвественности и избежать внешнего вызова. Внедрение зависимости здесь может помочь мне внедрить noop имплементацию persist_article.

что мешает в тесте написать


MyContainer.register "persist_article" do
  NoOpPersistence.new
end

тем не менее, согласен, явное понятней неявного, но это как обычно палка о двух концах

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

Похоже тут дело вкуса. В любом случае имена явно записываются в начале класса. Если в дело не вступает наследование, и если файл не слишком большой, то вроде бы не должно быть проблем.
Надо пробовать. Я думаю, что зависимости стоит использовать в том файле, где они подключаются, иначе действительно непонятно откуда они взялись. Но в любом случае здесь не больше проблем, чем в обычном руби, тем более, что оба гема (dry-container и dry-auto_inject) очень небольшие. Вот dry-component пока сыроват как мне кажется.
В прицнипе интересно, но очень не нравится конструкция вида:
include AutoInject["validate_article", "persist_article"]


include подразумевает добавление определенной функциональности к классу/модулю, а тут она не только добавляется, но и сразу совершаются определенные операции. Как по мне, лаконичней потратить несколько строк, но сделать это более наглядным:
include AutoInject

inject :validate_article
inject :persist_article


Например, в Java тот же Spring-framework делает нечто похожее:
@ Inject
private ArticleValidator validator;

@ Inject
private ArticlePersisting persisting


(имена переменных не лучшие в этом примере, а пробел между собакой и Inject поставил, чтобы никого не упомянуть))
Но все это ИМХО, конечно.

ИМХО, это дело вкуса. Для меня запись


include AutoInject["validate_article", "persist_article"]

однозначно означает, что в текущий класс вносится код, который позволяет использовать определенные методы. А как оно работает внутри — не важно


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

AutoInject.[] на самом деле генерирует анонимный модуль, который добавляется к классу. Но я написал такой враппер:
module Imprint
  Import = Dry::AutoInject(Container)
  KwargsImport = Import.kwargs

  class << self
    def inject(target)
      -> *values { target.send(:include, KwargsImport[*values]) }
    end
  end

  module Mixin
    def inject
      Imprint.inject(self)
    end
  end
end


И потом использую так

module Operations
  class Operation
    extend Imprint::Mixin
  end

  class AcceptPayment < Operation

    inject['repo.account_repo',
           'repo.bill_repo']

    def call(...)
      # ...
    end
  end
end
Внедрение зависимостей это хорошо, лишь бы это не превратилось в «горе от ума», паттерн ради паттерна.
К счастью, здесь этим не пахнет. У этих библиотек нету и не будет таких целей, хотя некоторые опасаются, начинают пугать джавой, в глаза ее не видев, и так далее. Ключ-значение + небольшая обертка, вот и все. dry-component только чуть посложнее с логической точки зрения, потому что берет на себя логику по загрузке приложения.
Only those users with full accounts are able to leave comments. Log in, please.