- PVSM.RU - https://www.pvsm.ru -
Фреймворк Ruby on Rails меня просто очаровывает, но недавнего времени некоторую сложность представляла из себя генерация CRUD контроллеров.
Почти всегда мне было необходимо реализовывать списки с сортировкой, фильтрацией и пагинацией, а стандартного способа этого достичь в рельсах я не обнаружил. Перепробовал несколько вариантов и ни один меня не удовлетворил:
Долгое время не удавалось найти ничего похожего на полюбившийся мне виджет CGridView из Yii framework. Уже почти смирился с необходимостью писать свой велосипед, но наткнулся на DRY CRUD [3] и хочу поделится опытом его использования. Может кому-то он окажется полезным, а может кто-то подскажет еще более подходящий инструмент.
DRY CRUD это гем который с помощью генератора создает в проекте свой класс контроллера, после этой генерации гем можно отключать, а сгенерированный им код при необходимости изменять.
Для установки достаточно указать гем в Gemfile
gem 'dry_crud', '= 1.7.0'
запустить
bundle install
и генератор
rails generate dry_crud --templates haml --tests rspec
В качестве демонстрации я создал приложение из двух моделей User и Post, связанные отношением один-к-многим.
rails generate model User name:string email:string pass:string
rails generate model Post author_id:integer title:string content:text is_draft:boolean
Для более понятного для человека отображения определил метод to_s и получился такой код моделей:
#user.rb
class User < ActiveRecord::Base
attr_accessible :email, :name, :pass
has_many :posts
def to_s
"#{name}"
end
end
#post.rb
class Post < ActiveRecord::Base
attr_accessible :user_id, :content, :is_draft, :title
belongs_to :user
def to_s
"#{title}"
end
end
после чего в папке /app/controllers достаточно создать файл контроллера и наследовать его от CrudController. Можно также указать по каким столбцам обеспечить фильтрацию.
#users_controller.rb
class UsersController < CrudController
self.search_columns = [:name, :email]
end
Вот и все, не обязательно даже создавать папку для вьюх, пока не потребуется более сложный функционал. Запускаем приложение и получаем работающий CRUD с сортировкой (кликабельные столбцы) и фильтрацией (если в контроллере установлена self.search_columns) а пагинацию можно прикрутить с минимальными усилиями [4] и она будет совместима и с фильтром и с сортировкой.
Форма редактирования тоже не разочаровала, она определяет тип каждого поля и использует соответствующий контрол для boolean, string и text поля, а также для связи «belongs_to»:
и конечно же все это можно настраивать!
Много моих проектов используют Twitter bootstrap и адаптация DRY CRUD происходит очень просто, достаточно подключить гем «twitter-bootstrap-rails», установить генератором
rails generate bootstrap:install less
удалить app/views/layouts/crud.html.haml и /app/assets/stylesheets/sample.scss и переписать /app/views/layouts/application.html. Кому интересно, может посмотреть исходник проекта.
В итоге получаем узнаваемый дизайн:
Если например необходимо скрыть колонки created_at и updated_at во всех контроллерах, достаточно изменить хелпер базового класса.
#/app/helpers/list_helper.rb
def default_attrs
attrs = model_class.column_names.collect(&:to_sym)
attrs - [:id, :position, :password, :created_at, :updated_at] #< добавил сюда
end
Теперь в Users скроем столбец pass и добавим created_at. Для этого необходимо создать папку /app/views/users скопировать в неё общий партиал /app/views/crud/_form.html.haml
В первую и единственную строку = crud_table
добавим список столбцов, которые мы хотим видеть:
= crud_table :name, :email, :created_at
Результат:
По умолчанию содержимое постов отображается полностью:
Что бы сделать его обрезку до 100 символов достаточно создать в хелпере метод format_
#/app/helpers/post_helper.rb
module PostHelper
def format_content(post)
truncate(post.content, :length => 100, :omission => '...')
end
end
результат:
Менять надписи для полей не изменяя представления можно используя возможности рельсового L18n. Для этого необходимо установить язык по умолчанию:
#/config/application.rb
....
module DryCrudSample
class Application < Rails::Application
...
I18n.default_locale = :ru
end
end
для генерации языковых файлов я воспользовался гемом «l18n_generator»
#Gemfile
group :development do
gem 'i18n_generators'
end
качаем файл локали с репозитория rails-i18n
rails generate i18n_locale ru
копируем сгенерированный при установке DRY CRUD файл перевода /config/locales/en_crud.yml в ru_crud.yml и переводим.
Генерируем YAML файл для наших моделей
rails generate i18n_translation ru
получаем файл /config/locales/translation_ru.yml который я предпочитаю переименовать в ru_models.yml
Остается только написать желаемые названия для полей наших моделей [5]
После рестарта приложения получаем локализированный интерфейс:
Картинки кончились, начинается более глубокая настройка.
По умолчанию после успешного создания или редактирования записи происходит redirect на просмотр этой записи (действие «show») мне было более привычно, что бы в таком случае редирект происходил на «index» решение:
#/app/controllers/crud_controller.rb
def update(options = {}, &block)
assign_attributes
updated = with_callbacks(:update, :save) { entry.save }
#respond_with(entry, options.reverse_merge(:success => updated), &block) #<- было
respond_with(entry, options.reverse_merge(:success => updated, :location=> index_url), &block) #стало
end
Подобная замена понадобилась и в методе create
Потом в проекте понадобилось после редактирования записи возвращаться на её же форму редактирования. Что бы это реализовать в нашем контроллере Posts всего лишь необходимо переопределить метод update
в котором передавать желаемый URL:
#/app/controllers/posts_controller.rb
def update
super location: edit_post_path(params[:id])
end
На одном из проектов надо было реализовать функцию клонирования записи. Эта функциональность, насколько я понимаю отсутствует по умолчанию, поэтому я реализовывал её следующим образом. На примере Post, сначала определял новое действие в ресурсном маршруте:
#routes.rb
resources :posts do
get :clone, :on => :member
end
Это действие должно будет получать id
копируемой записи и отображать форму создания с уже заполненными полями. Submit формы произойдет на действие create
.
код клонирования модели я разместил в базовом классе:
#/app/controllers/crud_controller.rb
def duplicate_entry()
set_model_ivar( find_entry.dup() )
end
Код генерации кнопки разместился в хелпере:
#/app/helpers/crud_helper.rb
def action_col_clone(table, &block)
action_col(table) do |e|
link_table_action('list-alt', action_path(e, &block)) #тут указывается какое изображение отображать на кнопке
end
end
Код добавления новой кнопки в столбец место в представлении:
#/app/views/posts/_list.html.haml
= crud_table :title, :content, :is_draft do |t|
- action_col_clone t do |e|
- clone_post_path e #тут указывается урл на действие
Все заработало. Добавлено всего две функции, и теперь по всему проекту можно добавить кнопку «клонировать» всего одной строкой!
На самом деле с помощью гема «deep_cloneable» дублицировать ActiveRecord можно вместе с её зависимостями. А в форме можно редактировать не только саму запись, но и её связанные через «has_many». А фильтр сделать более сложным, не один textfield а несколько select'ов. И все это достаточно просто, частично описано в документации, частично постигается изучением исходников.
Я пришел к выводу, что DRY CRUD очень гибкий и мощный.
Немного смущает, что для внесения некоторых изменений приходится править «исходный код», хоть он и находится внутри проекта, наверное это может усложнить применение обновлений от автора. Но пока с такой необходимостью я не сталкивался, да и объем изменений невелик, что бы это составляло реальную проблему.
Инструкция по гему DRY CRUD [3]
Исходник примера на GitHub [6]
Автор: charger_lda
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ruby-on-rails/31494
Ссылки в тексте:
[1] nifty Scaffold: https://github.com/ryanb/nifty-generators
[2] DataTable: http://editor.datatables.net/release/DataTables/extras/Editor/examples/REST.html
[3] DRY CRUD: https://github.com/codez/dry_crud
[4] минимальными усилиями: https://github.com/codez/dry_crud#adapt-general-behavior
[5] моделей: https://github.com/charger/dry_crud_sample/blob/master/config/locales/ru_models.yml
[6] Исходник примера на GitHub: https://github.com/charger/dry_crud_sample
[7] Источник: http://habrahabr.ru/post/175887/
Нажмите здесь для печати.