conhecendo ruby

Upload: lucas-fonseca

Post on 28-Oct-2015

146 views

Category:

Documents


0 download

DESCRIPTION

Programando com Ruby

TRANSCRIPT

  • 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]