Skip to content

Latest commit

 

History

History
81 lines (47 loc) · 7.99 KB

a-hierarchy-of-controllers.md

File metadata and controls

81 lines (47 loc) · 7.99 KB

Статья Кирилла Мокевнина ссылка хабр

Поместил сюда дабы не потерять.

Иерархия контроллеров

В большинстве встречавшихся мне rails проектов, структура контроллеров не имеет никакой организации и проект растет как придется. В больших проектах это приводит к тому что контроллеры становятся огромными (с десятками actions), а условные фильтры растягиваются на весь экран. Разобраться в таком коде бывает очень не просто.

Поработав с большим количеством rails проектов, у меня был сформирован подход к организации иерархии контроллеров, которая позволила унифицировать их структуру и упростить код.

Верхний уровень

В первую очередь удобно на верхнем уровне расположить основные неймспейсы проекта, такие как web, mobile, api, feeds и другие. При этом модуль admin будет находиться в web.

Внутри апи можно так же выделить подмодули, разделенные по версии, например, v1, v2. Либо делать папки на верхнем уровне api1, api2 и т.п. тыц

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

Для каждого модуля создается Namespace::ApplicationController для того чтобы не смешивать логику подходящую для конкретных неймспейсов.

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

Вложенные ресурсы

Начну сразу с примера: профайл пользователя на сайте. Он может быть доступен по такому урлу /users/:id и его контроллер UsersController. Список постов пользователя доступен по /users/:id/posts. Соответственно мы имеем PostsController с index внутри которого получаем посты по юзеру. Затем у нас появляется задача выводить все посты одним списком. Мы используем тот же контроллер posts и добавляем туда метод index_all. При этом юзер тут уже не нужен. В конце концов, добавляя множество представлений, появится часть методов которые выводят эти посты (и обрабатывают их) для юзера, а часть без него. Дальше появятся фильтры, например, before_filter :load_user, :only => [:index …], и так до тех пор, пока не останется людей способных понимать что тут происходит.

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

В данном случае так же будет разумным создать модули для соответствующих ресурсов.

И для каждого неймспейса будет собственный ApplicationController содержащий общие методы модуля.

Для всех контроллеров находящихся в этом модуле будет доступен current_section без фильтров.

Профит:

Контроллеры становятся меньше и проще. Глядя на структуру контроллеров уже много можно сказать о том как организован проект и чего можно ожидать в конкретном контроллере. Уходит много условных фильтров. Упрощается тестирование; Единый организационный подход;

Так же, для удобства и единообразия, входные контроллеры неймспейса обзываются welcome. И подключаются в роутах через root :to => ‘welcome#index’ внутри соответствующего scope. Реальный пример:

Рассмотрим как организовать контроллеры на реальном примере. Допустим потребовалось сделать интеграцию с facebook.

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

Подход с разделением:

{% highlight ruby linenos %} resource :facebook, :only => [:new, :create] {% endhighlight %}

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

{% highlight ruby linenos %} # app/controllers/web/account/facebook_controller.rb resource :account do scope :module => :account do resource :facebook, :only => [:edit, :update, :destroy] end end {% endhighlight %}

А если понадобится дать возможность интегрироваться уже авторизованному юзеру? Тогда в последний пример добавляем :new, :create.

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