Como fazer uma Ruby Gem

Tutoriais - 10/Fev/2021 - por Ícaro Siqueira

Ruby Gems são ferramentas amplamente utilizadas no desenvolvimento de aplicações, oferecendo soluções prontas para resolver problemas comuns em muitos projetos. Neste tutorial vamos mostrar passo a passo como criar sua própria Ruby Gem.

Antes de começar

A seguir, alguns conceitos importantes que serão necessários para compreender esse tutorial: - Resumidamente, uma gem é uma biblioteca de código Ruby (conjunto de classes/módulos e métodos) reutilizável, ou seja, pode ser aproveitada em diferentes partes das aplicações para realizar uma determinada função. - Rubygems é a plataforma mais conhecida e utilizada pela comunidade, oferecendo hospedagem dos códigos publicados por usuários. - Bundler é o gerenciador de gems e dependências do ruby.

Construindo a gem

O próprio Bundler possui um comando para criar o repositório e a estrutura com os arquivos padrão recomendados para a gem, mas também é possível construir os arquivos individualmente ou adicionar o gemspec (vamos falar sobre ele mais pra frente) a um projeto já existente. Neste tutorial utilizaremos o inicializador do bundle para criar uma gem chamada “example”. Para evitar a criação de uma gem cujo nome já existe, recomenda-se utilizar a pesquisa no RubyGems com nome desejado para verificar a pré-existência de alguma gem com este nome. Este tutorial utiliza a versão 2.7.0 do Ruby.

Dica: O Rubygems possui algumas recomendações em relação ao nome da gem. A tabela a seguir demonstra as diferenças da estrutura e referenciação no código em relação ao seu nome.

Nome da gem (não utilizar maiúsculas) Referência no código Classe/Módulo principal
test_gem require ‘test_gem’ TestGem
test-gem require ‘test/gem’ Test::Gem

Além disso, / é utilizado sua gem esteja adicionando funcionalidades a outra gem. Para mais informações, veja esse guia.

1° passo - Criando o projeto

Execute o comando bundle gem example, substituindo example com o nome da sua gem:

$ bundle gem example                                                                                                                                        ruby-2.7.0
Creating gem 'example'...
    create  example/Gemfile
    create  example/lib/example.rb
    create  example/lib/example/version.rb
    create  example/example.gemspec
    create  example/Rakefile
    create  example/README.md
    create  example/bin/console
    create  example/bin/setup
    create  example/.gitignore
    create  example/.travis.yml
    create  example/test/test_helper.rb
    create  example/test/example_test.rb
Initializing git repo in /home/icaro/Workspace/rebase/example
Gem 'example' was successfully created. For more information on making a RubyGem visit https://bundler.io/guides/creating_gem.html

Os arquivos são criados com a seguinte estrutura:

2° passo - Criação do gemspec

Para construir uma gem é necessário possuir o arquivo gemspec na raíz da aplicação. Este arquivo é um manual de instruções da gem para o Ruby, nele estão contidos dados e metadados da gem como nome, versão, autores, entre outras. Além disso, as dependências externas como outras gems são declaradas no gemspec (add_development_dependency e add_runtime_dependency dependendo da função da dependência, ou apenas add_dependency). Atualizaremos nosso gemspec padrão para o seguinte:

require_relative 'lib/example/version'

Gem::Specification.new do |spec|
 spec.name          = "example"
 spec.version       = Example::VERSION
 spec.authors       = ["Your User"]
 spec.email         = ["your@user.com.br"]

 spec.summary       = %q{Write a short summary, because RubyGems requires one.}
 spec.description   = %q{Write a longer description or delete this line.}
 spec.homepage      = "https://homepage.com.br"
 spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")

 spec.metadata["allowed_push_host"] = "Set to 'http://mygemserver.com'"

 spec.metadata["homepage_uri"] = spec.homepage
 spec.metadata["source_code_uri"] = "https://github.com/User/your_repository"
 spec.metadata["changelog_uri"] = "https://github.com/User/your_repository/changelog.md"

 # Specify which files should be added to the gem when it is released.
 # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
 spec.files         = Dir.chdir(File.expand_path('..', __FILE__)) do
   `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
 end
 spec.bindir        = "exe"
 spec.executables   = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
 spec.require_paths = ["lib"]

 spec.add_development_dependency 'bundler', '~> 2.0'
 spec.add_development_dependency 'minitest', '~> 2.1.0' 
 spec.add_development_dependency 'mocha', '~> 1.11.2'

end

Neste exemplo, foram inseridas as informações de contato e adicionadas as dependências que serão utilizadas pela aplicação.

3° passo - Criação da base do código

Recomenda-se que todos os arquivos de classes e/ou módulos estejam na pasta lib e os testes na pasta test ou spec. Para este exemplo, iremos construir um simples selecionador de nomes aleatório (como a gem Faker), uma formatação do nome no formato de e-mail e seus respectivos testes:

Arquivo: example/lib/example.rb.

class Example
 NAMES = ['Bilbo Baggins',
   'Samwise Gangee',
   'Meriadoc Brandebuque',
   'Frodo Baggins',
   'Peregrin Took']

 def self.random_name
     NAMES.sample
 end

 def self.random_email
     name = NAMES.sample
     "#{name.gsub(' ', '.').downcase}@shire.com.me"
 end
end

Arquivo: example/test/example_test.rb.

require 'minitest/autorun'
require './lib/example'
require 'mocha/minitest' # gem utilizada para fazer o mock do resultado no teste

class ExampleTest < Minitest::Test

 def test_random_name
   Array.any_instance.stubs(:sample).returns('Samwise Gangee')
   assert Example.random_name == 'Samwise Gangee'
 end

 def test_random_email
   Array.any_instance.stubs(:sample).returns('Samwise Gangee')
   assert Example.random_email == 'samwise.gangee@shire.com.me'
 end
end

Para executar os testes rode bundle install e, então, ruby test/*.

$ ruby test/*                                                                                                                                   ruby-2.7.0 master
Run options: --seed 60160

# Running:

..

Finished in 0.001388s, 1441.1890 runs/s, 1441.1890 assertions/s.
2 runs, 2 assertions, 0 failures, 0 errors, 0 skips

Dica: Acesse os repositórios das gems que costuma utilizar no dia a dia e veja a estruturação do código e os testes, isso pode ajudar a identificar boas práticas e aplicá-las em seu código.

4° passo - Documentação

A documentação é de extrema importância, pois uma gem muitas vezes é uma dependência utilizada por terceiros. Logo, sua utilização correta depende do entendimento por quem for utilizá-la. A documentação padrão que foi inicializada pelo bundler já possui uma estrutura que pode ser seguida como base, nesse caso será substituída por uma simples documentação da app:

5° passo - Construindo a gem (example.gem)

Para criar o .gem basta executar o comando gem build passando o gemspec. O bundler utilizará o gemspec e criará o arquivo example-0.1.0.gem, que contém o código “empacotado” já no seu formato instalável pelo bundler, ou seja, a gem propriamente dita.

$ gem build example.gemspec                                                                                                                     ruby-2.7.0 master
WARNING:  licenses is empty, but is recommended.  Use a license identifier from
http://spdx.org/licenses or 'Nonstandard' for a nonstandard license.
WARNING:  See http://guides.rubygems.org/specification-reference/ for help
  Successfully built RubyGem
  Name: example
  Version: 0.1.0
  File: example-0.1.0.gem

Feito isso, já temos a gem pronta para ser instalada e utilizada! Podemos instalá-la localmente com o comando gem install passando o arquivo .gem criado no passo anterior como opção:

$ gem install example-0.1.0.gem                                                                                                                 ruby-2.7.0 master  ✗
Successfully installed example-0.1.0
1 gem installed

Podemos verificar se a gem foi instalada corretamente e sua versão através do comando gem list:

gem list grep example                                                                                                                           ruby-2.7.0 master  ✗

*** LOCAL GEMS ***



*** LOCAL GEMS ***

example (0.1.0)

Agora, com a gem instalada, vamos testá-la no IRB:

irb                                                                                                                                             ruby-2.7.0 master  ✗
2.7.0 :001 > require 'example'
 => true
2.7.0 :002 > Example.random_name
 => "Frodo Baggins"
2.7.0 :003 > Example.random_name
 => "Peregrin Took"
2.7.0 :004 > Example.random_email
 => "bilbo.baggins@shire.com.me"
2.7.0 :005 > Example.random_email
 => "samwise.gangee@shire.com.me"

Agora a gem está instalada e pronta para ser utilizada, como podemos ver se os métodos são chamados e funcionam corretamente.

6° passo - Publicando a gem

Para disponibilizar a gem no site RubyGems e torná-la disponível para ser utilizada pela comunidade, é necessário criar uma conta no RubyGems (caso ainda não tenha) para configurar a autenticação. Agora já podemos enviar a gem ao repositório remoto com o comando gem push nome-da-gem.gem e, em seguida, inserir o e-mail e senha correspondente à sua conta do RubyGems:

$ gem push nome-da-sua-gem.gem

Enter your RubyGems.org credentials.
Don't have an account yet? Create one at https://rubygems.org/sign_up
   Email:   gem_author@example
Password:
Signed in.
Pushing gem to RubyGems.org...
Successfully registered gem: nome-da-sua-gem (versão)

Pronto! Sua Ruby Gem está disponível para ser utilizada pela comunidade. :D

Referências

Foto de perfil do autor
Ícaro Siqueira

Dev na Rebase

Estudando programação desde 2019, apaixonado por arte, gatos e videogame.