Professional Documents
Culture Documents
interpretador ficar feliz em fazer com que o objeto seja tratado como um pato.
Na prtica, isso significa que em vez de fazer verificaes 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 significa 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 flexveis, com extrema facilidade.
Isso no significa 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 flutuantes, 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 verificar se esse mtodo existe no
receiver padroself a menos que voc especifique-o explicitamente. E, pelo
fato de o receiver padro ser self, voc nem precisa especific-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 ficasse mais poludo. No entanto, definir 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"
Na prtica, o receiver especificado 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 definir variveis de instncia em qualquer objeto, como classes.
class Person
def self.count
@count ||= 0
end
def self.count=(increment)
@count = increment
end
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 definindo um contador de instncias da
classe Person, cujo valor ser armazenado em @count. Depois, em nossa prpria
instncia, definimos 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.
Confira neste captulo quais so as convenes mais utilizadas e evite olhares
estranhos.
Nomeando classes, variveis e constantes
Use o formato snake_case para definir variveis.
# 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 prefixo is.
# recomendado
def ready?
status == "ready"
end
# estranho
def is_ready?
status == "ready"
end
Mtodos que modificam 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.
# recomendado: 2 espaos
def hello
puts "Hello!"
end
# estranho: 4 espaos
def hello
puts "Hello!"
end
# mais estranho: hard tabs
def hello
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 final do seu arquivo.
Evite deixar espaamentos ao final da linha (trailing spaces).
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
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 definido 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()
#=> Hello
No entanto, se voc precisar encerrar o fluxo 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 blocos2. 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 palavraschave do..end.
File.open("file.txt", "w+") do |file|
file.write "Ruby"
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.
Conhecendo o IRB
O Ruby vem com um shell REPL1 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, tudo3 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:
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>
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 especificar 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.
$ ./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)
#=> "HELLO"
#=> false
GC.enable
GC.enable
#=> true
#=> false
1 Read-Eval-Print-Loop
2 No se preocupe com o que so blocos por enquanto. Voc ver mais sobre este
assunto mais__frente.
3 Na verdade, quase tudo no Ruby objeto. Algumas coisas no so objetos
diretamente, embora voc consiga acess-las de outras maneiras.
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 definir 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
puts 'Time: #{now}'
#=> Time: #{now}
Voc pode definir 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 definir strings de outras formas. Uma delas usando o formato
Output:
Fixnum => 1000000
Fixnum => 1000000000000
Bignum => 1000000000000000000000000
#=>
#=>
#=>
#=>
#=>
#=>
#=>
1234
1234
1234
-1234
1234
1234
1234
Float
Nmeros de ponto flutuante so definidos 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 flutuante no podem ser definidos em outra base diferente de 10.
puts
puts
puts
puts
1.234
-1.234
1_234.0
12e2
#=>
#=>
#=>
#=>
1.234
-1.234 - Negativo
1234.0 - Underscores so ignorados
1200.0 - 12.0 x 10e2
puts 12.3e2
puts 12.3E2
No Ruby, no possvel definir nmeros com ponto flutuante sem ter um nmero
antes do ponto decimal. Se voc tentar definir um nmero como .1 ir receber
uma mensagem de erro como no .<digit> floating literal anymore; put 0 before
dot.
Nmeros de ponto flutuante seguem a especificao IEEE-754, assim como a
maioria das linguagens e hardwares do mercado. Dada a forma como os nmeros de
ponto flutuante 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 linguagens2.
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 financeiros. 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 especificar 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.
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 = Array.new(10, "Hello", :ruby)
items = [10, "Hello", 3.5]
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.
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.
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
#=> [0, 2]
#=> [16, 18]
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 definir 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!!!
Voc tambm pode definir 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
identificam a chave e o valor.
user = Hash[[["name", "John Doe"], ["age", 32]]]
#=> {"name" => "John Doe", "age" => 32}
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 definir 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
identificadores, 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
nil
O Ruby define 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
hello.class
#=> NilClass
def hello
return
end
hello.class
#=> NilClass
Range
Para definir intervalos de nmeros e strings podemos utilizar a classe Range.
numbers = 1..10
numbers.class
#=> Range
letters = "a".."z"
letters.class
#=> Range
possvel definir 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.
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 definir 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 verificar 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 verificar se uma string
contm um determinado padro ou para extrair partes dessa string. Para criar
uma expresso regular voc deve definir o padro utilizando a sintaxe /pattern/
ou %r(pattern).
regex = /hello/
Alguns caracteres precisam ser escapados pois eles tem um significado 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 definirmos 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 verificar
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 definir uma nova proc, voc pode utilizar o mtodo Proc.new ou o mtodo
Kernel#proc.
# alternativa 1
message = Proc.new { "Hello" }
# alternativa 2
message = proc { "Hello" }
Para executar essas procs voc pode utilizar trs mtodos diferentes.
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.
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
definido.
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
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}>
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.
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"
elsif x == 3
name = "three"
end
puts name.inspect
#=> nil
No exemplo acima, estamos verificando 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
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
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"
x
1, 2, 3 then "one, two, or three"
4, 5, 6 then "for, five, or six"
"other"
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 === implementado1
pelas classes e mdulos ir verificar 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
verificar se a classe daquela instncia a prpria classe ou um de seus
descendentes.
value = "Hello"
case
when
"A
else
"Y
end
value
String
String was provided"
U MAD BRO?"
Estruturas de repetio
O Ruby possui trs expresses que definem 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
especificada. 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.
inside loop: 1
inside loop: 2
inside loop: 3
outside loop: 3
No caso de hashes, voc pode definir 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.
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 modificadores. 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]
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 definir blocos begin..end para utilizar estes modificadores
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
condio ser testada. Sendo assim, o bloco begin..end seguir sempre exibir a
mensagem OH NOES! THIS WILL BE DISPLAYED ANYWAY!.
begin
puts "OH NOES!"
puts "THIS WILL BE DISPLAYED ANYWAY!"
end while false
Embora seja uma
desencorajado e
possvel ter um
basta delimitar
(
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 indefinidamente 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
Controlando o loop
Muitas vezes necessrio controlar o fluxo de execuo de um loop. s vezes
preciso interromper a execuo, outras preciso passar para o prximo tem da
iterao em alguma condio especfica. 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
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:
number:
number:
number:
1,
2,
3,
3,
3,
3,
4,
tries:
tries:
tries:
tries:
tries:
tries:
tries:
0
0
0
1
2
3
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!" }
Output:
1
2
3
Output:
3
2
1
Existe ainda um outro mtodo chamado Integer#step. Este mtodo permite fazer
iteraes usando nmeros inteiros e de ponto-flutuante.
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
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]
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
deve 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|
sum += number * 2 if number.even?
end
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.
Escopo de variveis
Blocos introduzem um novo escopo de variveis. Toda vez que voc define
parmetros que sero recebidos pelo bloco, estas variveis sero acessveis
apenas no contexto do bloco.
O exemplo seguir mostra como a varivel i definida 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 modificar 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 modificam
variveis de mesmo nome que foram definidas 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
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 verificar se algum bloco
foi passado com o mtodo Kernel#block_given? e tomar as aes necessrias.
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
O yield quase sempre suficiente. 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.
Criando classes
Para definir 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 dinamicamente1. Isso faz com que a linguagem se
torne extremamente poderosa e flexvel.
class Page
end
AnotherPage = Class.new
Page.class
#=> Class
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 definido 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
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 definir essas variveis de instncia sem
ser pelo mtodo construtor. Para fazer isso, preciso definir mtodos setters.
Em outras linguagens, normalmente isso feito com um mtodo setTitle(title) ou
set_title(title), ou algo parecido. No Ruby, voc pode definir o seu prprio
mtodo title=.
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
page.title = "Learning Ruby"
page.title
#=> Learning Ruby
Esta definio de getters e setters em classes to comum que o prprio Ruby
fornece uma maneira de automatizar esta definio. Basta usar os mtodos
Module#attr_reader e Module#attr_writer2.
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 definir o mtodo de instncia de leitura
(getter), enquanto o mtodo Module#attr_writer ir definir o mtodo de
instncia de escrita (setter). Para os casos onde voc sempre define 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 ficar muito mais simples.
class Page
attr_accessor :title, :content
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 definirem 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 definir o objeto que ir receber o valor,
chamado de receiver.
O exemplo seguir define 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
def initialize(title, content)
self.title = title
self.content = content
end
end
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
@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
private
privados
def method2
end
protected
protegidos
def method3
end
public
pblicos
def method4
end
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
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
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
ficar 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
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
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
Entendendo classes Singleton
Todo objeto do Ruby est associado a duas classes: a classe que a instanciou e
uma classe annima, escondida, especfica do objeto. Esta classe annima
chamada de Singleton Class, mas antes de ter um nome oficial 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 definir mtodos em uma classe.
class Person
class << self
def count
@count ||= 0
end
end
end
Aqui, estamos definindo um mtodo na classe Singleton do objeto Person (lembrese: tudo no Ruby objeto, inclusive classes). Como consequncia, isso ir
definir 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
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 definidos, 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"]
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.
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 Marshal3 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 definimos 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"]
Com base nesse exemplo, possvel afirmar que mtodos de classe no existem no
Ruby! Pelo menos no no sentido de mtodos estticos! O que acontece que
estes mtodos pertencem a um objeto, que por acaso uma classe.
1 Classes criadas dinamicamente podem ser atribudas a qualquer tipo de
varivel, e no apenas a constantes.
2 A classe Class possui a classe Module como superclasse. Para saber mais sobre
mdulos, leia Mdulos.
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.
Mdulos
Hmmm.
Este contedo est sendo escrito e estar disponvel em breve.
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
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.
"%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, prefira
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
coding: utf-8
encoding: utf-8
-*- coding: utf-8 -*vim:fileencoding=utf-8
vim:set fileencoding=utf-8 :
#=> ISO-8859-1
Nem todas as strings so compatveis entre diferentes codificaes. Toda vez
que voc tentar fazer a converso entre codificaes 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 codificao 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 codificao de uma string com o mtodo
String#force_encoding. Este mtodo no faz qualquer verificao 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 codificao 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
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]
Note como a string possui apenas 5 caracteres, mas precisa de 10 bytes
para ser representada.
Para acessar a lista de codificaes disponveis no Ruby, utilize o mtodo
Encoding.list. Algumas codificaes possuem um alias, cuja listagem completa
pode ser retornada pelo mtodo Encoding.aliases. O cdigo seguir far o
output das codificaes 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
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)
1 Veja mais detalhes sobre codificao de caracteres em Codificao.
2 Na verdade, magic comments seguem a PEP-263 definida pelo Python.
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
#=> 4
2 * 1.3 #=> 2.6
-2 * 1.3 #=> -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.
3 / 2
#=> 1
3 / 2.0 #=> 1.5
2 / 1.75 #=> 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
#=>
#=>
#=>
#->
#=>
#=> false
#=> true
0.zero?
0.nonzero?
#=> true
#=> false
#=>
#=>
#=>
#=>
#=>
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.
"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 flutuantes
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 flutuante para o nmero de
casas decimais que foram especificadas. 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
1.234567.round(2)
1.234567.round(3)
1.234567.round(4)
1.234567.round(5)
#=>
#=>
#=>
#=>
1.23
1.235
1.2346
1.23457
12345.67.round(-5)
12345.67.round(-4)
12345.67.round(-3)
12345.67.round(-2)
12345.67.round(-1)
12345.67.round(0)
#=>
#=>
#=>
#=>
#=>
#=>
0
10000
12000
12300
12350
12346
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 especfica 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]
items.delete("a")
#=> a
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 especificado 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 modificado.
items = [1, 2, 3]
items.drop(1)
#=> [2, 3]
items
#=> [1, 2, 3]
items = items.drop(2)
#=> [3]
Por fim, 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 true2, 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]
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 identificado 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 <=>.
items = %w[a d c b i k]
items.sort
#=> ["a", "b", "c", "d", "i", "k"]
A classe Array possui muitos mtodos que modificam 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 especificar 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
#=> true
#=> true
#=> false
#=> 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
#
#
#
#
Output:
3
2
1
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
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.
1 Este mtodo tambm conhecido como shovel.
2 Ou seja, qualquer valor diferente de false e nil.
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 eficientes 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 eficiente quanto o contrrio.
A classe Hash possui quatro mtodos diferentes para detectar se uma chave foi
definida. 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
options.key?("color")
#=> false - As chaves so smbolos
options.has_key?(:color) #=> true
options.include?(:width) #=> true
options.member?(:height) #=> 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 define 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 definida, nil ser retornado por padro.
options = {name:
options[:name]
options[:age]
options[:email]
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
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")
options.delete(:color)
options.delete(:invalid) do |key|
#=> nil
#=> green
#=> 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
options
#=> {:two=>2, :four=>4}
Alternativamente, voc pode usar o mtodo Hash#reject! que tambm ir modificar
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 fim, 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"]
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.
options = {name: "John Doe", age: 32}
options.each_key {|key| puts "#{key} => #{options[key]}"}
# Output:
# name => John Doe
# age => 32
Por fim, 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
Trabalhando com arquivos e diretrios
Manipulando nomes de arquivos
A classe File
diretrios. O
operacionais,
Windows, pode
fullpath = "/Users/fnando/ruby/samples.rb"
dir = File.dirname(fullpath)
#=> /Users/fnando/ruby
file = File.basename(fullpath)
#=> samples.rb
extension = File.extname(file)
#=> .rb
File.basename(fullpath, extension) #=> samples - Remove a extenso
File.dirname(file)
#=> retorna ".", o diretrio atual
File.join(dir, file)
#=> concatena com o separator de
diretrios
Nenhum destes mtodos ir verificar 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")
usr/bin
File.expand_path("ruby")
File.expand_path("~/ruby")
#=> /Users/fnando/ruby
File.expand_path("ruby", "/usr/local/bin") #=> /usr/local/bin/ruby
File.expand_path("~fnando/ruby")
#=> /Users/fnando/ruby
# 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 flexvel, este fluxo est sujeito
desenvolvedor mais descuidado pode no encerrar o uso de IO.
isto acontea, recomendado que voc use o mtodo File#open
bloco ir receber o objeto File e, aps a execuo do bloco,
automaticamente encerrado.
falhas. Um
Para evitar que
com um bloco. Este
ter o uso de IO
File.open(__FILE__) do |file|
content = file.read
end
Por padro, os mtodos File.open e File.new iro abrir o arquivo em modo de
leitura. Voc pode especificar a flag que ser usada na hora de abrir um
arquivo.
path = "/tmp/sample.txt"
File.open(path)
File.open(path,
File.open(path,
existente.
File.open(path,
existente.
File.open(path,
File.open(path,
File.open(path,
"r")
"w")
"a")
"w+")
"a+")
"rb")
page = open("http://localhost/").read
path = "/tmp/localhost.html"
File.open(path, "w") do |file|
file.write page
end
puts File.read(path)
Note que estamos usando o mtodo File#write para adicionar o contedo ao
arquivo. Voc tambm poderia ter usado o mtodo File#<<.
Para modificar um arquivo existente, voc deve se certificar que est usando a
flag 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
# 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 codificao, que
pode ser especificada no momento da abertura do arquivo para leitura e/ou
escrita. Voc pode fazer converso entre diferentes tipos de codificao 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 codificao 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")
#=> true
#=> false
File.file?(dir)
File.directory?(dir)
#=> false
#=> true
File.file?("/invalid")
File.directory?("/invalid")
#=> false
#=> false
#=> true
#=> false
#=> true
#=>
#=>
#=>
#=>
#=>
true
true
false
493
nil
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.
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 especfico quanto s
excees que voc quer capturar.
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 define 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!"
end
end
#
#
#
#
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.
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
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 definir se uma nova exceo deve ser instnciada ou se
a prpria instncia que deve ser utilizada.
Embora excees possam ser utilizadas para controlar fluxos 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
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}"
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 significa 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 definimos 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
Defina 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 definido. 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
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.
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
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
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
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 definido. Voc pode, ento, implementar este
mtodo. A frmula de converso de Celsius para Kelvin K = C + 273.15.
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:
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
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
(object)
assert_not_same
ed.equal?
(expected, actual)
assert_nothing_raised
(Exception, ...,
&block)
assert_nothing_thrown
(expected,
m o mtodo throw
&block)
flunk(message = "Epic
Fail!")
skip(message = nil)
pass
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 especificao, estamos definindo uma srie de informaes:
* o nome da gem, definido como simple_temperature
* a verso da gem, que vem do arquivo lib/temperature/version.rb e que ainda
no foi criado.
* 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.
$ gem install simple_temperature --local
Successfully installed simple_temperature-0.1.0
1 gem installed
Agora, vamos nos certificar 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 ficam
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
reflete 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.
$ 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 verificar 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 cdigo-fonte.
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