Убирать жирных рельсов помощников - PullRequest
4 голосов
/ 16 августа 2011

Сегодня, пытаясь высушить некоторый код, я извлек несколько дубликатов File.exists? код, используемый несколькими вспомогательными методами в закрытый метод

def template_exists?(*template) и случайно мартышка исправила этот уже существующий вспомогательный метод Rails. Это был довольно четкий индикатор запаха кода, мне не нужны были какие-то унаследованные методы, но я наследую их. Кроме того, что мой рефакторинговый метод вообще делал в этом помощнике?

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

Как мне идти дальше?

  1. Разделить методы на множество помощников?
  2. Извлечение кода внутри методов помощников в классы и делегирование им? Вы бы охватили эти классы (например, Mydomain :: TemplateFinder)?
  3. Разделить логику на модули и перечислить их как включенные вверху?
  4. другие подходы?

Как я вижу, № 2 более безопасен, чем случайные мартышки. Может быть, комбинация?

Примеры кода и сильные мнения приветствуются!

Ответы [ 6 ]

4 голосов
/ 25 августа 2011

Извлечение вспомогательных методов в классы (решение № 2)

хорошо, чтобы обратиться к этому конкретному способу очистки помощников, 1-й я выкопал этот старый Railscast:

http://railscasts.com/episodes/101-refactoring-out-helper-object

В то время это вдохновило меня на создание небольшой системы вкладок (работающей в одном из моих приложений в сочетании с конечным автоматом):

module WorkflowHelper

  # takes the block  
  def workflow_for(model, opts={}, &block)
    yield Workflow.new(model, opts[:match], self)
    return false
  end

  class Workflow
    def initialize(model, current_url, view)
      @view = view
      @current_url = current_url
      @model = model
      @links = []
    end

    def link_to(text, url, opts = {})
      @links << url
      url = @model.new_record? ? "" : @view.url_for(url)
      @view.raw "<li class='#{active_class(url)}'>#{@view.link_to(text, url)}</li>"
    end

  private
    def active_class(url)
      'active' if @current_url.gsub(/(edit|new)/, "") == url.gsub(/(edit|new)/, "") ||
                 ( @model.new_record? && @links.size == 1 )
    end

  end #class Workflow
end

И мои взгляды выглядят так:

  -workflow_for @order, :match => request.path do |w|
    = w.link_to "✎ Create/Edit an Order", [:edit, :admin, @order]
    = w.link_to "√ Decide for Approval/Price", [:approve, :admin, @order]
    = w.link_to "✉ Notify User of Approval/Price", [:email, :admin, @order]
    = w.link_to "€ Create/Edit Order's Invoice", [:edit, :admin, @order, :invoice] 

Как видите, это хороший способ инкапсулировать логику в классе и иметь только один метод в пространстве помощника / представления

2 голосов
/ 26 августа 2011

Хорошо, это действительно сложный вопрос. Rails в некотором роде ведет вас по пути помощников вида и, на самом деле, не дает вам достойной альтернативы, когда вы его обгоняете.

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

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

Вот несколько ссылок для чтения:

http://blog.jayfields.com/2007/03/rails-presenter-pattern.html

О шаблоне предъявителя в рельсах. лучший способ сделать это?

http://blog.jayfields.com/2007/01/another-rails-presenter-example.html

http://blog.jayfields.com/2007/09/railsconf-europe-07-presenter-links.html

Не стоит слишком увлекаться конкретными примерами кода, скорее попытайтесь понять, что пытается выполнить шаблон, и подумайте, как вы могли бы применить его к вашей конкретной проблеме. (хотя мне очень нравится пример во 2-й ссылке выше; стек переполняется один).

Это помогает?

2 голосов
/ 23 августа 2011
  1. Вы имеете в виду разделение больших помощников на более мелких помощников?Почему бы и нет.Я не очень хорошо знаю ваш код, но вы можете рассмотреть возможность передачи больших кусков кода на аутсорсинг ./lib.
  2. Нет.;-) Это звучит ужасно сложно.
  3. Звучит также сложно.То же предложение, что и в 1 .: ./lib.Модули там загружаются автоматически, если вы обращаетесь к ним.
  4. Нет

Мое предложение: не используйте слишком много пользовательских структур.Если у вас есть большие помощники, хорошо, может быть, дело.Хотя мне интересно, есть ли объяснение, почему весь этот вспомогательный код отсутствует в контроллере.Я использую помощники для небольших и простых методов, которые используются внутри шаблона.Сложная (Ruby-) логика должна быть помещена в контроллер.И если у вас действительно есть такое сложное приложение Javascript, почему бы вам не написать этот сложный материал на Javascript?Если это действительно нужно вызывать из шаблона, это путь.И, вероятно, делает ваш сайт немного более динамичным и гибким.

Относительно исправлений обезьян и коллизий пространства имен: если у вас есть имя класса, имена методов и т. Д., Которые звучат одинаково, проверьте, определены ли они.Google, grep или rails console для них.

Убедитесь, что вы понимаете, какой код принадлежит

  • Контроллер: Заполняйте переменные материалом, выполняйте действия пользователя (в основном, вычисления за вашей страницей)
  • Помощник: помогите сделать такие простые вещи, как создание необычной гиперссылки

    def my_awesome_hyperlink url, текст "Необычная ссылка на # {текст}" end

  • . / Lib: Более сложные вещи, которые используются более чем одним контроллером и / или также используются непосредственно другими компонентами, такими как определения шагов Cucumber

  • внутри шаблона в виде кода Ruby: Супер легкопрочитайте
  • внутри шаблона (или ./public) как код Javascript: вопрос вкуса.Но чем динамичнее ваше приложение, тем более вероятным здесь будет код.
1 голос
/ 22 августа 2011

Трудно предложить ясное решение без кода.Однако, поскольку все вспомогательные методы живут в одном и том же экземпляре глобального представления, коллизия имен является распространенной проблемой.

@ Slawosz может быть решением, но не совсем подходящим для философии помощников.

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

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

(раскрытие Я не являюсь создателем этого драгоценного камня, просто радостно использую его ....)

# some view
= render_cell :cart, :display, :user => @current_user

# cells/cart_cell.rb
# DO whatever you like in here
class CartCell < Cell::Rails

  include SomeGenericHelper

  def display(args)
    user    = args[:user]
    @items  = user.items_in_cart

    render  # renders display.html.haml
  end
end

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

0 голосов
/ 22 августа 2011

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

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

0 голосов
/ 16 августа 2011

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

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

Самая сильная сторона модулей в том, что они помогают вам в разработке и гибкости программ.

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

...