Как стать автором
Обновить

Комментарии 44

За kaminari спасибо.

Путь к красивому коду протекает через бурелом говнокода ))))
kaminari судя по тестом сильно медленнее, чем will_paginate, хотя может исправили уже.
Статья направляет человека на верный путь, однако реальность сурова и некоторые вещи просто не укладываются в концепцию REST, к которой строго принуждают эти ваши inherited_resources. Хотя к этому надо стремиться.
Давайте примеры. Разберем более предметно.
Яркий пример: главная страница сайта (где есть новости, блоги, последние комментарии к чему-либо). В общем где контроллер обращается к более чем одной модели.

P.S. Это, конечно, резко с моей стороны утверждать, что что-то не укладывается в REST — при желании можно уложить почти все, тут вопрос в том, стоит ли овчинка выделки.
Проще всего это организовать через before_filter. Вот пример контроллера главной страницы, который как раз показывает новости и последнии комментарии:

class PostsFeedsController < InheritedResources::Base
respond_to :html, :rss, :only => :index
actions :index

before_filter lambda{ @latest_comments = Comment.by_domain(Domain.current_domain).latest(10) }
before_filter lambda{ @latest_posts = Post.by_domain(Domain.current_domain).latest(10) }
caches_page :index, :unless => lambda {|c| c.user_signed_in?}

include Platform::Controllers::Paginate
include Platform::Controllers::Domain
include Platform::Controllers::EagerLoading
end


* This source code was highlighted with Source Code Highlighter.
Фильтры кажутся, как бы это сказать, wrong place for that.
В RoR есть какая-нибудь возможность вызвать другой Action из View и поместить его результат в месте вызова?
partials
Что-то мне подсказывает, что Partials — это не то. Мы же должны передать в Partial данные из «основного» шаблона, разве не так?
А, ну тогда cells, но это нестандартный механизм
Посмотрел, да, Cells именно для таких сценариев. Странно только, что он нестандартный.

Кстати, они их описывают как mini-контроллеры. А нет ли возможности вызывать один и тот же Action одного и того же контроллера:
— как из браузера напрямую (и тогда будет сформирована страница этого ресурса), и
— из другого View (и тогда результат будет в виде Partial вставлен в «основную» страницу)?
А почему вы думаете, что тут должен быть один контроллер?
Потому что контроллер — это штука формирующая ответ на запрос. Одно действие — один ответ. Можно конечно выдать скелет страницы в ответе, а блоки с новостями, блогами и т.п. подгружать динамически — по запросу на блок — это тоже вариант. Эдакая система виджетов.
«Потому что контроллер — это штука формирующая ответ на запрос. Одно действие — один ответ.»

Я бы так не сказал. Front Controller (независимо от того, как реализован на конкретной платформе) — да, выдает один ответ. А каждый отдельный контроллер вовсе не обязан выдавать только свой результат сразу в браузер пользователя, их можно комбинировать. В ASP.NET MVC это RenderAction, уверен, в RoR это делается похожим образом. В выходящий поток (в браузер пользователя) выдается уже полная страница, независимо от того, сколько контроллеров над ней «поработало».
Тут можно уйти в теорию (что не хотелось бы). Но по-моему концепция формирования одного view несколькими контроллерами — это уже не MVC. Хотя это удобно — спору нет. Кажется cells в Rails делает нечто подобное.
Не, в теорию уходить не нужно. Концепция формирования кусочков View несколькими контроллерами — вполне укладывается в MVC, сохраняется и Separation of Concerns, и паттерн Front Controller, и все прочее. ASP.NET MVC именно так и устроен. И да, это чертовски удобно :)
Cells — да, миниатюрные контроллеры со своими views, вызовы которых можно встроить в любой view.

Но мне кажется, тут больше подходит использование составной модели.
Создайте модель для этого.
тут бы как раз очень Engines пригодилось
Что за Engines?
Refinery CMS очень их использует, это так сказать собранные в один «модуль» MVC кусочки, которые можно вызывать из «центральной» системы :)
refinerycms.com/engines
Имхо главное правильно разбить логику на сущности. Да, иногда при этом сущности плодятся, но зато потом не возникает проблем с расширением.
Все верно. тогда эти советы работают максимально эффективно
А при чем тут REST? Если приложение поддерживает этот архитектурный стиль, вовсе не значит, что не может быть эдаких «обобщенных ресурсов» вроде «главной страницы» или какой-нибудь с виджетами и Pagelet-ами.
Спасибо, хорошая статья. Вот еще нашел интересную штуку в вики по Devise: github.com/kristianmandrup/cream
Объединяет аутенфикацию юзеров с помощью Devise, распределению ролей и групп через Roles и авторизации через CanCan.

Если буду создавать новый проект, то этот гем будет установлен в первую очередь.
Пробовали его использовать, но мне он показался сырым и глючным
А есть ли практический смысл уменьшать код контроллеров? Его там и так не много, это модели разрастаются до сотен строк, а контроллеры обычно тонкие.
Смысл есть когда в 5 контроллерах 90% кода — это нагенеренный CRUD, а остальные 10% — кастомные before filters.
Ну эстетически будет красивее, да. Но практически толку от этого мало, читаемость кода не повысит (что там читать в сгенерированом контроллере), скорость разработки это не увеличит, а вот если к проекту подключится новый человек не знакомый с методикой, то ему придется потратить какое-то время на то что бы понять принципы.
После этой статьи времени новичку потребуется минимум.
Если кто-то добавил в сгенерированный контроллер какие-то правки (скоуп, дополнительная проверка или инициализация), то читать придется все в полном объеме и вникать. А таким способом — все отклонения от стандартного поведения видны сразу.
Ну согласен, это плюс, но вот стоит ли овчинка выделки?
Стоит. На банальщину не отвлекаешься.
Забыли упомянуть Rabl для генерации json, xml ответов. Для создания API приложения, ему нет равных, очень удобен.
inherited_resources очень спорный, ни одно приложение (кроме блога за пять минут) не укладывается в стандартный crud-контроллер. А inherited_resources с нестандартным контроллером — это изучение дополнительного API, сложность чтения кода и лишний, достаточно тяжеловесный гем наполненный метапрограмминговой магией. Зачем? Вам трудно написать def edit; ...; end;?
Основные CRUD действия, как правило, остаются. Конечно приходится добавлять и другие, но inherited_resources здесь совсем не мешает, зато дисциплинирует.
Насчет сложности чтения, как с HAML — вопрос привычки.
def edit; ...; end; — написать не сложно, но зачем писать одно и тоже многократно?
Нет, я говорю о том, что дело никогда не ограничивается чистыми методами crud. Т.е. и index и update и пр. методы всегда расширяются: добавляются фильтры, работа с конечными автоматами, да хоть та же пагинация. По большому счету вы всю логику контроллере выносите в before_filter's и приватные методы которые дергаются IR. Это дезориентирует и в конечном счете превращает код в лапшу. Не говоря уже о том, что приходится писать больше кода.
Никто не мешает общие части вынести в плагины, гемы или модули.
Общие то понятно, но что делать с индивидуальными. Мы просто все переносим с больной головы на здоровую. Вместо методов мы все пишем в бифор-фильтры, super do и всячески экстендим collection; Зачем тогда вообще брать руби и рельсы?
Согласен, inherited_resources — это вещь в стиле «как сделать простые вещи сложными». Если в проекте много однотипных простеньких контроллеров, то в разы лучше будет создать обычный базовый класс контроллера с минимумом магии и метапрограммирования, который идеально подойдёт под потребности конкретного проекта.
А использовать inherited_resources для нетривиальных контроллеров == снижать читабельность кода в разы.
… заслуживают отдельной статьи

организуете отдельную статью?
НЛО прилетело и опубликовало эту надпись здесь
Паш, прикольно что статья в топ вышла :)
Твой подход помню, хотя скажу что у меня он не прижился.

Но вот очень понравилось, как ты объединял edit и new views, до сих пор использую.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.