Você está na página 1de 180

Conhecendo Ruby

por Nando Vieira

Copyright Hellobits & Nando Vieira. Todos os direitos reservados. Nenhuma parte desta publicao pode ser reproduzida sem o consentimento dos autores. Todas as marcas registradas so de propriedade de seus respectivos donos. Conhecendo Ruby, Nando Vieira, 1a verso

Contedo
1 2 2 5 9 9 9
Introduo Sobre o Rub Ruby y Instalao Tipo Variveis e constantes Mtodos Entendendo o self

35 Hash 37 Symbol 38 Boolean 39 nil 40 Range 41 Expresses regulares 42 Procs e lambdas 47 Set 49
Estrutur Estruturas as condicionais

12 Convenes 19 Atribuio de variveis 19 Constantes e variveis globais 20 Conhecendo o IRB 22 Executando cdigos Ruby 23 Acessando a documentao do Ruby 26
Rub Ruby y Core Classes

49 if 50 else 51 elsif 52 unless 53 Operador ternrio ?: 54 case 59


Estrutur Estruturas as de repetio

26 String 30 Nmeros 33 Array

59 for..in

61 while e until 63 loop 64 Controlando o loop 66 Usando iteradores 71


Blocos

102 Tamanho de uma string 103 Substrings 105 Codibcao 110


Trabalhando com nmeros

110 Operadores matemticos 111 Nmeros absolutos 111 Veribcando nmeros pares e mpares 112 Veribcando zeros e no-zeros 112 Converso entre diferentes bases 113 Fazendo arredondamentos 115
Trabalhando com arr arra ays

72 Escopo de variveis 73 Interagindo com blocos 76


Classes

76 Criando classes 78 Debnindo mtodos 83 Debnindo mtodos de classe 89 Debnindo constantes 92 Entendendo classes Singleton 97 98 99
Mdulos Trabalhando com o load path Trabalhando com strings

115 Acessando os elementos 115 Adicionando novos elementos 117 Removendo elementos 119 Filtrando elementos 120 Ordenando elementos 122 Buscando elementos 123 Iterando elementos 124 Compondo novos arrays

99 Concatenando strings 100 Formatando strings 101 Convertendo em maisculas e minsculas

126

Trabalhando com hashes

152

Escre Escrev vendo testes com T Test::Unit est::Unit

126 Lista de chaves e valores 126 Veribcando a existncia de chaves e valores 127 Acessando o hash 128 Filtrando elementos 129 Removendo valores de um hash 130 Compondo novos hashes 131 Iterando hashes 133
Trabalhando com arquiv arquivos os e diretrios

153 Organizando o cdigo 154 Convertendo temperaturas 167 Lista de mtodos de assero 169
Criando e distribuindo gems

169 A estrutura de uma gem 170 Criando sua prpria gem 172 Distribuindo sua gem 174 Mais sobre RubyGems

133 Manipulando nomes de arquivos 134 Manipulando arquivos 138 Manipulando diretrios 140 Testando arquivos e diretrios 141 Datas de modibcao e acesso de arquivos 142 143 144
Trabalhando com data e hor hora a URLs e requisies HTTP Lidando com e ex xcees

145 Capturando excees 151


Conhecendo o Rak Rake e

CAPTULO 1

Introduo
Ruby uma linguagem de programao interpretada, com tipagam forte e dinmica, que tem como foco a simplicidade e produtividade. Sua sintaxe extremamente elegante, o que facilita a leitura e escrita de cdigos. Criada pelo japons Yukihiro Matz Matsumoto em meados de 1993, a linguagem s foi lanada publicamente em 1995. Matz combinou partes de suas linguagens favoritas (Perl, Smalltalk, Eiffel, Ada e Lisp) para criar uma linguagem que fosse, segundo suas prprias palavras, mais poderosa que Perl e mais orientada a objetos que Python. Muitas linguagens no tratam nmeros e outros tipos primitivos como objetos, mas no Ruby isso diferente. No Ruby, tudo objeto. Tipos primitivos possuem mtodos e podem ter atributos. Classes so objetos. O Ruby uma linguagem extremamente cexvel. Hmmm. Este contedo est sendo escrito e estar disponvel em breve. Nando Vieira, Janeiro de 2012

CAPTULO 1

Sobre o Ruby
Instalao
O Ruby pode ser instalado em todos os sistemas operacionais. Veja abaixo como instalar em sua mquina de desenvolvimento. sempre uma boa ideia utilizar a ltima verso estvel do Ruby, a menos que voc tenha razes muito fortes para no fazer isso. Enquanto este livro est sendo escrito, a ltima verso estvel 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 endereo http://rubyinstaller.org/ downloads/ e faa o download do instalador da ltima verso estvel.

Figur Figura a 1: Download do Ruby Installer

Execute o arquivo baixado, algo como rubyinstaller-1.9.3-p0.exe, e o instalar ser iniciado. Aceite os termos de uso. Ser exibida, ento, uma janela onde voc deve marcar a conbgurao Add Ruby executables to your PATH. Sem essa opo, o Ruby no bcar disponvel globalmente no seu terminal.

Figur Figura a 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 verso do Ruby. Em verses mais antigas do Windows, voc pode usar o comando cmd. Se preferir, voc tambm pode instalar o PowerShell manualmente.

Figur Figura a 3: Usando o terminal

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

Instalando no Mac OS X
Embora o Mac OS X j venha com o Ruby instalado por padro, a sua verso muito antiga. Hmmm. Este contedo est sendo escrito e estar disponvel em breve.

Instalando no Linux Ubuntu


No Linux ns tambm iremos utilizar o RVM para gerenciar as instalaes 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 contedo est sendo escrito e estar disponvel em breve.

Tipo
O Ruby uma linguagem dinamicamente tipada tipada, o que signibca dizer que voc no precisa debnir o tipo de dado que uma varivel ir armazenar. Alm disso, uma mesma varivel pode receber tipos diferentes a qualquer momento.
value = 1234 puts value.class #=> Fixnum value = "Hello" puts value.class #=> String

O Ruby tambm uma linguagem fortemente tipada tipada, j que o tipo do objeto veribcado antes de efetuar determinadas operaes.

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

Ao tentar somar o nmero 1234 com a string Hello, o Ruby lanar a exceo TypeError. Isso acontece porque a coero precisa ser feita explicitamente.

Duck Typing
No Ruby, ns no declaramos o tipo de objetos, nem o tipo do retorno de mtodos. Embora isso possa parecer algo muito ruim para quem est acostumado com linguagens como Java, linguagens dinamicamente tipadas como o Ruby so muito cexveis, produtivas e, acredite, seguras. Na maioria das vezes, o medo de no poder contar com o compilador para fazer veribcaes de tipos no tem fundamento. Desenvolvedores Ruby esto mais acostumados em debnir objetos pelo que eles podem fazer, do que por seu tipo. Esta tcnica chamada de duck typing. Se anda como um pato e faz barulho como um pato, ento de dev ve ser um pato. E o interpretador bcar feliz em fazer com que o objeto seja tratado como um pato. Na prtica, isso signibca que em vez de fazer veribcaes de tipo de um objeto, voc deve se preocupar se este objeto capaz de executar o mtodo que voc precisa. Pegue como exemplo strings, arquivos e arrays. As classes Array, File e String implementam o mtodo de instncia <<, que quase sempre signibca append. Voc pode se aproveitar desta interface para criar, por exemplo, uma classe de log que no se importa com o tipo de objeto que ir armazenar esses logs.
class SimpleLogger def initialize(io) @io = io end

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 mtodo <<. O Ruby realmente abraa o duck typing por toda a linguagem. Diversos protocolos exigem que o objeto apenas implemente um mtodo to_<protocol>. Muitas operaes que envolvem arrays, por exemplo, exigem que o objeto do lado direito da expresso apenas implemente o mtodo 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 mtodo to_hash seja implementado.
class Configuration def to_hash {root: "/etc"} end end config = Configuration.new

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

No preciso dizer que este tipo de protocolo por conveno permite criar cdigos muito mais cexveis, com extrema facilidade. Isso no signibca que saber o tipo de objetos no seja til. Veja, por exemplo, as operaes matemticas. Qualquer objeto pode ser convertido em nmeros. Para isso, basta implementar o mtodo coerce, que recebe o objeto que est solicitando a coero. O exemplo abaixo mostra como criar uma classe cuja instncia pode ser convertida em nmeros inteiros e cutuantes, mas no em instncias 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"

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

O duck typing vai alm de simples regras; um estilo de programao. Antes de exigir tipos de objetos, pergunte-se se isso realmente necessrio. s vezes, o tipo do objeto muito importante, mas muitas vezes isso simplesmente no importa.

Variveis e constantes
Hmmm. Este contedo est sendo escrito e estar disponvel em breve.

Mtodos
Hmmm. Este contedo est sendo escrito e estar disponvel em breve.

Entendendo o self
self ser sempre uma referncia 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 prpria classe.
puts self #=> main

class Thing puts self end #=> Thing

Sempre que executar um mtodo, o Ruby ir veribcar se esse mtodo existe no receiver padroself a menos que voc especibqueo explicitamente. E, pelo fato de o receiver padro ser self, voc nem precisa especibc-lo se no quiser.
class Thing def do_something puts "doing something" end def do_something_else do_something end end

No mtodo do_something_else poderamos usar self.do_something, mas isso faria com que nosso cdigo apenas bcasse mais poludo. No entanto, debnir o receiver uma coisa muito comum e que, voc pode at no se dar conta, mas o faz constantemente quando escreve cdigo Ruby.
numbers = [3,1,2] numbers.sort #=> [1,2,3] text = "some string" text.upcase #=> "SOME STRING"

10

Na prtica, o receiver especibcado antes do ponto na chamada de mtodos, como em numbers.sort ou text.upcase. Alm de ser o receiver padro, self tambm o responsvel por armazenar variveis de instncia 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 instncia da classe Person possui uma nica varivel de instncia associada ao seu objeto, self, que retornada pelo mtodo Person#name. Analogamente, podemos debnir variveis de instncia 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 variveis de instncia podem ser usadas em contextos diferentes. Primeiro, estamos debnindo um contador de instncias da classe Person, cujo valor ser armazenado em @count. Depois, em nossa prpria instncia, debnimos o nome com a varivel @name.

Convenes
Os desenvolvedores Ruby seguem uma srie de convenes enquanto esto escrevendo seus cdigos. Embora voc no seja obrigado seguir essas convenes, sempre uma boa ideia garantir que est escrevendo cdigos do mesmo jeito que um desenvolvedor mais experiente. Conbra neste captulo quais so as convenes mais utilizadas e evite olhares estranhos.

Nomeando classes, variveis e constantes


Use o formato snake_case para debnir variveis.

12

# recomendado success_message = "You're done!" # estranho successMessage = "You're wrong!"

Classes e mdulos so nomeados em camel case. Rails ActiveSupport Net Se sua classe ou mdulo for um acrnimo, mantenha todas as letras em maisculas. HTTP HTTP::POST XML Outras constantes devem usar o formato SNAKE_CASE.
# recomendado SUCCESS_MESSAGE = "You're done!" # estranho SuccessMessage = "You're wrong!"

Mtodos predicados (aqueles que retornam valores booleanos) devem terminar com ? e no precisam de um prebxo is.
# recomendado def ready? status == "ready"
13

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

Mtodos que modibcam self, lanam excees ou so potencialmente perigosos/destrutivos devem terminar com uma exclamao.
# recomendado def save! save or raise("OMG!") end

Indentao, espaamento e quebras de linha


No Ruby, trechos de cdigo so indentados em 2 espaos espaos.
# recomendado: 2 espaos def hello puts "Hello!" end # estranho: 4 espaos def hello puts "Hello!" end # mais estranho: hard tabs def hello

14

puts "Hello!" end

Adicione espaamento em torno de operadores e depois de vrgulas.


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

No coloque espaamentos 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 espaamentos ao bnal da linha (trailing spaces).

15

Definindo e executando mtodos


Quando o mtodo receber argumentos, sempre coloque os parnteses 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 mtodo no recebe nenhum argumento, no adicione os parnteses.


# recomendado def message "Hello" end # estranho def message() "Hello" end

16

A mesma regra deve ser aplicada quando voc estiver executando mtodos.
# recomendado user.run # estranho user.run()

Esta regra possui uma exceo. Quando um mtodo debnido com o mesmo nome de uma constante, voc deve usar os parnteses. Caso contrrio, voc estar acessando a prpria constante, que normalmente ser um mdulo ou classe.
class Foo; end def Foo puts "You called the Foo method" end Foo.new Foo() #=> Instancia a classe Foo #=> Executa o mtodo Foo()

Retorno de mtodos e blocos


Mtodos 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 execuo 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
Mtodos podem receber blocos[2]. Quando o seu bloco possuir mais de uma instruo 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 instruo, voc deve utilizar as palavras-chave do..end.
File.open("file.txt", "w+") do |file| file.write "Ruby"

No se preocupe com o que so blocos por enquanto. Voc ver mais sobre este assunto mais frente. 18

file.write " #nice" end

Escrevendo sobre o Ruby


Uma conveno do Ruby que usada em textos que mtodos estticos (aqueles que podem ser acessados diretamente em uma classe ou mdulo) so referenciados como Modulo.metodo ou Modulo::metodo. Logo, se voc quiser escrever alguma documentao ou texto que cita o mtodo enable do mdulo GC, voc deve escrever GC.enable ou GC::enable. J mtodos de instncia, como "Hello".upcase devem ser referenciados como String#upcase. Esta conveno tambm utilizada para acessar a documentao atravs da linha de comando, como voc ver mais frente.

Atribuio de variveis
Hmmm. Este contedo est sendo escrito e estar disponvel em breve.

Constantes e variveis globais


Hmmm. Este contedo est sendo escrito e estar disponvel 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 execuo 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 cdigo Ruby. Experimente digitar alguma expresso matemtica simples.
irb(main):001:0> 1 + 1 => 2

No Ruby, tudo[3] objeto. Voc pode descobrir qual a classe de um objeto com o mtodo Object#class.
irb(main):002:0> 1234.class => Fixnum irb(main):003:0> "Hello".class => String

Mtodos so aes que um objeto pode realizar. No exemplo acima, o mtodo Object#class apenas informa qual a classe que instanciou um determinado objeto. Voc tambm pode acessar a lista de todos os mtodos que um objeto possui com o mtodo Object#methods. Veja, por exemplo, quais so os mtodos de uma string:
1 3

Read-Eval-Print-Loop

Na verdade, quase tudo no Ruby objeto. Algumas coisas no so 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 apresentao, de acordo com o nvel de indentao do cdigo.
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 cdigos Ruby interativamente. Escreva outros tipos de expresses para se familiarizar com esta excelente ferramenta.

Executando cdigos Ruby


A maneira mais comum de escrever Ruby colocando cdigos em um ou mais arquivos. Normalmente, estes arquivos possuem a extenso .rb, embora voc possa usar qualquer extenso (ou nenhuma extenso). Crie o arquivo hello.rb com o seguinte cdigo:
puts "Hello!" puts "Current time: #{Time.now}"

Para executar este cdigo, 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, possvel 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 executvel com o comando chmod +x hello.rb. Ao fazer isso, voc no mais precisar executar o interpretador Ruby.

22

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

Acessando a documentao do Ruby


Atualmente existem bibliotecas Ruby para tudo (ou quase tudo) o que voc possa imaginar. A maioria delas documentada com RDoc, que so apenas comentrios Ruby que podem ser extrados em HTML e no formato ri. Esta ferramenta ri permite visualizar a documentao de mtodos, constantes classes e mdulos da Standard Library (que j vem com o Ruby) e de cdigos de desenvolvedores 3rd party. Para visualizar a documentao do mdulo 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 tambm pode visualizar a documentao de um mtodo especbco. Veja, por exemplo, a documentao do mtodo 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 tambm pode visualizar a documentao de mtodos estticos 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 GC.enable GC.enable #=> false #=> true #=> false

25

CAPTULO 1

Ruby Core Classes


String
Strings so sequncias de caracteres normalmente delimitadas por aspas ou apstrofos.
hello = "Hello" ruby = 'Ruby'

A diferena entre os dois delimitadores que os apstrofos 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 diferena que strings delimitadas por apstrofos no podem ser interpoladas. Interpolao o processo de debnir uma expresso Ruby dentro de uma string, de modo que seu resultado substitua os delimitadores #{} que englobam a expresso.
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 mltiplas 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 variaes.
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 maisculas) que ser usado para iniciar e terminar a string. A diferena entre as duas variaes que a primeira forma exige que o identibcador de trmino esteja no comeo da linha linha. Voc pode executar mtodos 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 caracterstica de strings heredoc que elas preservam espaos em branco no comeo 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 prtica, 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

Nmeros
O Ruby possui 8 classes para representar nmeros. Todos os objetos que representam nmeros no Ruby so instncias da classe Numeric. Nmeros so imutveis e, por este motivo, no existem mtodos que podem mudar o valor de um nmero; se voc tentar fazer isso, receber a mensagem de erro Can't change the value of self. Em verses mais antigas do Ruby, as classes Complex e Rational no so nativas do Ruby, embora sejam distribudas como parte da Standard Library[1].

Fixnum
Nmeros inteiros no possuem um tamanho determinado, pois o seu tamanho debnido pela quantidade de memria disponvel. Quando debnidos nos intervalos entre 230 e 230-1 ou 262 e 262-1, inteiros so debnidos como instncias da classe Fixnum. Inteiros fora deste intervalo so automaticamente debnidos como objetos da classe Bignum, em um processo totalmente transparente e automtico.
number = 1000 3.times do number *= number puts "#{number.class} => #{number}"
1

A Standard Library o conjunto de bibliotecas que vem com a instalao do Ruby. 30

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

Voc pode debnir nmeros inteiros usando um sinal de + ou - opcional para debnir valores positivos e negativos, um indicador opcional da base do nmero, seguidos pela sequncia de nmeros especibcados na base escolhida.
1234 0d1234 1_234 -1234 0x4d2 02322 0b10011010010 #=> #=> #=> #=> #=> #=> #=> 1234 1234 1234 -1234 1234 1234 1234

Base decimal, padro Underscores so ignorados Negativo Hexadecimal Octal Binrio

Float
Nmeros de ponto cutuante so debnidos pelo . (ponto decimal) aps um ou mais nmeros decimais, seguido por mais nmeros decimais. Voc tambm pode, opcionalmente, utilizar um expoente. Ao contrrio dos nmeros inteiros, nmeros com ponto cutuante no podem ser debnidos em outra base diferente de 10.
puts puts puts puts puts puts 1.234 -1.234 1_234.0 12e2 12.3e2 12.3E2 #=> #=> #=> #=> #=> #=> 1.234 -1.234 1234.0 1200.0 1230.0 1230.0

Negativo Underscores so ignorados 12.0 x 10e2 12.3 x 10e2 12.3 x 10e2

31

No Ruby, no possvel debnir nmeros com ponto cutuante sem ter um nmero antes do ponto decimal. Se voc tentar debnir um nmero como .1 ir receber uma mensagem de erro como no .<digit> floating literal anymore; put 0 before dot. Nmeros de ponto cutuante seguem a especibcao IEEE-754, assim como a maioria das linguagens e hardwares do mercado. Dada a forma como os nmeros de ponto cutuante so tratados, fraes como 1/10 e 1/100 no podem ser representadas corretamente. muito comum clculos 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 clculos deste tipo quando objetos da classe BigNumber so utilizados.

BigDecimal
A classe BigDecimal permite realizar clculos onde o arredondamento muito importante, como em clculos bnanceiros. Nmeros do tipo BigDecimal so praticamente ilimitados (expoentes acima de 1 bilho so 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 dgitos decimais que sero computados. Para ver a referncia completa, acesse a documentao.

Complex
Para ver a referncia completa, acesse a documentao.

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

Rational
Para ver a referncia completa, acesse a documentao.

Array
O Ruby possui arrays, que so listas que podem guardar qualquer tipo de objeto e no 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 mtodo Array#initialize pode ser utilizado de maneiras diferentes. Voc pode dizer com quantos tens o array deve ser iniciado. Por padro, o array ser iniciado com o valor nil.
items = Array.new(5) #=> [nil, nil, nil, nil, nil]

Voc tambm pode passar um valor inicial que ser usado para popular este array.
items = Array.new(5, "hello") #=> ["hello", "hello", "hello", "hello", "hello"]

Tambm possvel 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 contm espaos, escape o espao com \.


words = %w[John\ Doe Jane\ Doe] #=> ["John Doe", "Jane Doe"]

Se voc precisa interpolar alguma varivel, 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 numricos. Os ndices comeam em 0 e tambm 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] numbers.first numbers[4] numbers[-1] numbers.last #=> #=> #=> #=> #=> 0 0 8 18 - A mesma coisa que numbers[numbers.size - 1] 18

Tambm possvel acessar intervalos passando dois nmeros.


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

Hash
Um outro tipo de estrutura de dados do Ruby o Hash. Hashes so como arrays, com a diferena 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 imprevisvel.
user = {"name" => "John Doe", "age" => 32}

Voc pode debnir o valor-padro de uma chave que ainda no 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 tambm pode debnir o valor-padro atravs de um bloco.


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

Perceba que os valores-padro que so retornados no so armazenados no hash. de responsabilidade do bloco fazer esta atribuio.
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 tambm pode inicializar arrays usando o mtodo 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 mtodo Hash.[] tambm 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 atravs do mtodo to_hash.
user = {"name" => "John Doe", "age" => 32} Hash[user] #=> {"name" => "John Doe", "age" => 32}

partir do Ruby 1.9 possvel debnir hashes usando uma sintaxe semelhante a do JavaScript. Uma caracterstica dessa sintaxe que as chaves so geradas como smbolos.
user = {name: "John Doe", age: 32} user.keys #=> [:name, :age]

Symbol
Smbolos so objetos que representam nomes no Ruby e so muito utilizados como identibcadores, principalmente como chaves de hashes. Eles so gerados atravs da sintaxe :symbol ou :"symbol", alm dos mtodos String#to_sym e String#intern.
symbol symbol symbol symbol = = = = :john :"john doe" "john".to_sym "john doe".intern

Alternativamente voc pode usar o delimitador %s.

37

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

Smbolos compartilham sempre o mesmo espao em memria, 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 so true e false, que so instncias das classes TrueClass e FalseClass. Infelizmente, ambas as classes no possuem uma superclasse comum.
true.class #=> TrueClass false.class #=> FalseClass

Os valores booleanos tambm podem ser acessados atravs das constantes TRUE e FALSE.
TRUE.class #=> TrueClass
38

FALSE.class #=> FalseClass

Os valores booleanos true e false ocupam sempre o mesmo espao em memria, atravs dos ids 2 e 0, respectivamente.
true.object_id #=> 2 false.object_id #=> 0

nil
O Ruby debne o tipo nulo atravs do do objeto nil, que uma instncia da classe NilClass. O nil ocupa sempre o mesmo espao em memria com o id 4.
nil.class #=> NilClass nil.object_id #=> 4

Este o valor de retorno de blocos e mtodos que no 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 nmeros e strings podemos utilizar a classe Range.
numbers = 1..10 numbers.class #=> Range letters = "a".."z" letters.class #=> Range

possvel debnir um intervalo excluindo o ltimo elemento.


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

Voc pode converter um intervalo em array com o mtodo 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 includo em um intervalo, utilize o mtodo Range#cover?. Ele muito mais rpido que transformar o intervalo em array e depois veribcar se o tem est includo com o mtodo Array#include?.
("aaa".."zzz").cover?("def") #=> bom #=> true ("aaa".."zzz").to_a.include?("def") #=> ruim #=> true

Expresses regulares
Expresses regulares so padres (ou patterns) que permitem descrever o contedo de uma string. Elas podem ser utilizadas para veribcar se uma string contm um determinado padro ou para extrair partes dessa string. Para criar uma expresso regular voc deve debnir o padro utilizando a sintaxe /pattern/ ou %r(pattern).

41

regex = /hello/

Alguns caracteres precisam ser escapados pois eles tem um signibcado especial em expresses regulares. So 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 expresso regular com %r(). Note que voc pode utilizar qualquer caracter como delimitador.
regex = %r(\Ahttps?://) regex = %r[\Ahttps?://]

Expresses regulares so extremamente poderosas. Elas permitem veribcar padres que seriam difceis (e em alguns casos impossveis) de serem feitas de outras maneiras. Se voc deseja aprender mais sobre assunto, leia o livro Expresses Regulares: uma abordagem divertida, escrito por Aurlio Marinho Jargas, e que est disponvel gratuitamente para leitura online.

Procs e lambdas
Procs so blocos de cdigo que podem ser associados a uma varivel e que funcionam como funes anonnimas. Muitas vezes voc precisa de um mtodo utilitrio que ir fazer algumas pequenas computaes onde criar um mtodo propriamente dito seria muito trabalho; a que entram as procs. Para debnir uma nova proc, voc pode utilizar o mtodo Proc.new ou o mtodo Kernel#proc.
# alternativa 1 message = Proc.new { "Hello" }

42

# alternativa 2 message = proc { "Hello" }

Para executar essas procs voc pode utilizar trs mtodos diferentes.
message = proc { "Hello" } message.call message[] message.()

A ltima maneira de execuo (sum.(1, 2)) est disponvel partir do Ruby 1.9. Por conveno, procs que possuem uma nica expresso devem ser debnidas por chaves.
message = proc { "One-line proc" }

E, tambm por conveno, quando uma proc possuir mais de uma expresso elas devem ser debnidas pelas palavras-chave do..end.
message = proc do puts "Hello!" puts "Ruby!" end

Procs podem receber parmetros, assim como mtodos. Basta delimitar os parmetros com |. Para receber mais de um parmetro, separe-os com vrgula.
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 mtodos, a ltima expresso que for executada. Se voc quiser encerrar o cuxo de execuo retornando um valor antes da ltima expresso, deve usar next, em vez do return utilizado por mtodos.
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 tambm introduziu uma nova sintaxe para a debnio de procs.
message = -> { "Hello" } message.call #=> Hello

Esta nova sintaxe tambm pode aceitar parmetros, mas faz com que a legibilidade do cdigo 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 parmetro.
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 mtodo Kernel#lambda.


sum = lambda {|n1, n2| n1 + n2} sum[1, 2] #=> 3

Embora o mtodo Kernel#lambda seja semelhante ao mtodo Kernel#proc, eles possui uma diferena muito importante. Lambdas iro validar a quantidade de parmetros que foram passados e lanaro a exceo ArgumentError: wrong number of arguments caso o nmero de argumentos seja incorreto. J as procs iro atribuir o valor nil para cada um dos parmetros.

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 diferena que se um return for debnido em uma proc, o mtodo que executou esta proc tambm ir encerrar o cuxo de execuo. No caso de lambdas, o cuxo de execuo 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 parmetros obrigatrios uma proc espera, use o mtodo Proc#arity. Se a proc possui argumentos opcionais, o valor de retorno ser -n - 1, onde n a quantidade de parmetros obrigatrios.

46

Proc.new Proc.new Proc.new Proc.new Proc.new Proc.new Proc.new Proc.new

{}.arity {||}.arity {|a|}.arity {|a,b|}.arity {|a,b,c|}.arity {|*a|}.arity {|a,*b|}.arity {|a,*b, c|}.arity

#=> 0 #=> 0 #=> 1 #=> 2 #=> 3 #=> -1 #=> -2 #=> -3

Para pegar uma representao dos parmetros que um bloco pode receber, use o mtodo Proc#parameters. Note que a representao 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 situaes onde voc pode precisar de uma lista com valores nicos. Isso pode ser facilmente resolvido com arrays e o mtodo 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 tcnicas funcionem, elas no so otimizadas. O Ruby possui a classe Set que faz justamente isso: garante que apenas tens nicos sero adicionados lista.
require "set" items = Set.new([1, 2, 3]) # adiciona novamente o nmero 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 mtodo Array#to_set.


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

48

CAPTULO 1

Estruturas condicionais
A estrutura de controle mais comum em qualquer linguagem de programao a condicional. apenas uma maneira de executar um trecho de cdigo se alguma condio for satisfeita. Uma condio uma expresso que quando veribcada retorna um valor diferente de false e nil. O Ruby possui diferentes formas de expresses condies, como voc pode conferir seguir.

if
O if a forma mais direta de se debnir uma expresso condicional. Sua sintaxe bastante simples.
if expression # do something end

O trecho de cdigo debnido dentro de if..end ser executado somente se o valor de expression for diferente de false e nil. Note que no necessrio adicionar parnteses em torno das condies. O resultado de um if pode ser atribudo a uma varivel.
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 construo vlida e muito cexvel, nem sempre a melhor maneira. A mesma expresso poderia ser escrita de um modo muito mais legvel.
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 condio ser sempre a primeira a ser executada, mesmo ela sendo escrita por ltimo.

else
O if pode conter uma clusula else, que ser executada quando a condio no for satisfeita. Caso o valor expression seja igual a false ou nil, ento o cdigo associado ao else ser executado.

50

if expression # do something else # do something else end

elsif
Se voc precisar adicionar mais clusulas else que dependem de outras condies, 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 expresses ir falhar caso o valor de retorno seja false ou nil, at que seja executada a ltima expresso 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 condies de nosso if ser satisfeita, o que faz com que o valor original de name no seja alterado. No Ruby o mtodo inspect normalmente retorna uma representao do self como uma string.

unless
Uma tendncia natural quando queremos executar alguma expresso somente se uma condio falhar adicionar uma exclamao antes da condio ou, alternativamente, usar a palavra-chave not, que tambm 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 tambm suporta uma clusula else, mas seu uso desencorajado; neste caso, seria muito mais simples escrever um if que coloca a expresso a ser executada se o valor da condio no 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 tambm pode ser usado de forma inline.
x = 0 x += 1 unless x.nonzero?

Operador ternrio ?:
O operador ?: o nico operador ternrio (que possui trs operandos) suportado pelo Ruby. O primeiro operando que vem antes da interrogao a condio. O segundo operando que vem antes dos dois-pontos a expresso que ser executada caso a condio seja diferente de false ou nil. O terceiro e ltimo operando que vem depois dos dois-pontos a expresso que ser executada caso a condio falhe. No exemplo seguir temos um mtodo que ir retornar uma string levando em considerao a quantidade de tens. Se o count for igual a 1, uma string que representa o singular retornada. Caso contrrio, uma string que representa o plural retornada.

53

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

A mesma condio poderia ser simplibcada usando o operador ternrio ?:.


def pluralize(count, singular, plural) count == 1 ? singular : plural end

case
O case expresso condicional que permite fazer diversos tipos de comparaes e que pode ser usada de duas formas diferentes. A primeira forma que apenas uma alternativa para if..elsif..else a mais simples, mas tambm a menos utilizada.
case when when when else end

x == 1 then "one" x == 2 then "two" x == 3 then "three" "other"

# # # # #

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

A palavra-chave then s necessria se voc quiser utilizar expresses 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 expresses diferentes. Esse modo extremamente poderoso e permite fazer comparaes com expresses regulares, classes e intervalos. O exemplo anterior poderia ser expressado de uma forma um pouco menos repetitiva.
case when when when else end x 1 then "one" 2 then "two" 3 then "three" "other"

O case tambm retorna o valor da expresso que for executada, podendo ser atribuda a uma varivel.
number = case when when when else end x 1 then "one" 2 then "two" 3 then "three" "other"

Voc pode debnir diversas comparaes em uma mesma expresso.


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

55

O case usa o operador === para fazer as comparaes. Em alguns casos, esse operador exatamente o mesmo que ==. Mas em outros casos, como classes e mdulos, o comportamento um pouco diferente. O operador === implementado[1] pelas classes e mdulos ir veribcar se um objeto uma instncia desta classe ou mdulo ou de um de seus descendentes. Vamos ver na prtica 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 contrrio no verdade. Isso acontece porque a implementao de String.=== (que na verdade implementada por Module#===) diferente de String#===, que compara se o objeto uma string e se ela possui o mesmo contedo. Voltando ao case, se a expresso de comparao for uma classe, ento ele ir veribcar se a classe daquela instncia a prpria classe ou um de seus descendentes.
value = "Hello" case value when String
1

Sim! O operador === apenas um mtodo e voc pode debn-lo em seus prprios objetos. 56

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

J o operador === implementado pelas expresses regulares ir veribcar se um determinado padro 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) tambm 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 conveno de se utilizar o operador === faz com que o case do Ruby seja muito mais poderoso que seu equivalente de outras linguagens.

58

CAPTULO 1

Estruturas de repetio
O Ruby possui trs expresses que debnem loops: for..in, while e until. Mas alm deles, possvel usar iteradores em objetos enumerveis como arrays e hashes, alm de outros objetos.

for..in
O loop for..in permite iterar em objetos que so enumerveis, como o caso de arrays e hashes. A cada iterao, um elemento ser atribudo para a varivel especibcada. Sua sintaxe bastante simples:
for item in collection # do something end

Note que cada valor atribudo varivel pode ser acessado fora da expresso for..in.
numbers = [1,2,3] for number in numbers puts "inside loop: #{number}" end puts "outside loop: #{number}"

Ao executar este cdigo, 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 variveis que iro 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 fornea apenas uma varivel, esta varivel 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, no assim que os desenvolvedores Ruby mais experientes costumam fazer. Mais frente voc ver como utilizar os iteradores em objetos enumerveis.

60

while e until
O while e until so as formas mais bsicas de looping do Ruby. Eles iro executar algum trecho de cdigo enquanto uma condio for verdadeira ou at que uma condio se torne verdadeira. Note que primeiro a condio testada e, ento, o cdigo 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

Tambm possvel usar o while e until como modibcadores. Eles iro executar alguma expresso at que a condio seja satisfeita.
items = [] items.push(items.size + 1) while items.size < 3 p items #=> [1, 2, 3]

61

No exemplo acima estamos adicionando um nmero ao array enquanto seu tamanho for menor que trs. O nmero 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]

Tambm possvel debnir blocos begin..end para utilizar estes modibcadores com mais de uma expresso.
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 contrrio das expresses de uma linha, o bloco executado antes de a condio 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 construo aceita pela linguagem, o uso de begin..end desencorajado e pode, inclusive, ser removido em verses futuras do Ruby. possvel ter um comportamento semelhante sem que voc caia nesta armadilha: basta delimitar diversas expresses com parnteses.
( puts "OH NOES!" puts "AIN'T GONNA BE DISPLAYED! :(" ) while false

loop
Para loops que no 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 execuo do script

63

Controlando o loop
Muitas vezes necessrio controlar o cuxo de execuo de um loop. s vezes preciso interromper a execuo, outras preciso passar para o prximo tem da iterao em alguma condio especbca. O Ruby possui algumas maneiras de fazer isso.

Interrompendo a execuo do loop


Para encerrar um loop a qualquer momento, utilize a palavra-chave break. Isso far com que a execuo seja imediatamente interrompida.
x = 0 while x < 10 x += 1 puts x break if x == 3 end # # # # Output: 1 2 3

Pulando para a prxima iterao


Para pular para a prxima iterao, utilize a palavra-chave next. Isso far com que a execuo 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 iterao
Para reiniciar a iterao, utilize a palavra-chave redo. Isso far com que a execuo 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: number: number: number:

1, 2, 3, 3,

tries: tries: tries: tries:

0 0 0 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 situaes, mais provvel que voc escreva loops utilizando mtodos chamados iteradores. Os iteradores so 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 mtodo Integer#times ir executar o bloco que foi fornecido 5 vezes. esse tipo de construo do Ruby que faz com que a linguagem seja elegante. Existem outros iteradores numricos, que nada mais so que mtodos iteradores implementados pela classe Integer, assim como o mtodo Integer#times. O mtodo Integer#upto ir executar o bloco especibcado o nmeros de vezes que for debnido pelo inteiro, at atingir o nmero que foi passado como argumento. O bloco especibcado ir receber o nmero da iterao como argumento. partir do Ruby 1.9 mtodos iteradores no exigem que voc passe um bloco para execuo; neste caso, ele ir retornar um objeto do tipo Enumerator.
1.upto(3) do |number| puts number end # Output:

66

# 1 # 2 # 3

O mtodo Integer#downto funciona como o mtodo 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 mtodo chamado Integer#step. Este mtodo permite fazer iteraes usando nmeros inteiros e de pontocutuante. 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 enumerveis
Objetos instanciados partir das classes Array, Hash e Range so enumerveis. O objeto considerado enumervel quando implementa o mtodo 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 enumerveis que implementa o iterador each inclui tambm o mdulo Enumerable. Este mdulo adiciona muitos mtodos que agem em cima do mtodo each e que permitem iterar, buscar e ordenar os tens da coleo. O mdulo Enumerable, por exemplo, inclui os mtodos Enumerable#map, Enumerable#select, Enumerable#reject, Enumerable#find e Enumerable#inject, s para citar alguns. O mtodo Enumerable#map permite criar um novo array contendo os elementos retornados pelo bloco.
[1,2,3].map {|number| number * 2} #=> [2, 4, 6]

O mtodo 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 mtodo Enumerable#reject faz exatamente o contrrio do mtodo 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 mtodo 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 mtodo 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 iteraes passadas. O bloco que foi fornecido pode ou no incrementar este acumulador, dependendo de suas condies. O acumulador de dev ve ser o valor de retorno do bloco. Veja, por exemplo, como retornar um nico nmero que ser a soma do dobro dos mltiplos 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 cdigo 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

CAPTULO 1

Blocos
Os blocos so essenciais no uso de iteradores. Embora tenhamos usado blocos quando falamos sobre objetos enumerveis, no dedicamos tempo para explicar o que eles so. Blocos nunca podem estar sozinhos; eles sempre precisam estar associados a uma execuo de mtodo. Todo mtodo que executado pode receber um bloco, mas apenas os mtodos que esperam este bloco e que faam o yield que iro de fato execut-los; caso contrrio, o bloco ser ignorado silenciosamente. Por baixo dos panos, blocos so apenas procs. Assim como as procs, blocos seguem a conveno de se usar chaves para blocos com uma nica expresso e do..end para blocos com mais de uma expresso.
# Apenas uma expresso [1, 2, 3].map {|number| number * 2} # Diversas expresses [1, 2, 3, 4, 5].inject(0) do |acc, number| acc += number if number.even? acc end

Lembre-se! Como blocos sempre esto associados execuo de mtodos, voc no deve usar este termo para se referir a procs ou lambdas.

71

Escopo de variveis
Blocos introduzem um novo escopo de variveis. Toda vez que voc debne parmetros que sero recebidos pelo bloco, estas variveis sero acessveis apenas no contexto do bloco. O exemplo seguir mostra como a varivel i debnida apenas no escopo local do bloco.
1.upto(5) {|i| } p defined?(i) #=> nil

No entanto, blocos tambm podem referenciar variveis do contexto externo ao bloco e, nesse caso, podem inclusive modibcar estas variveis.
total = 0 1.upto(10) {|i| total += i} puts total #=> 55

partir do Ruby 1.9 os parmetros esperados pelo bloco no mais modibcam variveis 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 iterao, ou seja, 10.
i = 0 1.upto(10) {|i| } puts i #=> 0

72

Interagindo com blocos


Como foi dito antes, mtodos podem receber blocos mesmo quando eles no 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 mtodo.
def say puts yield end say { "Hello" } #=> Hello

O bloco ser executado toda vez que voc usar yield. Ento, se em um mesmo mtodo voc usar trs vezes a palavra-chave yield, o bloco ser executado trs 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 mtodo say, uma exceo LocalJumpError ser lanada. Para evitar que isto acontea, voc pode veribcar se algum bloco foi passado com o mtodo Kernel#block_given? e tomar as aes necessrias.

73

No exemplo seguir lanamos uma exceo caso um bloco no 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 parmetros 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 parmetro para outro mtodo ou agindo como proxy de um outro mtodo que tambm espera um bloco. O Ruby permite que voc capture blocos passados para mtodos usando a construo &variavel. A nica exigncia que essa varivel deve ser sempre a ltima varivel da lista. O exemplo anterior poderia ser reescrito usando essa construo.
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 no estamos mais usando o yield; agora, executamos o mtodo Proc#call passando os argumentos. Alternativamente, poderamos usar algum outro mtodo que executa procs, como Proc#[].

75

CAPTULO 1

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

Criando classes
Para debnir uma classe use a palavra-chave class. Classes so constantes e, por este motivo, devem comear com uma letra maiscula.
class Page end

Classes so 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 cexvel.
class Page end AnotherPage = Class.new Page.class #=> Class

Classes criadas dinamicamente podem ser atribudas a qualquer tipo de varivel, e no apenas a constantes. 76

AnotherPage.class #=> class

Embora ainda no tenhamos adicionado nenhum mtodo classe Page, ns j podemos instnci-la. Para isso voc ir usar o mtodo Page.new.
page = Page.new

Mesmo no tendo debnido atributos e mtodos, ns podemos executar alguns mtodos 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 mtodos. Para saber quais so as superclasses de uma classe, use o mtodo Class.ancestors. Note que a prpria classe ser adicionada lista.
Page.ancestors #=> [Page, Object, Kernel, BasicObject]

Toda vez que o mtodo Class.new for executado, ele ir iniciar a instncia com o mtodo construtor. No Ruby, o mtodo construtor Class#initialize. Este mtodo debnido automaticamente como privado e no pode ser executado diretamente de fora do objeto. Vamos fazer o mtodo Page#initialize receber dois argumentos que iro debnir o ttulo e contedo da pgina.

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 variveis de instncia, identibcados por uma arroba na frente da varivel. Elas pertencem ao objeto self que referencia o prprio objeto instanciado. Cada instncia da classe Page ter sua prpria cpia das variveis @title e @content. O mtodo Page#initialize debne duas variveis de instncia, que receber os argumentos passados no momento da instanciao. No entanto, ainda no temos nenhuma maneira de acessar tais valores diretamente.

Definindo mtodos
Para acessar as variveis de instncia que debnimos no mtodo Page#initialize, ns iremos debnir dois getters, que so mtodos que apenas retornam valores. Isso precisa ser feito pois variveis de instncia no podem ser acessadas diretamente. Variveis de instncia so encapsuladas de tal forma que apenas os mtodos de um prprio 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 variveis de instncia que foram debnidas no nosso mtodo construtor, voc precisa debnir mtodos que iro retorn-las. Embora o nome do mtodo no precise necessariamente recetir o nome da varivel, 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 ns j podemos acessar as variveis armazenadas na instncia da classe Page.


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

Ainda no existe nenhuma maneira de debnir essas variveis de instncia sem ser pelo mtodo construtor. Para fazer isso, preciso debnir mtodos setters. Em outras linguagens, normalmente isso feito com um mtodo setTitle(title) ou set_title(title), ou algo parecido. No Ruby, voc pode debnir o seu prprio mtodo 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 mtodos como este. Agora, j possvel atribuir valores para as variveis @title e @content com os mtodos setters.
page = Page.new("Ruby", "OMG! I'm learning Ruby!") page.title #=> Ruby

80

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

Esta debnio de getters e setters em classes to comum que o prprio Ruby fornece uma maneira de automatizar esta debnio. Basta usar os mtodos 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 mtodo Module#attr_reader ir debnir o mtodo de instncia de leitura (getter), enquanto o mtodo Module#attr_writer ir debnir o mtodo de instncia de escrita (setter). Para os casos onde voc sempre debne tanto o getter quanto o setter, possvel usar o mtodo Module#attr_accessor, que far isso de uma vez s! Com esta alterao, nossa classe pode bcar muito mais simples.
class Page attr_accessor :title, :content

A classe Class possui a classe Module como superclasse. Para saber mais sobre mdulos, leia Mdulos. 81

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

Lembre-se que os mtodos Module#attr_accessor e companhia permitem criar apenas getters e setters que mapeiam para uma varivel de instncia de mesmo nome. Voc ter que implementar os seus prprios mtodos se eles forem mais complexos (se eles precisarem computar valores, por exemplo) ou debnirem variveis de instncia de nomes diferentes. Alternativamente, voc pode fazer com que o mtodo construtor use os mtodos Page#title= e Page#content=, em vez de atribuir as variveis de instncia. Um erro muito comum de iniciantes no debnir o objeto que ir receber o valor, chamado de receiver. O exemplo seguir debne variveis locais, em vez de executar os mtodos 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 mtodos atravs do receiver self.
class Page attr_accessor :title, :content

82

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

A atribuio direta das variveis de instncia mais rpida que executar os mtodos atravs do receiver. A menos que voc manipule as variveis no mtodo setter antes de atribu-las, prebra sempre debnir as variveis de instncia.

Definindo mtodos de classe


Classes tambm podem ter mtodos. Algumas linguagens chamam estes mtodos de estticos. No Ruby, eles so apenas mtodos adicionados a um objeto que uma instncia da classe Class. Vamos implementar o mtodo Page.load, que ir ler um arquivo em formato YAML (Yet Another Markup Language) e retornar uma nova instncia da classe Page com os valores j atribudos.
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 representao de objetos com a classe YAML. Para isso, basta carregar a standard library com o mtodo Kernel#require. O mtodo YAML.load_file l um arquivo e converte seu contudo em objetos Ruby. Neste exemplo, nosso arquivo deve retornar um hash. Classes possuem um objeto self, assim como todos os objetos. Dentro da instruo class..end, o self faz referncia prpria classe. Por isso, uma abordagem mais comum usada por desenvolvedores mais experientes debnir mtodos de classe usando def self.load(file)..end, em vez de usar o nome da prpria 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 mtodo de classe est no contexto da prpria classe (lembre-se, o self faz referncia a prpria classe), ns podemos fazer mais uma alterao. Em vez de instanciar a classe Page.new, basta executar o mtodo 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 no permite salvar sua representao em YAML. Vamos adicionar um mtodo 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 mtodo 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 representao em YAML inclui a informao sobre qual classe este objeto foi instanciado, no precisamos mais fazer isso manualmente no mtodo Page#load. Agora, podemos simplesmente retornar o objeto instanciado com o mtodo 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 mtodos e controle de acesso


O Ruby possui trs nveis diferentes de visibilidade e controle de acesso dos mtodos de um objeto. Mtodos pblicos podem ser executados em qualquer situao. Mtodos so pblicos por padro, com uma nica exceo: o mtodo Class#initialize sempre privado. Mtodos protegidos podem ser executados por objetos de uma classe e suas subclasses. Mtodos privados no podem ser executados atravs de um receiver explcito. O receiver sempre ser o objeto atual self. Para debnir a visibilidade de mtodos voc utilizar os mtodos Module.public, Module.private e Module.protected.
class SomeClass def method1 end private def method2 end protected def method3 end public def method4

# mtodos so pblicos por padro

# todos os mtodos definidos partir daqui sero privados

# todos os mtodos definidos partir daqui sero protegidos

# todos os mtodos definidos partir daqui sero pblicos

87

end end

Alternativamente voc poderia ter debnido a visibilidade dos mtodos passando uma lista de nomes de mtodos.
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 mtodo for executado que o controle de acesso ser determinado. Se a visibilidade deste mtodo for violada, uma exceo NoMethodError ser lanada.
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 mtodos utilizando o mtodo Object.__send__.


object.__send__ :some_private_method # nenhuma exceo lanada

Para garantir que mensagens sejam enviadas apenas para mtodos pblicos, use o mtodo 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 informaes que poderiam bcar espalhadas pelo cdigo, como nmeros mgicos. Vamos alterar a classe Page de modo que ela possa receber tambm um permalink, uma representao de como essa pgina 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 mtodo Page#save_to para Page#save. Este mtodo ir salvar os arquivos sempre em um mesmo diretrio, 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 diretrio onde os arquivos sero salvos no mtodo Page#save, vamos extrair esta informao para uma constante. Essa alterao permite, dentre outras coisas, expor esta informao na documentao 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 annima, escondida, especbca do objeto. Esta classe annima chamada de Singleton Class, mas antes de ter um nome obcial tambm 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 tambm est disponvel 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 mtodos em uma classe.
class Person class << self def count @count ||= 0 end end end

Aqui, estamos debnindo um mtodo na classe Singleton do objeto Person (lembre-se: tudo no Ruby objeto, inclusive classes). Como consequncia, isso ir debnir o mtodo Person.count. O efeito exatamente o mesmo que o cdigo abaixo, porm este mais objetivo e fcil de entender.
class Person def self.count @count ||= 0 end end

92

No Ruby 1.9, foi adicionado o mtodo Object#singleton_class, que apenas um atalho para a sintaxe class << self; self; end. Em verses mais antigas, voc pode injetar este mtodo com o cdigo abaixo.
class Object def singleton_class class << self; self; end end unless respond_to?(:singleton_class) end

Toda vez que injeta mtodos em um objeto, eles so adicionados como mtodos singleton. O que realmente importante saber que estes mtodos pertecem unicamente ao objeto em que foram debnidos, no 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 mtodo to_yo singleton, podemos utilizar o mtodo Object#singleton_methods.
string.singleton_methods #=> ["to_yo"]

93

another_string.singleton_methods #=> []

Voc tambm pode adicionar mtodos singleton de outras maneiras. Uma delas estendendo um objeto com um mdulo.
module Extension def sum self.reduce(:+) end end numbers = [1,2,3] numbers.extend Extension numbers.singleton_methods #=> ["sum"]

Outra maneira usando a prpria classe Singleton.


numbers = [1,2,3] class << numbers def sum self.reduce(:+) end end numbers.singleton_methods #=> ["sum"]

Ou ainda, executando cdigo 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, no poder mais utilizar o mtodo Marshal.dump, j que a biblioteca Marshal[3] no suporta objetos com classes Singleton (ela ir lanar a exceo TypeError: singleton can't be dumped). A nica maneira de fazer isso e ainda poder utilizar o Marshal utilizando o mtodo Object#extend. Agora, sabendo que voc pode adicionar mtodos em um objeto com uma sintaxe como def object.some_method; end, perceba que exatamente isso que fazemos quando debnimos um mtodo em uma classe; a nica diferena que passamos o prprio self.
class Person def self.say_hello "Hello there!" end end Person.singleton_methods #=> ["say_hello"]

A biblioteca Marshal permite converter objetos Ruby em uma sequncia de bytes que podem ser restaurados por outros scripts, que podem reconstituir os objetos originais. 95

Com base nesse exemplo, possvel abrmar que mtodos de classe no e existem xistem no Rub Ruby y! Pelo menos no no sentido de mtodos estticos! O que acontece que estes mtodos pertencem a um objeto, que por acaso uma classe.

96

CAPTULO 1

Mdulos
Hmmm. Este contedo est sendo escrito e estar disponvel em breve.

97

CAPTULO 1

Trabalhando com o load path


Hmmm. Este contedo est sendo escrito e estar disponvel em breve.

98

CAPTULO 1

Trabalhando com strings


Concatenando strings
Para concatenar strings, voc pode utilizar os mtodos 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 trs mtodos atingem o objetivoconcatenar strings, existe uma diferena muito importante. O mtodo String#+ ir criar um novo objeto em memria, enquanto os mtodos String#<< e String#concat iro realocar a memria 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 possvel utilizar expresses arbitrrias que podem ser interpoladas.
language = "Ruby" puts "#{language} is nice!" #=> Ruby is nice!

O Ruby tambm suporta outros tipos de formatao de strings que tambm permitem interpolar expresses. o caso dos mtodos String#%, Kernel#printf e Kernel#sprintf. Estes mtodos permitem ter maior controle no espaamento e formatao de nmeros. Alm disso, eles permitem desacoplar os valores que devem ser interpolados da string, facilitando, por exemplo, a internacionalizao de strings. A mesma string que foi interpolada acima pode ser formatada com os mtodos 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 fcil interpolar mltiplos valores com arrays, seu cdigo pode acabar confuso quando a lista de parmetros 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 nmeros.


"%d" % "%f" % "%.2f" "%x" % "%X" % "%o" % "%e" % "%E" % "%b" % 1.5 1.1245 % 1 1234 1234 1234 1234 1234 1234 #=> #=> #=> #=> #=> #=> #=> #=> #=> 1 1.124500 1.00 4d2 4D2 2322 1.234000e+03 1.234000E+03 10011010010 nmero inteiro ponto-flutuante com todos os nmeros ponto-flutuante com duas casas decimais hexadecimal com letras minsculas hexadecimal com letras maisculas nmero inteiro ctal nmero exponencial nmero exponencial em maiscula nmero binrio

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

Convertendo em maisculas e minsculas


Para converter uma string em letras maisculas, utilize o mtodo String#upcase.

101

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

Para converter uma string em letras minsculas, utilize o mtodo String#downcase.


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

Note que os mtodos String#upcase e String#downcase iro retornar uma nova string. Para alterar a string existente, utilize os mtodos 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 mtodos String#size ou String#length. Note que esses mtodos iro retornar a quantidade de caracteres. Se voc precisa saber a quantidade de bytes necessrios para representar a string, utilize o mtodo
String#bytes[1].

Veja mais detalhes sobre codibcao de caracteres em Codibcao. 102

text = "Ruby is nice!" puts text.size puts text.length #=> 13 #=> 13

Substrings
O Ruby permite pegar substrings (trechos de uma determinada string) com o mtodo String#[]. Em verses anteriores ao Ruby 1.9, o mtodo String#[] com um nmero inteiro retorna a representao numrica daquela posio, o que nem sempre recete a represeno de caracteres com mais de um byte. A maneira mais simples passar um nmero inteiro que identibca o caracter que ser retornado. Se o ndice for negativo, a posio 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 tambm pode passar como segundo parmetro, 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 tambm pode utilizar intervalos com valores negativos.
text = "Ruby is nice!" text[0..3] #=> Ruby text[-5..-2] #=> nice

O mtodo String#[] tambm pode receber expresses regulares. Neste caso, o primeiro resultado ser retornado.
text = "Ruby is nice!" text[/ruby/i] #=> Ruby

Voc pode especibcar o nmero ou nome do grupo quando estiver usando uma expresso regular.
text = "ruby RUBY rUbY Ruby" text[/(ruby) (ruby) (ruby) (ruby)/i, 2] text[/(ruby) (?<upcase>ruby) (ruby) (ruby)/i, :upcase]

#=> RUBY #=> RUBY

Por ltimo, se voc passar uma string qualquer e ela estiver presente, ela retornada. Caso contrrio, nil retornado.
text = "Ruby is nice!" text["Ruby"] #=> Ruby text["ruby"] #=> nil

104

Codificao
Uma das grandes mudanas introduzidas pelo Ruby 1.9 foi a codibcao das strings, que agora so verdadeiramente sequncias de caracteres que no precisam estar contidas na tabela de caracteres ASCII. Caracteres debnidos como UTF-8, por exemplo, que usam um nmero varivel de bytes para representar os caracteres, no possuem mais uma relao 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 contm caracteres multibytes, a quantidade de bytes usados para represent-la no ser a mesma do nmero 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 codibcao ser usada no arquivo. Esse comentrio chamado de magic comment e sem ele o Ruby no saberia decidir qual codibcao utilizar. O magic comment pode ser qualquer string que consiga ser casada pela seguinte expresso regular[2]. Magic comments devem ser a primeira linha do arquivo ou vir aps a linha de shebang.
/coding[:=] ?/

Os seguintes comentrios so vlidos na hora de debnir a codibcao do arquivo.


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

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

A codibcao de uma string baseada na codibcao do cdigo-fonte, mas no ser necessariamente a mesma.
# -*- encoding: utf-8 -*text = "hello".encode("iso-8859-1") puts text.encoding #=> ISO-8859-1

Nem todas as strings so compatveis entre diferentes codibcaes. Toda vez que voc tentar fazer a converso entre codibcaes que no so compatveis, uma exceo ser lanada.
# -*- encoding: utf-8 -*text = "".encode("iso-8859-1") #=> Encoding::UndefinedConversionError: U+221A from UTF-8 to ISO-8859-1

Neste caso voc pode forar a codibcao utilizando o mtodo String#encode, passando a estratgia de converso e qual string ser usada no lugar dos caracteres que no so reconhecidos.
# -*- encoding: utf-8 -*text = "Learn Ruby ".encode("iso-8859-1", :undef => :replace, :replace => "[DONE]") puts text #=> Learn Ruby [DONE]

Voc pode forar a codibcao de uma string com o mtodo String#force_encoding. Este mtodo no faz qualquer veribcao ou converso de caracteres; apenas muda a interpretao que o Ruby faz dos caracteres, mas sem alterar os bytes que representam a string. Para validar se os bytes daquela string so vlidos na codibcao escolhida, utilize o mtodo String#valid_encoding?.
text = "\xF4".force_encoding("utf-8") #=> Fora um valor binrio em UTF-8 puts text.valid_encoding? #=> false

106

Para acessar a representao nmerica de cada caracter de uma string, utilize o mtodo String#codepoints. Este mtodo espera um bloco, mas voc pode utilizar o mtodo 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 mtodo String#ord para pegar a representao nmerica de um nico caracter.
"".ord #=> 9786

Voc tambm pode acessar os bytes de uma string com o mtodo String#bytes. Assim como o mtodo 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 codibcaes disponveis no Ruby, utilize o mtodo Encoding.list. Algumas codibcaes possuem um alias, cuja listagem completa pode ser retornada pelo mtodo Encoding.aliases. O cdigo seguir far o output das codibcaes disponveis 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 cdigo acima, quando executado no Ruby 1.9.3-p0, pode ser visto seguir (os nomes foram listados em colunas para no ocupar tanto espao).
ASCII-8BIT (BINARY) Big5 Big5-HKSCS (Big5-HKSCS:2008) Big5-UAO CP50220 CP50221 CP51932 CP850 (IBM850) CP852 CP855 CP949 CP950 CP951 Emacs-Mule EUC-JP (eucJP) EUC-KR (eucKR) EUC-TW (eucTW) eucJP-ms (euc-jp-ms) GB12345 GB18030 GB1988 GB2312 (EUC-CN) GBK (CP936) IBM437 (CP437) IBM737 (CP737) IBM775 (CP775) IBM852 IBM855 IBM857 (CP857) IBM860 (CP860) IBM861 (CP861) IBM862 (CP862) IBM863 (CP863) IBM864 (CP864) IBM865 (CP865) IBM866 (CP866) IBM869 (CP869) ISO-2022-JP (ISO2022-JP) ISO-2022-JP-2 (ISO2022-JP2) ISO-2022-JP-KDDI ISO-8859-1 (ISO8859-1) ISO-8859-10 (ISO8859-10) ISO-8859-11 (ISO8859-11) ISO-8859-13 (ISO8859-13) ISO-8859-14 (ISO8859-14) ISO-8859-15 (ISO8859-15) ISO-8859-16 (ISO8859-16) ISO-8859-2 (ISO8859-2) ISO-8859-3 (ISO8859-3) ISO-8859-4 (ISO8859-4) ISO-8859-5 (ISO8859-5) ISO-8859-6 (ISO8859-6) ISO-8859-7 (ISO8859-7) ISO-8859-8 (ISO8859-8) ISO-8859-9 (ISO8859-9) KOI8-R (CP878) KOI8-U macCentEuro macCroatian macCyrillic macGreek macIceland MacJapanese (MacJapan) macRoman

108

macRomania macThai macTurkish macUkraine Shift_JIS SJIS-DoCoMo SJIS-KDDI SJIS-SoftBank stateless-ISO-2022-JP

stateless-ISO-2022-JP-KDDI TIS-620 US-ASCII (ASCII) UTF-16 UTF-16BE (UCS-2BE) UTF-16LE UTF-32 UTF-32BE (UCS-4BE) UTF-32LE (UCS-4LE)

UTF-7 (CP65000) UTF-8 (CP65001) UTF8-DoCoMo UTF8-KDDI UTF8-MAC (UTF-8-MAC) UTF8-SoftBank Windows-1250 (CP1250) Windows-1251 (CP1251) Windows-1252 (CP1252)

Windows-1253 (CP1253) Windows-1254 (CP1254) Windows-1255 (CP1255) Windows-1256 (CP1256) Windows-1257 (CP1257) Windows-1258 (CP1258) Windows-31J (CP932) Windows-874 (CP874)

109

CAPTULO 1

Trabalhando com nmeros


Operadores matemticos
Para efetuar somas, utilize o mtodo Numeric#+.
1 + 1 #=> 2 1 + 2.1 #=> 3.1 -1 + -1 #=> -2

Para efetuar subtraes, utilize o mtodo Numeric#-.


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

Para efetuar multiplicaes, utilize o mtodo Numeric#*.


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

Para efetuar divises, utilize o mtodo Numeric#/. Note que o resultado depende da classe do nmero que ser usado como divisor. No caso de nmeros inteiros, o resultado ser um nmero inteiro.

110

3 / 2 3 / 2.0 2 / 1.75

#=> 1 #=> 1.5 #=> 1.1428571428571428

Para efetuar potenciaes, utilize o mtodo Numeric#**.


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

Nmeros absolutos
Para pegar o valor absoluto de um nmero, use o mtodo Numeric#abs.
1.abs -1.abs +1.2.abs -1.2.abs -1.2.magnitude #=> #=> #=> #-> #=> 1 - Mtodo Numeric#abs o mais utilizado 1 1.2 1.2 1.2

Verificando nmeros pares e mpares


Para descobrir se um determinado nmero par ou mpar, voc pode utilizar o operador %, que retorna o mdulo de um nmero.
4 % 2 == 0 # true - significa que um nmero par 5 % 2 == 1 # true - significa que um nmero mpar

111

Felizmente a classe Integer implementa dois mtodos que permitem fazer a mesma veribcao, mas de uma forma muito mais elegante.
4.even? #=> true - um nmero par 5.odd? #=> true - um nmero mpar

Verificando zeros e no-zeros


O Ruby possui dois mtodos muito teis que permitem veribcar se um nmero zero ou no.
1.zero? 1.nonzero? 0.zero? 0.nonzero? #=> false #=> true #=> true #=> false

Converso entre diferentes bases


Nmeros inteiros podem ser convertidos entre diferentes bases. possvel fazer converses entre bases de 2 a 36.
1234.to_s(2) 1234.to_s(8) 1234.to_s(10) 1234.to_s(16) 1234.to_s(36) #=> #=> #=> #=> #=> 10011010010 2322 1234 - base 10 o padro 4d2 ya

O valor retornado ser uma string que pode ser convertida novamente em nmero com o mtodo String#to_i.

112

"10011010010".to_i(2) "2322".to_i(8) "1234".to_i(10) "4d2".to_i(16) "ya".to_i(36)

#=> #=> #=> #=> #=>

1234 1234 1234 - base 10 o padro 1234 1234

Fazendo arredondamentos
O Ruby possui dois mtodos que permitem arredondar nmeros de ponto cutuantes para inteiros. O mtodo Float#ceil ir arredondar para o prximo nmero inteiro maior ou igual o prprio nmero.
1.0.ceil #=> 1 1.1.ceil #=> 2 -1.1.ceil #=> -1

J o mtodo Float#floor ir arredondar para o nmero inteiro que for menor ou igual ao prprio nmero.
1.0.floor #=> 1 1.1.floor #=> 1 -1.1.floor #=> -2

O mtodo Float#round ir arredondar nmeros de ponto cutuante para o nmero de casas decimais que foram especibcadas. Por padro, a preciso zero e pode ser um nmero negativo (nesse caso, o nmero retornado ser um inteiro).
1.4.round 1.5.round 1.6.round -1.5.round #=> #=> #=> #=> 1 2 2 -2

113

1.234567.round(2) 1.234567.round(3) 1.234567.round(4) 1.234567.round(5) 12345.67.round(-5) 12345.67.round(-4) 12345.67.round(-3) 12345.67.round(-2) 12345.67.round(-1) 12345.67.round(0)

#=> #=> #=> #=> #=> #=> #=> #=> #=> #=>

1.23 1.235 1.2346 1.23457 0 10000 12000 12300 12350 12346

Tambm possvel fazer o truncamento de um nmero de ponto cutuante para inteiro.


1.1.to_i -1.1.to_i 1.0.truncate #=> 1 - O mtodo Float#to_i mais utilizado #=> -1 #=> 1

114

CAPTULO 1

Trabalhando com arrays


Acessando os elementos
Embora voc tenha visto algumas maneiras de como acessar elementos de arrays, existe um mtodo muito interessante chamado Array#fetch. Este mtodo permite retornar um valor padro caso o ndice especibcado no exista.
items = [1, 2, 3] items.fetch(0) items.fetch(4, "not found") items.fetch(4) do |index| "index #{index} not found" end #=> 1 #=> not found #=> index 4 not found

Outra caracterstica interessante que se nenhum valor padro for especibcado, uma exceo IndexError ser lanada.
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 mtodos Array#<<[1] ou o mtodo Array#push.

115

items = [] items << 1 items << 2 items.push(3) p items #=> [1, 2, 3]

Para adicionar novos elementos ao comeo de um array, use o mtodo Array#unshift.


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

Voc tambm pode inserir novos elementos em uma posio especbca com o mtodo Array#add_elements.rb#insert. Note que o ndice pode ser um valor negativo.
items = [1, 2, 3] items.insert(0, 4) #=> [4, 1, 2, 3] items.insert(-1, 5) #=> [4, 1, 2, 3, 5] #=> insere o nmero 4 na posio 0.

#=> insere o nmero 5 no final do array.

items.insert(3, 6, 7, 8) #=> insere os nmeros 6, 7 e 8 na posio 3. #=> [4, 1, 2, 6, 7, 8, 3, 5]

Este mtodo tambm conhecido como shovel. 116

Removendo elementos
Para remover sempre o ltimo elemento de um array, use o mtodo Array#pop. O elemento removido ser o valor de retorno do mtodo.
items = [1, 2, 3] items.pop #=> 3 p items #=> [1, 2]

Voc tambm pode especibcar a quantidade de elementos que sero 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 mtodo Array#delete. Caso este elemento no seja encontrado, nil ser retornado. Voc tambm pode fornecer um bloco, cujo valor ser retornado quando o elemento no 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 mtodo Array#delete_at. Se o ndice especibcado no 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 mtodo Array#shift.


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

Alternativamente, voc pode utilizar o mtodo Array#drop com o nmero de elementos que devem ser removidos. Note que o array original no 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 mtodo Array#clear.
items = [1, 2, 3] items.clear items #=> []

Filtrando elementos
Para gerar um novo array com todos os elementos que satisfaam uma determinada condio, use o mtodo 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 mtodos iteradores que no recebem um bloco iro retornar um objeto Enumerator.
items = [1, 2, 3, 4, 5] items.select {|n| n.odd?} #=> [1, 3, 5]

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

No Ruby 1.9, o exemplo acima pode ser ainda mais simples. partir desta verso do Ruby, o mtodo Symbol#to_proc permite criar um bloco que ir executar o mtodo identibcado pelo smbolo que foi passado.
items = [1, 2, 3, 4, 5] items.select(&:odd?) #=> [1, 3, 5]

Se voc precisa apenas do primeiro elemento que satisfaa a condio, utilize o mtodo Array#find.
items = [1, 2, 3, 4, 5] # recomendado items.find(&:odd?) #=> 1 # estranho items.select(&:odd?).first #=> 1

O mtodo Array#select possui uma contra-parte; trata-se do mtodo 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 mtodo Array#sort. A comparao 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 mtodos 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 tambm pode especibcar qual o tipo de valor deve ser usado na comparao. Isso pode ser feito com o mtodo Enumerator#sort_by. O mdulo Enumerator includo 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 aleatriamente, utilize o mtodo Array#shuffle.


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

Para pegar um nico elemento aleatriamente, utilize o mtodo Array#sample, disponvel 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 mtodo Array#include?. A comparao feita atravs do mtodo ==.
items = [1, "a"] items.include?(1) items.include?("a") items.include?(0) #=> true #=> true #=> false

O mtodo Array#index ir retornar o ndice do primeiro elemento encontrado.


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

Se um bloco for passado para o mtodo Array#index, o ndice ser cujo bloco retorne true.
items = [1, 2, 3, 1] items.index {|i| i == 3} #=> 2

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

122

items = [1, 2, 3, 1] items.rindex(1) items.rindex {|i| i == 1} #=> 3 #=> 3

Iterando elementos
A classe Array inclui o mdulo Enumerator, que implementa diversos mtodos iteradores, mas outros destes mtodos so implementados pela prpria classe.
items = [1, 2, 3] items.each {|item| puts item} # # # # Output: 1 2 3

O mtodo Array#reverse_each permite iterar no array de trs para frente.


items.reverse_each {|item| puts item} # # # # Output: 3 2 1

Para iterar em cada ndice de um array, use o mtodo 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 iterao, use o mtodo 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 mtodo 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 mtodo 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 mtodo Array#inject ou Array#reduce permite reduzir um array a um nico objeto. Se um valor inicial no for fornecido, o primeiro elemento do array ser utilizado.
items = [1, 2, 3, 4, 5] items.reduce(:+) items.reduce(10, :+) items.reduce(10) do |acc, i| acc += i if i.odd? acc end

#=> 15 - Inicia em 1 e soma todos os elementos #=> 25 - Inicia em 10 e soma todos os elementos #=> 19 - Inicia em 10 e soma apenas os nmeros mpares

Perceba como estamos utilizando o mtodo Symbol#to_proc para executar a soma dos elementos. Esta tcnica ser til apenas para operaes mais simples. Para executar operaes mais complexas, fornea um bloco, que ir receber o acumulador e o elemento da iterao.

125

CAPTULO 1

Trabalhando com hashes


Lista de chaves e valores
Para pegar uma lista das chaves de um hash, voc deve usar o mtodo Hash#keys.
options = {name: "John Doe", age: 32} options.keys #=> [:name, :age]

J a lista de valores pode ser acessada com o mtodo Hash#values.


options = {name: "John Doe", age: 32} options.values #=> ["John Doe", 32]

Verificando a existncia de chaves e valores


Hashes so muito ebcientes em buscar um determinado valor atravs de uma chave. Voc tambm pode fazer buscas de chaves atravs de um valor, embora esta operao no seja to ebciente quanto o contrrio. A classe Hash possui quatro mtodos diferentes para detectar se uma chave foi debnida. Trata-se dos mtodos 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") options.has_key?(:color) options.include?(:width) options.member?(:height)

#=> #=> #=> #=>

false - As chaves so smbolos true true true

Para descobrir se um hash possui um determinado valor, use os mtodos 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 mtodo Hash#key. No Ruby 1.8, este mtodo se chama Hash#index.
options = {color: "green", width: 150, height: 30} options.key("green") #=> :color options.key("missing") #=> nil

Acessando o hash
O mtodo mais utilizado para acessar valores de hashes Hash#[]. Caso uma chave no tenha sido debnida, nil ser retornado por padro.
options = {name: "John Doe", age: 32} options[:name] #=> "John Doe" options[:age] #=> 32 options[:email] #=> nil

127

Voc tambm pode usar o mtodo Hash#fetch para acessar valores em um hash. Ele possui mais opes na hora de lidar com chaves inexistentes.
options = {name: "John Doe", age: 32} options.fetch(:name) #=> John Doe - Funciona como o mtodo Hash#[] options.fetch(:email) #=> Lana a exceo "KeyError: key not found: :email" options.fetch(:email, nil) #=> Retorna nil caso chave no exista options.fetch(:email) do |key| #=> Executa o bloco caso a chave no exista "Missing #{key}" end

Para extrair mais de um valor de um hash, use o mtodo Hash#values_at. Se uma determina chave no 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 satisfaam uma determinada condio, use o mtodo 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 mtodo 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 mtodo 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 mtodo Hash#delete. O valor retornado ser o valor removido. Caso a chave no 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 no exista raise KeyError, "key not found: #{key.inspect}" end

Para remover diversas chaves caso uma condio no seja satisfeita, use o mtodo 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 mtodo Hash#reject! que tambm 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 mtodo Hash#clear.
options = {a: 1, b: 2, c: 3} options.clear options #=> {}

Compondo novos hashes


O mtodo 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 mtodo 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 mtodo 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 variveis diferentes, conforme vimos em Atribuio de variveis.

Iterando hashes
Para iterar em hashes, utilize o mtodo Hash#each. O bloco ir receber a chave e o valor em cada iterao.
options = {name: "John Doe", age: 32} options.each {|key, value| puts "#{key} => #{value}"} # Output: # name => John Doe # age => 32

Voc tambm pode iterar apenas nas chaves com o mtodo 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 mtodo Hash#each_value.
options = {name: "John Doe", age: 32} options.each_value {|value| puts value} # Output: # John Doe # 32

132

CAPTULO 1

Trabalhando com arquivos e diretrios


Manipulando nomes de arquivos
A classe File possui alguns mtodos que permitem manipular nomes de arquivos e diretrios. O Ruby usa o separador de diretrios / em todos os sistemas operacionais, inclusive Windows. No entanto, se voc estiver executando Ruby no Windows, pode tambm 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) File.dirname(file) File.join(dir, file) #=> samples - Remove a extenso #=> retorna ".", o diretrio atual #=> concatena com o separator de diretrios

Nenhum destes mtodos ir veribcar a existncia de arquivos e diretrios. Eles apenas permitem compor nomes, sem se importar com sua existncia e tipo (voc pode usar o mtodo File.basename em um diretrio, por exemplo.). O mtodo File.expand_path permite expandir caminhos partir do diretrio atual ou de um caminho raz.
Dir.chdir("/usr/bin") File.expand_path("ruby") #=> muda o diretrio atual para /usr/bin #=> /usr/bin/ruby

133

File.expand_path("~/ruby") File.expand_path("ruby", "/usr/local/bin")

#=> /Users/fnando/ruby #=> /usr/local/bin/ruby

File.expand_path("~fnando/ruby") #=> /Users/fnando/ruby # Lana a exceo "ArgumentError: user fnando doesn't exist" # caso o diretrio `~/fnando` no exista.

Para expandir o caminho de links simblicos, use o mtodo 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, no ser possvel efetuar nenhuma operao aps executar o mtodo File#close e, caso alguma tentativa de uso seja realizada, a exceo IOError: closed stream ser lanada.
file = File.new(__FILE__) #=> Abre o prprio arquivo para leitura content = file.read #=> Armazena o contedo do arquivo file.close #=> Encerra o uso de IO

Embora seja extremamente cexvel, este cuxo est sujeito falhas. Um desenvolvedor mais descuidado pode no encerrar o uso de IO. Para evitar que isto acontea, recomendado que voc use o mtodo File#open com um bloco. Este bloco ir receber o objeto File e, aps a execuo do bloco, ter o uso de IO automaticamente encerrado.
File.open(__FILE__) do |file| content = file.read end
134

Por padro, os mtodos File.open e File.new iro 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) File.open(path, File.open(path, File.open(path, File.open(path, File.open(path, File.open(path, #=> #=> #=> #=> #=> #=> #=> Usa a flag "r" Abre o arquivo Abre o arquivo Abre o arquivo Abre o arquivo Abre o arquivo Abre o arquivo (leitura) por padro. para leitura. para escrita, apagando o contedo existente. para escrita, mantendo o contedo existente. para escrita e leitura. para escrita e leitura. para leitura binria.

"r") "w") "a") "w+") "a+") "rb")

Para ler um arquivo apenas, use o mtodo File.read.


puts File.read(__FILE__)

Veja um exemplo que mostra como ler uma URL, salvar seu contedo em um arquivo e depois exibir o contedo 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 mtodo File#write para adicionar o contedo ao arquivo. Voc tambm poderia ter usado o mtodo File#<<. Para modibcar um arquivo existente, voc deve se certibcar que est usando a cag a. Caso contrrio, todo o contedo de seu arquivo ser substitudo. 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 mtodo 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 codificao do arquivo


Assim como a classe String, a classe File tambm tem suporte a codibcao, que pode ser especibcada no momento da abertura do arquivo para leitura e/ou escrita. Voc pode fazer converso entre diferentes tipos de codibcao 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 gravao, a codibcao ser feita no caminho contrrio, convertendo o texto de ISO-8859-1 para UTF-8.
file = File.open("/tmp/sample.txt", "w+:utf-8:iso-8859-1") # verifica a codificao de gravao do arquivo file.external_encoding #=> <Encoding:UTF-8> # verifica a codificao 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 contedo do arquivo content = file.read # retorna a codificao do contedo do arquivo (string) content.encoding #=> <Encoding:ISO-8859-1>

137

file.close

Manipulando diretrios
Para listar todos os arquivos e diretrios, voc pode usar o mtodo Dir.entries. Note que este mtodo ir adicionar os caminhos . e .., que fazem referncia ao diretrio atual e diretrio-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 mtodo Dir.[] permite listar todas as entradas que atenderem um determinado padro. Este mtodo apenas um atalho para Dir.glob e aceita os mesmos padres. Para conhecer todos os padres aceitos, acesse a documentao com o comando ri Dir.glob.
# Arquivos no diretrio atual com a extenso .rb Dir["*.rb"] # Arquivos no diretrio atual que comeam com a letra e. Dir["e*"] # Arquivos com nomes "encoding" e/ou "open" que tenham a extenso .rb Dir["{encoding,open}.rb"] # Qualquer arquivo, de qualquer diretrio, partir do diretrio atual. Dir["./**/{.*,*.*}"] # Qualquer arquivo, de qualquer diretrio, partir do diretrio atual e que # tenha a extenso .rb Dir["./**/*.rb"]
138

# Arquivos que no tenham extenso comeadas com a letra r. Dir["*.[^r]*"]

Para criar um diretrio, use o mtodo Dir.mkdir. Se o diretrio j existir, a exceo Errno::EEXIST: File exists ser lanada.
Dir.mkdir("/tmp/sample")

Para remover um diretrio, use o mtodo Dir.delete, Dir.unlink ou Dir.rmdir. O diretrio precisa estar vazio. Caso o diretrio no esteja vazio, a exceo Errno::ENOTEMPTY: Directory not empty ser lanada.
Dir.delete("/tmp/sample")

Muitas dessas limitaes que a classe Dir impe podem ser removidas se voc carregar a biblioteca padro FileUtils. Ela possui uma srie de mtodos que permitem criar e remover diretrios, dentre outros mtodos 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 diretrios


Para veribcar se um caminho um arquivo ou diretrio, voc pode usar os mtodos File.file? e File.directory?.
file = __FILE__ dir = File.dirname(file) File.file?(file) File.directory?(file) File.file?(dir) File.directory?(dir) File.file?("/invalid") File.directory?("/invalid") #=> true #=> false #=> false #=> true #=> false #=> false

Voc tambm pode veribcar se um caminho um link simblico.


path = "/usr/bin/ruby" File.file?(path) File.directory?(path) File.symlink?(path) #=> true #=> false #=> true

Voc tambm pode veribcar se arquivos e diretrios podem ser lidos ou escritos.
path = "/usr/bin/ruby" File.executable?(path) File.readable?(path) #=> true #=> true

140

File.writable?(path) File.world_readable?(path) File.world_writable?(path)

#=> false #=> 493 #=> nil

Datas de modificao e acesso de arquivos


Para saber quando um arquivo foi modibcado, use o mtodo File.mtime.
File.mtime(__FILE__) #=> 2011-12-31 12:32:11 -0200

Para saber quando um arquivo foi acessado, use o mtodo File.atime.


File.atime(__FILE__) #=> 2011-12-31 12:35:30 -0200

141

CAPTULO 1

Trabalhando com data e hora


Hmmm. Este contedo est sendo escrito e estar disponvel em breve.

142

CAPTULO 1

URLs e requisies HTTP


Hmmm. Este contedo est sendo escrito e estar disponvel em breve.

143

CAPTULO 1

Lidando com excees


No importa quo bem testado seja seu cdigo ou quo cuidadoso voc seja, bugs sempre existiro. No Ruby, erros na execuo do cdigo so sinalizados com excees, que so instncias da classe Exception. Excees podem ser lanadas com os mtodos Kernel#raise e Kernel#fail. Estes mtodos podem ser executados com trs argumentos opcionais, mas a assinatura completa do mtodo raise(exception_class, message, backtrace).
raise ArgumentError raise ArgumentError, "you've failed" raise ArgumentError, "you've failed", caller

Quando o mtodo Kernel#raise chamado sem nenhum argumento, uma exceo do tipo RuntimeError lanada. As duas excees seguir so equivalentes.
raise raise RuntimeError

Alternativamente, voc pode passar uma string para o mtodo Kernel#raise, que equivalente a lanar uma exceo com raise(RuntimeError, message).
raise "You've failed" raise RuntimeError, "You've failed"

Quando uma exceo lanada, a classe que foi utilizada para debnir a exceo 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 mtodo Exception.exception. Se uma instncia da classe Exception for fornecida, ento a exceo obtida com o mtodo Exception#exception. 2. O backtrace da exceo debnido. 3. A varivel global @$! debnida com a ltima exceo ativa. 4. A exceo lanada na pilha de execuo.

Capturando excees
O Ruby permite capturar excees com a clusula 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 varivel, ou pode utilizar a varivel global $!.
begin raise "OH NOES!" rescue => error puts "error: #{error.message}" puts "$!: #{$!.message}" end

Por padro, apenas as classes que herdam de StandardError sero capturadas. Para capturar outras classes, voc pode passar uma lista de argumentos com as classes de exceo.
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 excees nativas do Ruby so lanadas como Exception, em vez de StandardError. o caso das classes LoadError e SyntaxError, lanadas quando um arquivo no consegue ser carregado com o mtodo Kernel#require e quando o Ruby encontra uma sintaxe invlida, respectivamente. Embora seja possvel capturar a classe base Exception, isso no uma boa ideia. Seja especbco quanto s e ex xcees que v voc oc quer captur capturar ar.. Voc pode ter diversas clusulas rescue que tratam excees diferentes de modos diferentes. Voc pode, inclusive, adicionar vrias classes de excees em uma nica clusula enquanto debne diferentes pontos de captura da exceo.
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 RuntimeError => Dammit! ScriptError => Y U MAD? SyntaxError => Y U MAD? couldn't load the specified file Something went wrong JUST GIMME SOME VALID RUBY! JUST GIMME SOME VALID RUBY!

O Ruby tambm possui uma clusula chamada ensure, que ser sempre executada independente de uma exceo ter sido lanada. Isso permite efetuar operaes que, em outras situaes, poderia deixar a sua aplicao 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 prtica fazer com que a clusula ensure execute apenas operaes simples e seguras, diminuindo a probabilidade de uma segunda exceo ser lanada, o que pode fazer com que as operaes de limpeza no sejam concludas, alm de tornar a depurao do erro mais complexa. Em algumas situaes, pode fazer sentido executar novamente um determinado bloco begin..end quando uma exceo lanada. a que entra a clusula retry. O exemplo abaixo mostra como tentar executar um determinado bloco trs 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 Trying Trying Sorry! #1 #2 #3 I couldn't make it work!

Dentro da clusula rescue pode existir qualquer tipo de cdigo. Voc pode, inclusive, lanar uma nova exceo ou, se preferir, a mesma exceo que foi capturada inicialmente. Isso permite criar, por exemplo, mecanismos que iro rastrear uma exceo lanada. O exemplo seguir mostra como enviar para um arquivo de log qualquer tipo de exceo, 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 excees utilizam os mtodos Exception#exception e Exception.exception para debnir se uma nova exceo deve ser instnciada ou se a prpria instncia que deve ser utilizada. Embora excees possam ser utilizadas para controlar cuxos de aplicaes, este no o mecanismo mais recomendado para a tarefa. Neste caso, voc deve usar os mtodos Kernel#throw e Kernel#catch, criados para interromper rapidamente a execuo de loops aninhados e chamadas a mtodos sem necessariamente lanar uma exceo. O mtodo Kernel#throw permite lanar um smbolo que pode ser capturado com o mtodo Kernel#catch. Se voc executar o mtodo Kernel#throw com um argumento que ser usado como valor de retorno do mtodo Kernel#catch. O exemplo abaixo mostra como descobrir quantas vezes um loop teve que ser executado at que o nmero 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 execuo dos loops quando a letra n e o nmero 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

CAPTULO 1

Conhecendo o Rake
Hmmm. Este contedo est sendo escrito e estar disponvel em breve.

151

CAPTULO 1

Escrevendo testes com Test::Unit


Unit testing uma tcnica de testes que foca em pequenos trechos de cdigos, normalmente cada um dos mtodos pblicos que seu cdigo implementa.
Testes ajudam os desenvolvedores a escrever cdigos melhores. Quando seu cdigo ainda no foi escrito, testes ajudam a pensar em um design de cdigo mais desacoplado. Enquanto seu cdigo est sendo escrito, testes ajudam a veribcar que suas alteraes no quebraram outras partes do cdigo. Depois que seu cdigo foi escrito, testes ajudam no processo de refatorao, dando feedback instntaneo caso alguma coisa deixe de funcionar, alm de ajudar novos desenvolvedores que tenham que manter uma base de cdigo. O Ruby vem com seu prprio framework de testes chamado Test::Unit. No Ruby 1.9 ele foi reescrito em cima de um outro framework mais simples, que tambm 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 prximo 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 padro. Voc ir especibcar um resultado esperado e ir compar-lo com o resultado gerado por seu cdigo. Caso esta comparao falhe, o framework de testes ir exibir uma mensagem dizendo qual trecho de cdigo falhou. No nem preciso dizer que este tipo de abordagem inbnitamente melhor que testar todo um cuxo de aes manualmente cada vez que uma alterao for realizada. Para mostrar como usar o framework de testes Test::Unit, ns iremos implementar um conversor de temperaturas. Antes de comear, vamos organizar o nosso cdigo de modo que ele possa ser facilmente distribudo depois como uma gem.

152

Organizando o cdigo
Bibliotecas do Ruby seguem mais ou menos o mesmo padro. O cdigo-fonte normalmente bca em um diretrio lib. Caso sua biblioteca seja composta por mais de um arquivo, um diretrio com o mesmo nome da biblioteca deve ser criado no diretrio lib. Arquivos de teste devem ser criados no diretrio test. Organize seu diretrio de modo que ele seja parecido com a imagem seguir.

Figur Figura a 1: Estrutura inicial da biblioteca Temperature

Para executar os testes ns iremos utilizar uma rake task. Crie o arquivo Rakefile na raz de seu projeto com o seguinte contedo.
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 disponveis, 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 esto sendo debnidos corretamente. Crie o arquivo test/temperature_test.rb. Esse arquivo ser responsvel por carregar o cdigo de nossa biblioteca, alm de debnir um caso de teste teste, que apenas uma classe que herda da classe Test::Unit::TestCase. Todos os testes so mtodos que comeam com as letras test, mas o padro 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 veribcaes que seu teste far so chamadas de asseres asseres. No Test::Unit, voc ir usar os mtodos assert_* para garantir que seu cdigo est de acordo com uma determinada condio. Execute os testes com o comando rake test. Voc ver que este teste ir falhar com um erro. Isso acontece porque ainda no debnimos nossa classe Temperature.
$ rake test # Running tests: E 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 cdigo em pequenos passos, voc deve executar os testes a cada alterao. Faa 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 no espera receber nenhum argumento, mas estamos passando dois. Crie o mtodo Temperature#initialize de forma que ele receba estes dois parmetros.
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 no 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 mtodo 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 no est sendo debnido. Para fazer isso, basta fazer a atribuio dos valores recebidos como argumentos para as variveis de instncia 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 iro 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 prprio teste passasse. Esta tcnica chamada de Test-Driven Development. Nossa classe Temperature ter trs mtodos diferentes que iro permitir a converso entre as diferentes unidades. Estes mtodos sero 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 40C, que so equivalentes a 104F. 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 mtodo Temperature#to_fahrenheit no existe. Adicione este mtodo.
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 frmula que faz a converso 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 diviso no seja feita entre dois nmeros inteiros. Execute os testes mais uma vez. Veja que a converso est sendo feita de forma correta. Um problema de nossa implementao que estamos assumindo que a temperatura inicial ser sempre em Celsius. Mas o que acontece se passarmos uma unidade diferente, que exige a aplicao de uma outra frmula? Obviamente, o clculo ser feito de forma incorreta.
159

Para evitar que nossos mtodos tenham diversas expresses condicionais que iro fazer o clculo de acordo com a unidade atual, ns iremos sempre utilizar o mtodo Temperature#to_celsius como base para os demais clculos. Dessa forma, teremos que converter apenas de Celsius para Fahrenheit e de Celsius para Kelvin. Altere a classe Temperature de modo que o mtodo que o mtodo Temperature#to_fahrenheit utilize o retorno do mtodo 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 iro falhar dizendo que o mtodo Temperature#to_celsius ainda no existe. Vamos adicionar alguns testes para garantir que este mtodo converta os valores corretamente. Primeiro, vamos escrever um teste para a converso 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 continuaro falhando. Altere o mtodo 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 iro passar. Adicione mais um teste para garantir que a converso 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 converso ao mtodo Temperature#to_celsius. A frmula que faz a converso 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 converso que precisa ser feita de Kelvin para Celsius. Esta frmula 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 converso 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 converso de temperaturas em Fahrenheit e Kelvin em Celsius j podem ser realizadas, embora o contrrio ainda no seja verdade. Ainda falta implementarmos a converso para Kelvin. O prximo 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 mtodo Temperature#to_kelvin no foi debnido. Voc pode, ento, implementar este mtodo. A frmula de converso 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

Parabns! 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 mtodos de assero


Embora tenhamos escritos alguns testes, todos eles usaram o mesmo mtodo de assero assert_equal. O Test::Unit implementa muito mais mtodos como voc pode conferir abaixo.
assert(boolean) assert_block(&block) assert_empty(list) assert_equal(expected, actual) assert_in_delta(expected, actual, delta)

Falha se boolean for nil ou false Falha se o bloco block for nil ou false Falha se o mtodo list#empty? retornar false Passa se expected for igual a actual quando o operador == usado Passa se o nmero de ponto cutuante expected for igual a actual dentro do delta esperado Calcula o delta com epsilon * min(expected, actual) e ento executa assert_in_delta com este valor Veribca se collection inclui o objeto element com o mtodo collection#include? Veribca se object uma instncia da classe klass Veribca se object do tipo da classe klass com o mtodo
object.kind_of?(klass)

assert_in_epsilon(expected, actual, epsilon)

assert_includes(collection, element) assert_instance_of(klass, object) assert_kind_of(klass, object)

assert_match(regexp, string) assert_nil(object)

Veribca se string casa a expresso regular regexp Veribca se object nil

167

assert_operator(object1, operator, object2)

Veribca se o envio da mensagem operator para object1 com o parmetro object2 retorna true Veribca se o bloco block lana uma das excees listadas quando executado

assert_raises(Exception, ..., &block) assert_raise(Exception, ..., &block) assert_respond_to(object, message) assert_same(expected, actual) assert_send(array)

Veribca se object responde a message com o mtodo object.respond_to? Veribca se expected igual a actual com o mtodo expected.equal? Veribca se o valor de retorno do envio da mensagem array[1] para o objeto array[0], com o restante do array como parmetros, true Veribca se a execuo do bloco block lana o smbolo expected com o mtodo throw Veribca expected diferente de actual quando o operador == usado Veribca se a string string no casa a expresso regular regexp Veribca se object no nil Veribca se expected diferente de actual com o mtodo expected.equal? Veribca se o bloco executado sem lanar as excees listadas Veribca se o bloco executado sem lanar o smbolo expected com o mtodo throw Sempre falha o teste com a mensagem message Faz com que o teste no seja executado Faz com que o teste sempre passe

assert_throws(expected, &block) assert_not_equal(expected, actual) assert_not_match(regexp, string) assert_not_nil(object) assert_not_same(expected, actual) assert_nothing_raised(Exception, ..., &block) assert_nothing_thrown(expected, &block) flunk(message = "Epic Fail!") skip(message = nil) pass

168

CAPTULO 1

Criando e distribuindo gems


RubyGems o gerenciador de pacotes padro do Ruby. partir do Ruby 1.9, comeou a ser distribudo como parte da standard library. Ele permite resolver dependncias e resolve, inclusive, dependncias entre verses. Cada pacote, chamado de gem, pode conter arquivos Ruby que podem ser carregados pelo seu prprio cdigo. Voc pode instalar gems disponibilizadas por outros desenvolvedores e pode criar e distribuir suas prprias 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 dvida, o arquivo .gemspec. nele que so debnidas as informaes sobre como seu pacote ser distribudo. Este arquivo contm o nome e verso da sua gem, arquivos que podem ser carregados, informaes sobre os autores, dentre outras informaes. Os cdigos que podem ser carregados bcam no diretrio lib. Caso sua gem inclua arquivos executveis, eles devem bcar no diretrio bin. Uma estrutura muito usada por conveno pode ser vista seguir:
example Rakefile bin example example.gemspec lib example version.rb example.rb

169

test example_test.rb

Criando sua prpria gem


Neste captulo voc ver como distribuir a biblioteca Temperature, criada no captulo anterior. O primeiro passo debnir o arquivo
.gemspec. Crie o arquivo simple_temperature.gemspec[1] na raz 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 especibcao, estamos debnindo uma srie de informaes: o nome da gem, debnido como simple_temperature a verso da gem, que vem do arquivo lib/temperature/version.rb e que ainda no 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 descrio sobre o que nossa gem faz e que pode ser exibida com o comando gem list simple_temperature. os arquivos que compes a gem, que neste exemplo tudo o que est dentro do diretrio lib. os arquivos de teste, que neste exemplo tudo o que est dentro do diretrio 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 raz 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 instalao, use o parmetro --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 atravs do IRB.
$ irb >> require "temperature" => true >> Temperature.new(60, :celsius).to_fahrenheit => 140.0 >>

Note que no estamos carregando a gem simple_temperature. Toda vez que voc usa require, todos os arquivos das gems que esto no diretrio lib bcam disponveis no $LOAD_PATH. Por isso podemos simplesmente carregar o arquivo temperature.rb. Em uma situao normal, onde o nome do arquivo principal da gem recete o nome da prpria gem, isso no seria motivo de confuso.

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 no seja muito prtico. Uma outra maneira distribuir sua gem publicamente atravs do site http://rubygems.org/. Para fazer isso, voc precisar criar sua conta. Acesse o endereo 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 publicaes sero 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 publicao da gem pode demorar alguns minutos. Para veribcar se sua gem j est disponvel 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 tambm pode ser acessada atravs do endereo https://rubygems.org/gems/simple_temperature. Aproveite que voc acessou a pgina de sua gem e atualize as outras informaes como endereo de onde as pessoas podero reportar bugs e visualizar o cdigofonte.

173

Mais sobre RubyGems


Uma gem basicamente o que voc acabou de ver. No entanto, voc pode ir alm. possvel, por exemplo, criar extenses nativas usando C. Voc tambm pode criar o seu prprio servidor de gems, privado, onde s suas aplicaes podem acessar. Desta forma, voc pode distribuir bibliotecas entre os diversos aplicativos de sua empresa sem se preocupar com a privacidade e segurana. Para mais informaes sobre como fazer estas e outras coisas, visite o guia publicado pelo site RubyGems.org no endereo http://guides.rubygems.org/.

174

Copyright Hellobits & Nando Vieira. Todos os direitos reservados. Veja outras publicaes em http://howtocode.com.br.