Настройка Guard для автоматизации Ruby on Rails разработки
Всем привет! По моему мнению, каждый программист должен стремиться к автоматизации и оптимизации всего, что движется и еще нет. В этой статье будет рассказано о том, как автоматизировать рабочий процесс Ruby on Rails разработчика с помощью Ruby гема под названием Guard. Эта статья в первую очередь полезна Ruby разработчикам, но может пригодиться и другим.
Guard — это инструмент, позволяющий автоматически выполнять какие-либо команды при изменении какого-либо файла. Например, при изменении файла настроек сервера Guard может автоматически перезапускать сервер. Или можно настроить автоматическую компиляцию LESS в CSS при сохранении файла. Всё зависит от того, как Guard будет настроен разработчиком.У Guard есть специальный файл настроек Guardfile, где указывается, какие команды нужно запускать при изменении каких файлов. Все настройки можно указать самому, а можно использовать написанные сообществом Guard Plugins, в которых самые часто используемые настройки написаны заранее.
Лучший способ интегрировать Guard в проект — это добавить его в Gemfile. group: development do gem 'guard' end И затем установить его командой $ bundle После чего необходимо создать Guardfile командой $ bundle exec guard init Запустить Guard лучше всего используя Bundler командой $ bundle exec guard После установки рассмотрим использование Guard для стандартного RoR проекта. Предположим, что RoR приложение уже создано. Пусть Guard будет автоматически устанавливать все необходимые гемы при изменении Gemfile.1. Добавление в проект Для этого в Gemfile добавим в группу для разработки гем guard-bundler group: development do # And updates gems when needed gem 'guard-bundler', require: false end Установим гем $ bundle install А затем инициализируем плагин командой $ guard init bundler Обратите внимание на Guardfile, расположенный в корне проекта. Теперь там есть строчки guard: bundler do watch ('Gemfile') end В них написано, что Guard будет следить за файлом Gemfile и будет выполнять команду, заранее записанную в геме guard-bundler. В данном случае, это $ bundle install 2. Проверка Проверим! Включим Guard в терминале командой $ bundle exec guard Добавим в Gemfile какой-нибудь гем. Например, guard-rspec, который будет автоматом прогонять тесты для Rspec. gem 'guard-rspec', require: false Откроем терминал с процессом guard и увидим, что он там автоматически запустил bundler, в результате работы которого guard-rspec был автоматически установлен. Как видно, подобная настройка Guard позволяет разработчику автоматизировать одну из часто выполняемых задач.3. Настройка Инициализируем плагин для Rspec после его установки $ guard init rspec Теперь в Guardfile появились новые строчки. Рассмотрим их: # Note: The cmd option is now required due to the increasing number of ways # rspec may be run, below are examples of the most common uses. # * bundler: 'bundle exec rspec' # * bundler binstubs: 'bin/rspec' # * spring: 'bin/rsspec' (This will use spring if running and you have # installed the spring binstubs per the docs) # * zeus: 'zeus rspec' (requires the server to be started separetly) # * 'just' rspec: 'rspec' guard: rspec, cmd: 'bundle exec rspec' do watch (%r{^spec/.+_spec\.rb$}) watch (%r{^lib/(.+)\.rb$}) { |m| «spec/lib/#{m[1]}_spec.rb» } watch ('spec/spec_helper.rb') { «spec» }
# Rails example watch (%r{^app/(.+)\.rb$}) { |m| «spec/#{m[1]}_spec.rb» } watch (%r{^app/(.*)(\.erb|\.haml|\.slim)$}) { |m| «spec/#{m[1]}#{m[2]}_spec.rb» } watch (%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| [«spec/routing/#{m[1]}_routing_spec.rb», «spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb», «spec/acceptance/#{m[1]}_spec.rb»] } watch (%r{^spec/support/(.+)\.rb$}) { «spec» } watch ('config/routes.rb') { «spec/routing» } watch ('app/controllers/application_controller.rb') { «spec/controllers» } watch ('spec/rails_helper.rb') { «spec» }
# Capybara features specs watch (%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| «spec/features/#{m[1]}_spec.rb» }
# Turnip features and steps watch (%r{^spec/acceptance/(.+)\.feature$}) watch (%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join (»**/#{m[1]}.feature»)][0] || 'spec/acceptance' } end Эти строчки настраивают автоматический запуск тестов Rspec. Рассмотрим некоторые из них. Например, строка ruby watch ('spec/spec_helper.rb') { «spec» } говорит о том, что Guard будет следить за файлом spec/spec_helper.rb (путь относительно корня проекта — Guardfile файла) и при любом его изменении он будет запускать тестирование всей папки spec. Начало блока ruby guard: rspec, cmd: 'bundle exec rspec» do говорит о том, что для любого правила все Rspec команды будут запускаться с параметрами bundle exec rspec. То есть, в рассмотренном случае при изменении ruby spec/spec_helper.rb будет запускаться команда $ bundle exec rspec spec Строка rubywatch (%r{^app/(.+)\.rb$}) { |m| «spec/#{m[1]}_spec.rb» } говорит о том, что при изменении любого .rb файла будет запускаться тестирование теста, связанного с этим файлом. То есть при изменении app/models/user.rb автоматически запустится команда $ bundle exec spec spec/models/user_spec.rb Для создания и редактирования подобных действий используются регулярные выражения. Рекомендую использовать в Ruby консоли команду match для отладки, например «app/views/units/index.html.slim».match (%r{^app/views/(.+)/(.*)\.(.*)\.(erb|haml|slim)$})
Для Guard написано большое количество плагинов на все случаи жизни. Каждому разработчику стоит самостоятельно найти нужные для него и настроить их под себя. Я вкратце опишу те, которые используются у меня в данный момент. Я сам до сих пор не нашел идеальных решений, поэтому буду рад любым замечаниям и предложениям! В Gemfile group: development, : test do # Integrates jasmine js testing gem 'jasmine-rails' # With guard gem 'guard-jasmine', git: «git://github.com/guard/guard-jasmine.git», branch: «jasmine-2»
# Checks ruby code grammar gem 'rubocop', require: false # With rspec gem 'rubocop-rspec' # With guard gem 'guard-rubocop» end
group: development do # Automagically launches tests for changed files gem 'guard' gem 'guard-rspec', require: false # And updates gems when needed gem 'guard-bundler', require: false # And auto starts rails server gem 'guard-rails' # And auto runs migrations gem 'guard-migrate' end В Guardfile # More info at https://github.com/guard/guard#readme
# https://github.com/guard/guard-bundler guard: bundler do watch ('Gemfile') end
# https://github.com/guard/guard-rspec guard: rspec, cmd: 'zeus rspec' do watch (%r{^spec/.+_spec\.rb$}) watch (%r{^lib/(.+)\.rb$}) { |m| «spec/lib/#{m[1]}_spec.rb» }
# Run the model specs related to the changed model watch (%r{^app/(.+)\.rb$}) { |m| «spec/#{m[1]}_spec.rb» }
# Controller changes watch (%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| [«spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb», «spec/acceptance/#{m[1]}_spec.rb»] }
watch ('config/routes.rb') { «spec/controllers» } watch ('app/controllers/application_controller.rb') { «spec/controllers» }
watch (%r{^spec/support/(.+)\.rb$}) { «spec» } watch ('spec/rails_helper.rb') { «spec» } watch ('spec/spec_helper.rb') { «spec» }
# Capybara features specs watch (%r{^app/views/(.+)/.*\.(erb|haml|slim)$}) { |m| «spec/acceptance/#{m[1]}» } watch (%r{^app/views/(.+)/(.*)\.(.*)\.(erb|haml|slim)$}) { |m| «spec/acceptance/#{m[1]}» } watch (%r{^app/views/(.+)/_.*\.(erb|haml|slim)$}) { |m| «spec/acceptance/#{m[1].partition ('/').first}/#{m[1].partition ('/').last}_spec.rb» } end
# Checks any changed ruby file for code grammar # https://github.com/yujinakayama/guard-rubocop guard: rubocop, all_on_start: false, cli: ['--out', 'log/rubocop.log'] do watch (%r{^(.+)\.rb$}) { |m| »#{m[1]}.rb» } end
# Restarts server on config changes # https://github.com/ranmocy/guard-rails guard: rails, zeus: true, daemon: true do watch ('Gemfile.lock') watch (%r{^(config|lib)/.*}) end
# Restarts all jasmine tests on any js change # https://github.com/guard/guard-jasmine guard: jasmine, all_on_start: false, server_mount: '/specs' do watch (%r{^app/(.+)\.(js\.coffee|js|coffee)}) { «spec/javascripts» } watch (%r{^spec/javascripts/(.+)\.(js\.coffee|js|coffee)}) { «spec/javascripts» } end
# Runs migrations on migrate files changes # https://github.com/glanotte/guard-migrate guard: migrate do watch (%r{^db/migrate/(\d+).+\.rb}) watch ('db/seeds.rb') end Немного о rubocop Rubocop — гем для Ruby, позволяющий проверить .rb файл на корректность синтаксиса. В данном примере он настроен вместе с Guard, благодаря чему при каждом изменении .rb файла Rubocop проверяет его и выводит результат в консоль и в log/rubocop.log файл.У Rubocop огромное количество настроек, благодаря чему его можно адаптировать под любые требования к синтаксису. Можно даже сделать так, чтобы он автоматически корректировал код. Для настройки гема используется файл .rubocop.yml, например, rubocop обычно ругается на строки больше 90 символов, но благодаря файлу настроек можно сделать так, чтобы он указывал только на строки больше 140.
Чтобы увидеть все настройки, достаточно прогнать команду
$ rubocop --auto-gen-config которая создаст файл со всеми отключенными настройками. Можно таким образом по одной включать и получить итоговый нужный .rubocop.yml файл.Результаты Что в итоге настроено? В данном проекте достаточно запустить отдельными процессами zeus и guard. После чего происходит следующее: Автоматически поддерживается запущенный через zeus Rails сервер, который перезапускается при каждом изменении основных файлов настроек проекта При каждом изменении Gemfile устанавливаются все гемы При изменении любого файла с тестом прогоняется этот тест При изменении любого файла контроллеров/моделей/либов/вьюх запускается связанный с ним тест, если такой имеется Каждый измененный ruby файл проверяется на грамотность с помощью rubocop При изменении любого javascript/coffeescript файла запускаются все jasmine тесты При изменении любого файла миграции или seeds прогоняются все необходимые миграции Таким образом, достаточно большое количество процессов удалось автоматизировать. Я бы хотел сделать так, чтобы у каждого проекта достаточно было бы лишь запустить guard и полностью сфокусироваться на творческом процессе.
Теперь опишу текущий процесс работы с Guard. Ниже идут рекомендации, которые я дал остальным разработчикам, с которыми я сейчас работаю.Откройте терминал и перейдите в папку проекта Запустите zeus для ускоренной работы тестов/сервера $ zeus start Запустите Guard $ bundle exec guard Теперь Guard автоматически запустит и будет поддерживать включенным Rails server, включенный через Zeus. Запустите все тесты, нажав в терминале Enter. После исправления всех тестов можно работать! На что стоит обращать внимание: при изменении файлов тестов тесты будут прогоняться автоматически. То есть я рекомендую одновременно с окном IDE держать открытым окно терминала (в Rubymine, например, это можно сделать прямо в под окне), где тут же можно будет увидеть, обвалились ли тесты с внесенными изменениями.
Спасибо за чтение! Не утверждаю, что я специалист в Guard, поэтому буду рад любым замечаниям и предложениям.