Пишем свой первый gem

Хочу рассказать Вам свой опыт написания gem. Данная идея возникала у меня давно, но до реализации все никак не доходила. Все упиралась в банальную лень и отсутствие достаточной мотивации. Однако неделю назад мне достался проект, связанный с сервером рассылки писем.

Выбираем генератор

На первом этапе нужно было определиться с тем, как будет создаваться библиотека: с нуля или с помощью какого-то генератора. Для первого раза, на мой взгляд, писать с нуля будет достаточно сложно и долго, поэтому рассмотрим известные генераторы. Небольшой поиск показал такие gems: hoe, newgem, bundler. Лично мне понравились два – newgem и bundler. Первый представляет полный комплект шаблонов, охватывающий множество случаев. Но я выбрал bundler за его простоту, минимальный набор сгенерированных файлов и набор rake tasks для создания пакета и его дальнейшей публикации.

Что внутри

Итак, для создания шаблона выполним в терминале команду:

bundle gem foogem

Данная команда создаст каталог foogem, в котором будет папка lib и файлы gemfile, Rakefile и foogem.gemspec. Рассмотрим каждый по отдельности. Файл gemfile, как и в любом bundle совместимом framework, содержит все необходимые gems. Также там присутствует строка gemspec, которая подгружает объявленные зависимости в файле foofem.gemspec. На многих ресурсах и в самом файле советуют заполнять gemfile, а указывать необходимые gems в .gemspec файле. Там разработчик может указать не только зависимости, необходимые для функционирования кода, но и что нужно на этапе разработки. Для указания глобальной зависимости используется метод add_dependency, для режима разработки — add_development_dependency. Кроме этого файл также хранит в себе информацию о разработчиках, краткое описание и версию библиотеки. В Rakefile мы можем прописать rake tasks, которые необходимы для нормального функционирования

Папка lib является основной частью вашего компонента. Она содержит файл foogem.rb и одноименную папку. В ней лежит файл для задания версии vesion.rb. В файле foogem.rb bundler любезно положил следующий код:

require ‘foogem/version’
module Foogem
#code place here
end


Далее вы воплощаете в жизнь свои идеи.

Проверка написанного

Закончив разрабатывать gem, будет неплохо написать тесты для проверки. Лично я предпочитаю rspec другим средам тестирования. Для этого добавим в foogem.spec зависимость

gem.add_development_dependency ‘rspec’

Далее создадим папку spec, которая будет хранить все наши тесты. Так как все тесты могут включать в себя много зависимостей, то выделим их в отдельный файл spec_helper.rb и будем его подключать. Все ж тесты положим в подкаталоги. Для большей автоматизации добавим следующий rake task:

desc 'Spec all functionality of gem'
task :spec_all do
system("rspec spec/*/")
end


Теперь командой rake spec_all мы запускаем все спеки.

Последним этапом будет публикация нашего gem. Для начала нам нужно создать пару акаунтов(если они отсутствуют) на github и rubygem. После этого создайте пустой репозиторий на github. Добавьте его в remote в вашем локальном git репозитории. И выполните команды:

rake build #создаст gem
rake release #опубликует его на github & rubygems


После этого вы можете его устанавливать через команды

gem install foogem #из удаленного сервера
rake install #из локального хранилища


В будущем для продолжения разработки необходимо будет только изменить версию gem в lib/foogem/version.rb и повторить этап публикации.
Поделиться публикацией
Комментарии 21
    +6
    Придется-таки написать статью, а то забыл о ней.

    В общем, что-то у вас неплохо. Но не стоит забывать, что RSpec предлагает удобный класс для запуска спеков. Для RSpec 2 это выглядит где-то так:

    begin
      require 'rspec/core/rake_task'
    
      RSpec::Core::RakeTask.new
    rescue LoadError
      $stderr.puts "RSpec not available. Install it with: gem install rspec-core rspec-expectations"
    end
    
      +1
      Только собирался про это написать. А то запуск системной команды выглядит как-то дико, что ли.

      rescue LoadError
        $stderr.puts "RSpec not available. Install it with: gem install rspec-core rspec-expectations"
      end
      


      Если гем RSpec есть в зависимостях проекта, разве есть нужда в этом?
        0
        Это у меня старый кусочек кода, который еще не рассчитывал на bundle install.
        Можно убрать.
        0
        в первоначальной задумке я так и хотел, но почему-то тесты не пошли. Потом посмотрев пару gems на github я остановился на решении использованном в cancan
          0
          Ремарка — это rspec rake job для 1.9, для 1.8 немного другой синтаксис:

          require 'rake'
          require 'spec/rake/spectask'

          desc «Run all examples»
          Spec::Rake::SpecTask.new('examples') do |t|
          t.spec_files = FileList['examples/**/*.rb']
          end

          Просто потратил на это минут 20 когда столкнулся.
            0
            Да. кстати, статья написана — Создание гемов — Руководство.
            +2
            Спасибо за статью. Возможно тем, кто будет озадачен вопросом создания собственных гемов с помощью bundler-а, будет так же интересен эпизод #245 New Gem with Bundler от небезызвестного ресурса Railscasts.
              +1
              Я использую jeweler для управления гемом — очень удобно. У него и генератор есть, но для генерирования я использовал что-то другое, не могу сходу вспомнить…

                0
                Я во всех своих проектах ушел от Jeweler к Bundler.

                Единственное, о чем жалею, это version:bump:xxx, но добавил его как отдельные команды.
                  0
                  Может быть, добавить метод в gem_helper.rb и попросить pull? Фича, полезная всем бы была.
                +1
                Спасибо, нужная статья. Важный шаг к опенсорсу и мастерству программирования. Ну и конечно модульности и чистоте приложения и кода. Добро пожаловать на хабр! Рад что ROR программистов здесь все больше.
                  0
                  У вас какая-то путаница. Там не spec файл, а gemspec.
                    0
                    спасибо за статью. как никогда вовремя ибо задавался этим вопросом несколько минут назад. еще раз спасибо :)
                      0
                      спасибо за найденную ошибку, мне нравятся дотошные читатели — они делают авторов лучше
                      0
                      Хорошая статья, полезная. Вот только фразы типо «Последним этапом будет публикация нашего gem» режут слух, все привыкли к «гему», как мне кажется — надо его так и переводить.
                        –1
                        Лично меня перевод гем сильно бесит, так как я их называю «джем». Поэтому все иностранные слова-термины я пытаюсь писать на английском.
                          0
                          Может, gem-a? :)
                            0
                            можно, но порой такие сочетания очень криво смотрятся
                            0
                            «Джем» — это такое варенье :) Так что это слово уже занято.
                              0
                              ну, омонимы и не только в русском есть
                                0
                                и всё-таки правильно джем

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

                          Самое читаемое