- PVSM.RU - https://www.pvsm.ru -
Всем привет! По моему мнению, каждый программист должен стремиться к автоматизации и оптимизации всего, что движется и еще нет. В этой статье будет рассказано о том, как автоматизировать рабочий процесс Ruby on Rails разработчика с помощью Ruby гема под названием Guard. Эта статья в первую очередь полезна Ruby разработчикам, но может пригодиться и другим.
Guard [1] – это инструмент, позволяющий автоматически выполнять какие-либо команды при изменении какого-либо файла. Например, при изменении файла настроек сервера 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.
Для этого в Gemfile добавим в группу для разработки гем guard-bundler [2]
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
Проверим! Включим Guard в терминале командой
$ bundle exec guard
Добавим в Gemfile какой-нибудь гем. Например, guard-rspec [3], который будет автоматом прогонять тесты для Rspec.
gem 'guard-rspec', require: false
Откроем терминал с процессом guard и увидим, что он там автоматически запустил bundler, в результате работы которого guard-rspec был автоматически установлен. Как видно, подобная настройка Guard позволяет разработчику автоматизировать одну из часто выполняемых задач.
Инициализируем плагин для 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 написано большое количество плагинов [4] на все случаи жизни. Каждому разработчику стоит самостоятельно найти нужные для него и настроить их под себя. Я вкратце опишу те, которые используются у меня в данный момент. Я сам до сих пор не нашел идеальных решений, поэтому буду рад любым замечаниям и предложениям!
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
# 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 [5] — гем для Ruby, позволяющий проверить .rb файл на корректность синтаксиса. В данном примере он настроен вместе с Guard, благодаря чему при каждом изменении .rb файла Rubocop проверяет его и выводит результат в консоль и в log/rubocop.log файл.
У Rubocop огромное количество настроек, благодаря чему его можно адаптировать под любые требования к синтаксису. Можно даже сделать так, чтобы он автоматически корректировал код. Для настройки гема используется файл .rubocop.yml, например, rubocop обычно ругается на строки больше 90 символов, но благодаря файлу настроек можно сделать так, чтобы он указывал только на строки больше 140.
Чтобы увидеть все настройки, достаточно прогнать команду
$ rubocop --auto-gen-config
которая создаст файл со всеми отключенными настройками. Можно таким образом по одной включать и получить итоговый нужный .rubocop.yml файл.
Что в итоге настроено? В данном проекте достаточно запустить отдельными процессами zeus и guard. После чего происходит следующее:
Таким образом, достаточно большое количество процессов удалось автоматизировать. Я бы хотел сделать так, чтобы у каждого проекта достаточно было бы лишь запустить guard и полностью сфокусироваться на творческом процессе.
Теперь опишу текущий процесс работы с Guard. Ниже идут рекомендации, которые я дал остальным разработчикам, с которыми я сейчас работаю.
$ zeus start
$ bundle exec guard
Теперь Guard автоматически запустит и будет поддерживать включенным Rails server, включенный через Zeus.
На что стоит обращать внимание: при изменении файлов тестов тесты будут прогоняться автоматически. То есть я рекомендую одновременно с окном IDE держать открытым окно терминала (в Rubymine, например, это можно сделать прямо в под окне), где тут же можно будет увидеть, обвалились ли тесты с внесенными изменениями.
Спасибо за чтение! Не утверждаю, что я специалист в Guard, поэтому буду рад любым замечаниям и предложениям.
Автор: cbrwizard
Источник [6]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/ruby/71227
Ссылки в тексте:
[1] Guard: https://github.com/guard/guard
[2] guard-bundler: https://github.com/guard/guard-bundler
[3] guard-rspec: https://github.com/guard/guard-rspec
[4] большое количество плагинов: https://github.com/guard/guard/wiki/List-of-available-Guards
[5] Rubocop: https://github.com/bbatsov/rubocop
[6] Источник: http://habrahabr.ru/post/238667/
Нажмите здесь для печати.