Você está na página 1de 180

Conhecendo Ruby

por Nando Vieira


Copyright © Hellobits & Nando Vieira. Todos os direitos reservados.

Nenhuma parte desta publicação pode ser reproduzida sem o consentimento dos autores.
Todas as marcas registradas são de propriedade de seus respectivos donos.

Conhecendo Ruby, Nando Vieira, 1a versão


Conteúdo
1 Introdução 35 Hash
2 Sobre o Rub
Rubyy 37 Symbol
2 Instalação 38 Boolean
5 Tipo 39 nil
9 Variáveis e constantes 40 Range
9 Métodos 41 Expressões regulares
9 Entendendo o self 42 Procs e lambdas
12 Convenções 47 Set
19 Atribuição de variáveis 49 Estrutur
Estruturas
as condicionais

19 Constantes e variáveis globais 49 if


20 Conhecendo o IRB 50 else
22 Executando códigos Ruby 51 elsif
23 Acessando a documentação do Ruby 52 unless
26 Rub
Rubyy Core Classes 53 Operador ternário ?:
26 String 54 case
30 Números 59 Estrutur
Estruturas
as de repetição

33 Array 59 for..in
61 while e until 102 Tamanho de uma string
63 loop 103 Substrings
64 Controlando o loop 105 Codibcação
66 Usando iteradores 110 Trabalhando com números

71 Blocos 110 Operadores matemáticos


72 Escopo de variáveis 111 Números absolutos
73 Interagindo com blocos 111 Veribcando números pares e ímpares
76 Classes 112 Veribcando zeros e não-zeros
76 Criando classes 112 Conversão entre diferentes bases
78 Debnindo métodos 113 Fazendo arredondamentos
83 Debnindo métodos de classe 115 Trabalhando com arr
arraays

89 Debnindo constantes 115 Acessando os elementos


92 Entendendo classes Singleton 115 Adicionando novos elementos
97 Módulos 117 Removendo elementos
98 Trabalhando com o load path 119 Filtrando elementos
99 Trabalhando com strings 120 Ordenando elementos
99 Concatenando strings 122 Buscando elementos
100 Formatando strings 123 Iterando elementos
101 Convertendo em maiúsculas e minúsculas 124 Compondo novos arrays
126 Trabalhando com hashes 152 Escre
Escrevvendo testes com T
Test::Unit
est::Unit

126 Lista de chaves e valores 153 Organizando o código


126 Veribcando a existência de chaves e valores 154 Convertendo temperaturas
127 Acessando o hash 167 Lista de métodos de asserção
128 Filtrando elementos 169 Criando e distribuindo gems

129 Removendo valores de um hash 169 A estrutura de uma gem


130 Compondo novos hashes 170 Criando sua própria gem
131 Iterando hashes 172 Distribuindo sua gem
133 Trabalhando com arquiv
arquivos
os e diretórios 174 Mais sobre RubyGems
133 Manipulando nomes de arquivos
134 Manipulando arquivos
138 Manipulando diretórios
140 Testando arquivos e diretórios
141 Datas de modibcação e acesso de arquivos
142 Trabalhando com data e hor
horaa

143 URLs e requisições HTTP

144 Lidando com e


exxceções

145 Capturando exceções


151 Conhecendo o Rak
Rake
e
CAPÍTULO 1

Introdução
Ruby é uma linguagem de programação interpretada, com tipagam forte e dinâmica, que tem como foco a simplicidade e produtividade.
Sua sintaxe é extremamente elegante, o que facilita a leitura e escrita de códigos.

Criada pelo japonês Yukihiro “Matz” Matsumoto em meados de 1993, a linguagem só foi lançada publicamente em 1995. Matz
combinou partes de suas linguagens favoritas (Perl, Smalltalk, Eiffel, Ada e Lisp) para criar uma linguagem que fosse, segundo suas
próprias palavras, mais poderosa que Perl e mais orientada a objetos que Python.

Muitas linguagens não tratam números e outros tipos primitivos como objetos, mas no Ruby isso é diferente. No Ruby, tudo é objeto.
Tipos primitivos possuem métodos e podem ter atributos. Classes são objetos.

O Ruby é uma linguagem extremamente cexível.

Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

—Nando Vieira, Janeiro de 2012

1
CAPÍTULO 1

Sobre o Ruby
Instalação
O Ruby pode ser instalado em todos os sistemas operacionais. Veja abaixo como instalar em sua máquina de desenvolvimento. É
sempre uma boa ideia utilizar a última versão estável do Ruby, a menos que você tenha razões muito fortes para não fazer isso.
Enquanto este livro está sendo escrito, a última versão estável é 1.9.3-p0
1.9.3-p0.

Instalando no Windows
A maneira mais simples de instalar Ruby no Windows é utilizando o Ruby Installer. Acesse o endereço http://rubyinstaller.org/
downloads/ e faça o download do instalador da última versão estável.

Figur
Figuraa 1: Download do Ruby Installer

2
Execute o arquivo baixado, algo como rubyinstaller-1.9.3-p0.exe, e o instalar será iniciado. Aceite os termos de uso. Será exibida,
então, uma janela onde você deve marcar a conbguração “Add Ruby executables to your PATH”. Sem essa opção, o Ruby não bcará
disponível globalmente no seu terminal.

Figur
Figuraa 2: Conbgurando o instalador

Clique em “Install” e, depois, clique em “Finish”. Pronto! Você já tem o Ruby instalado. Para certibcar-se que tudo está funcionando
corretamente, abra o terminal. Para isso, clique no menu “Windows” e execute “powershell” para listar o terminal. Execute o comando
ruby -v para listar a versão do Ruby. Em versões mais antigas do Windows, você pode usar o comando cmd. Se preferir, você também
pode instalar o PowerShell manualmente.

3
Figur
Figuraa 3: Usando o terminal

Se aparecer alguma mensagem muito diferente de ruby 1.9.3-p0 (2011-10-30) [i386-mingw32], a instalação provavelmente não
foi completada com sucesso. Neste caso, reinicie a instalação e siga exatamente os passos descritos acima.

Instalando no Mac OS X
Embora o Mac OS X já venha com o Ruby instalado por padrão, a sua versão é muito antiga.

Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

4
Instalando no Linux Ubuntu
No Linux nós também iremos utilizar o RVM para gerenciar as instalações do Ruby. Assim como no Mac OS X, execute o comando
abaixo em seu terminal.

bash -s stable < <(curl -s https://raw.github.com/wayneeseguin/rvm/master/binscripts/rvm-installer)

Isso irá baixar e instalar o RVM.

Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

Tipo
O Ruby é uma linguagem dinamicamente tipada
tipada, o que signibca dizer que você não precisa debnir o tipo de dado que uma variável irá
armazenar. Além disso, uma mesma variável pode receber tipos diferentes a qualquer momento.

value = 1234
puts value.class
#=> Fixnum

value = "Hello"
puts value.class
#=> String

O Ruby também é uma linguagem fortemente tipada


tipada, já que o tipo do objeto é veribcado antes de efetuar determinadas operações.

5
number = 1234
string = "Hello"

number + string
#=> TypeError: String can't be coerced into Fixnum

Ao tentar somar o número 1234 com a string Hello, o Ruby lançará a exceção TypeError. Isso acontece porque a coerção precisa ser
feita explicitamente.

Duck Typing
No Ruby, nós não declaramos o tipo de objetos, nem o tipo do retorno de métodos. Embora isso possa parecer algo muito ruim para
quem está acostumado com linguagens como Java, linguagens dinamicamente tipadas como o Ruby são muito cexíveis, produtivas e,
acredite, seguras. Na maioria das vezes, o medo de não poder contar com o compilador para fazer veribcações de tipos não tem
fundamento.

Desenvolvedores Ruby estão mais acostumados em debnir objetos pelo que eles podem fazer, do que por seu tipo. Esta técnica é
chamada de duck typing.

Se anda como um pato e faz barulho como um pato, então de devve ser um pato. E o interpretador bcará feliz em fazer com que o objeto
seja tratado como um pato. Na prática, isso signibca que em vez de fazer veribcações de tipo de um objeto, você deve se preocupar se
este objeto é capaz de executar o método que você precisa.

Pegue como exemplo strings, arquivos e arrays. As classes Array, File e String implementam o método de instância <<, que quase
sempre signibca append. Você pode se aproveitar desta interface para criar, por exemplo, uma classe de log que não se importa com o
tipo de objeto que irá armazenar esses logs.

class SimpleLogger
def initialize(io)
@io = io
end

6
def log(message)
@io << "#{Time.now} - #{message}\n"
end
end

A classe SimpleLogger consegue enviar os logs para arrays, strings, arquivos e, se quiser, para qualquer outro objeto que implemente
o método <<.

O Ruby realmente abraça o duck typing por toda a linguagem. Diversos protocolos exigem que o objeto apenas implemente um
método to_<protocol>. Muitas operações que envolvem arrays, por exemplo, exigem que o objeto do lado direito da expressão
apenas implemente o método to_ary.

class Numbers
def to_ary
[4, 5, 6]
end
end

[1, 2, 3] + Numbers.new
#=> [1, 2, 3, 4, 5, 6]

A classe Hash, por exemplo, permite que você una dois objetos, desde que o método to_hash seja implementado.

class Configuration
def to_hash
{root: "/etc"}
end
end

config = Configuration.new

7
{name: "Custom config"}.merge(config)
#=> {:name=>"Custom config", :root=>"/etc"}

Não é preciso dizer que este tipo de protocolo por convenção permite criar códigos muito mais cexíveis, com extrema facilidade.

Isso não signibca que saber o tipo de objetos não seja útil. Veja, por exemplo, as operações matemáticas. Qualquer objeto pode ser
convertido em números. Para isso, basta implementar o método coerce, que recebe o objeto que está solicitando a coerção. O
exemplo abaixo mostra como criar uma classe cuja instância pode ser convertida em números inteiros e cutuantes, mas não em
instâncias da classe BigDecimal.

class NumberOne
def coerce(object)
case object
when Integer
[object, 1]
when Float
[object, 1.0]
else
raise TypeError, "#{self.inspect} can't be coerced into #{object.class}"
end
end
end

puts 1 + NumberOne.new
#=> 2

puts 1.0 + NumberOne.new


#=> 2.0

require "bigdecimal"

8
puts BigDecimal.new("1.0") + NumberOne.new
#=> TypeError: FakeNumber can't be coerced into BigDecimal

O duck typing vai além de simples regras; é um estilo de programação. Antes de exigir tipos de objetos, pergunte-se se isso é realmente
necessário. Às vezes, o tipo do objeto é muito importante, mas muitas vezes isso simplesmente não importa.

Variáveis e constantes
Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

Métodos
Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

Entendendo o self
self será sempre uma referência ao receiver atual e pode ser diferente dependendo do contexto em que você estiver. Por exemplo,
quando estamos no namespace global, nosso self será o objeto main. Já em uma classe, nosso self será a própria classe.

puts self
#=> main

9
class Thing
puts self
end
#=> Thing

Sempre que executar um método, o Ruby irá veribcar se esse método existe no receiver padrão—self— a menos que você especibque-
o explicitamente. E, pelo fato de o receiver padrão ser self, você nem precisa especibcá-lo se não quiser.

class Thing
def do_something
puts "doing something"
end

def do_something_else
do_something
end
end

No método do_something_else poderíamos usar self.do_something, mas isso faria com que nosso código apenas bcasse mais
poluído. No entanto, debnir o receiver é uma coisa muito comum e que, você pode até não se dar conta, mas o faz constantemente
quando escreve código Ruby.

numbers = [3,1,2]
numbers.sort
#=> [1,2,3]

text = "some string"


text.upcase
#=> "SOME STRING"

10
Na prática, o receiver é especibcado antes do ponto na chamada de métodos, como em numbers.sort ou text.upcase.

Além de ser o receiver padrão, self também é o responsável por armazenar variáveis de instância de um objeto. Veja o exemplo
abaixo.

class Person
def initialize(name)
@name = name
end

def name
@name
end
end

john = Person.new("John Doe")


john.name
#=> "John Doe"

A instância da classe Person possui uma única variável de instância associada ao seu objeto, self, que é retornada pelo método
Person#name. Analogamente, podemos debnir variáveis de instância em qualquer objeto, como classes.

class Person
def self.count
@count ||= 0
end

def self.count=(increment)
@count = increment
end

11
def initialize(name)
@name = name
self.class.count += 1
end

def name
@name
end
end

john = Person.new("John Doe")


Person.count
#=> 1

O exemplo acima mostra como variáveis de instância podem ser usadas em contextos diferentes. Primeiro, estamos debnindo um
contador de instâncias da classe Person, cujo valor será armazenado em @count. Depois, em nossa própria instância, debnimos o nome
com a variável @name.

Convenções
Os desenvolvedores Ruby seguem uma série de convenções enquanto estão escrevendo seus códigos. Embora você não seja obrigado
à seguir essas convenções, é sempre uma boa ideia garantir que está escrevendo códigos do mesmo jeito que um desenvolvedor mais
experiente.

Conbra neste capítulo quais são as convenções mais utilizadas e evite olhares estranhos.

Nomeando classes, variáveis e constantes


Use o formato snake_case para debnir variáveis.

12
# recomendado
success_message = "You're done!"

# estranho
successMessage = "You're wrong!"

Classes e módulos são nomeados em camel case.

• Rails
• ActiveSupport
• Net

Se sua classe ou módulo for um acrônimo, mantenha todas as letras em maiúsculas.

• HTTP
• HTTP::POST
• XML

Outras constantes devem usar o formato SNAKE_CASE.

# recomendado
SUCCESS_MESSAGE = "You're done!"

# estranho
SuccessMessage = "You're wrong!"

Métodos predicados (aqueles que retornam valores booleanos) devem terminar com ? e não precisam de um prebxo is.

# recomendado
def ready?
status == "ready"

13
end

# estranho
def is_ready?
status == "ready"
end

Métodos que modibcam self, lançam exceções ou são potencialmente perigosos/destrutivos devem terminar com uma exclamação.

# recomendado
def save!
save or raise("OMG!")
end

Indentação, espaçamento e quebras de linha


No Ruby, trechos de código são indentados em 2 espaços
espaços.

# recomendado: 2 espaços
def hello
puts "Hello!"
end

# estranho: 4 espaços
def hello
puts "Hello!"
end

# mais estranho: hard tabs


def hello

14
puts "Hello!"
end

Adicione espaçamento em torno de operadores e depois de vírgulas.

# recomendado
sum = 1 + 1
x, y = 1, 2

# estranho
sum = 1+1
x,y = 1,2

Não coloque espaçamentos depois de [ e (, nem antes de ] e ).

# recomendado
hello("John")
numbers = [1, 2, 3]

# estranho
hello( "John" )
numbers = [ 1, 2, 3 ]

As quebras de linha devem seguir o estilo Unix, ou seja, devem ser inseridas como \n. Sempre adicione uma nova linha \n ao bnal do
seu arquivo.

Evite deixar espaçamentos ao bnal da linha (trailing spaces).

15
Definindo e executando métodos
Quando o método receber argumentos, sempre coloque os parênteses e separe os argumentos corretamente.

# recomendado
def sum(n1, n2)
n1 + n2
end

# estranho
def sum( n1, n2 )
n1 + n2
end

# mais estranho
def sum n1, n2
n1 + n2
end

Se o método não recebe nenhum argumento, não adicione os parênteses.

# recomendado
def message
"Hello"
end

# estranho
def message()
"Hello"
end

16
A mesma regra deve ser aplicada quando você estiver executando métodos.

# recomendado
user.run

# estranho
user.run()

Esta regra possui uma exceção. Quando um método é debnido com o mesmo nome de uma constante, você deve usar os parênteses.
Caso contrário, você estará acessando a própria constante, que normalmente será um módulo ou classe.

class Foo; end

def Foo
puts "You called the Foo method"
end

Foo.new #=> Instancia a classe Foo


Foo() #=> Executa o método Foo()

Retorno de métodos e blocos


Métodos e blocos no Ruby retornam o resultado da última linha executada, dispensando o uso de return.

def message
"Hello"
end

puts message
#=> Hello

17
No entanto, se você precisar encerrar o cuxo de execução antes da última linha, deve usar o return.

def message(text)
return "Hello stranger!" unless text
text
end

message(nil)
#=> Hello stranger!

message("Hello there!")
#=> Hello there!

Usando blocos
Métodos podem receber blocos[2]. Quando o seu bloco possuir mais de uma instrução ou precisar encadeado, utilize chaves ({ e }) para
criar blocos inline.

contents = File.open("file.txt") {|file| file.read}


#=> Ruby #nice

numbers = [1,2,3].map {|n| n * 2}.reject {|n| n % 2 == 0}


#=> [2]

Se o seu bloco possuir mais de uma instrução, você deve utilizar as palavras-chave do..end.

File.open("file.txt", "w+") do |file|


file.write "Ruby"

2
Não se preocupe com o que são blocos por enquanto. Você verá mais sobre este assunto mais à frente.

18
file.write " #nice"
end

Escrevendo sobre o Ruby


Uma convenção do Ruby que é usada em textos é que métodos estáticos (aqueles que podem ser acessados diretamente em uma
classe ou módulo) são referenciados como Modulo.metodo ou Modulo::metodo. Logo, se você quiser escrever alguma documentação
ou texto que cita o método enable do módulo GC, você deve escrever GC.enable ou GC::enable.

Já métodos de instância, como "Hello".upcase devem ser referenciados como String#upcase.

Esta convenção também é utilizada para acessar a documentação através da linha de comando, como você verá mais à frente.

Atribuição de variáveis
Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

Constantes e variáveis globais


Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

19
Conhecendo o IRB
O Ruby vem com um shell REPL[1] chamado Interactive Ruby (IRB). Ele faz exatamente o que o nome sugere: ele lê uma linha, executa
esta linha, exibe o resultado da execução e faz o loop, esperando por uma nova linha. O IRB é a melhor maneira de testar q

Para iniciar o IRB, execute o comando irb.

$ irb
irb(main):001:0>

Neste shell você pode digitar qualquer código Ruby. Experimente digitar alguma expressão matemática simples.

irb(main):001:0> 1 + 1
=> 2

No Ruby, tudo[3] é objeto. Você pode descobrir qual a classe de um objeto com o método Object#class.

irb(main):002:0> 1234.class
=> Fixnum
irb(main):003:0> "Hello".class
=> String

Métodos são ações que um objeto pode realizar. No exemplo acima, o método Object#class apenas informa qual a classe que
instanciou um determinado objeto. Você também pode acessar a lista de todos os métodos que um objeto possui com o método
Object#methods. Veja, por exemplo, quais são os métodos de uma string:

1
Read-Eval-Print-Loop
3
Na verdade, quase tudo no Ruby é objeto. Algumas coisas não são objetos diretamente, embora você consiga acessá-las de outras
maneiras.

20
irb(main):004:0> "Hello".methods
=> [:<=>, :==, :===, :eql?, :hash, :casecmp, :+, :*, :%, :[], :[]=, :insert,
:length, :size, :bytesize, :empty?, :=~, :match, :succ, :succ!, :next, :next!,
:upto, :index, :rindex, :replace, :clear, :chr, :getbyte, :setbyte, :byteslice,
:to_i, :to_f, :to_s, :to_str, :inspect, :dump, :upcase, :downcase, :capitalize,
:swapcase, :upcase!, :downcase!, :capitalize!, :swapcase!, :hex, :oct, :split,
:lines, :bytes, :chars, :codepoints, :reverse, :reverse!, :concat, :<<, :prepend,
:crypt, :intern, :to_sym, :ord, :include?, :start_with?, :end_with?, :scan,
:ljust, :rjust, :center, :sub, :gsub, :chop, :chomp, :strip, :lstrip, :rstrip,
:sub!, :gsub!, :chop!, :chomp!, :strip!, :lstrip!, :rstrip!, :tr, :tr_s, :delete,
:squeeze, :count, :tr!, :tr_s!, :delete!, :squeeze!, :each_line, :each_byte,
:each_char, :each_codepoint, :sum, :slice, :slice!, :partition, :rpartition,
:encoding, :force_encoding, :valid_encoding?, :ascii_only?, :unpack, :encode,
:encode!, :to_r, :to_c, :>, :>=, :<, :<=, :between?, :nil?, :!~, :class,
:singleton_class, :clone, :dup, :initialize_dup, :initialize_clone, :taint,
:tainted?, :untaint, :untrust, :untrusted?, :trust, :freeze, :frozen?, :methods,
:singleton_methods, :protected_methods, :private_methods, :public_methods,
:instance_variables, :instance_variable_get, :instance_variable_set,
:instance_variable_defined?, :instance_of?, :kind_of?, :is_a?, :tap, :send,
:public_send, :respond_to?, :respond_to_missing?, :extend, :display, :method,
:public_method, :define_singleton_method, :object_id, :to_enum, :enum_for,
:equal?, :!, :!=, :instance_eval, :instance_exec, :__send__, :__id__]

Perceba como o shell do IRB muda sua apresentação, de acordo com o nível de indentação do código.

irb(main):005:0> def sum(n1, n2)


irb(main):006:1> n1 + n2
irb(main):007:1> end
=> nil
irb(main):008:0> sum(3, 2)
=> 5
irb(main):009:0>

21
O IRB permite testar códigos Ruby interativamente. Escreva outros tipos de expressões para se familiarizar com esta excelente
ferramenta.

Executando códigos Ruby


A maneira mais comum de escrever Ruby é colocando códigos em um ou mais arquivos. Normalmente, estes arquivos possuem a
extensão .rb, embora você possa usar qualquer extensão (ou nenhuma extensão).

Crie o arquivo hello.rb com o seguinte código:

puts "Hello!"
puts "Current time: #{Time.now}"

Para executar este código, basta executar o interpretador Ruby, passando o caminho do arquivo como argumento.

$ ruby hello.rb
Hello!
Current time: 2011-12-23 10:39:20 -0200

Em sistemas operacionais Unix, é possível especibcar o shebang, que determina qual o tipo de interpretador que será usado para
executar aquele arquivo.

#!/usr/bin/env ruby
puts "Hello!"
puts "Current time: #{Time.now}"

Agora você pode fazer com que o arquivo seja executável com o comando chmod +x hello.rb. Ao fazer isso, você não mais precisará
executar o interpretador Ruby.

22
$ ./hello.rb
Hello!
Current time: 2011-12-23 10:44:30 -0200

Acessando a documentação do Ruby


Atualmente existem bibliotecas Ruby para tudo (ou quase tudo) o que você possa imaginar. A maioria delas é documentada com RDoc,
que são apenas comentários Ruby que podem ser extraídos em HTML e no formato ri.

Esta ferramenta ri permite visualizar a documentação de métodos, constantes classes e módulos da Standard Library (que já vem com
o Ruby) e de códigos de desenvolvedores 3rd party.

Para visualizar a documentação do módulo GC, execute o comando ri GC.

$ ri GC
= GC

(from ruby core)


------------------------------------------------------------------------------
The GC module provides an interface to Ruby's mark and sweep garbage
collection mechanism. Some of the underlying methods are also available via
the ObjectSpace module.

You may obtain information about the operation of the GC through GC::Profiler.
------------------------------------------------------------------------------
= Class methods:

count, disable, enable, malloc_allocated_size, malloc_allocations, start,


stat, stress, stress=

= Instance methods:

23
garbage_collect

Você também pode visualizar a documentação de um método especíbco. Veja, por exemplo, a documentação do método
String#upcase.

$ ri String#upcase
= String#upcase

(from ruby core)


------------------------------------------------------------------------------
str.upcase -> new_str

------------------------------------------------------------------------------

Returns a copy of str with all lowercase letters replaced with their
uppercase counterparts. The operation is locale insensitive---only characters
``a'' to ``z'' are affected. Note: case replacement is effective only in ASCII
region.

"hEllO".upcase #=> "HELLO"

Você também pode visualizar a documentação de métodos estáticos como GC.enable.

$ ri GC.enable
= GC.enable

(from ruby core)


------------------------------------------------------------------------------

24
GC.enable -> true or false

------------------------------------------------------------------------------

Enables garbage collection, returning true if garbage collection was


previously disabled.

GC.disable #=> false


GC.enable #=> true
GC.enable #=> false

25
CAPÍTULO 1

Ruby Core Classes


String
Strings são sequências de caracteres normalmente delimitadas por aspas ou apóstrofos.

hello = "Hello"
ruby = 'Ruby'

A diferença entre os dois delimitadores é que os apóstrofos ignoram caracteres como \n e \t.

puts "Ruby is really\nnice language."


#=> Ruby is really
#=> nice language.

puts 'Ruby is really\nbeautiful.'


#=> Ruby is really\nbeautiful.

Outra diferença é que strings delimitadas por apóstrofos não podem ser interpoladas. Interpolação é o processo de debnir uma
expressão Ruby dentro de uma string, de modo que seu resultado substitua os delimitadores #{} que englobam a expressão.

now = Time.now
puts "Time: #{now}"
#=> Time: 2011-12-21 01:35:30 -0200

26
puts 'Time: #{now}'
#=> Time: #{now}

Você pode debnir strings com múltiplas linhas sem precisar de nenhuma sintaxe especial.

text = "This can be a long text with


multiple lines."

text = 'This can be a long text with


multiple lines.'

Caracteres podem ser escapados com uma barra invertida antes do caracter.

puts "Ruby for \"rubyists\"."


#=> Ruby for "rubyists".

puts "Ruby for\\nrubyists."


#=> Ruby for\nrubyists.

Você pode debnir strings de outras formas. Uma delas é usando o formato heredoc, que possui duas variações.

text = <<TEXT
This can be a long text with
multiple lines. And I don't
need to escape "quotes".
TEXT

text = <<-TEXT
This can be a long text with
multiple lines. And I don't

27
need to escape "quotes".
TEXT

A string heredoc precisa de um identibcador (escrito em letras maiúsculas) que será usado para iniciar e terminar a string. A diferença
entre as duas variações é que a primeira forma exige que o identibcador de término esteja no começo da linha
linha.

Você pode executar métodos em strings heredoc.

puts <<-TEXT.upcase
This can be a long text with
multiple lines.
TEXT
#=> THIS CAN BE A LONG TEXT WITH
#=> MULTIPLE LINES.

Uma outra característica de strings heredoc é que elas preservam espaços em branco no começo da linha.

puts <<-TEXT
This can be a long text with
multiple lines.
TEXT
#=> This can be a long text with
#=> multiple lines.

Outra forma de especibcar strings é utilizando os delimitadores %, %q, %Q.

puts %q[Time: #{Time.now}]


#=> Time.now #{Time.now}

puts %Q(Time: #{Time.now})

28
#=> Time: 2011-12-21 01:40:09 -0200

puts %!Time: #{Time.now}!


#=> Time: 2011-12-21 01:40:09 -0200

Perceba nos exemplos acima que foram usados diferentes tipos de delimitadores: %q[], %Q() e %!!. Na prática, você pode usar
qualquer caracter como delimitador. Note que você precisará escapar os caracteres da string que forem iguais ao delimitador.

puts %q~Time: #{Time.now}~


#=> Time.now #{Time.now}

puts %Q/Time: #{Time.now}/


#=> Time: 2011-12-21 01:40:09 -0200

puts %<Time: #{Time.now}>


#=> Time: 2011-12-21 01:40:09 -0200

puts %:Time\: #{Time.now}:


#=> Time: 2011-12-21 01:40:09 -0200

29
Números
O Ruby possui 8 classes para representar
números. Todos os objetos que representam
números no Ruby são instâncias da classe
Numeric. Números são imutáveis e, por este
motivo, não existem métodos que podem
mudar o valor de um número; se você tentar
fazer isso, receberá a mensagem de erro Can't
change the value of self.

Em versões mais antigas do Ruby, as classes


Complex e Rational não são nativas do Ruby,
embora sejam distribuídas como parte da
Standard Library[1].

Fixnum
Números inteiros não possuem um tamanho determinado, pois o seu tamanho é debnido pela quantidade de memória disponível.
Quando debnidos nos intervalos entre −230 e 230-1 ou −262 e 262-1, inteiros são debnidos como instâncias da classe Fixnum. Inteiros
fora deste intervalo são automaticamente debnidos como objetos da classe Bignum, em um processo totalmente transparente e
automático.

number = 1000

3.times do
number *= number
puts "#{number.class} => #{number}"

1
A Standard Library é o conjunto de bibliotecas que vem com a instalação do Ruby.

30
end

# Output:
# Fixnum => 1000000
# Fixnum => 1000000000000
# Bignum => 1000000000000000000000000

Você pode debnir números inteiros usando um sinal de + ou - opcional para debnir valores positivos e negativos, um indicador opcional
da base do número, seguidos pela sequência de números especibcados na base escolhida.

1234 #=> 1234


0d1234 #=> 1234 - Base decimal, padrão
1_234 #=> 1234 - Underscores são ignorados
-1234 #=> -1234 - Negativo
0x4d2 #=> 1234 - Hexadecimal
02322 #=> 1234 - Octal
0b10011010010 #=> 1234 - Binário

Float
Números de ponto cutuante são debnidos pelo . (ponto decimal) após um ou mais números decimais, seguido por mais números
decimais. Você também pode, opcionalmente, utilizar um expoente. Ao contrário dos números inteiros, números com ponto cutuante
não podem ser debnidos em outra base diferente de 10.

puts 1.234 #=> 1.234


puts -1.234 #=> -1.234 - Negativo
puts 1_234.0 #=> 1234.0 - Underscores são ignorados
puts 12e2 #=> 1200.0 - 12.0 x 10e2
puts 12.3e2 #=> 1230.0 - 12.3 x 10e2
puts 12.3E2 #=> 1230.0 - 12.3 x 10e2

31
No Ruby, não é possível debnir números com ponto cutuante sem ter um número antes do ponto decimal. Se você tentar debnir um
número como .1 irá receber uma mensagem de erro como no .<digit> floating literal anymore; put 0 before dot.

Números de ponto cutuante seguem a especibcação IEEE-754, assim como a maioria das linguagens e hardwares do mercado. Dada a
forma como os números de ponto cutuante são tratados, frações como 1/10 e 1/100 não podem ser representadas corretamente. É
muito comum cálculos como o exemplo à seguir causarem espanto, mesmo ele acontecendo em muitas outras linguagens[2].

0.3 - 0.2 == 0.1


#=> false

O Ruby consegue efetuar cálculos deste tipo quando objetos da classe BigNumber são utilizados.

BigDecimal
A classe BigDecimal permite realizar cálculos onde o arredondamento é muito importante, como em cálculos bnanceiros. Números do
tipo BigDecimal são praticamente ilimitados (expoentes acima de 1 bilhão são suportados) e possuem controle preciso dos modos de
arredondamento.

require "bigdecimal"
BigDecimal("0.3") - BigDecimal("0.2") == 0.1
#=> true

Você pode especibcar os modos de arredondamento e quantidade de dígitos decimais que serão computados. Para ver a referência
completa, acesse a documentação.

Complex
Para ver a referência completa, acesse a documentação.

2
O mesmo problema acontece no C, Java, Python e JavaScript.

32
Rational
Para ver a referência completa, acesse a documentação.

Array
O Ruby possui arrays, que são listas que podem guardar qualquer tipo de objeto e não precisam ser criadas com tamanho determinado;
novos ítens podem ser adicionados a qualquer momento.

Para criar um novo array, basta instanciar a classe Array ou utilizar o atalho [].

items = [10, "Hello", 3.5]


items = Array.new
items = Array.new([1, 2, 3])

O método Array#initialize pode ser utilizado de maneiras diferentes. Você pode dizer com quantos ítens o array deve ser iniciado.
Por padrão, o array será iniciado com o valor nil.

items = Array.new(5)
#=> [nil, nil, nil, nil, nil]

Você também pode passar um valor inicial que será usado para popular este array.

items = Array.new(5, "hello")


#=> ["hello", "hello", "hello", "hello", "hello"]

Também é possível iniciar um array com um bloco. Neste caso, o valor retornado pelo bloco será usado.

33
items = Array.new(5) {|i| i * 2}
#=> [0, 2, 4, 6, 8]

Se todos os elementos do array forem strings, você pode utilizar a sintaxe %w ou %W. Assim como as strings, você pode utilizar qualquer
delimitador.

words = %w[jan fev mar apr may jun jul aug sep oct nov dec]
#=> ["jan", "fev", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"]

letters = %w(a b c)
#=> ["a", "b", "c"]

Para adicionar elementos que contém espaços, escape o espaço com \.

words = %w[John\ Doe Jane\ Doe]


#=> ["John Doe", "Jane Doe"]

Se você precisa interpolar alguma variável, utilize %W.

name = "John Doe"

words = %w[Jane\ Doe #{name}]


#=> ["Jane Doe", "\#{name}"]

words = %W[Jane\ Doe #{name}]


#=> ["Jane Doe", "John Doe"]

Arrays só podem ter índices numéricos. Os índices começam em 0 e também podem ser acessados de forma negativa.

34
numbers = Array.new(10) {|n| n * 2}
#=> [0, 2, 4, 6, 8, 10, 12, 14, 16, 18]

numbers[0] #=> 0
numbers.first #=> 0
numbers[4] #=> 8
numbers[-1] #=> 18 - A mesma coisa que numbers[numbers.size - 1]
numbers.last #=> 18

Também é possível acessar intervalos passando dois números.

numbers[0, 2] #=> [0, 2] - À partir do índice 0, pegue dois elementos.


numbers[-2, 2] #=> [16, 18] - À partir do índice size - 2, pegue dois elementos.

Hash
Um outro tipo de estrutura de dados do Ruby é o Hash. Hashes são como arrays, com a diferença que o índice de um hash pode ser
qualquer objeto. À partir do Ruby 1.9, hashes enumeram seus valores na ordem que as chaves foram inseridas. No Ruby 1.8 esse
comportamento era imprevisível.

user = {"name" => "John Doe", "age" => 32}

Você pode debnir o valor-padrão de uma chave que ainda não existe no array. Para isso, basta passar um argumento na hora que for
instanciar o hash.

options = Hash.new("OMG!!!")
options["invalid key"]
#=> OMG!!!

35
Você também pode debnir o valor-padrão através de um bloco.

options = Hash.new {|hash, key| "OMG!!!"}


options["invalid key"]
#=> OMG!!!

Perceba que os valores-padrão que são retornados não são armazenados no hash. É de responsabilidade do bloco fazer esta atribuição.

options = Hash.new {|hash, key| "OMG!!!"}


options["invalid key"] #=> retorna "OMG!!!"
options.keys
#=> []

options = Hash.new {|hash, key| hash[key] = "OMG!!!"}


options["invalid key"] #=> retorna "OMG!!!"
options.keys
#=> ["invalid key"]

Você também pode inicializar arrays usando o método Hash.[]. Você pode passar uma lista de argumentos que alternam entre chave e
valor.

user = Hash["name", "John Doe", "age", 32]


#=> {"name" => "John Doe", "age" => 32}

O método Hash.[] também aceita um array de arrays de dois elementos que identibcam a chave e o valor.

user = Hash[[["name", "John Doe"], ["age", 32]]]


#=> {"name" => "John Doe", "age" => 32}

36
Por último, você pode passar um objeto que pode ser convertido em hash através do método to_hash.

user = {"name" => "John Doe", "age" => 32}


Hash[user]
#=> {"name" => "John Doe", "age" => 32}

À partir do Ruby 1.9 é possível debnir hashes usando uma sintaxe semelhante a do JavaScript. Uma característica dessa sintaxe é que
as chaves são geradas como símbolos.

user = {name: "John Doe", age: 32}

user.keys
#=> [:name, :age]

Symbol
Símbolos são objetos que representam nomes no Ruby e são muito utilizados como identibcadores, principalmente como chaves de
hashes. Eles são gerados através da sintaxe :symbol ou :"symbol", além dos métodos String#to_sym e String#intern.

symbol = :john
symbol = :"john doe"
symbol = "john".to_sym
symbol = "john doe".intern

Alternativamente você pode usar o delimitador %s.

37
symbol = %s[john doe]
symbol.class
#=> Symbol

Símbolos compartilham sempre o mesmo espaço em memória, independente do lugar onde foram criados.

name = :john
name.object_id
#=> 302248

other_name = :john
other_name.object_id
#=> 302248

Boolean
No Ruby os valores booleanos são true e false, que são instâncias das classes TrueClass e FalseClass. Infelizmente, ambas as
classes não possuem uma superclasse comum.

true.class
#=> TrueClass

false.class
#=> FalseClass

Os valores booleanos também podem ser acessados através das constantes TRUE e FALSE.

TRUE.class
#=> TrueClass

38
FALSE.class
#=> FalseClass

Os valores booleanos true e false ocupam sempre o mesmo espaço em memória, através dos ids 2 e 0, respectivamente.

true.object_id
#=> 2

false.object_id
#=> 0

nil
O Ruby debne o tipo nulo através do do objeto nil, que é uma instância da classe NilClass. O nil ocupa sempre o mesmo espaço em
memória com o id 4.

nil.class
#=> NilClass

nil.object_id
#=> 4

Este é o valor de retorno de blocos e métodos que não retornam nada, o que explicitamente usam as palavras-chave return e next
sem nenhum valor.

def hello
end

39
hello.class
#=> NilClass

def hello
return
end

hello.class
#=> NilClass

Range
Para debnir intervalos de números e strings podemos utilizar a classe Range.

numbers = 1..10
numbers.class
#=> Range

letters = "a".."z"
letters.class
#=> Range

É possível debnir um intervalo excluindo o último elemento.

numbers = 1...10
numbers.cover?(10)
#=> false

Você pode converter um intervalo em array com o método to_a.

40
letters = "a".."z"
letters.to_a
#=> [
#=> "a", "b", "c", "d", "e", "f", "g", "h", "i", "j",
#=> "k", "l", "m", "n", "o", "p", "q", "r", "s", "t",
#=> "u", "v", "w", "x", "y", "z"
#=> ]

Você pode debnir limites de strings com mais de um caracter.

strings = "1a".."1e"
strings.to_a
#=> ["1a", "1b", "1c", "1d", "1e"]

Sempre que precisar descobrir se um valor está incluído em um intervalo, utilize o método Range#cover?. Ele é muito mais rápido que
transformar o intervalo em array e depois veribcar se o ítem está incluído com o método Array#include?.

("aaa".."zzz").cover?("def") #=> bom


#=> true

("aaa".."zzz").to_a.include?("def") #=> ruim


#=> true

Expressões regulares
Expressões regulares são padrões (ou patterns) que permitem descrever o conteúdo de uma string. Elas podem ser utilizadas para
veribcar se uma string contém um determinado padrão ou para extrair partes dessa string. Para criar uma expressão regular você deve
debnir o padrão utilizando a sintaxe /pattern/ ou %r(pattern).

41
regex = /hello/

Alguns caracteres precisam ser escapados pois eles tem um signibcado especial em expressões regulares. São os chamados
metacaracteres: (, ), [, ], {, }, ., ?, + e *.

regex = /\Ahttps?:\/\//

Perceba que estamos escapando a /. Este caracter em particular pode continuar sendo utilizado sem precisar ser escapado se
debnirmos a expressão regular com %r(). Note que você pode utilizar qualquer caracter como delimitador.

regex = %r(\Ahttps?://)
regex = %r[\Ahttps?://]

Expressões regulares são extremamente poderosas. Elas permitem veribcar padrões que seriam difíceis (e em alguns casos
impossíveis) de serem feitas de outras maneiras. Se você deseja aprender mais sobre assunto, leia o livro Expressões Regulares: uma
abordagem divertida, escrito por Aurélio Marinho Jargas, e que está disponível gratuitamente para leitura online.

Procs e lambdas
Procs são blocos de código que podem ser associados a uma variável e que funcionam como funções anonônimas. Muitas vezes você
precisa de um método utilitário que irá fazer algumas pequenas computações onde criar um método propriamente dito seria muito
trabalho; é aí que entram as procs.

Para debnir uma nova proc, você pode utilizar o método Proc.new ou o método Kernel#proc.

# alternativa 1
message = Proc.new { "Hello" }

42
# alternativa 2
message = proc { "Hello" }

Para executar essas procs você pode utilizar três métodos diferentes.

message = proc { "Hello" }

message.call
message[]
message.()

A última maneira de execução (sum.(1, 2)) está disponível à partir do Ruby 1.9.

Por convenção, procs que possuem uma única expressão devem ser debnidas por chaves.

message = proc { "One-line proc" }

E, também por convenção, quando uma proc possuir mais de uma expressão elas devem ser debnidas pelas palavras-chave do..end.

message = proc do
puts "Hello!"
puts "Ruby!"
end

Procs podem receber parâmetros, assim como métodos. Basta delimitar os parâmetros com |. Para receber mais de um parâmetro,
separe-os com vírgula.

sum = proc {|n1, n2| n1 + n2}

43
sum.call(1, 2)
sum[1, 2]
sum.(1, 2)

O valor de retorno de uma proc, assim como métodos, é a última expressão que for executada. Se você quiser encerrar o cuxo de
execução retornando um valor antes da última expressão, deve usar next, em vez do return utilizado por métodos.

divide = proc do |n1, n2|


next 0 if n1.zero?
next nil if n2.zero?

n1 / n2
end

divide[3.0, 2]
#=> 1.5

divide[0, 2]
#=> 0

divide[2, 0]
#=> nil

O Ruby 1.9 também introduziu uma nova sintaxe para a debnição de procs.

message = -> { "Hello" }


message.call
#=> Hello

Esta nova sintaxe também pode aceitar parâmetros, mas faz com que a legibilidade do código seja prejudicada.

44
sum = -> n1, n2 { n1 + n2 }
sum[1, 2]
#=> 3

Procs podem ser convertidas em blocos[3]. Basta adicionar um & na hora que passar o bloco como parâmetro.

def sum(n1, n2, &block)


block[n1 + n2]
end

# Passando um bloco explicitamente


sum(1, 2) {|result| puts result}

# Convertendo uma proc em bloco


output = proc {|result| puts result}
sum(1, 2, &output)

Existem ainda as procs criadas com o método Kernel#lambda.

sum = lambda {|n1, n2| n1 + n2}

sum[1, 2]
#=> 3

Embora o método Kernel#lambda seja semelhante ao método Kernel#proc, eles possui uma diferença muito importante. Lambdas
irão validar a quantidade de parâmetros que foram passados e lançarão a exceção ArgumentError: wrong number of arguments
caso o número de argumentos seja incorreto. Já as procs irão atribuir o valor nil para cada um dos parâmetros.

3
Para saber mais sobre blocos, leia Blocos.

45
proc_message = proc {|message| p message}
lambda_message = lambda {|message| p message}

proc_message.call
#=> nil

lambda_message.call
#=> ArgumentError: wrong number of arguments (0 for 1)

Uma outra diferença é que se um return for debnido em uma proc, o método que executou esta proc também irá encerrar o cuxo de
execução. No caso de lambdas, o cuxo de execução será encerrado apenas no lambda que originou a chamada ao return.

def return_proc
proc { return }.call
puts "return_proc"
end

def return_lambda
lambda { return }.call
puts "return_lambda"
end

return_proc
return_lambda

# Output:
# return_lambda

Para descobrir quantos parâmetros obrigatórios uma proc espera, use o método Proc#arity. Se a proc possui argumentos opcionais, o
valor de retorno será -n - 1, onde n é a quantidade de parâmetros obrigatórios.

46
Proc.new {}.arity #=> 0
Proc.new {||}.arity #=> 0
Proc.new {|a|}.arity #=> 1
Proc.new {|a,b|}.arity #=> 2
Proc.new {|a,b,c|}.arity #=> 3
Proc.new {|*a|}.arity #=> -1
Proc.new {|a,*b|}.arity #=> -2
Proc.new {|a,*b, c|}.arity #=> -3

Para pegar uma representação dos parâmetros que um bloco pode receber, use o método Proc#parameters. Note que a
representação muda quando um lambda é debnido.

proc {|a, b = 42, *args|}.parameters


#=> [[:opt, :a], [:opt, :b], [:rest, :args]]

lambda {|a, b = 42, *args|}.parameters


#=> [[:req, :a], [:opt, :b], [:rest, :args]]

Set
Existem situações onde você pode precisar de uma lista com valores únicos. Isso pode ser facilmente resolvido com arrays e o método
Array#include? ou Array#uniq.

items = [1, 2, 3]

# alternativa 1: verificar se o ítem existe antes de adicioná-lo


items << 3 unless items.include?(3)

# alternativa 2: adicione o ítem e depois pegue os elementos únicos

47
items << 3
items.uniq

Embora essas técnicas funcionem, elas não são otimizadas. O Ruby possui a classe Set que faz justamente isso: garante que apenas
ítens únicos serão adicionados à lista.

require "set"
items = Set.new([1, 2, 3])

# adiciona novamente o número 2


items << 2

# converte o set em array


items.entries #=> [1, 2, 3]
items.to_a #=> [1, 2, 3]

Alternativamente, você pode criar um novo Set usando o método Array#to_set.

require "set"
[1, 2, 3, 3, 2, 4].to_set
#=> #<Set: {1, 2, 3, 4}>

48
CAPÍTULO 1

Estruturas condicionais
A estrutura de controle mais comum em qualquer linguagem de programação é a condicional. É apenas uma maneira de executar um
trecho de código se alguma condição for satisfeita. Uma condição é uma expressão que quando veribcada retorna um valor diferente
de false e nil.

O Ruby possui diferentes formas de expressões condições, como você pode conferir à seguir.

if
O if é a forma mais direta de se debnir uma expressão condicional. Sua sintaxe é bastante simples.

if expression
# do something
end

O trecho de código debnido dentro de if..end será executado somente se o valor de expression for diferente de false e nil. Note
que não é necessário adicionar parênteses em torno das condições.

O resultado de um if pode ser atribuído a uma variável.

x = 1
y = 0

y = if x > 0
y + 1
end

49
puts y
#=> 1

No exemplo acima, estamos atribuindo o valor y + 1 sempre que o valor de x for maior que zero. Embora seja uma construção válida e
muito cexível, nem sempre é a melhor maneira. A mesma expressão poderia ser escrita de um modo muito mais legível.

x = 1
y = 0

if x > 0
y += 1
end

puts y
#=> 1

O if pode ser usado de forma inline, agindo como um modibcador.

x = 0
x += 1 if x.zero?

A condição será sempre a primeira a ser executada, mesmo ela sendo escrita por último.

else
O if pode conter uma cláusula else, que será executada quando a condição não for satisfeita. Caso o valor expression seja igual a
false ou nil, então o código associado ao else será executado.

50
if expression
# do something
else
# do something else
end

elsif
Se você precisar adicionar mais cláusulas else que dependem de outras condições, pode usar o elsif.

if expression
# do something
elsif expression2
# do something else
elsif expressionN
# do something else
else
# do something else
end

Cada uma das expressões irá falhar caso o valor de retorno seja false ou nil, até que seja executada a última expressão else. Note
que o else é opcional.

x = 4
name = nil

if x == 1
name = "one"
elsif x == 2
name = "two"

51
elsif x == 3
name = "three"
end

puts name.inspect
#=> nil

No exemplo acima, estamos veribcando se o x possui os valores 1, 2 ou 3. Como o valor de x é 4, nenhuma das condições de nosso if
será satisfeita, o que faz com que o valor original de name não seja alterado. No Ruby o método inspect normalmente retorna uma
representação do self como uma string.

unless
Uma tendência natural quando queremos executar alguma expressão somente se uma condição falhar é adicionar uma exclamação
antes da condição ou, alternativamente, usar a palavra-chave not, que também tem suporte no Ruby.

if !expression
# do something
end

O if acima poderia ser escrito de uma forma diferente usando o unless.

unless expression
# do something
end

O unless também suporta uma cláusula else, mas seu uso é desencorajado; neste caso, seria muito mais simples escrever um if que
coloca a expressão a ser executada se o valor da condição não for false ou nil primeiro!

52
# estranho
unless x
x = 1
else
x += 1
end

# recomendado
if x
x += 1
else
x = 1
end

Assim como o if, o unless também pode ser usado de forma inline.

x = 0
x += 1 unless x.nonzero?

Operador ternário ?:
O operador ?: é o único operador ternário (que possui três operandos) suportado pelo Ruby.

• O primeiro operando que vem antes da interrogação é a condição.


• O segundo operando que vem antes dos dois-pontos é a expressão que será executada caso a condição seja diferente de false ou
nil.
• O terceiro e último operando que vem depois dos dois-pontos é a expressão que será executada caso a condição falhe.

No exemplo à seguir temos um método que irá retornar uma string levando em consideração a quantidade de ítens. Se o count for igual
a 1, uma string que representa o singular é retornada. Caso contrário, uma string que representa o plural é retornada.

53
def pluralize(count, singular, plural)
if count == 1
singular
else
plural
end
end

A mesma condição poderia ser simplibcada usando o operador ternário ?:.

def pluralize(count, singular, plural)


count == 1 ? singular : plural
end

case
O case é expressão condicional que permite fazer diversos tipos de comparações e que pode ser usada de duas formas diferentes.

A primeira forma que é apenas uma alternativa para if..elsif..else é a mais simples, mas também a menos utilizada.

case
when x == 1 then "one" # if x == 1 then "one"
when x == 2 then "two" # elsif x == 2 then "two"
when x == 3 then "three" # elsif x == 3 then "three"
else "other" # else "other"
end # end

A palavra-chave then só é necessária se você quiser utilizar expressões na mesma linha do comparador. Alternativamente, você pode
utilizar o caracter ;.

54
A segunda forma irá receber um objeto que pode ser comparado com diversos tipos de expressões diferentes. Esse modo é
extremamente poderoso e permite fazer comparações com expressões regulares, classes e intervalos.

O exemplo anterior poderia ser expressado de uma forma um pouco menos repetitiva.

case x
when 1 then "one"
when 2 then "two"
when 3 then "three"
else "other"
end

O case também retorna o valor da expressão que for executada, podendo ser atribuída a uma variável.

number = case x
when 1 then "one"
when 2 then "two"
when 3 then "three"
else "other"
end

Você pode debnir diversas comparações em uma mesma expressão.

case x
when 1, 2, 3 then "one, two, or three"
when 4, 5, 6 then "for, five, or six"
else "other"
end

55
O case usa o operador === para fazer as comparações. Em alguns casos, esse operador é exatamente o mesmo que ==. Mas em outros
casos, como classes e módulos, o comportamento é um pouco diferente. O operador === implementado[1] pelas classes e módulos irá
veribcar se um objeto é uma instância desta classe ou módulo ou de um de seus descendentes.

Vamos ver na prática como funcionam os operadores String.=== e String#===.

hello = "hello"
one = 1

String === hello


#=> true

hello === String


#=> false

String === one


#=> false

Perceba que String === "hello" retorna true, mas o contrário não é verdade. Isso acontece porque a implementação de
String.=== (que na verdade é implementada por Module#===) é diferente de String#===, que compara se o objeto é uma string e se
ela possui o mesmo conteúdo.

Voltando ao case, se a expressão de comparação for uma classe, então ele irá veribcar se a classe daquela instância é a própria classe
ou um de seus descendentes.

value = "Hello"

case value
when String

1
Sim! O operador === é apenas um método e você pode dební-lo em seus próprios objetos.

56
"A String was provided"
else
"Y U MAD BRO?"
end

Já o operador === implementado pelas expressões regulares irá veribcar se um determinado padrão foi satisfeito pela string.

text = "Hello Ruby!"

case text
when /\bruby\b/
"You passed a lowercased Ruby"
when /\bRuby\b/
"You passed a capitalized Ruby"
when /\bRUBY\b/
"You passed an uppercased Ruby"
else
"WAT? NO RUBY?"
end

O operador === implementado por intervalos (Range) também é diferente. Ele veribca se um determinado ítem está presente naquele
intervalo.

number = 100

case number
when 0..10
"Between 0 and 10"
when 11..20
"Between 11 and 20"

57
else
"You're outside my limits"
end

Essa convenção de se utilizar o operador === faz com que o case do Ruby seja muito mais poderoso que seu equivalente de outras
linguagens.

58
CAPÍTULO 1

Estruturas de repetição
O Ruby possui três expressões que debnem loops: for..in, while e until. Mas além deles, é possível usar iteradores em objetos
enumeráveis como arrays e hashes, além de outros objetos.

for..in
O loop for..in permite iterar em objetos que são enumeráveis, como é o caso de arrays e hashes. A cada iteração, um elemento será
atribuído para a variável especibcada. Sua sintaxe é bastante simples:

for item in collection


# do something
end

Note que cada valor atribuído à variável pode ser acessado fora da expressão for..in.

numbers = [1,2,3]

for number in numbers


puts "inside loop: #{number}"
end

puts "outside loop: #{number}"

Ao executar este código, você verá as seguintes mensagens.

59
inside loop: 1
inside loop: 2
inside loop: 3
outside loop: 3

No caso de hashes, você pode debnir duas variáveis que irão receber a chave e o valor, respectivamente.

numbers = {one: 1, two: 2, three: 3}

for key, value in numbers


puts "#{key} => #{value}"
end

Caso você forneça apenas uma variável, esta variável irá armazenar um array com dois elementos: a chave e o valor.

numbers = {one: 1, two: 2, three: 3}

for array in numbers


puts "#{array.first} => #{array.last}"
end

# Output:
# one => 1
# two => 2
# three => 3

Embora esse tipo de loop funcione muito bem, não é assim que os desenvolvedores Ruby mais experientes costumam fazer. Mais à
frente você verá como utilizar os iteradores em objetos enumeráveis.

60
while e until
O while e until são as formas mais básicas de looping do Ruby. Eles irão executar algum trecho de código enquanto uma condição for
verdadeira ou até que uma condição se torne verdadeira. Note que primeiro a condição é testada e, então, o código é executado.

x = 3

while x.nonzero?
puts x
x -= 1
end

O mesmo exemplo poderia ser escrito com o until.

x = 3

until x.zero?
puts x
x -= 1
end

Também é possível usar o while e until como modibcadores. Eles irão executar alguma expressão até que a condição seja satisfeita.

items = []
items.push(items.size + 1) while items.size < 3

p items
#=> [1, 2, 3]

61
No exemplo acima estamos adicionando um número ao array enquanto seu tamanho for menor que três. O número que é adicionado
será a quantidade de elementos mais um.

O mesmo exemplo poderia ser escrito com o until.

items = []
items.push(items.size + 1) until items.size >= 3

p items
#=> [1, 2, 3]

Também é possível debnir blocos begin..end para utilizar estes modibcadores com mais de uma expressão.

items = []

begin
items.push(items.size + 1)
puts "index #{items.size - 1} => #{items.last}"
end while items.size < 3

# Output:
# index 0 => 1
# index 1 => 2
# index 2 => 3

Mas ao contrário das expressões de uma linha, o bloco é executado antes de a condição ser testada
testada. Sendo assim, o bloco begin..end à
seguir sempre exibirá a mensagem OH NOES! THIS WILL BE DISPLAYED ANYWAY!.

begin
puts "OH NOES!"

62
puts "THIS WILL BE DISPLAYED ANYWAY!"
end while false

Embora seja uma construção aceita pela linguagem, o uso de begin..end é desencorajado e pode, inclusive, ser removido em versões
futuras do Ruby. É possível ter um comportamento semelhante sem que você caia nesta “armadilha”: basta delimitar diversas
expressões com parênteses.

(
puts "OH NOES!"
puts "AIN'T GONNA BE DISPLAYED! :("
) while false

loop
Para loops que não devem interrompidos, uma alternativa é usar algo como while true. No entanto, o Ruby possui o loop, que irá
executar indebnidamente um bloco.

loop do
puts Time.now
sleep 1
end

# Output:
# 2011-12-24 15:42:06 -0200
# 2011-12-24 15:42:07 -0200
# 2011-12-24 15:42:08 -0200
# ... continua até que você interrompa a execução do script

63
Controlando o loop
Muitas vezes é necessário controlar o cuxo de execução de um loop. Às vezes é preciso interromper a execução, outras é preciso
passar para o próximo ítem da iteração em alguma condição especíbca. O Ruby possui algumas maneiras de fazer isso.

Interrompendo a execução do loop


Para encerrar um loop a qualquer momento, utilize a palavra-chave break. Isso fará com que a execução seja imediatamente
interrompida.

x = 0

while x < 10
x += 1
puts x

break if x == 3
end

# Output:
# 1
# 2
# 3

Pulando para a próxima iteração


Para pular para a próxima iteração, utilize a palavra-chave next. Isso fará com que a execução seja imediatamente interrompida.

O exemplo à seguir irá exibir apenas o valor 4.

x = 0

64
while x < 5
x += 1
next unless x == 4
puts x
end

# Output:
# 4

Reiniciando a iteração
Para reiniciar a iteração, utilize a palavra-chave redo. Isso fará com que a execução seja reiniciada imediatamente.

O exemplo à seguir irá executar 3 vezes o output do elemento 3.

numbers = [1,2,3,4]
tries = 1

for number in numbers


puts "number: #{number}, tries: #{tries}"

if tries < 3 && number == numbers[-2]


tries += 1
redo
end
end

# Output:
# number: 1, tries: 0
# number: 2, tries: 0
# number: 3, tries: 0
# number: 3, tries: 1

65
# number: 3, tries: 2
# number: 3, tries: 3
# number: 4, tries: 3

Usando iteradores
Embora loops como for..in, while/until e loop sejam úteis para algumas situações, é mais provável que você escreva loops
utilizando métodos chamados iteradores. Os iteradores são provavelmente uma das funcionalidades mais importantes do Ruby.

Um dos exemplos mais comuns de iteradores do Ruby pode ser visto à seguir.

5.times { puts "Ruby!" }

O método Integer#times irá executar o bloco que foi fornecido 5 vezes. É esse tipo de construção do Ruby que faz com que a
linguagem seja elegante.

Existem outros iteradores numéricos, que nada mais são que métodos iteradores implementados pela classe Integer, assim como o
método Integer#times.

O método Integer#upto irá executar o bloco especibcado o números de vezes que for debnido pelo inteiro, até atingir o número que
foi passado como argumento. O bloco especibcado irá receber o número da iteração como argumento.

À partir do Ruby 1.9 métodos iteradores não exigem que você passe um bloco para execução; neste caso, ele irá retornar um objeto do
tipo Enumerator.

1.upto(3) do |number|
puts number
end

# Output:

66
# 1
# 2
# 3

O método Integer#downto funciona como o método Integer#upto, mas faz a contagem de modo descrescente.

3.downto(1) do |number|
puts number
end

# Output:
# 3
# 2
# 1

Existe ainda um outro método chamado Integer#step. Este método permite fazer iterações usando números inteiros e de ponto-
cutuante.

O exemplo à seguir irá iterar de 0 a 1, com passos de 0.25.

0.step(1, 0.25) do |number|


puts number
end

# Output:
# 0.0
# 0.25
# 0.5
# 0.75
# 1.0

67
Objetos enumeráveis
Objetos instanciados à partir das classes Array, Hash e Range são enumeráveis. O objeto é considerado enumerável quando
implementa o método each, que deve receber um bloco e fazer o yield daquele bloco.

A classe Array, por exemplo, implementa o iterador Array#each, o que permite passar por cada um dos ítens de um array, assim como
o for..in.

[1,2,3].each do |number|
puts number
end

A maioria dos objetos enumeráveis que implementa o iterador each inclui também o módulo Enumerable. Este módulo adiciona
muitos métodos que agem em cima do método each e que permitem iterar, buscar e ordenar os ítens da coleção.

O módulo Enumerable, por exemplo, inclui os métodos Enumerable#map, Enumerable#select, Enumerable#reject,


Enumerable#find e Enumerable#inject, só para citar alguns.

O método Enumerable#map permite criar um novo array contendo os elementos retornados pelo bloco.

[1,2,3].map {|number| number * 2}


#=> [2, 4, 6]

O método Enumerable#select permite criar um novo array contendo os elementos cujo valor retornado pelo bloco sejam diferentes
de false ou nil.

(1..10).select {|number| number.even?}


#=> [2, 4, 6, 8, 10]

68
O método Enumerable#reject faz exatamente o contrário do método Enumerable#select e irá retornar um array contendo os
elementos cujo valor do bloco sejam false ou nil.

(1..10).reject {|number| number.odd?}


#=> [2, 4, 6, 8, 10]

O método Enumerable#find irá retornar o primeiro elemento cujo valor de retorno do bloco seja diferente de false ou nil.

(1..10).find {|number| number == 3}


#=> 3

Já o método Enumerable#inject é mais complexo que todos os iteradores que você viu até aqui. Ele permite passar um objeto que
será o acumulador e que irá armazenar o resultado de iterações passadas. O bloco que foi fornecido pode ou não incrementar este
acumulador, dependendo de suas condições. O acumulador de devve ser o valor de retorno do bloco.

Veja, por exemplo, como retornar um único número que será a soma do dobro dos múltiplos de 2.

sum = (1..10).inject(0) do |acc, number|


acc += number * 2 if number.even?
acc
end

Alternativamente, você poderia implementar este mesmo acumulador em mais de uma etapa. Um código que faz a mesma coisa, mas
de forma muito menos elegante, pode ser visto à seguir.

sum = 0

(1..10).each do |number|

69
sum += number * 2 if number.even?
end

70
CAPÍTULO 1

Blocos
Os blocos são essenciais no uso de iteradores. Embora tenhamos usado blocos quando falamos sobre objetos enumeráveis, não
dedicamos tempo para explicar o que eles são.

Blocos nunca podem estar sozinhos; eles sempre precisam estar associados a uma execução de método. Todo método que é executado
pode receber um bloco, mas apenas os métodos que esperam este bloco e que façam o yield é que irão de fato executá-los; caso
contrário, o bloco será ignorado silenciosamente. Por baixo dos panos, blocos são apenas procs.

Assim como as procs, blocos seguem a convenção de se usar chaves para blocos com uma única expressão e do..end para blocos com
mais de uma expressão.

# Apenas uma expressão


[1, 2, 3].map {|number| number * 2}

# Diversas expressões
[1, 2, 3, 4, 5].inject(0) do |acc, number|
acc += number if number.even?
acc
end

Lembre-se! Como blocos sempre estão associados à execução de métodos, você não deve usar este termo para se referir
a procs ou lambdas.

71
Escopo de variáveis
Blocos introduzem um novo escopo de variáveis. Toda vez que você debne parâmetros que serão recebidos pelo bloco, estas variáveis
serão acessíveis apenas no contexto do bloco.

O exemplo à seguir mostra como a variável i é debnida apenas no escopo local do bloco.

1.upto(5) {|i| }
p defined?(i)
#=> nil

No entanto, blocos também podem referenciar variáveis do contexto externo ao bloco e, nesse caso, podem inclusive modibcar estas
variáveis.

total = 0
1.upto(10) {|i| total += i}
puts total
#=> 55

À partir do Ruby 1.9 os parâmetros esperados pelo bloco não mais modibcam variáveis de mesmo nome que foram debnidas no
contexto externo ao bloco. O exemplo à seguir mostra exatamente isso. Este mesmo exemplo quando executado no Ruby 1.8 irá exibir
o valor da última iteração, ou seja, 10.

i = 0
1.upto(10) {|i| }
puts i
#=> 0

72
Interagindo com blocos
Como foi dito antes, métodos podem receber blocos mesmo quando eles não esperam um. Para interagir com um bloco que foi
passado, você deve usar a palavra-chave yield. Ela irá executar o bloco que foi passado ao método.

def say
puts yield
end

say { "Hello" }
#=> Hello

O bloco será executado toda vez que você usar yield. Então, se em um mesmo método você usar três vezes a palavra-chave yield, o
bloco será executado três vezes.

def hello
yield "Hello!"
yield "Hi!"
yield "Wassup!"
end

hello {|message| puts message}

# Output:
# Hello!
# Hi!
# Wassup!

Se nenhum bloco for passado para este método say, uma exceção LocalJumpError será lançada. Para evitar que isto aconteça, você
pode veribcar se algum bloco foi passado com o método Kernel#block_given? e tomar as ações necessárias.

73
No exemplo à seguir lançamos uma exceção caso um bloco não seja passado.

def say
raise "Y U MAD BRO? JUST GIMME A BLOCK!" unless block_given?
puts yield
end

say { "Hello" }

begin
say
rescue Exception => error
puts error.message
end

# Output:
#=> Hello
#=> Y U MAD BRO? JUST GIMME A BLOCK!

Para passar parâmetros para o bloco que foi fornecido, basta fazer o yield passando os argumentos.

def first_and_last(list)
yield list.first, list.last
end

first_and_last([1,2,3]) do |first, last|


puts "First item: #{first}"
puts "Last item: #{last}"
end

74
O yield quase sempre é subciente. Mas às vezes, você quer ter um pouco mais de controle, seja passando o bloco como parâmetro
para outro método ou agindo como proxy de um outro método que também espera um bloco. O Ruby permite que você capture blocos
passados para métodos usando a construção &variavel. A única exigência é que essa variável deve ser sempre a última variável da
lista.

O exemplo anterior poderia ser reescrito usando essa construção.

def first_and_last(list, &block)


block.call(list.first, list.last)
end

first_and_last([1,2,3]) do |first, last|


puts "First item: #{first}"
puts "Last item: #{last}"
end

Note que não estamos mais usando o yield; agora, executamos o método Proc#call passando os argumentos. Alternativamente,
poderíamos usar algum outro método que executa procs, como Proc#[].

75
CAPÍTULO 1

Classes
O Ruby, como você pode perceber até agora, é uma linguagem orientada a objetos. Tudo o que manipulamos no Ruby são objetos e
cada objeto é gerado direta ou indiretamente de uma classe. Classes debnem os métodos que objetos podem responder. Elas também
podem estender ou ser subclasses de outras classes.

Criando classes
Para debnir uma classe use a palavra-chave class. Classes são constantes e, por este motivo, devem começar com uma letra maiúscula.

class Page
end

Classes são apenas objetos instanciados à partir da classe Class. Por isso, você pode instanciar classes dinamicamente[1]. Isso faz com
que a linguagem se torne extremamente poderosa e cexível.

class Page
end

AnotherPage = Class.new

Page.class
#=> Class

1
Classes criadas dinamicamente podem ser atribuídas a qualquer tipo de variável, e não apenas a constantes.

76
AnotherPage.class
#=> class

Embora ainda não tenhamos adicionado nenhum método à classe Page, nós já podemos instânciá-la. Para isso você irá usar o método
Page.new.

page = Page.new

Mesmo não tendo debnido atributos e métodos, nós podemos executar alguns métodos fornecidos pelas superclasses da classe Page.
Você pode, por exemplo, “perguntar” que tipo de objeto ele é.

page.class
#=> Page

page.is_a?(Page)
#=> true

As superclasses da classe Page implementam muitos outros métodos. Para saber quais são as superclasses de uma classe, use o
método Class.ancestors. Note que a própria classe será adicionada à lista.

Page.ancestors
#=> [Page, Object, Kernel, BasicObject]

Toda vez que o método Class.new for executado, ele irá iniciar a instância com o método construtor. No Ruby, o método construtor é
Class#initialize. Este método é debnido automaticamente como privado e não pode ser executado diretamente de fora do objeto.

Vamos fazer o método Page#initialize receber dois argumentos que irão debnir o título e conteúdo da página.

77
class Page
def initialize(title, content)
@title = title
@content = content
end
end

Todos os valores que devem ser persistidos em objetos devem ser debnidos como variáveis de instância, identibcados por uma arroba
na frente da variável. Elas pertencem ao objeto self que referencia o próprio objeto instanciado. Cada instância da classe Page terá
sua própria cópia das variáveis @title e @content.

O método Page#initialize debne duas variáveis de instância, que receber os argumentos passados no momento da instanciação. No
entanto, ainda não temos nenhuma maneira de acessar tais valores diretamente.

Definindo métodos
Para acessar as variáveis de instância que debnimos no método Page#initialize, nós iremos debnir dois getters, que são métodos
que apenas retornam valores. Isso precisa ser feito pois variáveis de instância não podem ser acessadas diretamente. Variáveis de
instância são encapsuladas de tal forma que apenas os métodos de um próprio objeto é que podem acessá-las e modibcá-las
diretamente.

page = Page.new("Ruby", "OMG! Ruby is awesome!")


page.title
#=> NoMethodError: undefined method ‘title’ for #<Page:0x007f88e3061648>

Para acessar estas variáveis de instância que foram debnidas no nosso método construtor, você precisa debnir métodos que irão
retorná-las. Embora o nome do método não precise necessariamente recetir o nome da variável, é sempre uma boa ideia dar nomes
que possam identibcar rapidamente o contexto de uso.

78
class Page
def initialize(title, content)
@title = title
@content = content
end

def title
@title
end

def content
@content
end
end

Agora nós já podemos acessar as variáveis armazenadas na instância da classe Page.

page = Page.new("Ruby", "OMG! I'm learning Ruby!")

page.title
#=> Ruby

page.content
#=> OMG! I'm learning Ruby!

Ainda não existe nenhuma maneira de debnir essas variáveis de instância sem ser pelo método construtor. Para fazer isso, é preciso
debnir métodos setters. Em outras linguagens, normalmente isso é feito com um método setTitle(title) ou set_title(title), ou
algo parecido. No Ruby, você pode debnir o seu próprio método title=.

79
class Page
def initialize(title, content)
@title = title
@content = content
end

def title
@title
end

def title=(title)
@title = title
end

def content
@content
end

def content=(content)
@content = content
end
end

O Ruby permite usar o operador = para executar métodos como este. Agora, já é possível atribuir valores para as variáveis @title e
@content com os métodos setters.

page = Page.new("Ruby", "OMG! I'm learning Ruby!")

page.title
#=> Ruby

80
page.title = "Learning Ruby"
page.title
#=> Learning Ruby

Esta debnição de getters e setters em classes é tão comum que o próprio Ruby fornece uma maneira de automatizar esta debnição.
Basta usar os métodos Module#attr_reader e Module#attr_writer[2].

class Page
attr_reader :title
attr_writer :title

attr_reader :content
attr_writer :content

def initialize(title, content)


@title = title
@content = content
end
end

O método Module#attr_reader irá debnir o método de instância de leitura (getter), enquanto o método Module#attr_writer irá
debnir o método de instância de escrita (setter). Para os casos onde você sempre debne tanto o getter quanto o setter, é possível usar o
método Module#attr_accessor, que fará isso de uma vez só!

Com esta alteração, nossa classe pode bcar muito mais simples.

class Page
attr_accessor :title, :content

2
A classe Class possui a classe Module como superclasse. Para saber mais sobre módulos, leia Módulos.

81
def initialize(title, content)
@title = title
@content = content
end
end

Lembre-se que os métodos Module#attr_accessor e companhia permitem criar apenas getters e setters que mapeiam para uma
variável de instância de mesmo nome. Você terá que implementar os seus próprios métodos se eles forem mais complexos (se eles
precisarem computar valores, por exemplo) ou debnirem variáveis de instância de nomes diferentes.

Alternativamente, você pode fazer com que o método construtor use os métodos Page#title= e Page#content=, em vez de atribuir
as variáveis de instância. Um erro muito comum de iniciantes é não debnir o objeto que irá receber o valor, chamado de receiver.

O exemplo à seguir debne variáveis locais, em vez de executar os métodos setters.

class Page
attr_accessor :title, :content

def initialize(_title, _content)


title = _title
content = _content
end
end

Para atribuir os atributos Page#title e Page#content corretamente, é preciso explicitamente executar os métodos através do
receiver self.

class Page
attr_accessor :title, :content

82
def initialize(title, content)
self.title = title
self.content = content
end
end

A atribuição direta das variáveis de instância é mais rápida que executar os métodos através do receiver. A menos que você manipule
as variáveis no método setter antes de atribuí-las, prebra sempre debnir as variáveis de instância.

Definindo métodos de classe


Classes também podem ter métodos. Algumas linguagens chamam estes métodos de estáticos. No Ruby, eles são apenas métodos
adicionados a um objeto que é uma instância da classe Class.

Vamos implementar o método Page.load, que irá ler um arquivo em formato YAML (Yet Another Markup Language) e retornar uma
nova instância da classe Page com os valores já atribuídos.

require "yaml"

class Page
attr_accessor :title, :content

def Page.load(filepath)
attrs = YAML.load_file(filepath)
Page.new(attrs["title"], attrs["content"])
end

def initialize(title, content)


@title = title
@content = content

83
end
end

No Ruby, você pode ler e gerar a representação de objetos com a classe YAML. Para isso, basta carregar a standard library com o
método Kernel#require. O método YAML.load_file lê um arquivo e converte seu contéudo em objetos Ruby. Neste exemplo, nosso
arquivo deve retornar um hash.

Classes possuem um objeto self, assim como todos os objetos. Dentro da instrução class..end, o self faz referência à própria
classe. Por isso, uma abordagem mais comum usada por desenvolvedores mais experientes é debnir métodos de classe usando def
self.load(file)..end, em vez de usar o nome da própria constante.

require "yaml"

class Page
attr_accessor :title, :content

def self.load(filepath)
attrs = YAML.load_file(filepath)
Page.new(attrs["title"], attrs["content"])
end

def initialize(title, content)


@title = title
@content = content
end
end

Como o nosso método de classe está no contexto da própria classe (lembre-se, o self faz referência a própria classe), nós podemos
fazer mais uma alteração. Em vez de instanciar a classe Page.new, basta executar o método new diretamente.

84
require "yaml"

class Page
attr_accessor :title, :content

def self.load(filepath)
attrs = YAML.load_file(filepath)
new(attrs["title"], attrs["content"])
end

def initialize(title, content)


@title = title
@content = content
end
end

A classe Page ainda não permite salvar sua representação em YAML. Vamos adicionar um método Page#save_to(file) que faz
exatamente isso.

require "yaml"

class Page
attr_accessor :title, :content

def self.load(filepath)
attrs = YAML.load_file(filepath)
new(attrs["title"], attrs["content"])
end

def initialize(title, content)


@title = title

85
@content = content
end

def save_to(filepath)
File.open(filepath, "w") {|file| file.write to_yaml }
end
end

A biblioteca YAML injeta um método Object#to_yaml, que retorna uma string representando aquele objeto. No nosso caso, ele irá
retornar algo como a string abaixo.

--- !ruby/object:Page
title: Ruby
content: OMG! I'm learning Ruby!

Como a representação em YAML inclui a informação sobre qual classe este objeto foi instanciado, não precisamos mais fazer isso
manualmente no método Page#load. Agora, podemos simplesmente retornar o objeto instanciado com o método YAML.load_file.

require "yaml"

class Page
attr_accessor :title, :content

def self.load(filepath)
YAML.load_file(filepath)
end

def initialize(title, content)


@title = title
@content = content

86
end

def save_to(filepath)
File.open(filepath, "w") {|file| file.write to_yaml }
end
end

Visibilidade de métodos e controle de acesso


O Ruby possui três níveis diferentes de visibilidade e controle de acesso dos métodos de um objeto.

• Métodos públicos podem ser executados em qualquer situação. Métodos são públicos por padrão, com uma única exceção: o
método Class#initialize é sempre privado.
• Métodos protegidos podem ser executados por objetos de uma classe e suas subclasses.
• Métodos privados não podem ser executados através de um receiver explícito. O receiver sempre será o objeto atual self.

Para debnir a visibilidade de métodos você utilizar os métodos Module.public, Module.private e Module.protected.

class SomeClass
def method1 # métodos são públicos por padrão
end

private # todos os métodos definidos à partir daqui serão privados


def method2
end

protected # todos os métodos definidos à partir daqui serão protegidos


def method3
end

public # todos os métodos definidos à partir daqui serão públicos


def method4

87
end
end

Alternativamente você poderia ter debnido a visibilidade dos métodos passando uma lista de nomes de métodos.

class SomeClass
def method1
end

def method2
end

def method3
end

def method4
end

public :method1, :method4


private :method2
protected :method3
end

O controle de acesso é determinado dinamicamente. Somente quando o método for executado é que o controle de acesso será
determinado. Se a visibilidade deste método for violada, uma exceção NoMethodError será lançada.

class SomeClass
private
def some_private_method
end
end

88
object = SomeClass.new
object.some_private_method
#=> NoMethodError: private method ‘some_private_method’ called for #<SomeClass:0x007fc8f3853860>

Você pode contornar o controle de acesso de métodos utilizando o método Object.__send__.

object.__send__ :some_private_method # nenhuma exceção é lançada

Para garantir que mensagens sejam enviadas apenas para métodos públicos, use o método Object.public_send, introduzido no Ruby
1.9.

object = SomeClass.new
object.public_send :some_private_method
#=> NoMethodError: private method `some_private_method' called for #<SomeClass:0x007f9441897bf0>

Definindo constantes
Muitas classes podem usar constantes para armazenar informações que poderiam bcar espalhadas pelo código, como “números
mágicos”. Vamos alterar a classe Page de modo que ela possa receber também um permalink, uma representação de como essa página
poderia ser referenciada.

class Page
attr_accessor :title, :content

def self.load(filepath)
YAML.load_file(filepath)
end

89
def initialize(title, content, permalink)
@title = title
@content = content
@permalink = permalink
end

def save_to(filepath)
File.open(filepath, "w") {|file| file.write to_yaml }
end
end

Vamos renomear o método Page#save_to para Page#save. Este método irá salvar os arquivos sempre em um mesmo diretório,
usando o atributo permalink como nome do arquivo.

class Page
attr_accessor :title, :content

def self.load(filepath)
YAML.load_file(filepath)
end

def initialize(title, content, permalink)


@title = title
@content = content
@permalink = permalink
end

def save
File.open("/tmp/#{permalink}.yml", "w") {|file| file.write to_yaml }
end
end

90
Em vez de deixar o diretório onde os arquivos serão salvos no método Page#save, vamos extrair esta informação para uma constante.
Essa alteração permite, dentre outras coisas, expor esta informação na documentação RDoc.

class Page
attr_accessor :title, :content

ROOT = "/tmp"

def self.load(filepath)
YAML.load_file(filepath)
end

def initialize(title, content, permalink)


@title = title
@content = content
@permalink = permalink
end

def filepath
File.join(ROOT, "#{permalink}.yml")
end

def save
File.open(filepath, "w") {|file| file.write to_yaml }
end
end

91
Entendendo classes Singleton
Todo objeto do Ruby está associado a duas classes: a classe que a instanciou e uma classe anônima, escondida, especíbca do objeto.
Esta classe anônima é chamada de Singleton Class, mas antes de ter um nome obcial também era chamada de anonymous class,
metaclass, eigenclass ou ghost class.

O nome Singleton usado pelo Ruby nada tem a ver com o Singleton Pattern, que também está disponível com a biblioteca Singleton.

A sintaxe mais comum para acessar a classe Singleton é class << object..end, onde object é o objeto cuja classe Singleton você
quer. É muito comum vermos algo como o exemplo à seguir para debnir métodos em uma classe.

class Person
class << self
def count
@count ||= 0
end
end
end

Aqui, estamos debnindo um método na classe Singleton do objeto Person (lembre-se: tudo no Ruby é objeto, inclusive classes). Como
consequência, isso irá debnir o método Person.count. O efeito é exatamente o mesmo que o código abaixo, porém este é mais
objetivo e fácil de entender.

class Person
def self.count
@count ||= 0
end
end

92
No Ruby 1.9, foi adicionado o método Object#singleton_class, que é apenas um atalho para a sintaxe class << self; self;
end. Em versões mais antigas, você pode injetar este método com o código abaixo.

class Object
def singleton_class
class << self; self; end
end unless respond_to?(:singleton_class)
end

Toda vez que injeta métodos em um objeto, eles são adicionados como métodos singleton. O que é realmente importante saber é que
estes métodos pertecem unicamente ao objeto em que foram debnidos, não afetando nenhum outro objeto da hieraquia.

string = "Hi there!"


another_string = "Hi there!"

def string.to_yo
self.gsub(/\b(Hi|Hello)( there)\b?!?/, "Yo! Wassup?")
end

string.to_yo
#=> "Yo! Wassup?"

another_string.respond_to?(:to_yo)
#=> false

E para provar que o método to_yo é singleton, podemos utilizar o método Object#singleton_methods.

string.singleton_methods
#=> ["to_yo"]

93
another_string.singleton_methods
#=> []

Você também pode adicionar métodos singleton de outras maneiras. Uma delas é estendendo um objeto com um módulo.

module Extension
def sum
self.reduce(:+)
end
end

numbers = [1,2,3]
numbers.extend Extension
numbers.singleton_methods
#=> ["sum"]

Outra maneira é usando a própria classe Singleton.

numbers = [1,2,3]

class << numbers


def sum
self.reduce(:+)
end
end

numbers.singleton_methods
#=> ["sum"]

Ou ainda, executando código no contexto do objeto.

94
numbers = [1,2,3]

numbers.instance_eval do
def sum
self.reduce(:+)
end
end

numbers.singleton_methods
#=> ["sum"]

Quando você cria uma classe Singleton em um objeto, não poderá mais utilizar o método Marshal.dump, já que a biblioteca Marshal[3]
não suporta objetos com classes Singleton (ela irá lançar a exceção TypeError: singleton can't be dumped). A única maneira de
fazer isso e ainda poder utilizar o Marshal é utilizando o método Object#extend.

Agora, sabendo que você pode adicionar métodos em um objeto com uma sintaxe como def object.some_method; end, perceba
que é exatamente isso que fazemos quando debnimos um método em uma classe; a única diferença é que passamos o próprio self.

class Person
def self.say_hello
"Hello there!"
end
end

Person.singleton_methods
#=> ["say_hello"]

3
A biblioteca Marshal permite converter objetos Ruby em uma sequência de bytes que podem ser restaurados por outros scripts, que
podem reconstituir os objetos originais.

95
Com base nesse exemplo, é possível abrmar que métodos de classe não e
existem
xistem no Rub
Rubyy! Pelo menos não no sentido de métodos
estáticos! O que acontece é que estes métodos pertencem a um objeto, que por acaso é uma classe.

96
CAPÍTULO 1

Módulos
Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

97
CAPÍTULO 1

Trabalhando com o load path


Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

98
CAPÍTULO 1

Trabalhando com strings


Concatenando strings
Para concatenar strings, você pode utilizar os métodos String#+, String#<< e String#concat.

puts "Ruby" + " is nice!"


#=> Ruby is nice!

puts "Ruby" << " is nice!"


#=> Ruby is nice!

puts "Ruby".concat(" is nice!")


#=> Ruby is nice!

Embora os três métodos atingem o objetivo—concatenar strings—, existe uma diferença muito importante. O método String#+ irá
criar um novo objeto em memória, enquanto os métodos String#<< e String#concat irão realocar a memória previamente utilizada.

hello = "Hello"
ruby = "Ruby"

hello2 = hello + " " + ruby


puts "#{hello2}: #{hello2.object_id}"
#=> Hello Ruby: 70310699719020

puts "#{hello}: #{hello.object_id}"


#=> Hello: 70310699719100

99
hello << " "
hello << ruby
puts "#{hello}: #{hello.object_id}"
#=> Hello Ruby: 70310699719100

Formatando strings
Você já viu que no Ruby é possível utilizar expressões arbitrárias que podem ser interpoladas.

language = "Ruby"
puts "#{language} is nice!"
#=> Ruby is nice!

O Ruby também suporta outros tipos de formatação de strings que também permitem interpolar expressões. É o caso dos métodos
String#%, Kernel#printf e Kernel#sprintf. Estes métodos permitem ter maior controle no espaçamento e formatação de
números. Além disso, eles permitem desacoplar os valores que devem ser interpolados da string, facilitando, por exemplo, a
internacionalização de strings.

A mesma string que foi interpolada acima pode ser formatada com os métodos mencionados.

language = "Ruby"

"%s is nice!" % language #=> retorna a string "Ruby is nice!"


sprintf("%s is nice!", language) #=> retorna a string "Ruby is nice!"
printf("%s is nice!\n", language) #=> exibe a mensagem "Ruby is nice!" e retorna nil

Para formatar mais de um valor, passe um array.

100
"%s is %s!" % ["Ruby", "nice"]
#=> Ruby is nice!

Embora seja fácil interpolar múltiplos valores com arrays, seu código pode acabar confuso quando a lista de parâmetros é muito
grande. Neste caso, prebra usar um hash. Note que esta funcionalidade foi introduzida no Ruby 1.9.

"language isadjective!" % {language: "Ruby", adjective: "nice"}


#=> Ruby is nice!

Existem diversos campos que permitem formatar números.

"%d" % 1.5 #=> 1 - número inteiro


"%f" % 1.1245 #=> 1.124500 - ponto-flutuante com todos os números
"%.2f" % 1 #=> 1.00 - ponto-flutuante com duas casas decimais
"%x" % 1234 #=> 4d2 - hexadecimal com letras minúsculas
"%X" % 1234 #=> 4D2 - hexadecimal com letras maiúsculas
"%o" % 1234 #=> 2322 - número inteiro ctal
"%e" % 1234 #=> 1.234000e+03 - número exponencial
"%E" % 1234 #=> 1.234000E+03 - número exponencial em maiúscula
"%b" % 1234 #=> 10011010010 - número binário

Para saber mais sobre os formatos aceitos, acesse a documentação; basta executar o comando ri String#sprintf.

Convertendo em maiúsculas e minúsculas


Para converter uma string em letras maiúsculas, utilize o método String#upcase.

101
text = "ruby is nice!"
puts text.upcase
#=> RUBY IS NICE!

Para converter uma string em letras minúsculas, utilize o método String#downcase.

text = "RUBY IS NICE!"


puts text.downcase
#=> ruby is nice!

Note que os métodos String#upcase e String#downcase irão retornar uma nova string. Para alterar a string existente, utilize os
métodos String#upcase! e String#downcase!.

text = "Ruby is nice!"


puts text.object_id
#=> 70347156470500

text.upcase!
puts text.object_id
#=> 70347156470500

Tamanho de uma string


Para saber o tamanho de uma string, utilize os métodos String#size ou String#length. Note que esses métodos irão retornar a
quantidade de caracteres. Se você precisa saber a quantidade de bytes necessários para representar a string, utilize o método
String#bytes[1].

1
Veja mais detalhes sobre codibcação de caracteres em Codibcação.

102
text = "Ruby is nice!"

puts text.size #=> 13


puts text.length #=> 13

Substrings
O Ruby permite pegar substrings (trechos de uma determinada string) com o método String#[].

Em versões anteriores ao Ruby 1.9, o método String#[] com um número inteiro retorna a representação numérica
daquela posição, o que nem sempre recete a represenção de caracteres com mais de um byte.

A maneira mais simples é passar um número inteiro que identibca o caracter que será retornado. Se o índice for negativo, a posição
será movida à partir do bm da string.

text = "Ruby is nice!"


text[8] #=> n
text[-1] #=> !

No Ruby, índices de array e strings iniciam em zero.

Você também pode passar como segundo parâmetro, a quantidade de caracteres que a substring deve ter.

text = "Ruby is nice!"


text[8,4] #=> nice
text[-5, 4] #=> nice

103
Às vezes, pode ser mais conveniente passar o índice inicial e bnal. Neste caso, você pode passar um Range. Note que você também pode
utilizar intervalos com valores negativos.

text = "Ruby is nice!"


text[0..3] #=> Ruby
text[-5..-2] #=> nice

O método String#[] também pode receber expressões regulares. Neste caso, o primeiro resultado será retornado.

text = "Ruby is nice!"


text[/ruby/i] #=> Ruby

Você pode especibcar o número ou nome do grupo quando estiver usando uma expressão regular.

text = "ruby RUBY rUbY Ruby"


text[/(ruby) (ruby) (ruby) (ruby)/i, 2] #=> RUBY
text[/(ruby) (?<upcase>ruby) (ruby) (ruby)/i, :upcase] #=> RUBY

Por último, se você passar uma string qualquer e ela estiver presente, ela é retornada. Caso contrário, nil é retornado.

text = "Ruby is nice!"


text["Ruby"] #=> Ruby
text["ruby"] #=> nil

104
Codificação
Uma das grandes mudanças introduzidas pelo Ruby 1.9 foi a codibcação das strings, que agora são verdadeiramente sequências de
caracteres que não precisam estar contidas na tabela de caracteres ASCII. Caracteres debnidos como UTF-8, por exemplo, que usam
um número variável de bytes para representar os caracteres, não possuem mais uma relação um-para-um de bytes e caracteres.

A classe String foi reescrita no Ruby 1.9 para que pudesse ter suporte a caracteres multibytes. Quando uma string contém caracteres
multibytes, a quantidade de bytes usados para representá-la não será a mesma do número de caracteres.

# -*- encoding: utf-8 -*-


text = "ruby é legal!"
puts text.size #=> 13
puts text.bytesize #=> 14

Perceba que a primeira linha debne qual o tipo de codibcação será usada no arquivo. Esse comentário é chamado de magic comment e
sem ele o Ruby não saberia decidir qual codibcação utilizar. O magic comment pode ser qualquer string que consiga ser casada pela
seguinte expressão regular[2]. Magic comments devem ser a primeira linha do arquivo ou vir após a linha de shebang.

/coding[:=] ?/

Os seguintes comentários são válidos na hora de debnir a codibcação do arquivo.

# coding: utf-8
# encoding: utf-8
# -*- coding: utf-8 -*-
# vim:fileencoding=utf-8
# vim:set fileencoding=utf-8 :

2
Na verdade, magic comments seguem a PEP-263 debnida pelo Python.

105
A codibcação de uma string é baseada na codibcação do código-fonte, mas não será necessariamente a mesma.

# -*- encoding: utf-8 -*-


text = "hello".encode("iso-8859-1")
puts text.encoding
#=> ISO-8859-1

Nem todas as strings são compatíveis entre diferentes codibcações. Toda vez que você tentar fazer a conversão entre codibcações que
não são compatíveis, uma exceção será lançada.

# -*- encoding: utf-8 -*-


text = "√".encode("iso-8859-1")
#=> Encoding::UndefinedConversionError: U+221A from UTF-8 to ISO-8859-1

Neste caso você pode forçar a codibcação utilizando o método String#encode, passando a estratégia de conversão e qual string será
usada no lugar dos caracteres que não são reconhecidos.

# -*- encoding: utf-8 -*-


text = "Learn Ruby √".encode("iso-8859-1", :undef => :replace, :replace => "[DONE]")
puts text
#=> Learn Ruby [DONE]

Você pode forçar a codibcação de uma string com o método String#force_encoding. Este método não faz qualquer veribcação ou
conversão de caracteres; apenas muda a interpretação que o Ruby faz dos caracteres, mas sem alterar os bytes que representam a
string. Para validar se os bytes daquela string são válidos na codibcação escolhida, utilize o método String#valid_encoding?.

text = "\xF4".force_encoding("utf-8") #=> Força um valor binário em UTF-8


puts text.valid_encoding?
#=> false

106
Para acessar a representação númerica de cada caracter de uma string, utilize o método String#codepoints. Este método espera um
bloco, mas você pode utilizar o método Enumerator#to_a para converter este iterador em um array.

"hello".codepoints.to_a
#=> [104, 101, 108, 108, 111]

"maçã".codepoints.to_a
#=> [109, 97, 231, 227]

"☃".codepoints.to_a
#=> [9731]

Alternativamente, você pode utilizar o método String#ord para pegar a representação númerica de um único caracter.

"☺".ord
#=> 9786

Você também pode acessar os bytes de uma string com o método String#bytes. Assim como o método String#codepoints, você
pode converter este iterador em um array.

"hello".bytes.to_a
#=> [104, 101, 108, 108, 111]

"maçã".bytes.to_a
#=> [109, 97, 195, 167, 195, 163]

"áéíóú".bytes.to_a
#=> [195, 161, 195, 169, 195, 173, 195, 179, 195, 186]

107
Note como a string áéíóú possui apenas 5 caracteres, mas precisa de 10 bytes para ser representada.

Para acessar a lista de codibcações disponíveis no Ruby, utilize o método Encoding.list. Algumas codibcações possuem um alias, cuja
listagem completa pode ser retornada pelo método Encoding.aliases. O código à seguir fará o output das codibcações disponíveis
com seus respectivos aliases.

list = Encoding.list.collect do |encoding|


info = encoding.name
alias_name = Encoding.aliases.key(encoding.name)
info << " (#{alias_name})" if alias_name
info
end

puts list.sort_by(&:downcase)

O resultado do código acima, quando executado no Ruby 1.9.3-p0, pode ser visto à seguir (os nomes foram listados em colunas para
não ocupar tanto espaço).

ASCII-8BIT (BINARY) EUC-TW (eucTW) IBM863 (CP863) ISO-8859-3 (ISO8859-3)


Big5 eucJP-ms (euc-jp-ms) IBM864 (CP864) ISO-8859-4 (ISO8859-4)
Big5-HKSCS (Big5-HKSCS:2008) GB12345 IBM865 (CP865) ISO-8859-5 (ISO8859-5)
Big5-UAO GB18030 IBM866 (CP866) ISO-8859-6 (ISO8859-6)
CP50220 GB1988 IBM869 (CP869) ISO-8859-7 (ISO8859-7)
CP50221 GB2312 (EUC-CN) ISO-2022-JP (ISO2022-JP) ISO-8859-8 (ISO8859-8)
CP51932 GBK (CP936) ISO-2022-JP-2 (ISO2022-JP2) ISO-8859-9 (ISO8859-9)
CP850 (IBM850) IBM437 (CP437) ISO-2022-JP-KDDI KOI8-R (CP878)
CP852 IBM737 (CP737) ISO-8859-1 (ISO8859-1) KOI8-U
CP855 IBM775 (CP775) ISO-8859-10 (ISO8859-10) macCentEuro
CP949 IBM852 ISO-8859-11 (ISO8859-11) macCroatian
CP950 IBM855 ISO-8859-13 (ISO8859-13) macCyrillic
CP951 IBM857 (CP857) ISO-8859-14 (ISO8859-14) macGreek
Emacs-Mule IBM860 (CP860) ISO-8859-15 (ISO8859-15) macIceland
EUC-JP (eucJP) IBM861 (CP861) ISO-8859-16 (ISO8859-16) MacJapanese (MacJapan)
EUC-KR (eucKR) IBM862 (CP862) ISO-8859-2 (ISO8859-2) macRoman

108
macRomania stateless-ISO-2022-JP-KDDI UTF-7 (CP65000) Windows-1253 (CP1253)
macThai TIS-620 UTF-8 (CP65001) Windows-1254 (CP1254)
macTurkish US-ASCII (ASCII) UTF8-DoCoMo Windows-1255 (CP1255)
macUkraine UTF-16 UTF8-KDDI Windows-1256 (CP1256)
Shift_JIS UTF-16BE (UCS-2BE) UTF8-MAC (UTF-8-MAC) Windows-1257 (CP1257)
SJIS-DoCoMo UTF-16LE UTF8-SoftBank Windows-1258 (CP1258)
SJIS-KDDI UTF-32 Windows-1250 (CP1250) Windows-31J (CP932)
SJIS-SoftBank UTF-32BE (UCS-4BE) Windows-1251 (CP1251) Windows-874 (CP874)
stateless-ISO-2022-JP UTF-32LE (UCS-4LE) Windows-1252 (CP1252)

109
CAPÍTULO 1

Trabalhando com números


Operadores matemáticos
Para efetuar somas, utilize o método Numeric#+.

1 + 1 #=> 2
1 + 2.1 #=> 3.1
-1 + -1 #=> -2

Para efetuar subtrações, utilize o método Numeric#-.

2 - 1 #=> 1
2 - 1.3 #=> 0.7
-1 - -2 #=> 1

Para efetuar multiplicações, utilize o método Numeric#*.

2 * 2 #=> 4
2 * 1.3 #=> 2.6
-2 * 1.3 #=> -2.6

Para efetuar divisões, utilize o método Numeric#/. Note que o resultado depende da classe do número que será usado como divisor.
No caso de números inteiros, o resultado será um número inteiro.

110
3 / 2 #=> 1
3 / 2.0 #=> 1.5
2 / 1.75 #=> 1.1428571428571428

Para efetuar potenciações, utilize o método Numeric#**.

2 ** 2 #=> 4
2 ** 3 #=> 8
3 ** 2 #=> 9

Números absolutos
Para pegar o valor absoluto de um número, use o método Numeric#abs.

1.abs #=> 1 - Método Numeric#abs é o mais utilizado


-1.abs #=> 1
+1.2.abs #=> 1.2
-1.2.abs #-> 1.2
-1.2.magnitude #=> 1.2

Verificando números pares e ímpares


Para descobrir se um determinado número é par ou ímpar, você pode utilizar o operador %, que retorna o módulo de um número.

4 % 2 == 0 # true - significa que é um número par


5 % 2 == 1 # true - significa que é um número ímpar

111
Felizmente a classe Integer implementa dois métodos que permitem fazer a mesma veribcação, mas de uma forma muito mais
elegante.

4.even? #=> true - é um número par


5.odd? #=> true - é um número ímpar

Verificando zeros e não-zeros


O Ruby possui dois métodos muito úteis que permitem veribcar se um número é zero ou não.

1.zero? #=> false


1.nonzero? #=> true

0.zero? #=> true


0.nonzero? #=> false

Conversão entre diferentes bases


Números inteiros podem ser convertidos entre diferentes bases. É possível fazer conversões entre bases de 2 a 36.

1234.to_s(2) #=> 10011010010


1234.to_s(8) #=> 2322
1234.to_s(10) #=> 1234 - base 10 é o padrão
1234.to_s(16) #=> 4d2
1234.to_s(36) #=> ya

O valor retornado será uma string que pode ser convertida novamente em número com o método String#to_i.

112
"10011010010".to_i(2) #=> 1234
"2322".to_i(8) #=> 1234
"1234".to_i(10) #=> 1234 - base 10 é o padrão
"4d2".to_i(16) #=> 1234
"ya".to_i(36) #=> 1234

Fazendo arredondamentos
O Ruby possui dois métodos que permitem arredondar números de ponto cutuantes para inteiros. O método Float#ceil irá
arredondar para o próximo número inteiro maior ou igual o próprio número.

1.0.ceil #=> 1
1.1.ceil #=> 2
-1.1.ceil #=> -1

Já o método Float#floor irá arredondar para o número inteiro que for menor ou igual ao próprio número.

1.0.floor #=> 1
1.1.floor #=> 1
-1.1.floor #=> -2

O método Float#round irá arredondar números de ponto cutuante para o número de casas decimais que foram especibcadas. Por
padrão, a precisão é zero e pode ser um número negativo (nesse caso, o número retornado será um inteiro).

1.4.round #=> 1
1.5.round #=> 2
1.6.round #=> 2
-1.5.round #=> -2

113
1.234567.round(2) #=> 1.23
1.234567.round(3) #=> 1.235
1.234567.round(4) #=> 1.2346
1.234567.round(5) #=> 1.23457

12345.67.round(-5) #=> 0
12345.67.round(-4) #=> 10000
12345.67.round(-3) #=> 12000
12345.67.round(-2) #=> 12300
12345.67.round(-1) #=> 12350
12345.67.round(0) #=> 12346

Também é possível fazer o truncamento de um número de ponto cutuante para inteiro.

1.1.to_i #=> 1 - O método Float#to_i é mais utilizado


-1.1.to_i #=> -1
1.0.truncate #=> 1

114
CAPÍTULO 1

Trabalhando com arrays


Acessando os elementos
Embora você tenha visto algumas maneiras de como acessar elementos de arrays, existe um método muito interessante chamado
Array#fetch. Este método permite retornar um valor padrão caso o índice especibcado não exista.

items = [1, 2, 3]

items.fetch(0) #=> 1
items.fetch(4, "not found") #=> not found
items.fetch(4) do |index| #=> index 4 not found
"index #{index} not found"
end

Outra característica interessante é que se nenhum valor padrão for especibcado, uma exceção IndexError será lançada.

items = [1, 2, 3]

items.fetch(4)
#=> IndexError: index 4 outside of array bounds: -3...3

Adicionando novos elementos


Para adicionar novos elementos ao bnal de um array, use os métodos Array#<<[1] ou o método Array#push.

115
items = []
items << 1
items << 2
items.push(3)

p items
#=> [1, 2, 3]

Para adicionar novos elementos ao começo de um array, use o método Array#unshift.

items = [1, 2, 3]

items.unshift(0)
#=> [0, 1, 2, 3]

Você também pode inserir novos elementos em uma posição especíbca com o método Array#add_elements.rb#insert. Note que o
índice pode ser um valor negativo.

items = [1, 2, 3]

items.insert(0, 4) #=> insere o número 4 na posição 0.


#=> [4, 1, 2, 3]

items.insert(-1, 5) #=> insere o número 5 no final do array.


#=> [4, 1, 2, 3, 5]

items.insert(3, 6, 7, 8) #=> insere os números 6, 7 e 8 na posição 3.


#=> [4, 1, 2, 6, 7, 8, 3, 5]

1
Este método também é conhecido como shovel.

116
Removendo elementos
Para remover sempre o último elemento de um array, use o método Array#pop. O elemento removido será o valor de retorno do
método.

items = [1, 2, 3]

items.pop
#=> 3

p items
#=> [1, 2]

Você também pode especibcar a quantidade de elementos que serão removidos.

items = [1, 2, 3, 4, 5]

items.pop(3)
#=> [3, 4, 5]

p items
#=> [1, 2]

Para remover todos os elementos de um determinado valor, use o método Array#delete. Caso este elemento não seja encontrado,
nil será retornado. Você também pode fornecer um bloco, cujo valor será retornado quando o elemento não for encontrado no array.

items = [1, 2, 3, "a", "b", "c"]

items.delete("a")
#=> a

117
items.delete("d")
#=> nil

items.delete("e") { "Y U MAD BRO?" }


#=> Y U MAD BRO?

Para remover um elemento em um determinado índice, use o método Array#delete_at. Se o índice especibcado não existir, nil será
retornado.

items = [1, 2, 3]

items.delete_at(1)
#=> 2

items.delete_at(3)
#=> nil

Para remover o primeiro elemento de um array, use o método Array#shift.

items = [1, 2, 3]

items.shift
#=> 1

Alternativamente, você pode utilizar o método Array#drop com o número de elementos que devem ser removidos. Note que o array
original não é modibcado.

118
items = [1, 2, 3]

items.drop(1)
#=> [2, 3]

items
#=> [1, 2, 3]

items = items.drop(2)
#=> [3]

Por bm, para remover todos os elementos de um array, use o método Array#clear.

items = [1, 2, 3]
items.clear
items
#=> []

Filtrando elementos
Para gerar um novo array com todos os elementos que satisfaçam uma determinada condição, use o método Array#select. Caso o
bloco retorne um valor que seja detectado como true[2], o elemento será adicionado ao novo array. À partir do Ruby 1.9, todos os
métodos iteradores que não recebem um bloco irão retornar um objeto Enumerator.

items = [1, 2, 3, 4, 5]
items.select {|n| n.odd?}
#=> [1, 3, 5]

2
Ou seja, qualquer valor diferente de false e nil.

119
No Ruby 1.9, o exemplo acima pode ser ainda mais simples. À partir desta versão do Ruby, o método Symbol#to_proc permite criar um
bloco que irá executar o método identibcado pelo símbolo que foi passado.

items = [1, 2, 3, 4, 5]
items.select(&:odd?)
#=> [1, 3, 5]

Se você precisa apenas do primeiro elemento que satisfaça a condição, utilize o método Array#find.

items = [1, 2, 3, 4, 5]

# recomendado
items.find(&:odd?)
#=> 1

# estranho
items.select(&:odd?).first
#=> 1

O método Array#select possui uma contra-parte; trata-se do método Array#reject. Caso o bloco retorne false ou nil, o elemento
será adicionado ao novo array.

items = [1, 2, 3, 4, 5]
items.reject(&:odd?)
#=> [2, 4]

Ordenando elementos
Para ordenar os elementos de um array, utilize o método Array#sort. A comparação é feita usando o operador <=>.

120
items = %w[a d c b i k]
items.sort
#=> ["a", "b", "c", "d", "i", "k"]

A classe Array possui muitos métodos que modibcam o array original. É o caso de Array#sort! e Array#sort_by!.

items = %w[a d c b i k]
items.sort!
items
#=> ["a", "b", "c", "d", "i", "k"]

Você também pode especibcar qual o tipo de valor deve ser usado na comparação. Isso pode ser feito com o método
Enumerator#sort_by. O módulo Enumerator é incluído pela classe Array.

Veja, por exemplo, como ordenar um array pelo tamanho das strings.

items = %w[Ruby Python PHP C JavaScript]


items.sort_by(&:size)
#=> ["C", "PHP", "Ruby", "Python", "JavaScript"]

Para ordenar os elementos aleatóriamente, utilize o método Array#shuffle.

items = %w[Ruby Python PHP C JavaScript]


items.shuffle
#=> ["C", "PHP", "Ruby", "JavaScript", "Python"]

Para pegar um único elemento aleatóriamente, utilize o método Array#sample, disponível à partir do Ruby 1.9.

121
items = %w[Ruby Python PHP C JavaScript]
items.sample
#=> JavaScript

Buscando elementos
Para veribcar se um elemento existe em um determinado array, use o método Array#include?. A comparação é feita através do
método ==.

items = [1, "a"]

items.include?(1) #=> true


items.include?("a") #=> true
items.include?(0) #=> false

O método Array#index irá retornar o índice do primeiro elemento encontrado.

items = [1, 2, 3, 1]

items.index(1) #=> 0
items.index(0) #=> nil

Se um bloco for passado para o método Array#index, o índice será cujo bloco retorne true.

items = [1, 2, 3, 1]
items.index {|i| i == 3} #=> 2

Existe ainda o método Array#rindex, que funciona como o método Array#index mas atua nos elementos de forma reversa.

122
items = [1, 2, 3, 1]

items.rindex(1) #=> 3
items.rindex {|i| i == 1} #=> 3

Iterando elementos
A classe Array inclui o módulo Enumerator, que implementa diversos métodos iteradores, mas outros destes métodos são
implementados pela própria classe.

items = [1, 2, 3]
items.each {|item| puts item}

# Output:
# 1
# 2
# 3

O método Array#reverse_each permite iterar no array de trás para frente.

items.reverse_each {|item| puts item}

# Output:
# 3
# 2
# 1

Para iterar em cada índice de um array, use o método Array#each_index.

123
items = %w[Ruby Python PHP]
items.each_index {|i| puts i}

# Output:
# 0
# 1
# 2

Para iterar em cada elemento e, ainda por cima, ter o índice da iteração, use o método Array#each_with_index.

items = %w[Ruby Python PHP]


items.each_with_index {|item, i| puts "#{i} => #{item}"}

# Output:
# 0 => Ruby
# 1 => Python
# 2 => PHP

Compondo novos arrays


O método Array#map permite criar um novo array com os valores retornados pelo bloco, sem modibcar o array original.

[1, 2, 3].map {|i| i * 2}


#=> [2, 4, 6]

%w[Ruby Python PHP].map {|i| i.downcase}


#=> ["ruby", "python", "php"]

Alternativamente, você pode usar o método Array#collect.

124
[1, 2, 3].collect {|i| i * 2}
#=> [2, 4, 6]

%w[Ruby Python PHP].collect {|i| i.downcase}


#=> ["ruby", "python", "php"]

O método Array#inject ou Array#reduce permite reduzir um array a um único objeto. Se um valor inicial não for fornecido, o
primeiro elemento do array será utilizado.

items = [1, 2, 3, 4, 5]
items.reduce(:+) #=> 15 - Inicia em 1 e soma todos os elementos
items.reduce(10, :+) #=> 25 - Inicia em 10 e soma todos os elementos
items.reduce(10) do |acc, i| #=> 19 - Inicia em 10 e soma apenas os números ímpares
acc += i if i.odd?
acc
end

Perceba como estamos utilizando o método Symbol#to_proc para executar a soma dos elementos. Esta técnica será útil apenas para
operações mais simples. Para executar operações mais complexas, forneça um bloco, que irá receber o acumulador e o elemento da
iteração.

125
CAPÍTULO 1

Trabalhando com hashes


Lista de chaves e valores
Para pegar uma lista das chaves de um hash, você deve usar o método Hash#keys.

options = {name: "John Doe", age: 32}


options.keys
#=> [:name, :age]

Já a lista de valores pode ser acessada com o método Hash#values.

options = {name: "John Doe", age: 32}


options.values
#=> ["John Doe", 32]

Verificando a existência de chaves e valores


Hashes são muito ebcientes em buscar um determinado valor através de uma chave. Você também pode fazer buscas de chaves
através de um valor, embora esta operação não seja tão ebciente quanto o contrário.

A classe Hash possui quatro métodos diferentes para detectar se uma chave foi debnida. Trata-se dos métodos Hash#key?,
Hash#has_key?, Hash#member? e Hash#include?.

options = {color: "green", width: 150, height: 30}


options.key?(:color) #=> true

126
options.key?("color") #=> false - As chaves são símbolos
options.has_key?(:color) #=> true
options.include?(:width) #=> true
options.member?(:height) #=> true

Para descobrir se um hash possui um determinado valor, use os métodos Hash#value? e Hash#has_value?.

options = {color: "green", width: 150, height: 30}


options.value?("green") #=> true
options.has_value?("150") #=> false

Para pegar a primeira chave que debne um determinado valor, utilize o método Hash#key. No Ruby 1.8, este método se chama
Hash#index.

options = {color: "green", width: 150, height: 30}


options.key("green") #=> :color
options.key("missing") #=> nil

Acessando o hash
O método mais utilizado para acessar valores de hashes é Hash#[]. Caso uma chave não tenha sido debnida, nil será retornado por
padrão.

options = {name: "John Doe", age: 32}


options[:name] #=> "John Doe"
options[:age] #=> 32
options[:email] #=> nil

127
Você também pode usar o método Hash#fetch para acessar valores em um hash. Ele possui mais opções na hora de lidar com chaves
inexistentes.

options = {name: "John Doe", age: 32}


options.fetch(:name) #=> John Doe - Funciona como o método Hash#[]
options.fetch(:email) #=> Lança a exceção "KeyError: key not found: :email"
options.fetch(:email, nil) #=> Retorna nil caso chave não exista
options.fetch(:email) do |key| #=> Executa o bloco caso a chave não exista
"Missing #{key}"
end

Para extrair mais de um valor de um hash, use o método Hash#values_at. Se uma determina chave não existir, nil será retornado.

options = {name: "John Doe", age: 32}


options.values_at(:name, :age, :email)
#=> ["John Doe", 32, nil]

Filtrando elementos
Para gerar um novo hash com todos os elementos que satisfaçam uma determinada condição, use o método Hash#select. Caso o
bloco retorne um valor que seja detectado como true, o elemento será adicionado ao novo array. No Ruby 1.8, este método retorna
um array contendo as chaves e valores.

items = {:one => 1, :two => 2, :three => 3, :four => 4, :five => 5}

items.select {|key, value| value.even?}


#=> {:two=>2, :four=>4} - Ruby 1.9
#=> [[:four, 4], [:two, 2]] - Ruby 1.8

128
Assim como nos arrays, o método Hash#select possui sua contra-parte Hash#reject. Caso o bloco retorne false ou nil, o elemento
será adicionado ao novo hash.

items = {:one => 1, :two => 2, :three => 3, :four => 4, :five => 5}

items.reject {|key, value| value.odd?}


#=> {:two=>2, :four=>4}

Removendo valores de um hash


Para remover uma determinada chave de um hash, use o método Hash#delete. O valor retornado será o valor removido. Caso a chave
não exista, nil será retornado. Se você fornecer um bloco, o valor de retorno do bloco será usado em vez de nil.

options = {:color => "green", "size" => "300x50"}

options.delete("color") #=> nil


options.delete(:color) #=> green
options.delete(:invalid) do |key| #=> Executa o bloco caso a chave não exista
raise KeyError, "key not found: #{key.inspect}"
end

Para remover diversas chaves caso uma condição não seja satisfeita, use o método Hash#delete_if.

options = {one: 1, two: 2, three: 3, four: 4}


options.delete_if do |key, value|
value.odd?
end

129
options
#=> {:two=>2, :four=>4}

Alternativamente, você pode usar o método Hash#reject! que também irá modibcar o hash original.

options = {one: 1, two: 2, three: 3, four: 4}


options.reject! do |key, value|
value.odd?
end

options
#=> {:two=>2, :four=>4}

Por bm, para remover todos os elementos de um hash, use o método Hash#clear.

options = {a: 1, b: 2, c: 3}
options.clear
options
#=> {}

Compondo novos hashes


O método Hash#map irá converter o hash para um array, usando o valor retornado pelo bloco. Para compor um hash à partir de array,
você pode utilizar o método Hash.[].

options = {color: "red", language: "Ruby"}

options.map {|key, value| value.upcase}


#=> ["RED", "RUBY"]

130
Hash[options.map {|key, value| [key, value.upcase]}]
#=> {:color=>"RED", :language=>"RUBY"}

Alternativamente, você pode usar o método Hash#inject ou Hash#reduce, passando um hash vazio como valor inicial.

options = {color: "red", language: "Ruby"}

options.reduce({}) do |acc, (key, value)|


acc.merge(key => value.upcase)
end

Note que estamos explodindo a chave e valor em duas variáveis diferentes, conforme vimos em Atribuição de variáveis.

Iterando hashes
Para iterar em hashes, utilize o método Hash#each. O bloco irá receber a chave e o valor em cada iteração.

options = {name: "John Doe", age: 32}


options.each {|key, value| puts "#{key} => #{value}"}

# Output:
# name => John Doe
# age => 32

Você também pode iterar apenas nas chaves com o método Hash#each_key.

131
options = {name: "John Doe", age: 32}
options.each_key {|key| puts "#{key} => #{options[key]}"}

# Output:
# name => John Doe
# age => 32

Por bm, para iterar somente nos valores, use o método Hash#each_value.

options = {name: "John Doe", age: 32}


options.each_value {|value| puts value}

# Output:
# John Doe
# 32

132
CAPÍTULO 1

Trabalhando com arquivos e


diretórios
Manipulando nomes de arquivos
A classe File possui alguns métodos que permitem manipular nomes de arquivos e diretórios. O Ruby usa o separador de diretórios /
em todos os sistemas operacionais, inclusive Windows. No entanto, se você estiver executando Ruby no Windows, pode também
debnir o separador como sendo \.

fullpath = "/Users/fnando/ruby/samples.rb"
dir = File.dirname(fullpath) #=> /Users/fnando/ruby
file = File.basename(fullpath) #=> samples.rb
extension = File.extname(file) #=> .rb

File.basename(fullpath, extension) #=> samples - Remove a extensão


File.dirname(file) #=> retorna ".", o diretório atual
File.join(dir, file) #=> concatena com o separator de diretórios

Nenhum destes métodos irá veribcar a existência de arquivos e diretórios. Eles apenas permitem compor nomes, sem se importar com
sua existência e tipo (você pode usar o método File.basename em um diretório, por exemplo.).

O método File.expand_path permite expandir caminhos à partir do diretório atual ou de um caminho raíz.

Dir.chdir("/usr/bin") #=> muda o diretório atual para /usr/bin


File.expand_path("ruby") #=> /usr/bin/ruby

133
File.expand_path("~/ruby") #=> /Users/fnando/ruby
File.expand_path("ruby", "/usr/local/bin") #=> /usr/local/bin/ruby

File.expand_path("~fnando/ruby") #=> /Users/fnando/ruby


# Lança a exceção "ArgumentError: user fnando doesn't exist"
# caso o diretório `~/fnando` não exista.

Para expandir o caminho de links simbólicos, use o método File.readlink.

File.readlink("/usr/bin/ruby")
#=> ../../System/Library/Frameworks/Ruby.framework/Versions/Current/usr/bin/ruby

Manipulando arquivos
O Ruby possui algumas maneiras distintas de abrir arquivos. O modo mais manual que existe exige que você mesmo encerre o uso de
IO deste arquivo. Neste caso, não será possível efetuar nenhuma operação após executar o método File#close e, caso alguma
tentativa de uso seja realizada, a exceção IOError: closed stream será lançada.

file = File.new(__FILE__) #=> Abre o próprio arquivo para leitura


content = file.read #=> Armazena o conteúdo do arquivo
file.close #=> Encerra o uso de IO

Embora seja extremamente cexível, este cuxo está sujeito à falhas. Um desenvolvedor mais descuidado pode não encerrar o uso de IO.
Para evitar que isto aconteça, é recomendado que você use o método File#open com um bloco. Este bloco irá receber o objeto File e,
após a execução do bloco, terá o uso de IO automaticamente encerrado.

File.open(__FILE__) do |file|
content = file.read
end

134
Por padrão, os métodos File.open e File.new irão abrir o arquivo em modo de leitura. Você pode especibcar a cag que será usada na
hora de abrir um arquivo.

path = "/tmp/sample.txt"

File.open(path) #=> Usa a flag "r" (leitura) por padrão.


File.open(path, "r") #=> Abre o arquivo para leitura.
File.open(path, "w") #=> Abre o arquivo para escrita, apagando o conteúdo existente.
File.open(path, "a") #=> Abre o arquivo para escrita, mantendo o conteúdo existente.
File.open(path, "w+") #=> Abre o arquivo para escrita e leitura.
File.open(path, "a+") #=> Abre o arquivo para escrita e leitura.
File.open(path, "rb") #=> Abre o arquivo para leitura binária.

Para ler um arquivo apenas, use o método File.read.

puts File.read(__FILE__)

Veja um exemplo que mostra como ler uma URL, salvar seu conteúdo em um arquivo e depois exibir o conteúdo salvo.

require "open-uri"

page = open("http://localhost/").read
path = "/tmp/localhost.html"

File.open(path, "w") do |file|


file.write page
end

puts File.read(path)

135
Note que estamos usando o método File#write para adicionar o conteúdo ao arquivo. Você também poderia ter usado o método
File#<<.

Para modibcar um arquivo existente, você deve se certibcar que está usando a cag a. Caso contrário, todo o conteúdo de seu arquivo
será substituído. O exemplo abaixo mostra como normalizar as quebras de linha de um arquivo para \n.

path = "/tmp/editing.txt"

File.open(path, "w") do |file|


file << "This is the first line\r\n"
file << "This is the second line\n"
end

content = File.read(path)

File.open(path, "w") do |file|


file.write content.gsub(/\r?\n/, "\n")
end

p File.read(path)
#=> "This is the first line\nThis is the second line\n"

Para apagar um arquivo existente, use o método File.unlink ou File.delete.

paths = ["/tmp/1.txt", "/tmp/2.txt"]

# Itera em cada um dos caminhos e cria seu arquivo.


paths.each do |path|
File.open(path, "w") {|file| file << "OH NOES!"}
end

136
# Apaga diversos arquivos. O valor de retorno é a
# quantidade de nomes que foram passados como argumentos.
File.unlink(*paths)
#=> 2

Definindo a codificação do arquivo


Assim como a classe String, a classe File também tem suporte a codibcação, que pode ser especibcada no momento da abertura do
arquivo para leitura e/ou escrita. Você pode fazer conversão entre diferentes tipos de codibcação na hora da leitura e escrita.

O exemplo abaixo mostra como abrir um arquivo UTF-8 e convertê-lo para ISO-8859 no momento da leitura. Na hora da gravação, a
codibcação será feita no caminho contrário, convertendo o texto de ISO-8859-1 para UTF-8.

file = File.open("/tmp/sample.txt", "w+:utf-8:iso-8859-1")

# verifica a codificação de gravação do arquivo


file.external_encoding
#=> <Encoding:UTF-8>

# verifica a codificação de leitura do arquivo


file.internal_encoding
#=> <Encoding:ISO-8859-1>

# escreve uma string em UTF-8 no arquivo


file.write "áéíóú".encode("iso-8859-1")

# lê o conteúdo do arquivo
content = file.read

# retorna a codificação do conteúdo do arquivo (string)


content.encoding
#=> <Encoding:ISO-8859-1>

137
file.close

Manipulando diretórios
Para listar todos os arquivos e diretórios, você pode usar o método Dir.entries. Note que este método irá adicionar os caminhos . e
.., que fazem referência ao diretório atual e diretório-pai, respectivamente.

Dir.entries(".")
#=> [".", "..", "dir.rb", "editing.rb", "encoding.rb", "expand_path.rb", "filename.rb", "new.rb",
# "open.rb", "open_flags.rb", "read.rb", "save_page.rb", "unlink.rb"]

O método Dir.[] permite listar todas as entradas que atenderem um determinado padrão. Este método é apenas um atalho para
Dir.glob e aceita os mesmos padrões. Para conhecer todos os padrões aceitos, acesse a documentação com o comando ri Dir.glob.

# Arquivos no diretório atual com a extensão .rb


Dir["*.rb"]

# Arquivos no diretório atual que começam com a letra e.


Dir["e*"]

# Arquivos com nomes "encoding" e/ou "open" que tenham a extensão .rb
Dir["{encoding,open}.rb"]

# Qualquer arquivo, de qualquer diretório, à partir do diretório atual.


Dir["./**/{.*,*.*}"]

# Qualquer arquivo, de qualquer diretório, à partir do diretório atual e que


# tenha a extensão .rb
Dir["./**/*.rb"]

138
# Arquivos que não tenham extensão começadas com a letra r.
Dir["*.[^r]*"]

Para criar um diretório, use o método Dir.mkdir. Se o diretório já existir, a exceção Errno::EEXIST: File exists será lançada.

Dir.mkdir("/tmp/sample")

Para remover um diretório, use o método Dir.delete, Dir.unlink ou Dir.rmdir. O diretório precisa estar vazio. Caso o diretório
não esteja vazio, a exceção Errno::ENOTEMPTY: Directory not empty será lançada.

Dir.delete("/tmp/sample")

Muitas dessas limitações que a classe Dir impõe podem ser removidas se você carregar a biblioteca padrão FileUtils. Ela possui uma
série de métodos que permitem criar e remover diretórios, dentre outros métodos muito úteis.

require "fileutils"

dir = "/tmp/a/b/c/d/e/f"

FileUtils.mkdir_p(dir)

File.open(File.join(dir, "sample.txt"), "w") do |file|


file << "sample text"
end

FileUtils.rm_rf(dir)

139
Testando arquivos e diretórios
Para veribcar se um caminho é um arquivo ou diretório, você pode usar os métodos File.file? e File.directory?.

file = __FILE__
dir = File.dirname(file)

File.file?(file) #=> true


File.directory?(file) #=> false

File.file?(dir) #=> false


File.directory?(dir) #=> true

File.file?("/invalid") #=> false


File.directory?("/invalid") #=> false

Você também pode veribcar se um caminho é um link simbólico.

path = "/usr/bin/ruby"

File.file?(path) #=> true


File.directory?(path) #=> false
File.symlink?(path) #=> true

Você também pode veribcar se arquivos e diretórios podem ser lidos ou escritos.

path = "/usr/bin/ruby"

File.executable?(path) #=> true


File.readable?(path) #=> true

140
File.writable?(path) #=> false
File.world_readable?(path) #=> 493
File.world_writable?(path) #=> nil

Datas de modificação e acesso de arquivos


Para saber quando um arquivo foi modibcado, use o método File.mtime.

File.mtime(__FILE__)
#=> 2011-12-31 12:32:11 -0200

Para saber quando um arquivo foi acessado, use o método File.atime.

File.atime(__FILE__)
#=> 2011-12-31 12:35:30 -0200

141
CAPÍTULO 1

Trabalhando com data e hora


Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

142
CAPÍTULO 1

URLs e requisições HTTP


Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

143
CAPÍTULO 1

Lidando com exceções


Não importa quão bem testado seja seu código ou quão cuidadoso você seja, bugs sempre existirão. No Ruby, erros na execução do
código são sinalizados com exceções, que são instâncias da classe Exception.

Exceções podem ser lançadas com os métodos Kernel#raise e Kernel#fail. Estes métodos podem ser executados com três
argumentos opcionais, mas a assinatura completa do método é raise(exception_class, message, backtrace).

raise ArgumentError
raise ArgumentError, "you've failed"
raise ArgumentError, "you've failed", caller

Quando o método Kernel#raise é chamado sem nenhum argumento, uma exceção do tipo RuntimeError é lançada. As duas
exceções à seguir são equivalentes.

raise
raise RuntimeError

Alternativamente, você pode passar uma string para o método Kernel#raise, que é equivalente a lançar uma exceção com
raise(RuntimeError, message).

raise "You've failed"


raise RuntimeError, "You've failed"

Quando uma exceção é lançada, a classe que foi utilizada para debnir a exceção será instanciada. O processo completo pode ser
descrito em quatro etapas:

144
1. Caso uma classe tenha sido passada como argumento, ela é instanciada com o método Exception.exception. Se uma instância
da classe Exception for fornecida, então a exceção é obtida com o método Exception#exception.
2. O backtrace da exceção é debnido.
3. A variável global @$! é debnida com a última exceção “ativa”.
4. A exceção é lançada na pilha de execução.

Capturando exceções
O Ruby permite capturar exceções com a cláusula rescue.

begin
raise "OH NOES!"
rescue
puts "An exception has been raised"
end

# An exception has been raised

Você pode atribuir o objeto de erro a uma variável, ou pode utilizar a variável global $!.

begin
raise "OH NOES!"
rescue => error
puts "error: #{error.message}"
puts "$!: #{$!.message}"
end

Por padrão, apenas as classes que herdam de StandardError serão capturadas. Para capturar outras classes, você pode passar uma
lista de argumentos com as classes de exceção.

145
begin
raise "OH NOES!"
rescue StandardError => error
puts "error: #{error.message}"
end

begin
raise LoadError
rescue Exception, LoadError => error
puts "error: #{error.message}"
end

Muitas exceções nativas do Ruby são lançadas como Exception, em vez de StandardError. É o caso das classes LoadError e
SyntaxError, lançadas quando um arquivo não consegue ser carregado com o método Kernel#require e quando o Ruby encontra
uma sintaxe inválida, respectivamente. Embora seja possível capturar a classe base Exception, isso não é uma boa ideia. Seja
especíbco quanto às eexxceções que vvocê
ocê quer captur
capturar
ar..

Você pode ter diversas cláusulas rescue que tratam exceções diferentes de modos diferentes. Você pode, inclusive, adicionar várias
classes de exceções em uma única cláusula enquanto debne diferentes pontos de captura da exceção.

exceptions = [LoadError, RuntimeError, ScriptError, SyntaxError]

exceptions.each do |exception_class|
begin
raise exception_class, "just playing with exceptions!"
rescue LoadError => error
puts "#{error.class} => OH NOES! I couldn't load the specified file"
rescue RuntimeError => error
puts "#{error.class} => Dammit! Something went wrong"
rescue ScriptError, SyntaxError => error
puts "#{error.class} => Y U MAD? JUST GIMME SOME VALID RUBY!"

146
end
end

# LoadError => OH NOES! I couldn't load the specified file


# RuntimeError => Dammit! Something went wrong
# ScriptError => Y U MAD? JUST GIMME SOME VALID RUBY!
# SyntaxError => Y U MAD? JUST GIMME SOME VALID RUBY!

O Ruby também possui uma cláusula chamada ensure, que será sempre executada independente de uma exceção ter sido lançada. Isso
permite efetuar operações que, em outras situações, poderia deixar a sua aplicação em um estado inconsistente.

begin
puts "Starting execution"
raise
puts "Won't be displayed"
rescue
puts "OH NOES! An exception has occurred"
ensure
puts "I'll be always executed"
end

# Starting execution
# OH NOES! An exception has occurred
# I'll be always executed

Uma boa prática é fazer com que a cláusula ensure execute apenas operações simples e seguras, diminuindo a probabilidade de uma
segunda exceção ser lançada, o que pode fazer com que as operações de limpeza não sejam concluídas, além de tornar a depuração do
erro mais complexa.

Em algumas situações, pode fazer sentido executar novamente um determinado bloco begin..end quando uma exceção é lançada. É aí
que entra a cláusula retry. O exemplo abaixo mostra como tentar executar um determinado bloco três vezes antes de “desistir”.

147
tries = 0

begin
tries += 1
puts "Trying ##{tries}"
raise "OH NOES!"
rescue
retry if tries < 3
puts "Sorry! I couldn't make it work!"
end

# Trying #1
# Trying #2
# Trying #3
# Sorry! I couldn't make it work!

Dentro da cláusula rescue pode existir qualquer tipo de código. Você pode, inclusive, lançar uma nova exceção ou, se preferir, a mesma
exceção que foi capturada inicialmente. Isso permite criar, por exemplo, mecanismos que irão rastrear uma exceção lançada.

O exemplo à seguir mostra como enviar para um arquivo de log qualquer tipo de exceção, antes de lançá-la novamente.

require "logger"

LOGGER = Logger.new(STDOUT)

def bogus_method
raise "OMG! Something went wrong!"
end

begin
bogus_method
rescue Exception => error

148
LOGGER.error "#{error.class} => #{error.message}"
LOGGER.error error.backtrace.join("\n")

raise error
end

# E, [2012-01-05T14:23:04.338482 #17431] ERROR -- : RuntimeError => OMG! Something went wrong!


# E, [2012-01-05T14:23:04.339039 #17431] ERROR -- : /Users/fnando/exceptions/log.rb:6:in `bogus_method'
# /Users/fnando/Sites/howto-books/ruby/code/exceptions/log.rb:10:in `<main>'

Lembre-se que as exceções utilizam os métodos Exception#exception e Exception.exception para debnir se uma nova exceção
deve ser instânciada ou se a própria instância é que deve ser utilizada.

Embora exceções possam ser utilizadas para controlar cuxos de aplicações, este não é o mecanismo mais recomendado para a tarefa.
Neste caso, você deve usar os métodos Kernel#throw e Kernel#catch, criados para interromper rapidamente a execução de loops
aninhados e chamadas a métodos sem necessariamente lançar uma exceção.

O método Kernel#throw permite lançar um símbolo que pode ser capturado com o método Kernel#catch. Se você executar o
método Kernel#throw com um argumento que será usado como valor de retorno do método Kernel#catch.

O exemplo abaixo mostra como descobrir quantas vezes um loop teve que ser executado até que o número 42 fosse encontrado.

count = catch :done do


tries = 0

loop do
tries += 1
throw(:done, tries) if rand(100) == 42
end
end

149
puts "We tried #{count} times until we get 42"
#=> We tried 82 times until we get 42

Um outro uso seria para interromper loops aninhados. Veja, por exemplo, como para a execução dos loops quando a letra n e o número
42 forem encontrados e, ainda assim, ter acesso aos valores encontrados.

number, letter = catch :done do


("a".."z").each do |l|
(1..100).each do |n|
throw(:done, [n, l]) if l == "n" && n == 42
end
end
end

puts "number: #{number}"


puts "letter: #{letter}"

# number: 42
# letter: n

150
CAPÍTULO 1

Conhecendo o Rake
Hmmm….
Este conteúdo está sendo escrito e estará disponível em breve.

151
CAPÍTULO 1

Escrevendo testes com Test::Unit


Unit testing é uma técnica de testes que foca em pequenos trechos de códigos, normalmente cada um dos métodos públicos que seu
código implementa.

Testes ajudam os desenvolvedores a escrever códigos melhores. Quando seu código ainda não foi escrito, testes ajudam a pensar em
um design de código mais desacoplado. Enquanto seu código está sendo escrito, testes ajudam a veribcar que suas alterações não
quebraram outras partes do código. Depois que seu código foi escrito, testes ajudam no processo de refatoração, dando feedback
instântaneo caso alguma coisa deixe de funcionar, além de ajudar novos desenvolvedores que tenham que manter uma base de código.

O Ruby vem com seu próprio framework de testes chamado Test::Unit. No Ruby 1.9 ele foi reescrito em cima de um outro
framework mais simples, que também já vem como uma standard library, chamado MiniTest::Unit. Resumindo, no Ruby 1.9, o
Test::Unit é apenas uma camada de compatibilidade em cima do MiniTest::Unit.

O MiniTest::Unit vem ainda com um outro idioma chamado MiniTest::Spec, que tenta ser algo próximo do RSpec, um framework
de testes mantido por David Chelimsky.

Independente do framework de testes que você venha a utilizar, todos eles seguem mais ou menos o mesmo padrão. Você irá
especibcar um resultado esperado e irá compará-lo com o resultado gerado por seu código. Caso esta comparação falhe, o framework
de testes irá exibir uma mensagem dizendo qual trecho de código falhou. Não é nem preciso dizer que este tipo de abordagem é
inbnitamente melhor que testar todo um cuxo de ações manualmente cada vez que uma alteração for realizada.

Para mostrar como usar o framework de testes Test::Unit, nós iremos implementar um conversor de temperaturas. Antes de
começar, vamos organizar o nosso código de modo que ele possa ser facilmente distribuído depois como uma gem.

152
Organizando o código
Bibliotecas do Ruby seguem mais ou menos o mesmo padrão. O código-fonte normalmente bca em um diretório lib. Caso sua
biblioteca seja composta por mais de um arquivo, um diretório com o mesmo nome da biblioteca deve ser criado no diretório lib.
Arquivos de teste devem ser criados no diretório test. Organize seu diretório de modo que ele seja parecido com a imagem à seguir.

Figur
Figuraa 1: Estrutura inicial da biblioteca Temperature

Para executar os testes nós iremos utilizar uma rake task. Crie o arquivo Rakefile na raíz de seu projeto com o seguinte conteúdo.

require "rake/testtask"

Rake::TestTask.new do |t|
t.libs << "test"
t.test_files = FileList["test/*_test.rb"]
t.verbose = true
end

153
Agora, você poderá executar os seus testes com o comando rake test. Para visualizar todas as tarefas disponíveis, execute o
comando rake -T. Se tudo estiver certo, você verá algo como isto:

$ rake -T
rake test # Run tests

Convertendo temperaturas
Nossa classe Temperature deverá receber dois argumentos: o primeiro identibca a temperatura e o segundo a unidade de
temperatura utilizada. Nossa API será algo como Temperature.new(32, :celsius). Vamos escrever um primeiro teste que irá
garantir que estes atributos estão sendo debnidos corretamente.

Crie o arquivo test/temperature_test.rb. Esse arquivo será responsável por carregar o código de nossa biblioteca, além de debnir
um caso de teste
teste, que é apenas uma classe que herda da classe Test::Unit::TestCase. Todos os testes são métodos que começam
com as letras test, mas o padrão é utilizar test_.

Download temperature/test/temperature_test.rb

require "temperature"
require "test/unit"

class TemperatureTest < Test::Unit::TestCase


def test_assign_attributes
temp = Temperature.new(32, :celsius)

assert_equal 32, temp.number


assert_equal :celsius, temp.unit
end
end

154
Todas as veribcações que seu teste fará são chamadas de asserções
asserções. No Test::Unit, você irá usar os métodos assert_* para garantir
que seu código está de acordo com uma determinada condição.

Execute os testes com o comando rake test. Você verá que este teste irá falhar com um erro. Isso acontece porque ainda não
debnimos nossa classe Temperature.

$ rake test

# Running tests:

Finished tests in 0.000755s, 1324.5033 tests/s, 0.0000 assertions/s.

1) Error:
test_assign_attributes(TemperatureTest):
NameError: uninitialized constant TemperatureTest::Temperature
/Users/fnando/temperature/test/temperature_test.rb:7:in `test_assign_attributes'

1 tests, 0 assertions, 0 failures, 1 errors, 0 skips


rake aborted!

Abra o arquivo temperature/lib/temperature.rb e crie a classe Temperature.

Download temperature/lib/temperature.rb

class Temperature
end

Como a ideia é evoluir o seu código em pequenos passos, você deve executar os testes a cada alteração. Faça isso mais uma vez com o
comando rake test. Você que nosso teste continua falhando com um erro, embora o erro seja diferente.

155
1) Error:
test_assign_attributes(TemperatureTest):
ArgumentError: wrong number of arguments(2 for 0)
/Users/fnando/temperature/test/temperature_test.rb:6:in `initialize'
/Users/fnando/temperature/test/temperature_test.rb:6:in `new'
/Users/fnando/temperature/test/temperature_test.rb:6:in `test_assign_attributes'

1 tests, 0 assertions, 0 failures, 1 errors, 0 skips

Desta vez, o erro que temos é: ArgumentError: wrong number of arguments(2 for 0). Isso signibca que nossa classe não espera
receber nenhum argumento, mas estamos passando dois. Crie o método Temperature#initialize de forma que ele receba estes
dois parâmetros.

Download temperature/lib/temperature.rb

class Temperature
def initialize(number, unit)
end
end

Execute os testes mais uma vez. Agora, o teste irá falhar novamente com um erro. O motivo agora é que não debnimos os atributos
number e unit.

1) Error:
test_assign_attributes(TemperatureTest):
NoMethodError: undefined method `number' for #<Temperature:0x007fa1b40d6dc8>
/Users/fnando/temperature/test/temperature_test.rb:8:in `test_assign_attributes'

1 tests, 0 assertions, 0 failures, 1 errors, 0 skips

156
Debna os atributos utilizando o método Module.attr_accessor.

Download temperature/lib/temperature.rb

class Temperature
attr_accessor :number, :unit

def initialize(number, unit)


end
end

Execute os testes mais uma vez. Deste vez, o teste irá falhar, mas sem erros.

1) Failure:
test_assign_attributes(TemperatureTest) [/Users/fnando/temperature/test/temperature_test.rb:8]:
<32> expected but was
<nil>.

1 tests, 1 assertions, 1 failures, 0 errors, 0 skips

Agora, podemos ver que o motivo da falha é exatamente o ponto que queremos testar. O atributo number não está sendo debnido. Para
fazer isso, basta fazer a atribuição dos valores recebidos como argumentos para as variáveis de instância de mesmo nome.

Download temperature/lib/temperature.rb

class Temperature
attr_accessor :number, :unit

def initialize(number, unit)


@number = number
@unit = unit

157
end
end

Execute os testes mais uma vez e você verá que agora eles irão passar!

Você deve ter percebido que seguimos um cuxo bem debnido. Primeiro, escrevemos um teste que falhou. Depois, fomos corrigindo
cada uma das falhas individualmente, sempre depois de rodar os testes, até que o próprio teste passasse. Esta técnica é chamada de
Test-Driven Development.

Nossa classe Temperature terá três métodos diferentes que irão permitir a conversão entre as diferentes unidades. Estes métodos
serão chamados de Temperature#to_fahrenheit, Temperature#to_celsius e Temperature#to_kelvin.

Primeiro, vamos escrever um teste que garanta que estamos convertendo uma temperatura em Celsius para Fahrenheit. Neste teste,
iremos converter 40ºC, que são equivalentes a 104ºF. Adicione o teste à seguir ao arquivo temperature/test/
temperature_test.rb.

Download temperature/test/temperature_test.rb

def test_convert_celsius_to_fahrenheit
temp = Temperature.new(40, :celsius)
assert_equal 104, temp.to_fahrenheit
end

Execute os testes. Eles devem falhar dizendo que o método Temperature#to_fahrenheit não existe. Adicione este método.

Download temperature/lib/temperature.rb

class Temperature
attr_accessor :number, :unit

def initialize(number, unit)


@number = number

158
@unit = unit
end

def to_fahrenheit
end
end

Execute os testes mais uma vez. Ele irá falhar dizendo que o valor esperado era diferente de nil. A fórmula que faz a conversão de
Celsius para Fahrenheit é F = C * (9 / 5) + 32, onde F signibca Fahrenheit e C signibca Celsius.

Download temperature/lib/temperature.rb

class Temperature
attr_accessor :number, :unit

def initialize(number, unit)


@number = number
@unit = unit
end

def to_fahrenheit
number * (9 / 5.0) + 32
end
end

Perceba que estamos utilizando 5.0 para garantir que a divisão não seja feita entre dois números inteiros.

Execute os testes mais uma vez. Veja que a conversão está sendo feita de forma correta.

Um problema de nossa implementação é que estamos assumindo que a temperatura inicial será sempre em Celsius. Mas o que
acontece se passarmos uma unidade diferente, que exige a aplicação de uma outra fórmula? Obviamente, o cálculo será feito de forma
incorreta.

159
Para evitar que nossos métodos tenham diversas expressões condicionais que irão fazer o cálculo de acordo com a unidade atual, nós
iremos sempre utilizar o método Temperature#to_celsius como base para os demais cálculos. Dessa forma, teremos que converter
apenas de Celsius para Fahrenheit e de Celsius para Kelvin.

Altere a classe Temperature de modo que o método que o método Temperature#to_fahrenheit utilize o retorno do método
Temperature#to_celsius, em vez de Temperature#number.

Download temperature/lib/temperature.rb

class Temperature
attr_accessor :number, :unit

def initialize(number, unit)


@number = number
@unit = unit
end

def to_fahrenheit
to_celsius * (9 / 5.0) + 32
end
end

Ao executar os testes, eles irão falhar dizendo que o método Temperature#to_celsius ainda não existe. Vamos adicionar alguns
testes para garantir que este método converta os valores corretamente. Primeiro, vamos escrever um teste para a conversão de
Celsius para Celsius.

Download temperature/test/temperature_test.rb

def test_convert_celsius_to_celsius
temp = Temperature.new(40, :celsius)
assert_equal 40, temp.to_celsius
end

160
Execute os testes, que continuarão falhando. Altere o método Temperature#to_celsius, adicionando um case.

Download temperature/lib/temperature.rb

class Temperature
attr_accessor :number, :unit

def initialize(number, unit)


@number = number
@unit = unit
end

def to_fahrenheit
to_celsius * (9 / 5.0) + 32
end

def to_celsius
case unit
when :celsius then number
end
end
end

Execute os testes. Agora, ambos os testes irão passar.

Adicione mais um teste para garantir que a conversão de Fahrenheit para Celsius está sendo feita de forma correta.

Download temperature/test/temperature_test.rb

def test_convert_fahrenhet_to_celsius
temp = Temperature.new(104, :fahrenheit)

161
assert_equal 40, temp.to_celsius
end

Execute os testes. Esse teste que acabamos de adicionar deve falhar.

1) Failure:
test_convert_fahrenhet_to_celsius(TemperatureTest) [/Users/fnando/temperature/test/temperature_test.rb:24]:
<40> expected but was
<nil>.

4 tests, 5 assertions, 1 failures, 0 errors, 0 skips

Adicione mais esta conversão ao método Temperature#to_celsius. A fórmula que faz a conversão de Fahrenheit para Celsius é C =
(F - 32) * (5 / 9).

Download temperature/lib/temperature.rb

class Temperature
attr_accessor :number, :unit

def initialize(number, unit)


@number = number
@unit = unit
end

def to_fahrenheit
to_celsius * (9 / 5.0) + 32
end

def to_celsius
case unit

162
when :celsius
number
when :fahrenheit
(number - 32) * (5 / 9.0)
end
end
end

Execute os testes, que devem passar novamente.

A última conversão que precisa ser feita é de Kelvin para Celsius. Esta fórmula é a mais simples de todas: C = K - 273.15, onde C
signibca Celsius e K signibca Kelvin. Primeiro adicione o teste.

Download temperature/test/temperature_test.rb

def test_convert_kelvin_to_celsius
temp = Temperature.new(323.15, :kelvin)
assert_equal 50, temp.to_celsius
end

Execute os testes, que devem falhar dizendo que o valor esperado é diferente de nil. Agora, podemos implementar a conversão de
Kelvin para Celsius.

Download temperature/lib/temperature.rb

class Temperature
attr_accessor :number, :unit

def initialize(number, unit)


@number = number
@unit = unit
end

163
def to_fahrenheit
to_celsius * (9 / 5.0) + 32
end

def to_celsius
case unit
when :celsius
number
when :fahrenheit
(number - 32) * (5 / 9.0)
when :kelvin
number - 273.15
end
end
end

Execute os testes. Eles devem passar mais uma vez!

Muito bem! A conversão de temperaturas em Fahrenheit e Kelvin em Celsius já podem ser realizadas, embora o contrário ainda não
seja verdade. Ainda falta implementarmos a conversão para Kelvin. O próximo teste irá garantir que uma temperatura em Celsius seja
convetida em Kelvin.

def test_convert_celsius_to_kelvin
temp = Temperature.new(50, :celsius)
assert_equal 323.15, temp.to_kelvin
end

Ao executar os testes, você verá que ele irá falhar dizendo que o método Temperature#to_kelvin não foi debnido. Você pode, então,
implementar este método. A fórmula de conversão de Celsius para Kelvin é K = C + 273.15.

164
Download temperature/lib/temperature.rb

class Temperature
attr_accessor :number, :unit

def initialize(number, unit)


@number = number
@unit = unit
end

def to_fahrenheit
to_celsius * (9 / 5.0) + 32
end

def to_celsius
case unit
when :celsius
number
when :fahrenheit
(number - 32) * (5 / 9.0)
when :kelvin
number - 273.15
end
end

def to_kelvin
to_celsius + 273.15
end
end

Parabéns! Você acabou de escrever uma biblioteca que converte temperaturas entre diferentes unidades com testes. Para deixar o
exemplo completo, sua classe de testes deve se parecer com isto:

165
Download temperature/test/temperature_test.rb

require "temperature"
require "test/unit"

class TemperatureTest < Test::Unit::TestCase


def test_assign_attributes
temp = Temperature.new(32, :celsius)

assert_equal 32, temp.number


assert_equal :celsius, temp.unit
end

def test_convert_celsius_to_kelvin
temp = Temperature.new(50, :celsius)
assert_equal 323.15, temp.to_kelvin
end

def test_convert_celsius_to_fahrenheit
temp = Temperature.new(40, :celsius)
assert_equal 104, temp.to_fahrenheit
end

def test_convert_celsius_to_celsius
temp = Temperature.new(40, :celsius)
assert_equal 40, temp.to_celsius
end

def test_convert_kelvin_to_celsius
temp = Temperature.new(323.15, :kelvin)
assert_equal 50, temp.to_celsius
end

166
def test_convert_fahrenhet_to_celsius
temp = Temperature.new(104, :fahrenheit)
assert_equal 40, temp.to_celsius
end
end

Lista de métodos de asserção


Embora tenhamos escritos alguns testes, todos eles usaram o mesmo método de asserção assert_equal. O Test::Unit implementa
muito mais métodos como você pode conferir abaixo.

assert(boolean) Falha se boolean for nil ou false

assert_block(&block) Falha se o bloco block for nil ou false

assert_empty(list) Falha se o método list#empty? retornar false

assert_equal(expected, actual) Passa se expected for igual a actual quando o operador == é usado

assert_in_delta(expected, actual, delta) Passa se o número de ponto cutuante expected for igual a actual dentro do delta
esperado

assert_in_epsilon(expected, actual, epsilon) Calcula o delta com epsilon * min(expected, actual) e então executa
assert_in_delta com este valor

assert_includes(collection, element) Veribca se collection inclui o objeto element com o método collection#include?

assert_instance_of(klass, object) Veribca se object é uma instância da classe klass

assert_kind_of(klass, object) Veribca se object é do tipo da classe klass com o método


object.kind_of?(klass)

assert_match(regexp, string) Veribca se string casa a expressão regular regexp

assert_nil(object) Veribca se object é nil

167
assert_operator(object1, operator, object2) Veribca se o envio da mensagem operator para object1 com o parâmetro object2
retorna true

assert_raises(Exception, ..., &block) Veribca se o bloco block lança uma das exceções listadas quando executado
assert_raise(Exception, ..., &block)

assert_respond_to(object, message) Veribca se object responde a message com o método object.respond_to?

assert_same(expected, actual) Veribca se expected é igual a actual com o método expected.equal?

assert_send(array) Veribca se o valor de retorno do envio da mensagem array[1] para o objeto


array[0], com o restante do array como parâmetros, é true

assert_throws(expected, &block) Veribca se a execução do bloco block lança o símbolo expected com o método throw

assert_not_equal(expected, actual) Veribca expected é diferente de actual quando o operador == é usado

assert_not_match(regexp, string) Veribca se a string string não casa a expressão regular regexp

assert_not_nil(object) Veribca se object não é nil

assert_not_same(expected, actual) Veribca se expected é diferente de actual com o método expected.equal?

assert_nothing_raised(Exception, ..., &block) Veribca se o bloco é executado sem lançar as exceções listadas

assert_nothing_thrown(expected, &block) Veribca se o bloco é executado sem lançar o símbolo expected com o método throw

flunk(message = "Epic Fail!") Sempre falha o teste com a mensagem message

skip(message = nil) Faz com que o teste não seja executado

pass Faz com que o teste sempre passe

168
CAPÍTULO 1

Criando e distribuindo gems


RubyGems é o gerenciador de pacotes padrão do Ruby. À partir do Ruby 1.9, começou a ser distribuído como parte da standard library.
Ele permite resolver dependências e resolve, inclusive, dependências entre versões.

Cada pacote, chamado de gem, pode conter arquivos Ruby que podem ser carregados pelo seu próprio código. Você pode instalar gems
disponibilizadas por outros desenvolvedores e pode criar e distribuir suas próprias gems com muita facilidade. Atualmente existem
mais 30 mil gems cadastradas no site http://rubygems.org/, o site que hospeda os pacotes disponibilizados pela comunidade Ruby.

A estrutura de uma gem


O arquivo mais importante de uma gem é, sem dúvida, o arquivo .gemspec. É nele que são debnidas as informações sobre como seu
pacote será distribuído. Este arquivo contém o nome e versão da sua gem, arquivos que podem ser carregados, informações sobre os
autores, dentre outras informações.

Os códigos que podem ser carregados bcam no diretório lib. Caso sua gem inclua arquivos executáveis, eles devem bcar no diretório
bin. Uma estrutura muito usada por convenção pode ser vista à seguir:

example
├── Rakefile
├── bin
│ └── example
├── example.gemspec
├── lib
│ ├── example
│ │ └── version.rb
│ └── example.rb

169
└── test
└── example_test.rb

Criando sua própria gem


Neste capítulo você verá como distribuir a biblioteca Temperature, criada no capítulo anterior. O primeiro passo é debnir o arquivo
.gemspec. Crie o arquivo simple_temperature.gemspec[1] na raíz de seu projeto.

Download temperature/simple_temperature.gemspec

require "./lib/temperature/version"

Gem::Specification.new do |s|
s.name = "simple_temperature"
s.version = Temperature::Version::STRING
s.description = "Convert temperature between different units."
s.summary = s.description
s.author = "Nando Vieira"
s.email = "fnando.vieira@gmail.com"
s.files = Dir["lib/**/*"]
s.test_files = Dir["test/**/*"]
s.homepage = "http://rubygems.org/gems/simple_temperature"
end

Nesta especibcação, estamos debnindo uma série de informações:

• o nome da gem, debnido como simple_temperature


• a versão da gem, que vem do arquivo lib/temperature/version.rb e que ainda não foi criado.

1
Como a gem Temperature já existe, vamos usar um outro nome qualquer. Você provavelmente vai querer manter o nome do arquivo
gemspec igual ao nome de sua gem.

170
• uma pequena descrição sobre o que nossa gem faz e que pode ser exibida com o comando gem list simple_temperature.
• os arquivos que compões a gem, que neste exemplo é tudo o que está dentro do diretório lib.
• os arquivos de teste, que neste exemplo é tudo o que está dentro do diretório test.

Crie o arquivo lib/temperature/version.rb.

Download temperature/lib/temperature/version.rb

class Temperature
module Version
MAJOR = 0
MINOR = 1
PATCH = 0
STRING = "#{MAJOR}.#{MINOR}.#{PATCH}"
end
end

Sua gem já está pronta para ser empacotada. À partir da raíz do seu projeto, execute o comando gem build
simple_temperature.gemspec. Este comando irá gerar o arquivo simple_temperature.gem, que é o seu pacote, propriamente dito.

$ gem build simple_temperature.gemspec


Successfully built RubyGem
Name: simple_temperature
Version: 0.1.0
File: simple_temperature-0.1.0.gem

Para instalar a sua gem localmente, use o comando gem install simple_temperature --local. Para ver mais detalhes sobre a
instalação, use o parâmetro --verbose.

171
$ gem install simple_temperature --local
Successfully installed simple_temperature-0.1.0
1 gem installed

Agora, vamos nos certibcar que está tudo funcionando. Você pode fazer isso através do IRB.

$ irb
>> require "temperature"
=> true
>> Temperature.new(60, :celsius).to_fahrenheit
=> 140.0
>>

Note que não estamos carregando a gem simple_temperature. Toda vez que você usa require, todos os arquivos das gems que estão
no diretório lib bcam disponíveis no $LOAD_PATH. Por isso podemos simplesmente carregar o arquivo temperature.rb. Em uma
situação normal, onde o nome do arquivo principal da gem recete o nome da própria gem, isso não seria motivo de confusão.

Distribuindo sua gem


Como você pode ver, uma vez que temos o arquivo .gem, podemos instalar nossa biblioteca. Se você passar este arquivo para qualquer
desenvolvedor Ruby, ele poderá instalá-la com o mesmo comando que você utilizou, embora isso não seja muito prático.

Uma outra maneira é distribuir sua gem publicamente através do site http://rubygems.org/. Para fazer isso, você precisará criar sua
conta. Acesse o endereço https://rubygems.org/users/new, informe seu e-mail, username e senha e você está pronto para continuar.

Execute o comando gem push simple_temperature-0.1.0.gem. Este comando irá solicitar suas credenciais. Informe as mesmas que
foram cadastradas no RubyGems.org. Você só precisará fazer isso desta vez; futuras publicações serão feitas automaticamente.

172
$ gem push simple_temperature-0.1.0.gem
Enter your RubyGems.org credentials.
Don't have an account yet? Create one at http://rubygems.org/sign_up
Email: fnando.vieira@gmail.com
Password:
Pushing gem to https://rubygems.org...
Signed in.
Pushing gem to https://rubygems.org...
Successfully registered gem: simple_temperature (0.1.0)

A publicação da gem pode demorar alguns minutos. Para veribcar se sua gem já está disponível execute o comando gem list
simple_temperature -rd.

$ gem list simple_temperature -rd

*** REMOTE GEMS ***

simple_temperature (0.1.0)
Author: Nando Vieira
Homepage: http://rubygems.org/gems/simple_temperature

Convert temperature between different units.

Sua gem também pode ser acessada através do endereço https://rubygems.org/gems/simple_temperature. Aproveite que você acessou
a página de sua gem e atualize as outras informações como endereço de onde as pessoas poderão reportar bugs e visualizar o código-
fonte.

173
Mais sobre RubyGems
Uma gem é basicamente o que você acabou de ver. No entanto, você pode ir além. É possível, por exemplo, criar extensões nativas
usando C.

Você também pode criar o seu próprio servidor de gems, privado, onde só suas aplicações podem acessar. Desta forma, você pode
distribuir bibliotecas entre os diversos aplicativos de sua empresa sem se preocupar com a privacidade e segurança.

Para mais informações sobre como fazer estas e outras coisas, visite o guia publicado pelo site RubyGems.org no endereço
http://guides.rubygems.org/.

174
Copyright © Hellobits & Nando Vieira. Todos os direitos reservados.
Veja outras publicações em http://howtocode.com.br.

Você também pode gostar