RTFM по созданию ruby gem

Как вы наверно знаете – недавно вышел очередной релиз Ruby on Rails (3.2), в котором сделаны первые шаги в уходе от плагинов, размещаемых в vendor/plugins – в этом релизе они стали deprecated, а в 4й версии фрэймворка от них откажутся окончательно. Наверняка у многих из вас есть свои наработки, которыми бы вы хотели поделиться с коллегами и самое время научиться писать gem-ы, которые могут быть использованы в других проектах. Давайте рассмотрим процесс создания gem-а.

Первым делом создаем “каркас” для нашего будущего gem-а:

alec$ bundle gem super_gem
create  super_gem/Gemfile
create  super_gem/Rakefile
create  super_gem/LICENSE
create  super_gem/README.md
create  super_gem/.gitignore
create  super_gem/super_gem.gemspec
create  super_gem/lib/super_gem.rb
create  super_gem/lib/super_gem/version.rb
Initializating git repo in /Users/alec/Temp/super_gem

где super_gem это название нашего будущего расширения.

Большинство файлов вам наверняка знакомы, поэтому рассмотрим super_gem.gemspec, в котором описываются параметры нашего расширения:

gem.authors       = ["Alexey Poimtsev"] # Имя автора/авторов
gem.email         = ["alec@alec-c4.com"] # Его/их email
gem.description   = %q{TODO: Write a gem description} # краткое и
gem.summary       = %q{TODO: Write a gem summary} # расширенное описание
gem.homepage      = "" # url страницы проекта

# Кусок кода, описывающий какие файлы включаются в проект, название гема и его версия
gem.executables   = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
gem.files         = `git ls-files`.split("\n")
gem.test_files    = `git ls-files -- {test,spec,features}/*`.split("\n")
gem.name          = "super_gem"
gem.require_paths = ["lib"]
gem.version       = SuperGem::VERSION

Версия прописывается в файле lib/super_gem/version.rb (спасибо, кэп :))

Также можно (и даже нужно)  прописать зависимости для разработки и продуктива, например такие

gem.add_development_dependency "rspec"
gem.add_runtime_dependency "rails"

Теперь давайте создадим проект, который будет помогать нам тестировать gem:

$ rails new super_gem_example

и в его Gemfile пропишем путь к нашему расширению:

gem 'super_gem', :path => '../super_gem'

в параметре path мы прописываем путь к проекту гема.

К процессу разработки функционала мы вернемся чуть позже, а сейчас я ненадолго отвлекусь, чтобы рассказать вам, что нам надо делать, когда гем уже будет готов. Первым делом давайте зарегистрируемся на rubygems.org, на котором будут публиковаться наши расширения. Для того, чтобы выполнить сборку достаточно выполнить команду

$ rake build

из директории gem-проекта и в pkg/super_gem-0.0.1.gem появится наш свежесобранные гем. Его мы можем опубликовать с помощью команды

$ gem push pkg/super_gem-0.0.1.gem

а если мы случайно отправили не ту сборку, ее всегда можно удалить с помощью команды gem yank (для этого нам понадобится установить gemcutter). Опубликованный gem появляется на rubygems.org и мы можем даже посмотреть статистику его скачиваний и порадоваться его востребованности (ведь мы же не будем писаться всякий ненужный хлам, правда? :))

А теперь немного сниппетов, которые помогут вам при разработке гема:

1.  Расширение функционала контроллеров

module SuperGem
 module ControllerMethods
    def super_method
      lot_of_code()
    end
 end
end

if defined? ActionController
 ActionController::Base.class_eval do
  include SuperGem::ControllerMethods
 end
end

2. По аналогии – View-хэлперы

module SuperGem
 module ViewMethods
    def super_method
      lot_of_code()
    end
 end
end

if defined? ActionView
 ActionView::Base.class_eval do
  include SuperGem::ViewMethods
 end
end

3. Модели

в lib/super_gem.rb

ActiveRecord::Base.extend(SuperGem::ActiveRecordExtension)

и в файле с методами:

module SuperGem
  module ActiveRecordExtension

    def super_method
      lot_of_code()
    end
  end
end

Вроде несложно :) Если что-то вспомню или у вас будет интересный сниппет – присылайте и я его опубликую :)

Также рекомендую прочитать статью Mountable Engines in Rails 3.1 – она поможет вам разобраться как использовать rails engines в вашем геме.

Похожие записи в блоге:

  • http://twitter.com/ognevsky Andrey Ognevsky

    Коль уж `rake build`, так и `rake release` нужно писать, а не `gem push`. Или нет?
    Ну и `rake install`, чтобы сразу его поставить.

    • http://alec-c4.com Alexey Poimtsev

      На самом деле и то и другое работает :)

      • Kir S.

        Я всегда rake release пользовался.

  • http://twitter.com/HornedKavu Max Riveiro

    Леш, смотри, ты тут рассматриваешь гем, который предназначается только Рельсы. Я думаю, что еще хорошо показать как делать гем, просто гем (как в том анекдоте про лося). Хотя здесь и показаны базовые принципеы, согласен. А ещё, ты не пробовал Jeweler? Мне он нравится несколько больше чем бандлеровский генератор.

    • http://alec-c4.com Alexey Poimtsev

      Макс, ну ты же понимаешь, что в одной статье нельзя описать огромную кучу тулзов для написания гемов :) jeweler – хорошая вещь, но он дает просто чуть больше автоматизма при работе с командной строкой. Например – автоматическая генерация rspec-тестов или rake-таски для изменения версии гема :)

    • http://twitter.com/ognevsky Andrey Ognevsky

      А мне больше бандлеровый нравится, потому что я туда кучу всего дописал: автоматическая генерация README, LICENSE и автоматическая же вставка имени и почты автора. А Jeweler с первый версий как–то вообще не вдохновлял, уж и не знаю, почему.

  • Kir S.

    А можешь про создание спеков написать? С fake_app там, и прочими штуками.

    • http://alec-c4.com Alexey Poimtsev

      постараюсь в ближайшее время :)

  • Maxim Filatov

    gem.description = %q{TODO: Write a gem description} # краткое и
    gem.summary = %q{TODO: Write a gem summary} # расширенное описание

    а не наоборот?

    • http://alec-c4.com Alexey Poimtsev

      ага – спасибо, немного перепутал :) сорри за невнимательность :)