Download - Conhecendo Ruby
-
Conhecendo Rubypor 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
-
Contedo1 IntroduoIntroduo
2 Sobre o RubSobre o Rubyy
2 Instalao
5 Tipo
9 Variveis e constantes
9 Mtodos
9 Entendendo o self
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 RubRuby Core Classesy Core Classes
26 String
30 Nmeros
33 Array
35 Hash
37 Symbol
38 Boolean
39 nil
40 Range
41 Expresses regulares
42 Procs e lambdas
47 Set
49 EstruturEstruturas condicionaisas condicionais
49 if
50 else
51 elsif
52 unless
53 Operador ternrio ?:
54 case
59 EstruturEstruturas de repetioas de repetio
59 for..in
-
61 while e until
63 loop
64 Controlando o loop
66 Usando iteradores
71 BlocosBlocos
72 Escopo de variveis
73 Interagindo com blocos
76 ClassesClasses
76 Criando classes
78 Debnindo mtodos
83 Debnindo mtodos de classe
89 Debnindo constantes
92 Entendendo classes Singleton
97 MdulosMdulos
98 TTrrabalhando com o load pathabalhando com o load path
99 TTrrabalhando com stringsabalhando com strings
99 Concatenando strings
100 Formatando strings
101 Convertendo em maisculas e minsculas
102 Tamanho de uma string
103 Substrings
105 Codibcao
110 TTrrabalhando com nmerosabalhando 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 TTrrabalhando com arrabalhando com arraaysys
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
-
126 TTrrabalhando com hashesabalhando com hashes
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 TTrrabalhando com arquivabalhando com arquivos e diretriosos e diretrios
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 TTrrabalhando com data e horabalhando com data e horaa
143 URLs e requisies HTTPURLs e requisies HTTP
144 Lidando com eLidando com exxceescees
145 Capturando excees
151 Conhecendo o RakConhecendo o Rakee
152 EscreEscrevvendo testes com Tendo testes com Test::Unitest::Unit
153 Organizando o cdigo
154 Convertendo temperaturas
167 Lista de mtodos de assero
169 Criando e distribuindo gemsCriando e distribuindo gems
169 A estrutura de uma gem
170 Criando sua prpria gem
172 Distribuindo sua gem
174 Mais sobre RubyGems
-
CAPTULO 1
IntroduoRuby 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.Hmmm.
Este contedo est sendo escrito e estar disponvel em breve.
Nando Vieira, Janeiro de 2012
1
-
CAPTULO 1
Sobre o RubyInstalaoO 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-p01.9.3-p0.
Instalando no WindowsA 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.
FigurFigura 1:a 1: Download do Ruby Installer
2
-
Execute o arquivo baixado, algo como rubyinstaller-1.9.3-p0.exe, e o instalar ser iniciado. Aceite os termos de uso. Ser exibida,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.
FigurFigura 2: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 tambmpode instalar o PowerShell manualmente.
3
-
FigurFigura 3: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 nofoi completada com sucesso. Neste caso, reinicie a instalao e siga exatamente os passos descritos acima.
Instalando no Mac OS XEmbora o Mac OS X j venha com o Ruby instalado por padro, a sua verso muito antiga.
Hmmm.Hmmm.
Este contedo est sendo escrito e estar disponvel em breve.
4
-
Instalando no Linux UbuntuNo 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 < Fixnum
value = "Hello"puts value.class#=> String
O Ruby tambm uma linguagem fortemente tipadafortemente tipada, j que o tipo do objeto veribcado antes de efetuar determinadas operaes.
5
-
number = 1234string = "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 serfeita explicitamente.
Duck TypingNo 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 deSe anda como um pato e faz barulho como um pato, ento devve ser um pato.e 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
- def log(message)@io
-
{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. Oexemplo abaixo mostra como criar uma classe cuja instncia pode ser convertida em nmeros inteiros e cutuantes, mas no em
instncias da classe BigDecimal.
class NumberOnedef coerce(object)
case objectwhen Integer
[object, 1]when Float
[object, 1.0]else
raise TypeError, "#{self.inspect} can't be coerced into #{object.class}"end
endend
puts 1 + NumberOne.new#=> 2
puts 1.0 + NumberOne.new#=> 2.0
require "bigdecimal"
8
-
puts BigDecimal.new("1.0") + NumberOne.new#=> TypeError: FakeNumber can't be coerced into BigDecimal
O duck typing vai alm de simples regras; um estilo de programao. Antes de exigir tipos de objetos, pergunte-se se isso realmentenecessrio. s vezes, o tipo do objeto muito importante, mas muitas vezes isso simplesmente no importa.
Variveis e constantes
Hmmm.Hmmm.
Este contedo est sendo escrito e estar disponvel em breve.
Mtodos
Hmmm.Hmmm.
Este contedo est sendo escrito e estar disponvel em breve.
Entendendo o selfself 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
9
-
class Thingputs self
end#=> Thing
Sempre que executar um mtodo, o Ruby ir veribcar se esse mtodo existe no receiver padroself a menos que voc especibque-o explicitamente. E, pelo fato de o receiver padro ser self, voc nem precisa especibc-lo se no quiser.
class Thingdef do_something
puts "doing something"end
def do_something_elsedo_something
endend
No mtodo do_something_else poderamos usar self.do_something, mas isso faria com que nosso cdigo apenas bcasse maispoludo. No entanto, debnir o receiver uma coisa muito comum e que, voc pode at no se dar conta, mas o faz constantementequando 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 exemploabaixo.
class Persondef initialize(name)
@name = nameend
def name@name
endend
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 mtodoPerson#name. Analogamente, podemos debnir variveis de instncia em qualquer objeto, como classes.
class Persondef self.count
@count ||= 0end
def self.count=(increment)@count = increment
end
11
-
def initialize(name)@name = nameself.class.count += 1
end
def name@name
endend
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 nomecom a varivel @name.
ConvenesOs 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 constantesUse o formato snake_case para debnir variveis.
12
-
# recomendadosuccess_message = "You're done!"
# estranhosuccessMessage = "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.
# recomendadoSUCCESS_MESSAGE = "You're done!"
# estranhoSuccessMessage = "You're wrong!"
Mtodos predicados (aqueles que retornam valores booleanos) devem terminar com ? e no precisam de um prebxo is.
# recomendadodef ready?
status == "ready"
13
-
end
# estranhodef is_ready?
status == "ready"end
Mtodos que modibcam self, lanam excees ou so potencialmente perigosos/destrutivos devem terminar com uma exclamao.
# recomendadodef save!
save or raise("OMG!")end
Indentao, espaamento e quebras de linhaNo Ruby, trechos de cdigo so indentados em 2 espaos2 espaos.
# recomendado: 2 espaosdef hello
puts "Hello!"end
# estranho: 4 espaosdef hello
puts "Hello!"end
# mais estranho: hard tabsdef hello
14
-
puts "Hello!"end
Adicione espaamento em torno de operadores e depois de vrgulas.
# recomendadosum = 1 + 1x, y = 1, 2
# estranhosum = 1+1x,y = 1,2
No coloque espaamentos depois de [ e (, nem antes de ] e ).
# recomendadohello("John")numbers = [1, 2, 3]
# estranhohello( "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 doseu arquivo.
Evite deixar espaamentos ao bnal da linha (trailing spaces).
15
-
Definindo e executando mtodosQuando o mtodo receber argumentos, sempre coloque os parnteses e separe os argumentos corretamente.
# recomendadodef sum(n1, n2)
n1 + n2end
# estranhodef sum( n1, n2 )
n1 + n2end
# mais estranhodef sum n1, n2
n1 + n2end
Se o mtodo no recebe nenhum argumento, no adicione os parnteses.
# recomendadodef message
"Hello"end
# estranhodef message()
"Hello"end
16
-
A mesma regra deve ser aplicada quando voc estiver executando mtodos.
# recomendadouser.run
# estranhouser.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 Fooputs "You called the Foo method"
end
Foo.new #=> Instancia a classe FooFoo() #=> Executa o mtodo Foo()
Retorno de mtodos e blocosMtodos 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 texttext
end
message(nil)#=> Hello stranger!
message("Hello there!")#=> Hello there!
Usando blocosMtodos podem receber blocos[2]. Quando o seu bloco possuir mais de uma instruo ou precisar encadeado, utilize chaves ({ e }) paracriar 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"
2 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 RubyUma 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 documentaoou 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.Hmmm.
Este contedo est sendo escrito e estar disponvel em breve.
Constantes e variveis globais
Hmmm.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, executaesta 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.
$ irbirb(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=> Fixnumirb(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 queinstanciou 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 Read-Eval-Print-Loop3 Na verdade, quase tudo no Ruby objeto. Algumas coisas no so objetos diretamente, embora voc consiga acess-las de outrasmaneiras.
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, :=, : n1 + n2irb(main):007:1> end=> nilirb(main):008:0> sum(3, 2)=> 5irb(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 RubyA 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.rbHello!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 paraexecutar aquele arquivo.
#!/usr/bin/env rubyputs "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 precisarexecutar o interpretador Ruby.
22
-
$ ./hello.rbHello!Current time: 2011-12-23 10:44:30 -0200
Acessando a documentao do RubyAtualmente 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 como 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 garbagecollection mechanism. Some of the underlying methods are also available viathe 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 theiruppercase counterparts. The operation is locale insensitive---only characters``a'' to ``z'' are affected. Note: case replacement is effective only in ASCIIregion.
"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 waspreviously disabled.
GC.disable #=> falseGC.enable #=> trueGC.enable #=> false
25
-
CAPTULO 1
Ruby Core ClassesStringStrings 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.nowputs "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 withmultiple lines."
text = 'This can be a long text withmultiple 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 =
-
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 diferenaentre as duas variaes que a primeira forma exige que o identibcador de trmino esteja no comeo da linhacomeo da linha.
Voc pode executar mtodos em strings heredoc.
puts MULTIPLE LINES.
Uma outra caracterstica de strings heredoc que elas preservam espaos em branco no comeo da linha.
puts 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 usarqualquer 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: 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 estemotivo, no existem mtodos que podem
mudar o valor de um nmero; se voc tentar
fazer isso, receber a mensagem de erro Can'tchange 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].
FixnumNmeros 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 donumber *= numberputs "#{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 opcionalda base do nmero, seguidos pela sequncia de nmeros especibcados na base escolhida.
1234 #=> 12340d1234 #=> 1234 - Base decimal, padro1_234 #=> 1234 - Underscores so ignorados-1234 #=> -1234 - Negativo0x4d2 #=> 1234 - Hexadecimal02322 #=> 1234 - Octal0b10011010010 #=> 1234 - Binrio
FloatNmeros de ponto cutuante so debnidos pelo . (ponto decimal) aps um ou mais nmeros decimais, seguido por mais nmerosdecimais. 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 1.234 #=> 1.234puts -1.234 #=> -1.234 - Negativoputs 1_234.0 #=> 1234.0 - Underscores so ignoradosputs 12e2 #=> 1200.0 - 12.0 x 10e2puts 12.3e2 #=> 1230.0 - 12.3 x 10e2puts 12.3E2 #=> 1230.0 - 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 . 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.
BigDecimalA classe BigDecimal permite realizar clculos onde o arredondamento muito importante, como em clculos bnanceiros. Nmeros dotipo BigDecimal so praticamente ilimitados (expoentes acima de 1 bilho so suportados) e possuem controle preciso dos modos dearredondamento.
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.
ComplexPara ver a referncia completa, acesse a documentao.
2 O mesmo problema acontece no C, Java, Python e JavaScript.
32
-
RationalPara ver a referncia completa, acesse a documentao.
ArrayO 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.newitems = 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 qualquerdelimitador.
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] #=> 0numbers.first #=> 0numbers[4] #=> 8numbers[-1] #=> 18 - A mesma coisa que numbers[numbers.size - 1]numbers.last #=> 18
Tambm possvel acessar intervalos passando dois nmeros.
numbers[0, 2] #=> [0, 2] - partir do ndice 0, pegue dois elementos.numbers[-2, 2] #=> [16, 18] - partir do ndice size - 2, pegue dois elementos.
HashUm outro tipo de estrutura de dados do Ruby o Hash. Hashes so como arrays, com a diferena que o ndice de um hash pode serqualquer 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 evalor.
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]
SymbolSmbolos 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 = :johnsymbol = :"john doe"symbol = "john".to_symsymbol = "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 = :johnname.object_id#=> 302248
other_name = :johnother_name.object_id#=> 302248
BooleanNo Ruby os valores booleanos so true e false, que so instncias das classes TrueClass e FalseClass. Infelizmente, ambas asclasses 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
nilO Ruby debne o tipo nulo atravs do do objeto nil, que uma instncia da classe NilClass. O nil ocupa sempre o mesmo espao emmemria 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 nextsem nenhum valor.
def helloend
39
-
hello.class#=> NilClass
def helloreturn
end
hello.class#=> NilClass
RangePara debnir intervalos de nmeros e strings podemos utilizar a classe Range.
numbers = 1..10numbers.class#=> Range
letters = "a".."z"letters.class#=> Range
possvel debnir um intervalo excluindo o ltimo elemento.
numbers = 1...10numbers.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 quetransformar 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 regularesExpresses regulares so padres (ou patterns) que permitem descrever o contedo de uma string. Elas podem ser utilizadas paraveribcar 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 sedebnirmos 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 lambdasProcs so blocos de cdigo que podem ser associados a uma varivel e que funcionam como funes anonnimas. Muitas vezes vocprecisa 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 1message = Proc.new { "Hello" }
42
-
# alternativa 2message = proc { "Hello" }
Para executar essas procs voc pode utilizar trs mtodos diferentes.
message = proc { "Hello" }
message.callmessage[]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 doputs "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 / n2end
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 explicitamentesum(1, 2) {|result| puts result}
# Convertendo uma proc em blocooutput = 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. Lambdasiro validar a quantidade de parmetros que foram passados e lanaro a exceo ArgumentError: wrong number of argumentscaso o nmero de argumentos seja incorreto. J as procs iro atribuir o valor nil para cada um dos parmetros.
3 Para saber mais sobre blocos, leia Blocos.
45
-
proc_message = proc {|message| p message}lambda_message = lambda {|message| p message}
proc_message.call#=> nil
lambda_message.call#=> ArgumentError: wrong number of arguments (0 for 1)
Uma outra diferena que se um return for debnido em uma proc, o mtodo que executou esta proc tambm ir encerrar o cuxo deexecuo. No caso de lambdas, o cuxo de execuo ser encerrado apenas no lambda que originou a chamada ao return.
def return_procproc { return }.callputs "return_proc"
end
def return_lambdalambda { return }.callputs "return_lambda"
end
return_procreturn_lambda
# Output:# return_lambda
Para descobrir quantos parmetros obrigatrios uma proc espera, use o mtodo Proc#arity. Se a proc possui argumentos opcionais, ovalor de retorno ser -n - 1, onde n a quantidade de parmetros obrigatrios.
46
-
Proc.new {}.arity #=> 0Proc.new {||}.arity #=> 0Proc.new {|a|}.arity #=> 1Proc.new {|a,b|}.arity #=> 2Proc.new {|a,b,c|}.arity #=> 3Proc.new {|*a|}.arity #=> -1Proc.new {|a,*b|}.arity #=> -2Proc.new {|a,*b, c|}.arity #=> -3
Para pegar uma representao dos parmetros que um bloco pode receber, use o mtodo Proc#parameters. Note que arepresentao 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]]
SetExistem 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-loitems
-
items [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#=> #
48
-
CAPTULO 1
Estruturas condicionaisA estrutura de controle mais comum em qualquer linguagem de programao a condicional. apenas uma maneira de executar umtrecho de cdigo se alguma condio for satisfeita. Uma condio uma expresso que quando veribcada retorna um valor diferentede false e nil.
O Ruby possui diferentes formas de expresses condies, como voc pode conferir seguir.
ifO 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. Noteque no necessrio adicionar parnteses em torno das condies.
O resultado de um if pode ser atribudo a uma varivel.
x = 1y = 0
y = if x > 0y + 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 emuito cexvel, nem sempre a melhor maneira. A mesma expresso poderia ser escrita de um modo muito mais legvel.
x = 1y = 0
if x > 0y += 1
end
puts y#=> 1
O if pode ser usado de forma inline, agindo como um modibcador.
x = 0x += 1 if x.zero?
A condio ser sempre a primeira a ser executada, mesmo ela sendo escrita por ltimo.
elseO if pode conter uma clusula else, que ser executada quando a condio no for satisfeita. Caso o valor expression seja igual afalse ou nil, ento o cdigo associado ao else ser executado.
50
-
if expression# do something
else# do something else
end
elsifSe 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. Noteque o else opcional.
x = 4name = nil
if x == 1name = "one"
elsif x == 2name = "two"
51
-
elsif x == 3name = "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 ifser satisfeita, o que faz com que o valor original de name no seja alterado. No Ruby o mtodo inspect normalmente retorna umarepresentao do self como uma string.
unlessUma 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 quecoloca a expresso a ser executada se o valor da condio no for false ou nil primeiro!
52
-
# estranhounless x
x = 1else
x += 1end
# recomendadoif x
x += 1else
x = 1end
Assim como o if, o unless tambm pode ser usado de forma inline.
x = 0x += 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 ounil.
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 iguala 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
singularelse
pluralend
end
A mesma condio poderia ser simplibcada usando o operador ternrio ?:.
def pluralize(count, singular, plural)count == 1 ? singular : plural
end
caseO 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.
casewhen x == 1 then "one" # if x == 1 then "one"when x == 2 then "two" # elsif x == 2 then "two"when x == 3 then "three" # elsif x == 3 then "three"else "other" # else "other"end # end
A palavra-chave then s necessria se voc quiser utilizar expresses na mesma linha do comparador. Alternativamente, voc podeutilizar 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 xwhen 1 then "one"when 2 then "two"when 3 then "three"else "other"end
O case tambm retorna o valor da expresso que for executada, podendo ser atribuda a uma varivel.
number = case xwhen 1 then "one"when 2 then "two"when 3 then "three"else "other"end
Voc pode debnir diversas comparaes em uma mesma expresso.
case xwhen 1, 2, 3 then "one, two, or three"when 4, 5, 6 then "for, five, or six"else "other"end
55
-
O case usa o operador === para fazer as 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 irveribcar 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 deString.=== (que na verdade implementada por Module#===) diferente de String#===, que compara se o objeto uma string e seela 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 classeou um de seus descendentes.
value = "Hello"
case valuewhen 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 textwhen /\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 naqueleintervalo.
number = 100
case numberwhen 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 outraslinguagens.
58
-
CAPTULO 1
Estruturas de repetioO Ruby possui trs expresses que debnem loops: for..in, while e until. Mas alm deles, possvel usar iteradores em objetosenumerveis como arrays e hashes, alm de outros objetos.
for..inO loop for..in permite iterar em objetos que so enumerveis, como o caso de arrays e hashes. A cada iterao, um elemento seratribudo 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 numbersputs "inside loop: #{number}"
end
puts "outside loop: #{number}"
Ao executar este cdigo, voc ver as seguintes mensagens.
59
-
inside loop: 1inside loop: 2inside loop: 3outside 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 numbersputs "#{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 numbersputs "#{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 untilO while e until so as formas mais bsicas de looping do Ruby. Eles iro executar algum trecho de cdigo enquanto uma condio forverdadeira ou at que uma condio se torne verdadeira. Note que primeiro a condio testadaprimeiro a condio testada e, ento, o cdigo executado.
x = 3
while x.nonzero?puts xx -= 1
end
O mesmo exemplo poderia ser escrito com o until.
x = 3
until x.zero?puts xx -= 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 = []
beginitems.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 testadaantes de a condio ser testada. Sendo assim, o bloco begin..end seguir sempresempre exibir a mensagem OH NOES! THIS WILL BE DISPLAYED ANYWAY!.
beginputs "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 versesfuturas 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
loopPara loops que no devem interrompidos, uma alternativa usar algo como while true. No entanto, o Ruby possui o loop, que irexecutar indebnidamente um bloco.
loop doputs Time.nowsleep 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 loopMuitas 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 loopPara encerrar um loop a qualquer momento, utilize a palavra-chave break. Isso far com que a execuo seja imediatamenteinterrompida.
x = 0
while x < 10x += 1puts x
break if x == 3end
# Output:# 1# 2# 3
Pulando para a prxima iteraoPara 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 < 5x += 1next unless x == 4puts x
end
# Output:# 4
Reiniciando a iteraoPara 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 numbersputs "number: #{number}, tries: #{tries}"
if tries < 3 && number == numbers[-2]tries += 1redo
endend
# Output:# number: 1, tries: 0# number: 2, tries: 0# number: 3, tries: 0# number: 3, tries: 1
65
-
# number: 3, tries: 2# number: 3, tries: 3# number: 4, tries: 3
Usando iteradoresEmbora loops como for..in, while/until e loop sejam teis para algumas situaes, mais provvel que voc escreva loopsutilizando 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 alinguagem seja elegante.
Existem outros iteradores numricos, que nada mais so que mtodos iteradores implementados pela classe Integer, assim como omtodo Integer#times.
O mtodo Integer#upto ir executar o bloco especibcado o nmeros de vezes que for debnido pelo inteiro, at atingir o nmero quefoi 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 ponto-cutuante.
O exemplo seguir ir iterar de 0 a 1, com passos de 0.25.
0.step(1, 0.25) do |number|puts number
end
# Output:# 0.0# 0.25# 0.5# 0.75# 1.0
67
-
Objetos enumerveisObjetos instanciados partir das classes Array, Hash e Range so enumerveis. O objeto considerado enumervel quandoimplementa 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 comoo 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 adicionamuitos 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 diferentesde 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 oselementos 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 queser 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 deO acumulador devve ser o valor de retorno do bloco.e 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
BlocosOs 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 sempresempre 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; casocontrrio, 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 commais 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 variveisBlocos 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 = 01.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 = 01.upto(10) {|i| }puts i#=> 0
72
-
Interagindo com blocosComo 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 sayputs 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, obloco ser executado trs vezes.
def helloyield "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, vocpode 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 sayraise "Y U MAD BRO? JUST GIMME A BLOCK!" unless block_given?puts yield
end
say { "Hello" }
beginsay
rescue Exception => errorputs 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 parmetropara 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 dalista.
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
ClassesO 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 classesPara debnir uma classe use a palavra-chave class. Classes so constantes e, por este motivo, devem comear com uma letra maiscula.
class Pageend
Classes so apenas objetos instanciados partir da classe Class. Por isso, voc pode instanciar classes dinamicamente[1]. Isso faz comque a linguagem se torne extremamente poderosa e cexvel.
class Pageend
AnotherPage = Class.new
Page.class#=> Class
1 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 mtodoPage.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 omtodo 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 Pagedef initialize(title, content)
@title = title@content = content
endend
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 tersua 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. Noentanto, ainda no temos nenhuma maneira de acessar tais valores diretamente.
Definindo mtodosPara acessar as variveis de instncia que debnimos no mtodo Page#initialize, ns iremos debnir dois getters, que so mtodosque 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-lasdiretamente.
page = Page.new("Ruby", "OMG! Ruby is awesome!")page.title#=> NoMethodError: undefined method title for #
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 Pagedef initialize(title, content)
@title = title@content = content
end
def title@title
end
def content@content
endend
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), oualgo parecido. No Ruby, voc pode debnir o seu prprio mtodo title=.
79
-
class Pagedef 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
endend
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 Pageattr_reader :titleattr_writer :title
attr_reader :contentattr_writer :content
def initialize(title, content)@title = title@content = content
endend
O mtodo Module#attr_reader ir debnir o mtodo de instncia de leitura (getter), enquanto o mtodo Module#attr_writer irdebnir o mtodo de instncia de escrita (setter). Para os casos onde voc sempre debne tanto o getter quanto o setter, possvel usar omtodo Module#attr_accessor, que far isso de uma vez s!
Com esta alterao, nossa classe pode bcar muito mais simples.
class Pageattr_accessor :title, :content
2 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
endend
Lembre-se que os mtodos Module#attr_accessor e companhia permitem criar apenas getters e setters que mapeiam para umavarivel 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 atribuiras 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 Pageattr_accessor :title, :content
def initialize(_title, _content)title = _titlecontent = _content
endend
Para atribuir os atributos Page#title e Page#content corretamente, preciso explicitamente executar os mtodos atravs doreceiver self.
class Pageattr_accessor :title, :content
82
-
def initialize(title, content)self.title = titleself.content = content
endend
A atribuio direta das variveis de instncia mais rpida que executar os mtodos atravs do receiver. A menos que voc manipuleas variveis no mtodo setter antes de atribu-las, prebra sempre debnir as variveis de instncia.
Definindo mtodos de classeClasses tambm podem ter mtodos. Algumas linguagens chamam estes mtodos de estticos. No Ruby, eles so apenas mtodosadicionados 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 umanova instncia da classe Page com os valores j atribudos.
require "yaml"
class Pageattr_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
-
endend
No Ruby, voc pode ler e gerar a representao de objetos com a classe YAML. Para isso, basta carregar a standard library com omtodo Kernel#require. O mtodo YAML.load_file l um arquivo e converte seu contudo em objetos Ruby. Neste exemplo, nossoarquivo deve retornar um hash.
Classes possuem um objeto self, assim como todos os objetos. Dentro da instruo class..end, o self faz referncia prpriaclasse. Por isso, uma abordagem mais comum usada por desenvolvedores mais experientes debnir mtodos de classe usando defself.load(file)..end, em vez de usar o nome da prpria constante.
require "yaml"
class Pageattr_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
endend
Como o nosso mtodo de classe est no contexto da prpria classe (lembre-se, o self faz referncia a prpria classe), ns podemosfazer mais uma alterao. Em vez de instanciar a classe Page.new, basta executar o mtodo new diretamente.
84
-
require "yaml"
class Pageattr_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
endend
A classe Page ainda no permite salvar sua representao em YAML. Vamos adicionar um mtodo Page#save_to(file) que fazexatamente isso.
require "yaml"
class Pageattr_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 = contentend
def save_to(filepath)File.open(filepath, "w") {|file| file.write to_yaml }
endend
A biblioteca YAML injeta um mtodo Object#to_yaml, que retorna uma string representando aquele objeto. No nosso caso, ele irretornar algo como a string abaixo.
--- !ruby/object:Pagetitle: Rubycontent: 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 Pageattr_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 }
endend
Visibilidade de mtodos e controle de acessoO 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 SomeClassdef method1 # mtodos so pblicos por padroend
private # todos os mtodos definidos partir daqui sero privadosdef method2end
protected # todos os mtodos definidos partir daqui sero protegidosdef method3end
public # todos os mtodos definidos partir daqui sero pblicosdef method4
87
-
endend
Alternativamente voc poderia ter debnido a visibilidade dos mtodos passando uma lista de nomes de mtodos.
class SomeClassdef method1end
def method2end
def method3end
def method4end
public :method1, :method4private :method2protected :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 SomeClassprivatedef some_private_methodend
end
88
-
object = SomeClass.newobject.some_private_method#=> NoMethodError: private method some_private_method called for #
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 Ruby1.9.
object = SomeClass.newobject.public_send :some_private_method#=> NoMethodError: private method `some_private_method' called for #
Definindo constantesMuitas 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 pginapoderia ser referenciada.
class Pageattr_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 }
endend
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 Pageattr_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 saveFile.open("/tmp/#{permalink}.yml", "w") {|file| file.write to_yaml }
endend
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 Pageattr_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 filepathFile.join(ROOT, "#{permalink}.yml")
end
def saveFile.open(filepath, "w") {|file| file.write to_yaml }
endend
91
-
Entendendo classes SingletonTodo 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
-
No Ruby 1.9, foi adicionado o mtodo Object#singleton_class, que apenas um atalho para a sintaxe class 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 Extensiondef sum
self.reduce(:+)end
end
numbers = [1,2,3]numbers.extend Extensionnumbers.singleton_methods#=> ["sum"]
Outra maneira usando a prpria classe Singleton.
numbers = [1,2,3]
class ["sum"]
Ou ainda, executando cdigo no contexto do objeto.
94
-
numbers = [1,2,3]
numbers.instance_eval dodef 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 defazer 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, percebaque exatamente isso que fazemos quando debnimos um mtodo em uma classe; a nica diferena que passamos o prprio self.
class Persondef self.say_hello
"Hello there!"end
end
Person.singleton_methods#=> ["say_hello"]
3 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 emtodos de classe no existem no Rubxistem no Rubyy! 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
MdulosHmmm.Hmmm.
Este contedo est sendo escrito e estar disponvel em breve.
97
-
CAPTULO 1
Trabalhando com o load pathHmmm.Hmmm.
Este contedo est sendo escrito e estar disponvel em breve.
98
-
CAPTULO 1
Trabalhando com stringsConcatenando stringsPara concatenar strings, voc pode utilizar os mtodos String#+, String# Ruby is nice!
puts "Ruby" 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#+ ircriar um novo objeto em memria, enquanto os mtodos String# Hello Ruby: 70310699719020
puts "#{hello}: #{hello.object_id}"#=> Hello: 70310699719100
99
-
hello 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 denmeros. 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" % 1.5 #=> 1 - nmero inteiro"%f" % 1.1245 #=> 1.124500 - ponto-flutuante com todos os nmeros"%.2f" % 1 #=> 1.00 - ponto-flutuante com duas casas decimais"%x" % 1234 #=> 4d2 - hexadecimal com letras minsculas"%X" % 1234 #=> 4D2 - hexadecimal com letras maisculas"%o" % 1234 #=> 2322 - nmero inteiro ctal"%e" % 1234 #=> 1.234000e+03 - nmero exponencial"%E" % 1234 #=> 1.234000E+03 - nmero exponencial em maiscula"%b" % 1234 #=> 10011010010 - nmero binrio
Para saber mais sobre os formatos aceitos, acesse a documentao; basta executar o comando ri String#sprintf.
Convertendo em maisculas e minsculasPara 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 osmtodos String#upcase! e String#downcase!.
text = "Ruby is nice!"puts text.object_id#=> 70347156470500
text.upcase!puts text.object_id#=> 70347156470500
Tamanho de uma stringPara saber o tamanho de uma string, utilize os mtodos String#size ou String#length. Note que esses mtodos iro retornar aquantidade de caracteres. Se voc precisa saber a quantidade de bytes necessrios para representar a string, utilize o mtodo
String#bytes[1].
1 Veja mais detalhes sobre codibcao de caracteres em Codibcao.
102
-
text = "Ruby is nice!"
puts text.size #=> 13puts text.length #=> 13
SubstringsO 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 numricadaquela 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] #=> ntext[-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] #=> nicetext[-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 podeutilizar intervalos com valores negativos.
text = "Ruby is nice!"text[0..3] #=> Rubytext[-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] #=> RUBYtext[/(ruby) (?ruby) (ruby) (ruby)/i, :upcase] #=> 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"] #=> Rubytext["ruby"] #=> nil
104
-
CodificaoUma 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 caracteresmultibytes, 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 #=> 13puts text.bytesize #=> 14
Perceba que a primeira linha debne qual o tipo de codibcao ser usada no arquivo. Esse comentrio chamado de magic comment esem 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 :
2 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 serusada 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 ouconverso 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-8puts text.valid_encoding?#=> false
106
-
Para acessar a representao nmerica de cada caracter de uma string, utilize o mtodo String#codepoints. Este mtodo espera umbloco, 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, vocpode 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]