Criando sua própria Ruby gem

(com CLI)



por meleu

Motivação

Criar uma Ruby Gem é uma excelente forma de compartilhar código.

(não apenas no Open-Source, mas também internamente)


O que veremos?


quem sou eu?


Calibrando as expectativas


Calibrando o conteúdo


WTF is a gem?!


O comando gem

Um gerenciador de pacotes da linguagem Ruby que provê um formato padronizado de distribuir programas e bibliotecas.


O que é uma gem

É um pacote auto-contido que contêm código ruby que pode ser reutilizado.

Exemplos:


https://rubygems.org

É um serviço web utilizado para disponibilizar gems.


Espera! Mas e o bundler?!

O bundler é uma ferramenta criada para garantir que os desenvolvedores de um projeto utilizem a mesma versão das gems.

Gemfile e Gemfile.lock são arquivos usados pelo bundler (e não pelo comando gem).


Colocando a mão na massa! 💪

doc: https://guides.rubygems.org/what-is-a-gem/

O mínimo do mínimo para criarmos uma gem:

$ tree hello_meleu
hello_meleu/
├── hello_meleu.gemspec
└── lib
    └── hello_meleu.rb

obs.: usar um nome diferente!


Test First!!!

(???)

test/test_hello_meleu.rb


lib/hello_meleu.rb

class HelloMeleu
  def self.hello
    'Hello meleu!'
  end
end

hello_meleu.gemspec

Gem::Specification.new do |s|
  s.name = 'hello_meleu'
  s.authors = ['meleu']
  s.files = ['lib/hello_meleu.rb'] # <-- IMPORTANTE!
  s.summary = 'Greeting meleu'
  s.version = '0.0.1'
end

doc:
https://guides.rubygems.org/specification-reference/


Publicando sua gem

# buildando a gem
gem build hello_meleu.gemspec

# instalando localmente
gem install hello_meleu-0.0.1.gem
# experimentar no irb

# publicando no RubyGems.org
gem push hello_meleu-0.0.1.gem
# na primeira vez, vai pedir pra se cadastrar

🎉 Parabéns! 🎉

Sua gem foi publicada!

https://rubygems.org/gems/hello_meleu


enriquecendo a gemspec

criar um repositório no github


enriquecendo a gemspec

Gem::Specification.new do |s|
  # ...
  s.homepage = "https://github.com/meleu/hello_meleu"
end

enriquecendo a gemspec

Gem::Specification.new do |s|
  # ...
  s.metadata = {
    "bug_tracker_uri": "#{s.homepage}/issues",
    "changelog_uri": "#{s.homepage}/releases",
    "wiki_uri": "#{s.homepage}/wiki",
    "source_code_uri": s.homepage,
    "documentation_uri": "https://www.rubydoc.info/gems/hello_meleu",
  }
end

enriquecendo a gemspec

documentação

TODO: link para YARD

# Class used to greet meleu with "hello".
class HelloMeleu
  # Greets meleu with "hello".
  #
  # @return [String] "Hello meleu!"
  def self.hello
    "Hello meleu!"
  end
end

Publicando a nova versão

# buildando a gem
gem build hello_meleu.gemspec

# publicando no RubyGems.org
gem push hello_meleu-0.0.2.gem

🎉 Parabéns novamente! 🎉

Sua gem foi atualizada!

https://rubygems.org/gems/hello_meleu

(lembrete: mostrar rubydoc.info)


Sua gem está disponível para o mundo!

# instalar
gem install hello_meleu

# testar no irb

Recapitulando


Break time!

MC Hammer


(aka "piadas de tiozão")

Fornece uma API REST que não requer autenticação!


gem HTTParty

https://github.com/jnunemaker/httparty

experimentar no irb:


Criar uma gem pra buscar dad jokes!

Motivação:


Criando uma gem usando o bundler

(scaffold feelings 😇)

obs.: usar um nome diferente!

bundle gem dadjoke \
  --exe \
  --coc \
  --mit \
  --test=minitest \
  --ci=github \
  --linter=rubocop
  
cd dadjoke

git stuff


bundle install # vai falhar

Gemfile

# ...

# Specify your gem's dependencies in dadjoke.gemspec
gemspec

# ...

TODO: editar os TODOs! Principalmente os do gemspec.

Falar do arquivo version.rb


Listando dependências no gemspec

Gem::Specification.new do |s|
  # ...
  # ignorar código confuso e cheio de TODOs...
  # ...
  spec.add_runtime_dependency "httparty", "~> 0.21"
end
# instalar dependências
bundle install

Test First!!!

(??? link ???)

test/test_dadjoke.rb


Obtendo uma dadjoke aleatória

class Joke
  def self.random
    HTTParty.get(@url, headers: @headers)["joke"]
  end
end

Buscando dadjokes

class Joke
  def self.search(term, limit = 3)
    query = { term:, limit: }
    response = HTTParty.get(
      "#{@url}/search",
      headers: @headers,
      query:
    )
    response["results"].map { |result| result["joke"] }
  end
end

Testar no irb

bin/console

Tudo ok?

Boa hora para mais um git commit e subir pra um repo no github.


GitHub Actions

(opcional)

Mostrar action no repo.


Ajeitando o gemspec


Publicando a gem!

# buildando a gem
gem build dadjoke.gemspec

# publicando no RubyGems.org
gem push dadjoke-0.0.1.gem

# instalar do rubygems.org
gem install dadjoke

Criando um CLI

Primeiro um deleteme.rb.


Queremos um comando UNIX decente!


Conhecendo o Thor

http://whatisthor.com/

(criar um mycli com hello world e opção --up)


Adicionando dependência no gemspec

Gem::Specification.new do |s|
  # ...
  spec.add_runtime_dependency "thor", "~> 1.3"
end
bundle install

Obtendo uma dad joke aleatória

class DadjokeCLI < Thor
  desc "random", "Get a random dad joke"
  def random
    puts Dadjoke::Joke.random
  end
end
# comando:
dadjoke random

Comando default

Não quero ter que digitar dadjoke random quando apenas dadjoke já seria suficiente.

class DadjokeCLI < Thor
  # ...
  default_command :random
end
# comando:
dadjoke

Buscando uma dad joke

class DadjokeCLI < Thor
  desc "search TERM", "Get jokes based on a search TERM"
  def search(term)
    jokes = Dadjoke::Joke.search(term)
    puts jokes.join("\n\n---\n\n")
  end
end

Buscando uma dad joke

class DadjokeCLI < Thor
  desc "search TERM", "Get jokes based on a search TERM"
+ option :num, type: :numeric, default: 1, desc: "Amount of jokes to retrieve"
  def search(term)
-   jokes = Dadjoke::Joke.search(term)
+   jokes = Dadjoke::Joke.search(term, options[:num])
    puts jokes.join("\n\n---\n\n")
  end
end

Recapitulando


Fim! 🥲