ОО Дизайн в Rails: куда положить вещи

Я действительно наслаждаюсь Rails (хотя я вообще RESTless), и мне нравится, что Ruby очень хорош. Тем не менее, тенденция создавать огромные подклассы ActiveRecord и огромные контроллеры вполне естественна (даже если вы используете контроллер для каждого ресурса). Если бы вы создавали более глубокие объектные миры, куда бы вы поместили классы (и модули, я полагаю)? Я спрашиваю о представлениях (в самих помощниках?), Контроллерах и моделях.

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

Ответы на вопрос(4)

Вот отличное сообщение в блоге о рефакторинге толстых моделей, которые, похоже, возникают из-за «тонкого контроллера»; Philosphy:

http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/

Основное сообщение - «Не извлекать миксины из толстых моделей», вместо этого используйте классы обслуживания, автор предоставляет для этого 7 шаблонов.

... the tendency to make huge ActiveRecord subclasses and huge controllers is quite natural ...

& Quot; огромным & Quot; это тревожное слово ... ;-)

Как ваши контроллеры становятся огромными? Это то, на что вам следует обратить внимание: в идеале контроллеры должны быть тонкими. Выбирая эмпирическое правило, я полагаю, что если у вас регулярно, скажем, 5 или 6 строк кода на метод контроллера (действие), то ваши контроллеры, вероятно, слишком толстые. Есть ли дублирование, которое может перейти в вспомогательную функцию или фильтр? Есть ли бизнес-логика, которая могла бы быть внедрена в модели?

Как ваши модели становятся огромными? Стоит ли искать способы уменьшить количество обязанностей в каждом классе? Есть ли какие-либо общие поведения, которые вы можете извлечь в mixins? Или области функциональности, которые вы можете делегировать вспомогательным классам?

РЕДАКТИРОВАТЬ: Попытка немного расширить, надеюсь, не слишком сильно искажая ...

Помощники: живут вapp/helpers и в основном используются для упрощения просмотра. Они либо для конкретного контроллера (также доступны для всех представлений для этого контроллера), либо обычно доступны (module ApplicationHelper в application_helper.rb).

Фильтры: скажем, у вас одна и та же строка кода в нескольких действиях (довольно часто, поиск объекта с использованиемparams[:id] или похожие). Это дублирование может быть абстрагировано сначала для отдельного метода, а затем полностью исключено из действия путем объявления фильтра в определении класса, такого какbefore_filter :get_object, Смотрите Раздел 6 вActionController Rails Guide Пусть декларативное программирование станет вашим другом.

Рефакторинг моделей - это скорее религиозная вещь. УченикиДядя боб предложит, например, что вы будете следовать пяти заповедямSOLID, Джоэл & amp; Джеффможет рекомендовать более, э, "прагматичный" подход, хотя они, кажется,немного больше примириться впоследствии. Поиск одного или нескольких методов внутри класса, которые работают с четко определенным подмножеством его атрибутов, является одним из способов определения классов, которые могут быть реорганизованы из вашей модели, производной от ActiveRecord.

Кстати, модели Rails не обязательно должны быть подклассами ActiveRecord :: Base. Или, другими словами, модель не должна быть аналогом таблицы или даже иметь отношение к чему-либо, что хранится вообще. Еще лучше, пока вы называете свой файл вapp/models в соответствии с Rails & apos; условные обозначения (вызовите #underscore в имени класса, чтобы узнать, что будет искать Rails), Rails найдет его без каких-либоrequireЭто необходимо.

 Dan Rosenstark01 июл. 2009 г., 16:31
Верно по всем параметрам, Майк, и спасибо за вашу заботу ... Я унаследовал проект, в котором было несколько огромных методов для контроллеров. Я разбил их на более мелкие методы, но сам контроллер все еще "толстый". Так что я ищу все мои варианты разгрузки вещей. Ваши ответы: «вспомогательные функции»; & Quot; фильтры, & Quot; & Quot; модель, & Quot; & Quot; Mixins & Quot; и "вспомогательные классы". Итак, где я могу положить эти вещи? Могу ли я организовать иерархию классов, которая автоматически загружается в dev env?
Решение Вопроса

Поскольку Rails предоставляет структуру в терминах MVC, естественно, что в конечном итоге использоватьonly контейнеры модели, представления и контроллера, которые предоставляются для вас. Типичная идиома для начинающих (и даже для некоторых программистов среднего уровня) - втиснуть всю логику в приложении в модель (класс базы данных), контроллер или представление.

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

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

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

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

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

Хороший способ подумать об этом - это «принцип единой ответственности». в котором говорится, что класс должен отвечать за одно (или небольшое количество) вещей. Ваши модели отвечают за сохранение данных из вашего приложения в базу данных. Ваши контролеры несут ответственность за получение запроса и возврат жизнеспособного ответа.

Если у вас есть концепции, которые не вписываются в эти рамки (постоянство, управление запросами / ответами), вы, вероятно, захотите подумать о том, как выwould смоделируйте идею, о которой идет речь. Вы можете хранить немодельные классы в приложении / классах или в любом другом месте и добавить этот каталог в путь загрузки, выполнив:

config.load_paths << File.join(Rails.root, "app", "classes")

Если вы используете пассажиров или JRuby, возможно, вы также захотите добавить свой путь к путям активной загрузки:

config.eager_load_paths << File.join(Rails.root, "app", "classes")

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

Update: Этот ответ относится к Rails 2.x и выше.

 13 апр. 2012 г., 11:51
Можете ли вы обновить свой ответ, если он все еще работает с Rails 3?
 02 июл. 2009 г., 22:01
D & APOS; о. Добавление отдельного каталога для не-моделей мне не пришло в голову. Я чувствую приближение ...
 14 мар. 2012 г., 18:23
В более поздних версиях config.autoload_paths по умолчанию используется для всех каталогов в приложении. Поэтому вам не нужно изменять config.load_paths, как описано выше. Я не уверен насчет eager_load_paths (пока), и мне нужно разобраться с этим. Кто-нибудь уже знает?
 01 мая 2015 г., 18:12
Было бы хорошо, если бы Rails поставлялся с этим & quot; классами & quot; папка для поощрения «принципа единой ответственности»; и позволить разработчикам создавать объекты, которые не поддерживаются базой данных. & Quot; Проблемы & quot; реализация в Rails 4 (см. ответ Simone), похоже, позаботилась о реализации модулей для совместного использования логики между моделями. Однако такой инструмент не был создан для простых классов Ruby, которые не поддерживаются базой данных. Учитывая, что Rails очень самоуверенный, мне любопытно мыслить процесс НЕ включая такую папку?
 Dan Rosenstark03 июл. 2009 г., 11:25
Иегуда, спасибо за это. Отличный ответ. Это именно то, что я вижу в приложениях, которые я наследую (и те, которые я создаю): все в контроллерах, моделях, представлениях и помощниках, автоматически предоставляемых для контроллеров и представлений. Затем идут миксины из lib, но никогда не делается попытка реального ОО моделирования. Вы правы, хотя: в "приложениях / классах или где-либо еще". Просто хотел проверить, есть ли какой-то стандартный ответ, который я пропускаю ...

Update: Использование проблем былоподтверждено как новое значение по умолчанию в Rails 4.

Это действительно зависит от природы самого модуля. Я обычно помещаю расширения контроллера / модели в папку / Concerts внутри приложения.

# concerns/authentication.rb
module Authentication
  ...
end    

# controllers/application_controller.rb
class ApplicationController
  include Authentication
end



# concerns/configurable.rb
module Configurable
  ...
end    

class Model 
  include Indexable
end 

# controllers/foo_controller.rb
class FooController < ApplicationController
  include Indexable
end

# controllers/bar_controller.rb
class BarController < ApplicationController
  include Indexable
end

/ lib - мой предпочтительный выбор для библиотек общего назначения. У меня всегда есть пространство имен проекта в lib, куда я помещаю все библиотеки приложений.

/lib/myapp.rb
module MyApp
  VERSION = ...
end

/lib/myapp/CacheKey.rb
/lib/myapp/somecustomlib.rb

Расширения ядра Ruby / Rails обычно имеют место в инициализаторах конфигурации, так что библиотеки загружаются только один раз в boostrap Rails.

/config/initializer/config.rb
/config/initializer/core_ext/string.rb
/config/initializer/core_ext/array.rb

Для многократно используемых фрагментов кода я часто создаю (микро) плагины, чтобы их можно было использовать в других проектах.

Вспомогательные файлы обычно содержат вспомогательные методы и иногда классы, когда объект предназначен для использования помощниками (например, Form Builders).

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

 01 июл. 2009 г., 17:38
Кроме того, мне кажется довольно необычным, что вы хотите загрузить файл дважды за время существования экземпляра приложения. Вы генерируете код по ходу дела?
 Dan Rosenstark01 июл. 2009 г., 15:52
Странная вещь. Я не могу получить это require_dependency RAILS_ROOT + & quot; / lib / my_module & quot; работать с чем-то вне каталога lib. Он определенно выполняется и выдает сообщение, если файл не найден, но не перезагружает его.
 01 июл. 2009 г., 17:36
Рубин требует, чтобы вещи загружались только один раз. Если вы хотите загрузить что-то безоговорочно, используйте load.
 01 июл. 2009 г., 18:17
Почему вы используете require_dependency вместо require? Также обратите внимание, что если вы следуете соглашениям об именах, вам вообще не нужно использовать require. Если вы создаете MyModule в lib / my_module, вы можете вызывать MyModule без предварительного требования (даже если использование require должно быть быстрее и иногда более читабельным). Также обратите внимание, что файл в / lib загружается только один раз при загрузке.
 15 сент. 2015 г., 09:59
Использование проблем касается

Ваш ответ на вопрос