Про ActiveAdmin слышали все, но не все понимают насколько он хорош. Есть много мнений от сторонников до ненавистников, но суть в том что если в достаточной степени овладеть этим инструментом, то можно делать практически что угодно за очень сжатые сроки. Многих отпугивает то, что кривая обучения поначалу идёт резко вверх, но как только ты делаешь шаг в сторону, всё становится сложно. Я отношусь к этому также как и к инвестиции, уже около 5 лет я использую исключительно ActiveAdmin и наработал достаточную базу которая позволяет быстро решать большинство типовых задач. И мне есть с чем сравнить.
Админка — это та часть приложения, которой особо некому заниматься, поэтому на моём опыте ей в основном занимаются бекендщики, но был один забавный случай. Для написания админки одного проекта был выделен специально обученный JS-разработчик который пилил её как SPA, соответственно для этого понадобилось добавить в приложение ещё и специальный administrative API, поэтому была занята часть времени backend специалиста. Процесс шёл очень медленно, результат не внушал восхищения. После нескольких месяцев JS-разработчик то ли ушёл на другой проект, то ли совсем слился, получили сырую недоделку и фактически выброшенные на ветер человеко-месяцы времени. После, насколько я помню, админка была таки запилена на ActiveAdmin за пару недель. Может пример и не очень удачный, так как при должном умении и терпении можно было сделать неплохой фреймворк и переиспользовать его в будущем. С другой стороны, если можно исключить лишнее взаимодействие между JS и API и рендерить ответ прямо на сервере, это нужно сделать, это уменьшает сложность на порядок. Не для всего это правило годится, но для слабо нагруженной части приложения которую видит горстка избранных вполне. Keep it simple.
А теперь немного best practices
Используй templates
Например такой шаблон
# app/view/admin/shared/_customers.html.arb relation = [ defined?(through) ? through : nil, owner.class.name.underscore ].compact.join('_') path = [:admin, :customers, q: { "#{relation}_id_eq" => owner.id }] panel link_to(owner.class.human_attribute_name(:customers), path) do scope = owner.customers .order(id: :desc) .preload(:profile) .limit(50) table_for scope do column :uid column :full_name column :email column :balance column :_ do |customer| link_to 'Show', [:admin, customer] end end end
Можно переиспользовать следующим образом
# app/admin/resource.rb ActiveAdmin.register SomeResource do # ... show do |resource| attributes_table do # ... end render partial: 'admin/shared/customers', locals: { owner: resource } end end
Разбивай толстую страницу на независимые части
Если есть страница на которой очень много информации, возможно стоит переделать её, но если не получается можно сделать рендеринг её частей асинхронным через AJAX запросы. К тому же можно периодически обновлять эти кусочки если страница открыта. Для этого я сделал специальный gem activeadmin-async_panel. Использование выглядит примерно так:
# Место где вставляется панель panel link_to('Status', [:admin, :statuses, 'q[resource_id_eq]' => resource.id]), { class: 'async-panel', 'data-url' => status_admin_resource_path(resource), 'data-period' => 10.seconds } # Контроллер # app/admin/resource.rb member_action :status do @resource = resource render layout: false end # View # app/views/admin/resources/status.html.arb if status = resource.last_status attributes_table_for status do row :created_at do link_to status.created_at, [:admin, status] end row(:param1) row(:param2) row(:param3) end end
Пиши свои расширения
Однажды у нас возникла проблема из-за того что список пользователей которые подгружались в HTML тег select для фильтра на index странице стал настолько большим, что страница стала загружаться очень долго. Решено было сделать фильтр который запрашивает через AJAX пользователей по мере появления символов в поле ввода.
В результате появился gem activeadmin-ajax_filter. Который позволяет использовать AJAX-powered select в фильтрах и формах.
# Relation-resource ActiveAdmin.register User do include ActiveAdmin::AjaxFilter # ... end # Main resource # As a filter ActiveAdmin.register Invoice do filter :user, as: :ajax_select, data: { url: :filter_admin_users_path, search_fields: [:email, :customer_uid], limit: 7, } # ... end # As a form input ActiveAdmin.register Invoice do form do |f| f.input :language # used by ajax_search_fields f.input :user, as: :ajax_select, data: { url: filter_admin_users_path, search_fields: [:name], static_ransack: { active_eq: true }, ajax_search_fields: [:language_id], } # ... end end
В последнее время люди пользуются админками с мобильных телефонов, и это большая проблема для ActiveAdmin, он совершенно не приспособлен для этого. Нужно как-то решать эту проблему, но это уже в другой раз.