Статьи

Создайте API Sinatra, используя TDD, Heroku и непрерывную интеграцию с Travis

sinatra_travis_heroku

Этот пост был вдохновлен этим блестящим видео , где Константин Хаазе , сопровождающий Sinatra, создает полностью работающее приложение и развертывает его на Heroku с тестами на Travis (там, где он работает).

Я решил сделать аналогичный пример, который проходит через каждый этап создания службы API, которая демонстрирует типичный рабочий цикл цикла разработки Sinatra — напишите свои тесты, напишите свой код, отправьте код в GitHub и проверьте, работает ли он при развертывании в Heroku с непрерывным интеграция на Трэвис.

Пост будет идти от начала до конца и будет охватывать следующее:

  • Написание тестов с использованием MiniTest
  • Создание Sinatra API, который возвращает информацию в формате JSON
  • Использование Bundler для управления зависимостями
  • Использование Git для управления контролем версий
  • Хостинг код на GitHub
  • Развертывание и размещение сайта на Heroku
  • Выполнение непрерывной интеграции с Travis CI

Определите проблему

Приложение, которое я хочу построить, называется Number Cruncher. Он будет возвращать информацию о числах, таких как факторы, будь то нечетное или четное, и если это простое число.

Первое, что я хочу сделать, — это Integer класс Integer чтобы добавить метод, который возвращает множители числа в виде массива. Я тоже хочу добавить prime? метод для проверки, является ли число простым. После этого я буду использовать Sinatra для предоставления API, который будет возвращать информацию о числе в формате JSON.

  • Number Cruncher работает в прямом эфире на Heroku
  • Вы можете увидеть тесты непрерывной интеграции, работающие на Travis
  • Код есть на GitHub

тестирование

Прежде чем писать какой-либо код, мы должны написать несколько тестов, которые охватывают описание выше. Прежде всего, создайте файл с именем «number_cruncher.rb» и другой файл с именем «test.rb». Затем добавьте следующий код установки MiniTest в ‘test.rb’:

 ENV['RACK_ENV'] = 'test' require 'minitest/autorun' require 'rack/test' require_relative 'number_cruncher.rb' include Rack::Test::Methods def app Sinatra::Application end 

Теперь мы напишем спецификацию для проверки некоторых функций, описанных выше. Добавьте следующий код в конец test.rb:

 describe "Number Cruncher" do it "should return the factors of 6" do 6.factors.must_equal [1,2,3,6] end it "should say that 2 is prime" do assert 2.prime? end it "should say that 10 is not prime" do refute 10.prime? end it "should return json" do get '/6' last_response.headers['Content-Type'].must_equal 'application/json;charset=utf-8' end it "should return the correct info about 6 as json" do get '/6' six_info = { number: 6, factors: 6.factors, odd: 6.odd?, even: 6.even?, prime: 6.prime? } six_info.to_json.must_equal last_response.body end end 

Первый тест для метода factors который я планирую добавить в класс Integer . Мы проверяем, что если целое число 6 вызывает метод factors за которым следует, если факторы 6 возвращаются в виде массива.

Далее проверить prime? метод, который также будет добавлен в класс Integer . Прежде всего, проверьте, возвращает ли 2 значение true а затем, если 10 возвращает значение false .

Последние два теста относятся к API. Четвертый тест проверяет, возвращает ли приложение JSON. Это делается путем выполнения запроса get с ‘6’ в качестве параметра. last_response.headers проверяются на предмет типа «application / json; charset = utf-8». В последнем тесте мы проверяем, верна ли правильная информация, сравнивая строку JSON с методом last_response.body .

Чтобы запустить наши тесты, введите в окне терминала следующее:

 $ ruby test.rb 

Это дает следующий вывод:

 # Running: EEEEE Finished in 0.008729s, 572.8333 runs/s, 0.0000 assertions/s. 5 runs, 0 assertions, 0 failures, 5 errors, 0 skips 

Все пять наших тестов приводят к ошибкам, что ожидается, так как мы еще не написали код. Посмотрим, сможем ли мы сдать эти тесты.

Код числовой дробилки

Начните с добавления factors и prime? методы к классу Integer. Добавьте следующий код в number_cruncher.rb:

 class Integer def factors square_root = self**0.5 (1..square_root).map{ |n| [n,self/n] if self/n*n == self }.compact.flatten.sort end def prime? self.factors.size == 2 ? true : false end end 

Попробуйте снова запустить тесты.

 $ ruby test.rb 

Это дает следующий вывод:

 5 runs, 3 assertions, 0 failures, 2 errors, 0 skips 

Это лучше — наши первые три теста пройдены, но последние два все еще не пройдены, потому что мы еще не реализовали API. Мы будем использовать Синатру, чтобы сделать это. Это подразумевает использование sinatra и json , поэтому добавьте следующее в number_cruncher.rb:

 require 'sinatra' require 'json' 

Нам также понадобится маршрут, который люди могут использовать для доступа к API. Это простой GET который принимает число в качестве параметра. Будет возвращена информация о номере:

 get '/:number' do content_type :json number = params[:number].to_i { number: number, factors: number.factors, odd: number.odd?, even: number.even?, prime: number.prime? }.to_json end 

Сначала измените тип контента на JSON, используя удобный вспомогательный метод content_type который предоставляет Sinatra. Затем возьмите число из хэша params и преобразуйте его в целое число (все, что введено в маршрут, является строкой). Затем создайте хэш информации о числе, включая его факторы. Хеш включает в себя, является ли число нечетным или четным и является ли оно простым с использованием наших новых методов Integer . Хеш преобразуется в JSON с использованием метода to_json предоставленного to_json json . Поскольку это последняя строка обработчика маршрута, он будет возвращен автоматически.

Давайте снова запустим тесты:

 $ ruby test.rb 

На этот раз мы получаем следующий вывод:

 5 runs, 5 assertions, 0 failures, 0 errors, 0 skips 

Это здорово, наш код работает! Мы можем проверить его, проверив некоторые факты о пятом числе Ферма, используя curl . Сначала запустите сервер:

 $ ruby number_cruncher.rb 

Теперь откройте другое окно терминала и введите:

 $ curl http://localhost:4567/4294967297 

Это дает нам следующую информацию, подтверждающую то, что Эйлер обнаружил в 1732 году:

 {"number":4294967297,"factors":[1,641,6700417,4294967297],"odd":true,"even":false,"prime":false} 

Контроль версий с помощью Git

Всегда полезно держать ваш код под контролем версий. Git практически вездесущ в сообществе Ruby и не без причины (это круто!). Сначала убедитесь, что Git установлен в вашей системе, а затем выполните следующие команды:

 $git init 

Это должно дать вам сообщение, подобное приведенному ниже:

 Initialized empty Git repository in /path/to/number cruncher/.git/ 

Используйте add . Команда для добавления всех файлов в каталоге :.

 git add . git commit -m 'initial commit' 

Вы получите сообщение, подобное приведенному ниже, с подробным описанием файлов, которые были изменены:

 [master (root-commit) 6674d0c] initial commit 2 files changed, 56 insertions(+) create mode 100644 number_cruncher.rb create mode 100644 test.rb 

Поскольку мы используем Git для контроля версий, имеет смысл разместить наш код на GitHub. Если вы еще не создали учетную запись, зайдите туда и создайте ее . Существует полезный гем под названием ‘hub’ для взаимодействия с GitHub через терминал. Чтобы установить его, просто введите в терминал следующее:

 $ gem install hub 

Теперь вы сможете создать новый репозиторий для своего кода, используя следующую команду:

 $ hub create number_cruncher 

Это должно дать вам следующий вывод:

 Updating origin created repository: daz4126/number_cruncher 

Вставьте код в наш новый репозиторий GitHub, что делается так:

 $ git push origin master 

Если все пойдет по плану, вы увидите нечто похожее на следующий результат:

 Counting objects: 4, done. Delta compression using up to 4 threads. Compressing objects: 100% (4/4), done. Writing objects: 100% (4/4), 914 bytes, done. Total 4 (delta 0), reused 0 (delta 0) To [email protected]:daz4126/number_cruncher.git * [new branch] master -> master 

Bundler

Далее мы используем Bundler для управления нашими зависимостями. Это включает в себя создание файла с именем «Gemfile», который содержит следующий код:

 source 'https://rubygems.org' gem "sinatra" gem "json" gem "rack-test", :group => :test 

Чтобы использовать Bundler, используйте следующую команду в терминале:

 $ bundle install 

Вы должны увидеть следующую информацию, поскольку Bundler получает и устанавливает все необходимые гемы:

 Fetching gem metadata from https://rubygems.org/........... Fetching gem metadata from https://rubygems.org/.. Resolving dependencies... Using json (1.8.0) Using rack (1.5.2) Using rack-protection (1.5.0) Using rack-test (0.6.2) Using tilt (1.4.1) Using sinatra (1.4.3) Using bundler (1.3.5) Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed. 

Так как мы внесли некоторые изменения в наш код, нам нужно добавить и зафиксировать их в git перед отправкой обновленного кода в GitHub. Это легко достигается с помощью следующих трех команд в терминале:

 $ git add . $ git commit -m 'Added Gemfile and bundled gems' $ git push origin master 

Развернуть в Heroku

Теперь пришло время развернуть наш код на работающем сервере, используя сервис Heroku . Для этого нам нужно будет создать файл «rackup». Создайте файл с именем config.ru, содержащий следующий код:

 require './number_cruncher' run Sinatra::Application 

Чтобы проверить, что это работает, запустите команду, которую Heroku будет использовать в терминале:

 $ bundle exec rackup 

Это приводит к запуску сервера, и вы увидите вывод, подобный показанному ниже:

 [2013-08-29 13:27:36] INFO WEBrick 1.3.1 [2013-08-29 13:27:36] INFO ruby 2.0.0 (2013-06-27) [i686-linux] [2013-08-29 13:27:36] INFO WEBrick::HTTPServer#start: pid=4188 port=9292 

Еще раз, мы внесли некоторые изменения в код, так что добавьте, зафиксируйте и нажмите танец с Git:

 $ git add . $ git commit -m 'Added config.ru' $ git push origin master 

Теперь пришло время создать приложение в Heroku. Если вы еще не создали учетную запись, зайдите туда и получите ее . Вы также должны убедиться, что у вас установлен пояс Heroku . Как только это будет сделано, введите следующую команду в терминале:

 $ heroku create 

Git используется для развертывания кода в Heroku с помощью push , например:

 $ git push heroku master 

Если все пойдет хорошо, вы увидите нечто похожее на вывод ниже:

 Creating number-cruncher... done, stack is cedar http://number-cruncher.herokuapp.com/ | [email protected]:number-cruncher.git Git remote heroku added 

Запишите URL-адрес, созданный для вашего приложения, а затем протестируйте его, посетив его, указав свой любимый номер в качестве параметра:

http://number-cruncher.herokuapp.com/10

Непрерывная интеграция с Travis

Непрерывная интеграция — это практика запуска тестов в одном централизованном хранилище кода. Travis предоставляет эту услугу, выполняя тесты кода в репозитории GitHub каждый раз, когда код отправляется в GitHub. Это означает, что вы всегда можете проверить статус своего кода. Вы даже можете настроить его для развертывания своего кода после успешной сборки!

Для начала установите travis gem:

 $ gem install travis 

Вам также необходимо войти в Travis, используя свою учетную запись GitHub.

Travis работает, выполняя задачу rake, поэтому создайте файл с именем ‘Rakefile’, который содержит следующий код:

 task(:default) { require_relative 'test' } 

Это в основном говорит ему запускать файл ‘test.rb’ при запуске Rakefile. Вы можете проверить, работает ли это, введя в терминал следующее:

 $ rake 

Вы увидите тот же результат, что и при запуске наших тестов:

 Run options: --seed 58513 # Running tests: ..... Finished tests in 0.092591s, 54.0009 tests/s, 54.0009 assertions/s. 5 tests, 5 assertions, 0 failures, 0 errors, 0 skips 

Вам также нужно добавить ‘rake’ в наш Gemfile, отредактируйте его так, чтобы он выглядел следующим образом:

 source 'https://rubygems.org' ruby '2.0.0' gem "sinatra" gem "json" group :test do gem "rack-test" gem "rake" end 

Вам нужно снова запустить bundle install , так как мы изменили наш Gemfile:

 $ bundle install 

Теперь мы готовы настроить Travis для нашего проекта. Это можно сделать, введя следующую команду в терминале:

 $ travis init ruby --rvm 2.0.0 

Это дает следующий вывод:

 repository not known to Travis CI (or no access?) triggering sync: ........ done .travis.yml file created! daz4126/number_cruncher: enabled :) 

Будет создан файл с именем .travis.yml, содержащий информацию о нашем приложении. Сборка на Travis инициализируется, когда вы нажимаете на свою учетную запись Github, так что сделайте это сейчас:

 $ git add . $ git commit -m 'Created a Rakefile and set up Travis' $ git push origin master 

Чтобы проверить состояние сборки, выполните следующую команду в терминале:

 $ travis show 

Трэвису может потребоваться некоторое время, чтобы запустить сборку, поэтому вы можете получить следующее сообщение:

 no build yet for daz4126/Sinatra-API 

Но наберитесь терпения, приготовьте чашку чая, затем вернитесь и попробуйте снова:

 $ travis show 

Надеюсь, на этот раз вы получите подтверждение, что сборка прошла успешно:

 Job #1.1: Created a Rakefile and set up Travis State: passed Type: push Branch: master Compare URL: https://github.com/daz4126/number_cruncher/compare/4f55d5f9cd32...86c181d96f5d Duration: 30 sec Started: 2013-09-28 18:22:56 Finished: 2013-09-28 18:23:26 Allow Failure: false Config: rvm: 2.0.0 

Вы также получите приятное письмо от прекрасных людей из Трэвиса, чтобы подтвердить успешную сборку.

Это все люди!

Мы подошли к концу этого учебного руководства, где мы завершили полный цикл разработки, который можно повторять каждый раз, когда мы хотим добавить новый фрагмент кода: писать тесты, писать код, фиксировать изменения в Git, передавать код в GitHub, тестировать код с использованием Travis, и развернуть в Heroku Промыть и повторить.

Я надеюсь, что вы сочли полезным. Я хотел бы услышать любые отзывы о вашем рабочем процессе в комментариях ниже.