Olá pessoas, estamos de volta com mais um post. Bem hoje vou mostrar como criar uma gem, sem mais delongas, vamos nessa!

O que vamos criar e como?

O que?

Digamos que nós estamos precisando de uma classe que vai receber um Array de números e retorna a média deles.

Como?

Vamos dá o nome de array_to_average

Criando a gem

$ bundle gem array_to_average
$ cd array_to_average

Setar Specification

Preencha as variaveis abaixo:

# ./array_to_average.gemspec
...
  spec.authors       = ["Walmir Neto"]
  spec.email         = ["wfsneto@gmail.com"]

  spec.summary       = "Get array numbers and calculate the average"
  spec.description   = "This gem is a sample gem on 'http://blog.wfsneto.com.br/'"
  spec.homepage      = "https://github.com/wfsneto/array_to_average"
  spec.license       = "MIT"

  spec.required_ruby_version = '>= 2.0.0'
...
  spec.add_development_dependency "bundler", "~> 1.11"
  spec.add_development_dependency "rake", "~> 10.0"
  spec.add_development_dependency "rspec", "~> 3.0"
...

Rodar 1ª vez os tests

$ rspec

ArrayToAverage
  has a version number
  does something useful (FAILED - 1)

Failures:

  1) ArrayToAverage does something useful
     Failure/Error: expect(false).to eq(true)

       expected: true
            got: false

       (compared using ==)
     # ./spec/array_to_average_spec.rb:9:in `block (2 levels) in < top (required)>'

Finished in 0.0189 seconds (files took 0.11228 seconds to load)

Apagar o test

Remover o treecho de código abaixo:

# spec/array_to_average_spec.rb
  it 'does something useful' do
    expect(false).to eq(true)
  end

Rodar novamente os tests

$ rspec

ArrayToAverage
  has a version number

Finished in 0.00132 seconds (files took 0.11596 seconds to load)
1 example, 0 failures

Nossa necessidade

Levando em conta que eu vou precisar de uma classe que recebe um array de numeros e retorna a média. vamos criar uma classe que calcular as médias. Eu quero criar uma classe dentro do module ArrayToAvevage para isso é preciso dar um autoload nesse module:

# lib/array_to_average.rb

module ArrayToAverage
  autoload :Calculate, 'array_to_average/calculate' # adicione essa linha
end

Criando classe calculate

Vamos criar a classe abaixo:

# lib/array_to_average/calculate.rb

module ArrayToAverage
  class Calculate
    def initialize numbers
    end

    def average
    end
  end
end

Criando casos de testes

Baseado na classe acima criar três casos de teste, para quando a variavel numbers: * quando não estiver vazio * quando estiver vazio * quando não for um array

# spec/array_to_average/calculate_spec.rb

require 'spec_helper'

module ArrayToAverage
  describe Calculate, type: :lib do
    let(:numbers) { [7,8,5] }
    let(:average) { 20.0 / numbers.size }

    describe '.average' do
      it 'when numbers is not empty' do
        calculated = ArrayToAverage::Calculate.new(numbers).average
        expect(calculated).to eq average
      end

      it 'when numbers is empty' do
        calculated = ArrayToAverage::Calculate.new([]).average
        expect(calculated).to eq 0.0
      end

      it 'when numbers is array' do
        calculated = ArrayToAverage::Calculate.new(100).average
        expect(calculated).to eq 0.0
      end
    end
  end
end

Vamos rodar os tests para ver-los falhando:

$ rspec

ArrayToAverage::Calculate
  .average
    when numbers is not empty (FAILED - 1)
    when numbers is empty (FAILED - 2)
    when numbers is array (FAILED - 3)

ArrayToAverage
  has a version number

Failures:

  1) ArrayToAverage::Calculate.average when numbers is not empty
     Failure/Error: expect(calculated).to eq average

       expected: 6.666666666666667
            got: nil

       (compared using ==)
     # ./spec/array_to_average/calculate_spec.rb:11:in `block (3 levels) in < module:ArrayToAverage>'

  2) ArrayToAverage::Calculate.average when numbers is empty
     Failure/Error: expect(calculated).to eq 0.0

       expected: 0.0
            got: nil

       (compared using ==)
     # ./spec/array_to_average/calculate_spec.rb:16:in `block (3 levels) in < module:ArrayToAverage>'

  3) ArrayToAverage::Calculate.average when numbers is array
     Failure/Error: expect(calculated).to eq 0.0

       expected: 0.0
            got: nil

       (compared using ==)
     # ./spec/array_to_average/calculate_spec.rb:21:in `block (3 levels) in < module:ArrayToAverage>'

Finished in 0.01961 seconds (files took 0.11578 seconds to load)

Agora vamos fazer-los passar:

# lib/array_to_average/calculate.rb

module ArrayToAverage
  class Calculate
    def initialize numbers
      @numbers = numbers.kind_of?(Array) ? numbers : []
      @sum = @numbers.inject { |sum,number| sum ? sum + number.to_f : number.to_f } || 0
      @size = @numbers.empty? ? 1 : @numbers.size
    end

    def average
      @sum / @size
    end
  end
end

Roda tests novamente para ver se está tudo certo

$ rspec

ArrayToAverage::Calculate
  .average
    when numbers is not empty
    when numbers is empty
    when numbers is array

ArrayToAverage
  has a version number

Finished in 0.00255 seconds (files took 0.11515 seconds to load)
4 examples, 0 failures

Agora vamos gerar o build da gem, para isso rode um:

$ gem build array_to_average.gemspec

  Successfully built RubyGem
  Name: array_to_average
  Version: 0.0.1
  File: array_to_average-0.0.1.gem

Alguns gems comandos

bundle gem array_to_average
gem build array_to_average.gemspec
gem push array_to_average-0.0.1.gem

Links & Referencias

That's all folks!

Em breve vou tentar mostrar como publicar essa gem!