Ruby é uma linguagem consistente, orientada à objeto e dinâmica, ou seja, você receberá um feedback a cada comando. Nesse artigo será mostrado seus conceitos, como fazer e utilizar suas classes e métodos.
Guia do artigo:
- Sintaxe da linguagem
- Variáveis
- Operadores
- Tipos de dados Ruby
- String
- Números
- Symbol
- Array
- Hash
- Lógica
- Blocos e Iterações
- Classes, Métodos e Objetos
- Métodos
- Objetos
- Classe
- Fazendo uma Classe
Ao baixar seu pacote, um interpretador interativo chamado irb é instalado. Utilizando este no prompt de comando (cmd ou Terminal) você poderá executar os códigos. Para iniciar uma sessão abra seu prompt e digite:
$ irb
irb(main):001:0>
Agora você está dentro do Ruby.
Caso você ainda não tenha nenhum conhecimento da linguagem, aconselho que primeiro veja uma introdução sobre o Framework Ruby on Rails e caso já tenha um certo conhecimento e gostaria de se aprofundar ainda mais, sugiro que faça esse curso online de Ruby on Rails.
Sintaxe da linguagem
Variáveis
Variáveis são usadas para guardar valores enquanto há um processamento de dados. Em Ruby, como já deve ter sido notado, não é necessário definir tipos, apenas é necessário que a variável tenha um nome e um valor atribuído, depois disso o trabalho será do interpretador.
São quatro tipos de variáveis:
- Variáveis
Locais: Apenas existem no método ou bloco de código que
foram atribuídos. Os valores são atribuídos enquanto o programa roda. A escrita
dessas variáveis é sempre com letra minúscula.
Exemplo: meu_nome - Variáveis
Instanciadas:Começam com @. São as únicas variáveis
que dão uma instância à classe.
Exemplo: @usuario - Variáveis
da Classe: Começam com @@. Existem no escopo da classe, então todas as
instâncias de uma classe específica têm um valor para a variável da classe.
Exemplo: @@usuarios - Variáveis
constantes: São aquelas que não mudam durante
a execução do programa. Em Ruby, elas podem ser realocadas, mas será recebido
um aviso do interpretador. Além disso, são escritas em letra maiúscula.
Exemplo: IP_SERVER
Operadores
Para fazer operações com valores é necessário uma série de sinais, como os mostrados a seguir:
- Aritmético:*, /, %, +, **
- Atribuição: []=, =
- Comparação: <=, >=, <, >
- Lógico:&, ^, |
- E, OU:||, &&
Na documentação do Ruby você consegue maior conhecimento da aplicação desses conceitos, senão, o artigo ficaria muito extenso. Mas veremos exemplos a seguir da utilização de alguns deles.
Tipos de dados Ruby
Um tipo de dado é uma limitação colocada no interpretador, por exemplo, números e textos são dois tipos diferentes. Como em outras linguagens, no Ruby existe essa distinção.
String
Esse será uma sequência de caracteres representados por uma palavra ou alguma forma de texto. No Ruby, qualquer dado entre apóstrofo ou aspas será do tipo string.
Para testar, como o irb já está na sua tela de comando, digite:
2.0.0-p451 :001> ‘Ruby é uma linguagem diferente’
O irb irá responder:
=> “Ruby é uma linguagem diferente”
A principal diferença entre usar delimitações do string com apóstrofo (‘’) ou aspas (“”) é que apóstrofos são sujeitos à alterações ou erros como, por exemplo:
2.0.0-p451 :002> ‘King’s Boulevard’
2.0.0-p451 :003’> ' SyntaxError: (irb):12: syntax error, unexpected tIDENTIFIER, expecting end-of-input
'Kings's boulevard'
^
O que aconteceu é que o interpretador não soube onde era o término do código e continuou sem dar o feedback, mesmo com o símbolo do apóstrofo do lado do número, até que a frase fosse terminada.
Outro exemplo:
2.0.0-p451 :004> ‘Agora é #{Time.now}’
=> ‘Agora é \#{Time.now}’
No string acima foi utilizado o construtor #{}, que serve para substituições de varáveis. Essa técnica é conhecida como interpolation. Como a substituição foi feita com ‘’, a resposta foi inesperada. Se as aspas tivessem sido usadas, o retorno seria algo do gênero:
=> “Agora é 2014-09-10 18:28:09 -0300”
Quando você utiliza a hastag (#) com as chaves, o Ruby entende que deve-se avaliar o que está ali dentro. Porém, quando você usa o apóstrofo,a substituição não é checada.
Existem vários métodos para fazer uma manipulação string como, por exemplo, aumentara letra ou deixar apenas a primeira letra em maiúscula. Para isso, digite no seu prompt:
2.0.0-p451 :005>”Brasília-Brasil”.upcase
=> “BRASÍLIA-BRASIL”
2.0.0-p 451 :006> “RUBY ON RAILS”.capitalize
=> "Ruby on rails"
Esses são alguns dos vários exemplos e se você quiser, pode testar com outros métodos, como downcase e .length.
Números
Em Ruby, os números possuem algumas classes para os representarem como: Fixnum, Bignum e Float.
O Fixnume Bignum fazem parte dos números inteiros e o Floatos números reais, ou seja, fracionários. Como em outras linguagens, você pode fazer contas aritméticas como faria na calculadora no Terminal. Por exemplo:
2.0.0-p451 :001 > 1+2
=> 3
Ao utilizar inteiros, se dividí-los, o resultado mostrado será um do mesmo tipo. Para obter um resultado decimal é preciso indicar tal fato. Por exemplo:
2.0.0-p451 :002 > 3/5
=> 0
2.0.0-p451 :003 > 3.0/5
=> 0.6
Symbol
Os símbolos não são muito comuns nas linguagens de programação. Porém, são extremamente úteis. Eles são um tipo de dado que começam com dois pontos(:), como :nome. Estes são objetos usados para apontar alguma data que não é tradicionalmente um objeto String. A diferença é que um Symbol é imutável, enquanto que um String é mutável e pode ter problemas com resultados inesperados e desempenho reduzido. Vejamos um exemplo:
2.0.0-p451 :001 > module One
2.0.0-p451 :002?> class Fred
2.0.0-p451 :003?> end
2.0.0-p451 :004?> $f1 = :Fred
2.0.0-p451 :005?> end
=> :Fred
2.0.0-p451 :006 > module Two
2.0.0-p451 :007?> Fred = 1
2.0.0-p451 :008?> $f2 = :Fred
2.0.0-p451 :009?> end
=> :Fred
2.0.0-p451 :010 > $f1.object_id
=> 538668
2.0.0-p451 :011 > $f2.object_id
=> 538668
Nesse trecho fica claro que, independentemente das mudanças, o valor do símbolo continua retornando o mesmo número.
Array
Arrays são parte de quase todas as linguagens de programação. Eles mantem a informação em ordem e são definidos com a utilização de colchetes [ ]. Os primeiros elementos sempre têm índice zero. Alguns exemplos a seguir deixarão mais claro como funcionam no Ruby.
(1) 2.0.0-p451 :012 > array_cidades = ["Rio de Janeiro", "Salvador", "São Paulo"]
=>["Rio de Janeiro", "Salvador", "São Paulo"]
(2) 2.0.0-p451 :013 > array_cidades[0]
=> "Rio de Janeiro"
(3) 2.0.0-p451 :014 > array_cidades[2] = "Cuiabá"
=> "Cuiabá"
(4) 2.0.0-p451 :015 > array_cidades << "Brasília"
=> ["Rio de Janeiro", "Salvador", "Cuiabá", "Brasília"]
(5) 2.0.0-p451 :016 > array_cidades + ["Fortaleza"]
=> ["Rio de Janeiro", "Salvador", "Cuiabá", "Brasília", "Fortaleza"]
No primeiro exemplo foi definido o array das cidades com três elementos. No segundo a cidade com índice 0 foi referência para a resposta do irb: “Rio de Janeiro”. No terceiro exemplo substitui-se o objeto de índice 2, “São Paulo”, por “Cuiabá”. No quarto exemplo temos o operador conhecido como shovel, que adiciona um novo objeto à lista, assim como o último exemplo, que utiliza uma forma diferente para colocar um novo item no array.
Hash
Um Hash oferece uma forma diferente de representar uma coleção em relação ao array, pois ele utiliza uma chave (key). Em Ruby, frequentemente utiliza-se um símbolo como chave. Na verdade, qualquer objeto pode ter a função de uma key. A forma de escrever um Hash em Ruby é hash = {key: “value”}. Para ficar mais claro, seguem alguns exemplos:
(1) 2.0.0-p451 :008 > meu_hash = {nome: "João", idade: 12, estado: "GO"}
=> {:nome=>"João", :idade=>12, :estado=>"GO"}
(2) 2.0.0-p451 :009 > meu_hash[:nome]
=> "João"
(3) 2.0.0-p451 :013 > meu_hash.first
=> [:nome, "João"]
(4) 2.0.0-p451 :014 > meu_hash.keys
=> [:nome, :idade, :estado]
(5) 2.0.0-p451 :015 > meu_hash[:cidade] = "Florianópolis"
=> "Florianópolis"
(6)2.0.0-p451 :016 > meu_hash[:estado] = "SC"
=> "SC"
2.0.0-p451 :019 > meu_hash[:estado]
=> "SC"
No exemplo número 1 o hash foi criado. No segundo exemplo a key ‘nome’ é colocada para descobrir seu valor (value). No exemplo 3, o primeiro da lista é mostrado. No quarto exemplo é mostrado apenas os símbolos pedidos. No exemplo número 5 cria-se um novo objeto e no exemplo 6 substitui-se um valor existente por um novo na chave (key) de ‘:estado’.
NOTA:
Para versões de Ruby abaixo de 1.9, a representação de um hash será: exemplo = {:key=> “value”}.
Lógica
O Ruby possui todas as estruturas lógicas mais comuns, como while e if, mas enquanto os programadores de Java, C e Perl se perdem nas chaves, a estrutura aqui é muito mais simples.
Vejamos a seguir alguns exemplos de Estruturas de Controle:
2.0.0-p451 :020 > count = gets.to_i
3
=> 3
2.0.0-p451 :021 > if count == 3
2.0.0-p451 :022?> puts "Voce acertou!"
2.0.0-p451 :023?> else
2.0.0-p451 :024 > puts "Digite novamente"
2.0.0-p451 :025?> end
=> Voce acertou!
Na primeira linha, uma variável é criada e é transformada em inteiro (to_i) para receber um valor pedido pelo comando gets. Após isso, uma estrutura if e else é definida e o usuário recebe o valor que a lógica promove. O else if também existe no Ruby, porém, é escrito elsif e não utiliza o end no final.
Outra forma é o unless e é utilizado para fazer uma exceção com um apenas um if, como mostra o exemplo a seguir:
2.0.0-p451 :050 > a = 5
2.0.0-p451 :058 > unless a > 5
2.0.0-p451 :059?> puts "Não é maior que 5"
2.0.0-p451 :060?> end
Não é maior que 5
=> nil
A estrutura while também é terminada com end e tem formato comum de outras linguagens:
while condição
lógica
end
Um exemplo feito no terminal é:
2.0.0-p451 :041 > a = 5
=> 5
2.0.0-p451 :042 > b = 10
=> 10
2.0.0-p451 :043 > while a < b
2.0.0-p451 :044?> puts "a é #{a}"
2.0.0-p451 :045?> a += 1
2.0.0-p451 :046?> end
a é 5
a é 6
a é 7
a é 8
a é 9
=> nil
Nesse código foram definidas duas variáveis para descobrir os números que serão mostrados enquanto ‘b’ for maior que ‘a’.
Blocos e Iterações
O fragmento de código entre chaves ou a palavra do...end determinará a quantidade de vezes que o objeto ou variável será lido pelo interpretador e repetido no programa. Em Ruby, o for utilizado em muitas linguagens também existe, porém o mais comum é o each.
O times é uma forma de entender o funcionamento do each, apesar de não utilizado frequentemente por precisar de uma quantidade específica. Para melhor compreensão, seguem os exemplos:
3.times { puts "Olá!" }
Olá!
Olá!
Olá!
=> 3
É fácil de perceber que a palavra entre as chaves sobre o comando puts será mostrado três vezes e pulando linhas. Caso tivesse sido utilizado p ou print, a palavra seria mostrada na mesma linha, como é demonstrado a seguir:
2.0.0-p451 :062 > 3.times { print "Oi! "}
Oi! Oi! Oi! => 3
O bloco each tem a mesma lógica para ler toda a coleção, porém, seu formato pode ser de duas maneiras:
(1) exemplos.each {|exemplo| puts exemplo}
(2) exemplos.each do |exemplo|
puts exemplo
end
Alguns exemplos abaixo podem deixar mais claro o funcionamento da estrutura:
(1)
2.0.0-p451 :063 > numeros = [1, 2, 3]
2.0.0-p451 :065 > numeros.each {|numero| puts numero}
1
2
3
=> [1, 2, 3]
(2)
2.0.0-p451 :063 > numeros = [1, 2, 3]
2.0.0-p451 :066 > numeros.each do |numero|
2.0.0-p451 :067 > puts numero
2.0.0-p451 :068?> end
1
2
3
=> [1, 2, 3]
(3)
2.0.0-p451 :063 > numeros = [1, 2, 3]
2.0.0-p451 :072 > numeros.each_with_index do |numero, index|
2.0.0-p451 :073 > puts "Valor: #{numero}, Índice: #{index}"
2.0.0-p451 :074?> end
Valor: 1, Índice: 0
Valor: 2, Índice: 1
Valor: 3, Índice: 2
=> [1, 2, 3]
O primeiro e segundo exemplos seguem a estrutura já mostrada. Porém, no terceiro exemplo é utilizado each_with_index que irá fazer uma leitura no array e estabelecerá o número do índice, representado porindex.
Classes, Métodos e Objetos
Após demonstrar uma parte inicial da linguagem, é preciso introduzir a parte mais importante dela: a orientação a objetos (OO).
Orientação a objetos é dividida, basicamente, em: classes, métodos e objetos, onde a classe é a essência - a representação de um conjunto de objetos, enquanto o método é a ação e o objeto é uma referência a um local da memória que possui um valor. Esses conceitos serão aprofundados a seguir para um maior entendimento desse funcionamento no Ruby.
Métodos
São ações programáveis que você pode definir para facilitar o desenvolvimento do seu projeto. A estruturados métodos na linguagem Ruby é:
def nomedométodo(parâmetros)
código
end
Um outro exemplo:
def saudacao(nome)
"Oi, #{nome}!"
end
puts saudacao("Paula")
Os métodos podem ser muito mais complexos, incluir criação de variáveis e condições. Mas, por agora, o bom é o entendimento da estrutura,do funcionamento e sua importância para as, futuramente introduzidas, classes.
Objetos
O objeto é a instância de uma classe. A instância representa o objeto concretizado a partir de uma classe inicializada e alocada na memória do computador.A classe é apenas uma matriz, que especifica objetos, mas que não pode ser utilizada diretamente.
Basicamente, objetos são formas fáceis de separar seu código e os dados que este contém. Eles podem ser mudados, deletados, criados, movidos e outros. Na programação OO, utiliza-se objetos para chamar métodos ou passá-los para outros métodos. Isso ficará mais claro com os com explicações a seguir:
2.0.0-p451 :001 > time = [[10, "Atacante", "Pedro"], [8, "Meio de Campo", "Henrique"], [3, "Zagueiro",
"David"]]
=> [[10, "Atacante", "Pedro"], [8, "Meio de Campo", "Henrique"], [3, "Zagueiro", "David"]]
Esse é um array multidimensional, ou seja, simplesmente contém dentro mais de um array como elemento. O exemplo acima é sobre um time de futebol onde [número da camisa, função, nome] são componentes. Essa forma de representação agora funciona, mas é fácil de confundir, especialmente se continuarmos a adicionar novos times.
O estilo de código mostrado é processual e não orientado à objeto. Os dados mantidos na coleção são muitos e feitos com tipos simples. Para melhorar o código é necessário a organização, fazer um padrão para o objeto, conhecido como classe.
Classe
Uma classe é uma entidade, ou seja, abstrai um conjunto de características similares. Através de métodos, nela definidos, será possível fazer os comportamentos de seus objetos.
Em Ruby, sem saber, já trabalhamos com classes, como Array e String. Afinal, tudo nessa linguagem tem orientação a objetos. Agora, criaremos uma classe, conforme a Listagem 1, em um editor de texto, como exemplo para os conceitos ficarem mais claros.
NOTA:
Sempre salve um arquivo na linguagem Ruby com .rb no final.
class Jogador
attr_acessor :primeiro_nome, :ultimo_nome, :numero
def initialize (prim_nome, ult_nome, numero)
@primeiro_nome = prim_nome
@ultimo_nome = ult_nome
@numero = numero
end
def nome_completo
“#{ultimo_nome.capitalize}, #{primeiro_nome.capitalize}”
end
def posição
while @numero == 0 || @numero < 0
puts “Coloque novamente um número possível: “
@numero = gets.to_i
end
if @numero < 6 && @numero <0
return “Zaga”
elsif @numero >= 6 && @numero <=10
return “Meio Campo”
else
return “Ataque”
end
end
end
jogador = jogador .new(gets.chomp, gets.chomp, gets.to_i)
p “#{jogador.nome_completo} – Função no(a): #{jogador.posicao}”
Para checar se o código está funcionando, entre no terminal ou CMD, na pasta onde está o arquivo e digite: ruby nomedoarquivo.rb. Após, basta colocar os dados na sequência do programa.
No código acima foi criado uma classe jogador de futebol na qual cada um tem primeiro nome, último nome e número da camisa. O initialize é o construtor, ou seja, através do formato dele criaremos o objeto e os valores recebidos nos parâmetros serão iguais aos campos da classe. O método ‘nome’ coloca o ‘ultimo_nome’ na frente na hora de imprimir e os dois campos com letra inicial maiúscula, através do capitalize.
No método ‘posicao’ foi criado uma lógica para a numeração das camisas. As regras são: o jogador com camisa de 1 até 5 terá sua função na zaga do time; os com camisa número 6 à 10 estarão no meio de campo; os com números maiores serão os atacantes; e no time não é possível ter uma camisa com numeração igual ou menor que 0, sendo assim, enquanto esse valor for colocado, o código rodará novamente pedindo um número possível.
Quando a classe termina, um objeto é criado com a palavra new, responsável por chamar um construtor de uma classe e é salvo na variável ‘jogador’. O método gets foi usado para o usuário digitar os valores do initialize no terminal ou cmd. A diferença é que no primeiro e último nome os valores recebidos serão strings (e como são atribuídos em nova linha, precisam do chomp para não ter um \n no final da palavra (aparece sempre que aperta-se Enter)) e no número, obviamente, um int (to_i).
No início da classe foi utilizado o attr_accessor para definir os campos. Ele é responsável por ler e escrever uma variável instanciada, ou seja, através dele é possível que o interpretador Ruby consiga permitir uma atribuição de valor e ler esse valor. Se, por exemplo, ele não tivesse sido usado para criar a instância ‘primeiro_nome’, seria necessário o seguinte código:
class Jogador
attr_accessor :ultimo_nome, :numero
def primeiro_nome=(value)
@primeiro_nome = value
end
def primeiro_nome
@primeiro_nome
end
...
end
No primeiro método, a variável instanciada pode receber um valor e no segundo é possível ler esse valor. Existem o attr_reader e o attr_writer, que unidos, são o attr_accessor que, como o nome já diz, fará o acesso à instância.
Fazendo uma Classe
Para entender melhor uma linguagem de programação, é necessário praticá-la. Baseando-se no exemplo do jogador de futebol, será criada uma classe Time, conforme o código da Listagem 2. Afinal, os números das camisas seriam repetidos e utilizando apenas a classe Jogador assume-se que está se referindo apenas à um time, ou então, teria mais jogadores do que o comum.
O que é preciso fazer?
- Chamar a classe jogador;
- Estabelecer um nome para cada time;
- Ter uma lista dos jogadores de cada time;
- Poder adicionar mais jogadores à lista;
- Mostrar todos os jogadores de cada time.
require_relative ‘Jogador’
class Time
attr_accessor :nome_time, :jogadores
def initialize(nome_time)
@nome_time = nome_time
@jogadores = [ ]
end
def adiciona_jogador(primeiro_nome, ultimo_nome,numero)
jogador = Jogador.new(primeiro_nome,ultimo_nome,numero)
@jogadores << jogador
end
def print_time
puts “#{nome_time.capitalize}”
@jogadores.each do |jogador|
puts “#{jogador.numero} – “{jogador.nome_completo} (#{jogador.posicao}
end
end
end
time = Time.new(gets.chomp)
time.adiciona_jogador(“John”,“Smith”, 10)
time.adiciona_jogador(“Henrique”,“Carvalho”,5)
time.print_time
time2 = Time.new(gets.chomp)
time2.adiciona_jogador(“Alberto”, “Silva”,6)
time2.adiciona_jogador(“Vicente”, “Souza”,1)
time2.print_time
Com o require_relative foi chamado a classe jogador de outro arquivo para a classe Time. Para não precisar escrever um novo jogador antes de rodar o programa Time (porque o interpretador irá rodar primeiro a classe Jogador), comente tudo que está além da classe no outro arquivo.
No método initialize, o construtor recebe o nome do time e cria um array para receber os jogadores. O método seguinte adiciona novos jogadores e eles são alocados pela shovel no array . No último método, o nome do time é printado e depois todos os seus jogadores são mostrados através do each. Quando a classe acaba, o objeto é criado e os valores atribuídos. Mostra-se tudo, ao chamar o método ‘print_time’.
Para entender melhor o exemplo criado nesse artigo, na opção código-fonte, no topo do post, você pode baixar o código-fonte completo.