Сообщество Ruby охватывает открытый исходный код, как ни одно другое сообщество. В наши дни любой разработчик Ruby, достойный их соли, должен стать автором или внести свой вклад в библиотеку с открытым исходным кодом. Git и Github сделали код совместного использования простым и простым.
Однако, ваш первый вклад в библиотеку с открытым исходным кодом может быть очень сложным. Если вы похожи на меня, меня мучает неуверенность в себе и страх, что я «сделаю это неправильно». Я беспокоюсь о насмешках над другими разработчиками, все из-за многолетнего вклада открытого кода. Они уничтожат мой код и используют мой аватар как мем для ужасного кодирования.
В конце концов я преодолел этот страх. Я принимал участие в нескольких проектах с открытым исходным кодом ( AngularJS , neography , jquery-ui , просто назвать несколько), и я могу сказать вам, что это не так уж плохо. Если вы застряли в фазе неуверенности в себе, но хотите вмешаться, вы можете спросить: «Какой первый шаг?» Или «Как я могу внести свой вклад?». Ну, я стремлюсь ответить на такие вопросы, проводя вас через моя попытка внести свой вклад в чудесный драгоценный камень guard-jruby-rspec .
Шаги:
1. Поиск
2. Вилка
3. Подготовьте свою местную среду
4. Написать тест
5. Исправить это
6. Фиксируй и дави
7. Проверьте это
8. Выполните запрос на извлечение
Жемчужина
В настоящее время я работаю над проектом JRuby, который использует RSpec. Выполнение тестов непрерывно, автоматически и (что наиболее важно) быстро имеет первостепенное значение. Я использовал для этого гем Guard-jruby-rspec, и он запускает мои тесты быстрее, чем любое другое решение, которое я видел в любом проекте Ruby. Джо Катнер , автор книги « Развертывание с JRuby », проделал блестящую работу с этим драгоценным камнем. (Джо также рецензировал эту статью, так что спасибо за это тоже!)
Вкратце, позвольте мне объяснить, что делает guard-jruby-rspec. Gem — это плагин для Guard, который, согласно его странице Github, является «инструментом командной строки для простой обработки событий при изменениях файловой системы». Чаще всего он используется для мониторинга файлов в проекте Ruby или Rails и запуска уязвимых объектов. тесты по мере изменения файлов. Если файл контроллера изменяется, автоматически запускается соответствующий тестовый файл. Это бесценно, так как вы получаете постоянную обратную связь при кодировании. Автоматизируйте все вещи.
Guard имеет процветающую структуру плагинов, и guard-jruby-rspec является одним из многих плагинов. guard-jruby-rspec, как вы уже догадались, запускает RSpec для файлов на основе «наблюдателей» в вашем Guardfile. Он использует JRuby, чтобы минимизировать время загрузки для каждого теста.
Например, в приложении Rails, если Guardfile выглядит так:
interactor :simple guard 'jruby-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)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" } # HEY THIS LINE IS IMPORTANT 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('app/controllers/application_controller.rb') { "spec/controllers" } # Capybara features specs watch(%r{^app/views/(.+)/.*.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" } end
Каждый раз, когда файл изменяется, что соответствует наблюдателю, спецификации запускаются. Если вы передадите блок наблюдателю, вы можете сузить спецификации, которые будут выполняться. Другими словами, когда я изменяю файл модели, он запускает только спецификацию модели.
Проблема
Когда я работал, я заметил, что Guard потерпит неудачу каждый раз, когда я изменяю и сохраняю файл контроллера. Вот ошибка:
16:29:43 - ERROR - Guard::JRubyRSpec failed to achieve its <run_on_changes>, exception was: [# NoMethodError: undefined method `match' for #<Array:0x4816505b> [#] /home/vagrant/ws/rental_express/ROOT/rails/.bundle/jruby/1.8/gems/guard-rspec-2.5.4/lib/guard/rspec/inspector.rb:50:in `spec_folder?' [#] /home/vagrant/ws/rental_express/ROOT/rails/.bundle/jruby/1.8/gems/guard-rspec-2.5.4/lib/guard/rspec/inspector.rb:38:in `should_run_spec_file?' [#] /home/vagrant/ws/rental_express/ROOT/rails/.bundle/jruby/1.8/gems/guard-rspec-2.5.4/lib/guard/rspec/inspector.rb:30:in `clean' [#] org/jruby/RubyArray.java:2395:in `select' [#] /home/vagrant/ws/rental_express/ROOT/rails/.bundle/jruby/1.8/gems/guard-rspec-2.5.4/lib/guard/rspec/inspector.rb:30:in `clean' [#] /home/vagrant/ws/rental_express/ROOT/rails/.bundle/jruby/1.8/gems/guard-rspec-2.5.4/lib/guard/rspec/inspector.rb:62:in `clear_spec_files_list_after' [#] /home/vagrant/ws/rental_express/ROOT/rails/.bundle/jruby/1.8/gems/guard-rspec-2.5.4/lib/guard/rspec/inspector.rb:29:in `clean' [#] /home/vagrant/ws/rental_express/ROOT/rails/.bundle/jruby/1.8/gems/guard-rspec-2.5.4/lib/guard/rspec.rb:85:in `run_on_changes' [#] /vagrant/git/guard-jruby-rspec/lib/guard/jruby-rspec.rb:74:in `run_on_changes' [#] org/jruby/RubyKernel.java:2080:in `send' ...
Я начал смотреть вокруг, чтобы посмотреть, смогу ли я выяснить, как исправить эту ошибку, и вот как я это сделал.
Шаг 1: Поиск
Во многих случаях вы не первый, кто обнаруживает проблему. Особенно, если проблема связана с хорошо используемым гемом или фреймворком, таким как Rails. Прежде чем вы начнете резвиться с кодом, разумно попросить Google выяснить, есть ли у кого-то еще проблема.
Кроме того, вы должны найти кодовую базу на Github (подавляющее большинство гемов / кодов с открытым исходным кодом можно найти на Github) и поискать их проблемы по вашей проблеме. Скорее всего, кто-то еще имеет проблему и / или работает над исправлением.
Что ты ищешь? Я считаю, что использование текста сообщения об ошибке (если оно есть) имеет высокий уровень успеха. Выньте номера строк и имена вашего локального компьютера / каталога и найдите ошибку. В этом примере я искал следующее:
- NoMethodError: неопределенный метод match для массива
- JRuby Guard NoMethodError: неопределенный метод match для массива
Нет кости.
Если у вас нет ошибки или ваша ошибка не помогает, попробуйте поискать основные драгоценные камни. В этом случае я мог бы искать «Guard JRuby Rspec Rails Array match» и посмотреть, что всплывает. В большинстве случаев вы получаете несколько попаданий в StackOverflow и будете на пути к восстановлению. Это, однако, было не так для меня. Грустная панда.
Я нашел код на Github и искал в хранилище «неопределенное совпадение методов для массива» и BINGO! Конечно же, у кого-то еще была такая же проблема . Это был только день, так что в воде все еще была кровь.
Я добавил комментарий «Я тоже это получаю» и сообщил им, какую версию драгоценного камня я использую. В этом случае я также добавил «Я планирую провести дальнейшее расследование», чтобы сообщить им, что я исправляю это. Шаг 2
Шаг 2: Вилка
Поскольку код драгоценного камня, который я исследую, находится на Github, я легко могу сделать свою собственную копию. Это называется «разветвление» и является шагом в процессе, который стал возможен благодаря фантастичности Github. Некоторые люди думают, что разветвление — это мерзкая вещь, но это не так. Все, что нужно сделать, это скопировать / клонировать репозиторий в папку под вашим пользователем на Github. В моем случае, теперь у меня есть репозиторий ruprict / guard-jruby-rspec, который я могу взломать до глубины души.
После того, как у меня есть форк, я хотел бы добавить его в свой Gemfile и убедиться, что проблема все еще происходит. Чтобы добавить гем в ваш Gemfile, полученный из репозитория github, вы делаете это:
gem 'guard-jruby-rspec', github: 'ruprict/guard-jruby-rspec'
После быстрой bundle
я запускаю guard
и проверяю, что проблема все еще существует. Это.
Теперь мне нужно получить этот код локально на моем ноутбуке, чтобы я мог начать работать над ним. Это простой git clone [email protected]:ruprict/guard-jruby-rspec.git
из моего местного терминала, и мы готовим на газе. Шаг 3, вы встали.
Шаг 3: Подготовьте свою местную среду
ОК, я клонировал репозиторий и переключился в свой локальный каталог. Мне нравится отгораживаться от своей работы, когда я работаю над чем-то вроде этого, поэтому я использую RVM, потому что это невероятно.
Поскольку это гем JRuby, мне нужно убедиться, что у меня установлен JRuby (я использую 1.7.4, поэтому rvm install jruby
), а затем создать гемсет. Я назвал свой gemset «guard-jruby-rspec» и добавил в .ruby-version
и .ruby-gemset
чтобы он переключался в нужную среду, когда я в этом каталоге. Вы также можете использовать файл .rvmrc
(ссылка) для этого, но, похоже, все идет по пути .ruby-version
.
Как только вы окажетесь в новом наборе гемс (или в другой изолированной среде), запустите bundle install
чтобы установить все зависимости гема. Большинство gemspecs будут иметь Gemfile, который указывает на зависимости, перечисленные в файле gemspec. Вы увидите, как проходят все ваши зависимости, вот так:
Теперь мы подошли к самому важному этапу подготовки среды разработки: запуску тестов / спецификаций. Это Ruby, поэтому все драгоценные камни должны пройти некоторые тесты. Вы НЕ должны начинать разработку до тех пор, пока существующие тесты не будут успешно запущены в вашей среде. Чаще всего это просто запуск rake test
rake spec
или rake spec
или, в данном случае, rspec
.
ОК, тесты проходят. Excelllent. Теперь мы можем взломать.
Мне нравится создавать git-ветку для моей работы, поэтому «нетронутая» среда — это просто git checkout
.
git checkout -b array_error_in_custom_watcher git push -u origin array_error_in_custom_watcher
Вторая команда заставляет мою локальную ветку отслеживать ветку в моем репозитории github с тем же именем. Я копаю это.
У многих драгоценных камней / репозиториев в README есть раздел о том, как внести свой вклад, поэтому убедитесь, что вы прочитали это и выполняете запрошенный процесс. У guard-jruby-rspec нет той, которую я могу найти (но у Angular есть монстр ), поэтому я следую «наиболее распространенному» процессу, насколько я знаю.
Шаг 4: Написать тест
Test Driven Development говорит нам, что вы должны написать тест, чтобы воспроизвести любую ошибку, которую вы исправляете. Иногда это очень просто. В других случаях не так много. В этом случае это где-то посередине. Я не знаю, как работает Guard, поэтому мне придется немного разобраться в том, как собрать Guard, чтобы выяснить, как сделать тест, который воспроизводит ошибку.
В этом случае это немного сложнее, поскольку ошибка действительно происходит в коде guard-rspec. Маловероятно, что у guard-rspec есть проблема, хотя, похоже, никто не жалуется на эту ошибку в этом хранилище.
То, что делает это немного более расстраивающим, — то, что я думаю, что я знаю, что исправление. В этом случае, когда массив контрольных целей передается из блока наблюдателя, мы получаем NoMethodError
для Array
. Должно быть, это не просто совпадение, что Array
выдает ошибку, а мы возвращаем Array
в нашем наблюдателе. Вернитесь и посмотрите на Guardfile, чтобы увидеть строку, о которой я говорю.
Если посмотреть на трассировку стека, последний раз, когда тест выполняется по коду guard-jruby-rspec, находится в строке 74 в методе run_on_changes
, который выглядит следующим образом:
def run_on_changes(raw_paths) unload_previous_examples @reloaders.reload(raw_paths) unless @custom_watchers.nil? or @custom_watchers.empty? paths = [] raw_paths.each do |p| @custom_watchers.each do |w| if (m = w.match(p)) paths << (w.action.nil? ? p : w.call_action(m)) end end end super(paths) # THIS IS LINE 74 end end
Эта переменная paths
представляет собой массив файлов для запуска на основе изменений, переданных функции в аргументе raw_paths
. Вставляя различные операторы puts
, я знаю, что paths
— это массив массивов, когда он терпит неудачу. Вопрос в том, как мне написать тест, который воспроизводит проблему?
Сначала я смотрю на существующие тесты. Я хочу попробовать использовать тот же стиль и, конечно же, использовать те же приемы (издевательства и т. Д.), Поэтому я просматриваю тестовые файлы, чтобы найти место, где, по моему мнению, тест подходит лучше всего. Мои первые несколько попыток либо ломаются «неправильно», либо проходят, когда они терпят неудачу. Некоторые из существующих тестов макет двух объектов runner
и inspector
. Прослеживая код через гем guard-rspec ( super
вызов вызывает метод run_on_changes
для Guard::RSpec
), я понимаю, что run_on_changes
вызов run_on_changes
является ключом к тому, что я хочу.
Этот вызов принимает массив путей и, когда все правильно, он НЕ должен иметь массив как элемент во внешнем массиве. Обладая этими знаниями, я могу написать тест на ожидание, который гарантирует, что у смоделированного вызова инспектора есть соответствующие аргументы.
# guard-jruby-rspec/spec/guard/jruby-guard_spec.rb:226 it "works with watchers that have an array of test targets" do subject = described_class.new([Guard::Watcher.new(%r{^spec/(.+)$}, lambda { |m| ["spec/#{m[1]}_match", "spec/#{m[1]}_another.rb"]})]) test_targets = ["spec/quack_spec_match", "spec/quack_spec_another.rb"] inspector.should_receive(:clean).with(test_targets).and_return(test_targets) # THIS IS THE TEST runner.should_receive(:run).with(test_targets) { true } subject.run_on_change(['spec/quack_spec']) end
Тест создает наблюдателя, который возвращает массив тестовых местоположений. Переменная test_targets
— это то, что инспектор ДОЛЖЕН получить, когда все работает. Когда я запускаю этот тест, я получаю ошибку ожидания, потому что вызов inspector.clean
получает массив с массивом в качестве первого элемента.
Если это кажется пугающим, это не так. Фактический процесс занял у меня около часа, и у меня было много сбоев, погоня за диким гусем и т. Д. Каждый раз, когда я терпел неудачу, я узнавал немного больше о коде, пока не понял, как мне нужно структурировать свой тест. Существуют инструменты, которые значительно облегчают расследование проблемы.
Например, я недавно обнаружил Тим-папу « vim-bundler », «мне надо дать медаль». Этот великолепный плагин позволяет мне открывать драгоценные камни в моем комплекте с помощью простого Btabedit <gem name>
, где я могу редактировать драгоценный камень на месте и никогда не покидать свой редактор. Я закричал как бешеный фанат Битлз, когда нашел vim-bundler. В самом деле. Большая часть моего исследования заключалась в размещении операторов puts
и т. Д. В других драгоценных камнях (таких как Guard::RSpec
), что позволило мне увидеть, что происходит, когда я в коде запутался.
Аааа, с тестом, показывающим проблему. Шаг 5 будет легким.
Шаг 5: исправить это
Как я уже упоминал, написать тест для выявления проблемы почти всегда сложнее, чем исправить код. Это верно здесь, поскольку исправление просто добавляет .flatten
к массиву paths
мы передаем по нашей цепочке наследования.
def run_on_changes(raw_paths) unload_previous_examples @reloaders.reload(raw_paths) unless @custom_watchers.nil? or @custom_watchers.empty? paths = [] raw_paths.each do |p| @custom_watchers.each do |w| if (m = w.match(p)) paths << (w.action.nil? ? p : w.call_action(m)) end end end super(paths.flatten) # Here be the change, mon. end end
Тесты запускаются снова и они проходят. Я сижу и впитываюсь в хорошем чувстве.
Стоит отметить, что это может или не может быть лучшим решением. В этом случае, это не так много кода, и у меня есть тест. Я достаточно доволен кодом, чтобы перейти к шагу 6.
Шаг 6: зафиксируй и подтолкни (вставай!)
Теперь я могу зафиксировать код. Здесь предостережение: убедитесь, что ваши изменения включают только то, что нужно для исправления. В одном из моих самых ранних работ я оставил кучу пробелов, оставшихся после добавления операторов puts
или других отладочных операторов. Это заставило меня зафиксировать больше файлов, чем необходимо, и это не круто.
В этом случае вот мой git diff
:
Как видите, мои изменения — это всего лишь тест и изменение. Красиво и чисто.
Кроме того, убедитесь, что вы не зафиксировали новые файлы случайно. Если вы привыкли делать git add .
, тогда вы рискуете создать временные файлы редактора или что-то еще, что могло пробиться на вашем пути во время работы.
Наконец, сделайте ваше сообщение коммита информативным. Хорошим (если не, может быть, немного преувеличенным) примером являются соглашения о фиксации сообщений AngularJS . Этот пример, вероятно, находится в дальнем конце спектра сообщений SuperDuper Commit, но вряд ли кто-то будет жаловаться, если вы добавите слишком много информации в сообщение фиксации.
Вот мое сообщение коммита:
Шаг 7: Проверьте это
Мне нравится возвращаться к моему проекту, где я изначально нашел ошибку, и добавить свой репозиторий github в комплект Gemfile, а затем убедиться, что проблемы больше нет. В этом случае я добавляю
gem "guard-jruby-rspec, github: "ruprict/guard-jruby-rspec, branch: "array_error_in_custom_watcher"
в Gemfile и запустите bundle update guard-jruby-rspec
.
Теперь воссоздайте или вернитесь в среду, в которой происходит ошибка. Для меня я просто добавил пользовательский наблюдатель, который возвращает массив тестовых целей обратно в Guardfile в моем проекте, запустил guard ( guard
) и изменил файл контроллера. Я поставил очевидный сбой, затем удалил его.
Так как Охранник не взорвался повсюду, я в порядке. Ошибка, она побеждена!
Шаг 8. Выполните запрос на извлечение
Момент наконец наступил. Вы собираетесь отправиться в путешествие с открытым исходным кодом, которое перенесет вас в далекие страны и познакомит с инопланетным народом. Или что-то.
Перейдите в ветку репозитория GH и нажмите кнопку « Запрос на извлечение».
В своем сообщении укажите любые проблемы, которые вы, возможно, прочитали, которые исправлены или затронуты PR. В моем случае я упомянул проблему, в которой обнаружил ту же ошибку. Вы можете упомянуть проблему, просто сославшись на нее в своем комментарии. Если ваше сообщение о подтверждении в порядке, вам не нужно вводить намного больше, чем ссылка на проблему.
Ваша неуверенность в себе может подняться на поверхность здесь, но просто пойти на это. Мне еще предстоит встретиться с автором OSS, который не ценит того, кто пытается помочь. Даже если вы все испортили, изучите, исправьте и повторно отправьте. Это единственный способ стать машиной, поддерживающей OSS.
Поздравляем! Необязательный шаг 9 — выпить праздничный напиток для взрослых!
Заворачивать
Я надеюсь, что этот пост поможет кому-то справиться со страхом и опасением внести свой вклад в открытый исходный код. Поверьте мне, если я могу это сделать, ЛЮБОЙ может это сделать. Если вам трудно найти ошибку для работы, я бы предложил подписаться на Code Triage, чтобы облегчить поиск мест для помощи в популярных библиотеках с открытым исходным кодом.
Удачи!