Referência técnica da linguagem Ruby, baseado no material preparado por John Voloski (johnvoloski).
- @johnvoloski
- github
- bitbucket
- gmail.com
- cwi.com.br
- Projeto Atual ( Fábrica RoR )
- Yukihiro "Matz" Matsumoto
- Se tornou pública em 1995
- Ganhou muita popularidade com o Rails
- Open Source
- Orientada a Objetos
- Blocos de Código
- Mixins
- RubyGems
- Linguagem Interpretada
- MRI
- YARV
- JRuby
- Rubinius
- MagLev
- MacRuby
- irb
- Vim - Usem este :D
- TextMate
- Emacs
- RubyMine
- SublimeText
- Ruby Lang - Site Oficial do Ruby
- TryRuby - Console Online de Ruby
- Ruby ToolBox - Pesquisa da gem melhor conceituada para algum propósito
- RoR Brasil - Ruby on Rails Brasil
- RoR - Ruby on Rails
- RubyGems - Gems disponíveis para o Ruby
- Rails for Zombies - Site para aprendizado de Rails
- Rails Casts - Vídeo aula de Rails
- Akita on Rails - Blog de Ruby
Uma "RubyGem" ou simplesmente "Gem" é uma biblioteca, um conjunto de arquivos Ruby reusáveis, etiquetada com um nome e uma versão (via um arquivo chamado de "gemspec").
- RubyGems Commands
gem dependency GEMNAME
- Mostra as dependências da gem instalada.gem fetch GEMNAME
- Baixa a gem e coloca no diretório atual.gem help
- Ajudagem install GEMNAME
- Instala uma gem.gem list GEMNAME
- Lista as gems que começam com a GEMNAMEgem outdated
- Exibe todas as gems que necessitam de update.gem push
- Sobe uma gem para orubygems
gem search GEMNAME
- Pesquisa uma gem norubygems
com a GEMNAME.gem uninstall GEMNAME
- Desinstala uma gem.gem update GEMNAME
- Atualiza a gem para a última versão.gem yank GEMNAME -v VERSION
- Remove uma versão da gem norubygems
.
É um gerenciador de gems
da aplicação. Gerando uma lista de gems
ele se encarrega de instalar, verificar versões e compatibilidades, após instaladas
o bundler
ajuda a atualizar quando tiver versões novas disponíveis, e registra as versões instaladas pra que se possa replicar exatamente as mesmas versões em diversas máquinas.
É um automatizador de tarefas, você consegue criar tarefas utilizando a sintaxe do ruby
como por exemplo publicar algo em um ssh ou ftp automaticamente.
# Rakefile
task :ftp do
...
end
# SpongeBob SquarePants.
=begin
SpongeBob SquarePants.
Patrick isn't SquarePants.
=end
True representa o verdadeiro, False o falso e nil representa a abstenção de valor. Qualquer valor sem ser False e Nil é True.
puts true.class
# TrueClass
puts false.class
# FalseClass
puts nil.class
# NilClass
Pode ser utilizado o _ para melhor visualização.
puts 1_000_000_00
# 100000000
puts 1_000_000_00.class
# Fixnum
São inteiros que se encaixam dentro de 31 bits então sua instância é um Fixnum.
puts 999999999999999999.class
# Fixnum
São inteiros que se encaixam acima de 31 bits então sua instância é um Bignum.
puts 9999999999999999999.class
# Bignum
São números que utilizam pontos flutuantes, sua instância é um Float.
99.99.class
# Float
São números racionais, sua instância é um Rational.
puts Rational(8/4).class
# Rational
puts 'Sou uma string com aspas simples e com instância String'.class
# String
puts 'Sou uma string com aspas simples'
# Sou uma string com aspas simples
puts 'Sou uma string \' com um "escape"'
# Sou uma string ' com um "escape"
puts 'Sou uma string quebrada' \
'em multiplas linhas' \
'não somente em uma'
# Sou uma string quebrada em multiplas linhas não somente em uma
puts "Sou uma string com aspas duplas e com instância String".class
# String
puts "Sou uma string com aspas duplas"
# Sou uma string com aspas duplas
Interpolações em string são feitas através do #{}
:
adjective = 'SquarePants'
puts "SpongeBob #{adjective}"
# SpongeBob SquarePants
Um símbolo é um identificador único no ruby. O símbolo referencia ele mesmo. Um símbolo tem uma comparação muito mais rápida que uma string. Símbolos são ideais para definerem uma chave dentro de um hash, pois é um valor que não será alterado.
friends = ['SpongeBob', 'Patrick']
puts friends
# ["SpongeBob", "Patrick"]
friends = [:SpongeBob, :Patrick]
puts friends
# [:SpongeBob, :Patrick]
Um array
é uma sequência de valores acessíveis pela sua posição ou indíce.
Em ruby
o valor do primeiro indíce é 0.
puts [1, 2, 3, 4]
# [1, 2, 3, 4]
puts Array.new(4)
# [nil, nil, nil, nil]
Um hash
em ruby
é composto por objetos formados por chave
=> valor
.
sb = { 'SpongeBob' => 'SquarePants' }
puts sb['SpongeBob']
# SquarePants
sb = { :SpongeBob => 'SquarePants' }
puts sb[:SpongeBob]
# SquarePants
sb = { SpongeBob: 'SquarePants' }
puts sb[:SpongeBob]
# SquarePants
O range
representa o intervalo entre um início e um final.
# Irá gerar um intervalo de 1 à 10 incluindo o 10.
puts 1..10
# 1..10
# Irá gerar um intervalo de 1 à 10 excluíndo o 10.
puts 1...10
# 1...10
# Pode ser usado com strings também.
puts 'a'..'f'
# 'a'..'f'
- Em
ruby
as expressões regulares são representadas por/
puts 'SpongeBob'.gsub(/[aeiou]/, '')
# SpngBb
- Variáveis globais começam com $. Não inicializadas seu valor padrão é nil.
- Elas são visíveis de qualquer lugar.
$global_variable = 0
class HelloWorldOne
def increment
$global_variable += 1
end
def output
puts $global_variable
end
end
class HelloWorldTwo
def output
puts $global_variable
end
end
classOne = HelloWorldOne.new
classOne.increment
classOne.output
# 1
classTwo = HelloWorldTwo.new
classOne.increment
classTwo.output
# 2
- Variáveis de instância começam com @. Não inicializadas seu valor padrão é nil.
- Elas são visíveis apenas dentro da instância, compartilhada entre os métodos de instância.
- Elas podem ser acessadas externamente criando um
attr
.
class HelloWorldOne
def initialize(value)
@instance_variable = value
end
def output
puts @instance_variable
end
end
class HelloWorldTwo
def initialize(value)
@instance_variable = value
end
def output
puts @instance_variable
end
end
HelloWorldOne.new("SpongeBob SquarePants").output
# SpongeBob SquarePants
HelloWorldTwo.new("Patrick").output
# Patrick
- Variáveis de classe começam com @@. Devem ser inicializadas.
- Elas são visíveis e compartilhadas entre métodos de classe, métodos de instância e classes do mesmo tipo.
- Elas são encapsuladas, só podem ser acessadas e usadas na implementação e não de fora.
class HelloWorldOne
@@class_variable = ''
def assign_variable(value)
@@class_variable = value
end
def output
puts @@class_variable
end
end
one = HelloWorldOne.new
one.assign_variable("SpongeBob SquarePants")
one.output
# SpongeBob SquarePants
two = HelloWorldOne.new
two.output
# SpongeBob SquarePants
- Variáveis locais começam com uma letra minúscula ou _ .
- O escopo de uma variável local varia de classe, módulo, método ou a abertura e fechamento de um bloco, que corresponde ao final de seu ciclo.
class HelloWorld
def initialize(value)
puts value
end
end
HelloWorld.new('SpongeBob SquarePants')
# SpongeBob SquarePants
- Constantes começam com uma letra maiúscula.
- Constantes podem ser visualizadas internamente de uma classe ou módulo, apenas pelo seu nome, ou externamente através do seu módulo/classe mais o seu nome.
class HelloWorld
HELLO_WORLD = 'Hello SpongeBob SquarePants'
def output
HELLO_WORLD
end
end
puts HelloWorld.new.output
# Hello SpongeBob SquarePants
puts HelloWorld::HELLO_WORLD
# Hello SpongeBob SquarePants
+
# Soma o valor da variável a com o valor da variável b e returna o resultado.
a = 10
b = 20
puts a + b
# 30
-
# Subtrai da variável a o valor da váriavel b e returna o resultado.
a = 10
b = 20
puts a - b
# -10
*
# Multiplica o valor da variável a com o valor da variável b e retorna o resultado.
a = 10
b = 20
puts a * b
# 200
/
# Divide o valor da variável b por o valor da variável a e retorna o resultado.
a = 10
b = 20
puts b / a
# 2
%
# Divide o valor da váriavel b por o valor da variável a e retorna o quociente da divisão.
a = 10
b = 20
puts b % a
# 0
**
# Executa o cálculo exponencial sobre o valor da variável a quando o valor de seu expoente é o valor da variável b e retorna o resultado.
a = 10
b = 2
puts a**b
# 100
==
# Verifica se o valor da variável a é igual ao valor da variável b, se sim retorna true.
a = 10
b = 20
puts (a == b)
# false
!=
# Verifica se o valor da váriavel a é diferente do valor da variável b, se sim retorna true.
a = 10
b = 20
puts (a != b)
# true
>
# Verifica se o valor da variável a é maior que o valor da variável b, se sim retorna true.
a = 10
b = 20
puts (a > b)
# false
<
# Verifica se o valor da variável a é menor que o valor da variável b, se sim retorna true.
a = 10
b = 20
puts (a < b)
# true
>=
# Verifica se o valor da variável a é maior ou igual ao valor da variável b, se sim retorna true.
a = 10
b = 20
puts (a >= b)
# false
<=
# Verifica se o valor da variável a é menor ou igual ao valor da variável b, se sim retorna true.
a = 10
b = 20
puts (a <= b)
# true
<=>
# Verifica se o valor da variável a é igual ao valor da váriavel b, então retorna 0, verifica se o valor da variável a é maior que o valor da váriavel b, então retorna 1, e verifica se o valor da variável a é menor que o valor da variável b, então retorna -1.
a = 10
b = 20
puts (a <=> b)
# -1
===
# É um sinônimo do operador ==, e muda dependendo da implementação do seu contexto.
a = 10
puts a === 10
# true
puts Object === a
# true
puts Fixnum === a
# true
puts Integer === a
# true
puts (1..10) === a
# true
.eql?
# Verifica se o valor da variável a é igual ao valor da variável b, mas muda dependendo do seu contexto.
a = 10
b = 10.0
puts a == b
# true
# a.class(Fixnum) and b.class(Float)
puts a.eql?(b)
# false
.equal?
# Compara se os mesmos apontam para o mesmo objeto em memória.
a = 10
a.__id__
21
b = 10
b.__id__
21
puts a.equal?(b)
true
a = "word"
a.__id__
87897809
b = "word"
b.__id__
87893729
puts a.equal?(b)
false
=
# Atruibui o valor da variável a para a váriavel b.
a = 10
b = a
puts b
# 10
+=
# Atribui somando o valor da variável a por o valor da váriavel b.
a = 10
b = 10
b += a
puts b
# 20
-=
# Atribui subtraindo o valor da variável a por o valor da váriavel b.
a = 10
b = 10
b -= a
puts b
# 0
*=
# Atribui multiplicando o valor da variável a por o valor da váriavel b.
a = 10
b = 10
b *= a
puts b
# 100
/=
# Atribui dividindo o valor da variável a por o valor da váriavel b.
a = 10
b = 10
b /= a
puts b
# 1
%=
# Atribui o quociente da divisão do valor da variável a por o valor da váriavel b.
a = 10
b = 10
b %= a
puts b
# 0
**=
# Atribui calculando o valor exponencial do valor da variável a por o valor da váriavel b.
a = 10
b = 2
b **= a
puts b
# 1024
# Atribui paralelizadamente valores à variáveis seguindo sua ordem de definição.
a, b, c = 10, 20, 30
and
ou&&
# Se o valor da variável a e o valor da variável b forem verdadeiros, a condição é verdadeira.
a = true
b = true
puts 'True' if a and b
# True
puts 'True' if a && b
# True
or
ou||
# Se o valor da variável a ou o valor da variável b forem verdadeiros, a condição é verdadeira.
a = true
b = false
puts 'True' if a or b
# True
puts 'True' if a || b
# True
!
ounot
# Se o valor da variável a e o valor da variável b forem falsos, a condição é verdadeira.
a = false
b = false
puts 'True' if !(a && b)
# True
puts 'True' if not(a && b)
# True
?:
# Cria uma expressão condicional.
condition = false
puts conditidion ? true : false
# true
..
# Cria um intervalo entre o ponto de partida e o ponto de chegada incluido ele.
puts (1..10).to_a
# 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
...
# Cria um intervalo entre o ponto de partida e o ponto de chegada excluíndo ele.
puts (1...10).to_a
# 1, 2, 3, 4, 5, 6, 7, 8, 9
conditional = 1
if conditional > 2
puts 'conditional is greater than 2'
elsif conditional <= 2 && conditional != 0
puts 'conditional is 1'
else
puts 'I can`t guess the number'
end
# conditional is 1
conditional = true
puts 'Is true.' if conditional
# Is true
conditional = 1
unless conditional > 2
puts 'conditional is less than 2'
else
puts 'conditional is greater than 2'
end
# conditional is less than 2
conditional = false
puts 'Is false.' unless conditional
# Is false
conditional = 10
case conditional
when 1
puts 'Is 1.'
when 2 .. 5
puts 'Is between 2 and 5'
when 6 .. 9
puts 'Is between 6 and 9'
when 10
puts 'is 10'
else
puts 'I can`t guess the number'
end
# is 10
a = 0
b = 5
while a < b do
puts a
a += 1
end
# 0, 1, 2, 3, 4
a = 0
b = 5
begin
puts a
a += 1
end while a < b
# 0, 1, 2, 3, 4
a = 0
b = 5
until a > b do
puts a
a += 1
end
# 0, 1, 2, 3, 4, 5
a = 0
b = 5
begin
puts a
a += 1
end until a > b
# 0, 1, 2, 3, 4, 5
for a in 0..5
puts a
end
# 0, 1, 2, 3, 4, 5
Os blocks
assim como é definido são blocos de códigos formados por delimitadores { ... }
ou do ... end
,
a convensão que usamos é { ... }
para uma linha e do ... end
para mais de uma linha.
O bloco serve para armazenar uma implementação que for desejada, e será executada em um certo momento, com
seu pŕoprio escopo.
Blocos só podem ser usados com métodos.
Eles podem ser executados atráves do &
chamando na implementação block.call
, ou também através do yield
,
o yield
tem como função executar um bloco anônimo sem precisar ser especificado no método.
class SpongeBob
def is_squarepants?(&block)
block.call
end
def i_live_in_ocean?
yield
end
end
SpongeBob.new.is_squarepants? { puts true }
# true
SpongeBob.new.i_live_in_ocean? do
puts true
end
# true
for a in 0..5
return a if a > 2
puts a
end
# 0, 1, 2
for a in 0..5
break if a > 2
puts a
end
# 0, 1, 2
for a in 0..5
next if a < 2
puts a
end
# 2, 3, 4, 5
for a in 0..5
puts a
redo if a < 2
end
# Loop infinito
a = 0
begin
a += 1
raise NoMethodError
rescue
puts a
retry
end
# Retry infinito
Hierarquia da Classe de Exceção do Ruby:
- Object
- Exception
- NoMemoryError
- ScriptError
- LoadError
- NotImplementedError
- SyntaxError
- SecurityError
- SignalException
- Interrupt
- SystemExit
- SystemStackError
- StandardError
- ArgumentError
- FiberError
- IOError
- EOFError
- IndexError
- KeyError
- StopIteration
- LocalJumpError
- NameError
- NoMethodError
- RangeError
- FloatDomainError
- RegexpError
- RuntimeError
- SystemCallError
- ThreadError
- TypeError
- ZeroDivisionError
- Exception
Definindo uma exception class:
class MyError < StandardError; end
Levantando uma exception:
class MyError < StandardError; end
raise MyError
raise MyError, 'Exception'
Tratando exception com rescue
:
class MyError < StandardError; end
begin
# Minha implementação aqui.
raise MyError if true
rescue => ex
# Aqui o tratamento da minha exception.
puts "#{ex.class}: #{ex.message}"
end
# MyError: Mensagem
Tratando exception com rescue
pelo tipo:
class MyError < ArgumentError; end
begin
# Minha implementação aqui.
raise MyError if true
rescue NoMethodError => ex
# Aqui o tratamento de método não definido.
puts "NoMethodError: #{ex.class}: #{ex.message}"
rescue ArgumentError => ex
# Aqui o tratamento de erro nos argumentos.
puts "ArgumentError: #{ex.class}: #{ex.message}"
end
# ArgumentError: MyError: Mensagem
Quando ocorre um exceção durante um tratamento, então é propagada uma nova exceção.
Usando o retry
dentro de um tratamento de exceção:
tries = 0
begin
tries += 1
xMethod
rescue NoMethodError => ex
puts ex.message
retry if tries < 4
end
# Mensagem
# Mensagem
# Mensagem
# Mensagem
A cláusula else
geralmente é utilizada para um tratamento genérico onde outros tratamentos utilizando o rescue
não forma efetivos:
begin
raise StandardError
rescue NoMethodError => ex
puts "NoMethodError: #{ex.message}"
else
puts "GenericError"
end
# GenericError
A Cláusula ensure
é utilizada como finalização do tratamento, ela é chama sempre após executar um rescue
e até mesmo o else
:
begin
raise NoMethodError
rescue NoMethodError => ex
puts "NoMethodError: #{ex.message}"
ensure
puts "E finalizou a exceção."
end
# NoMethodError: Mensagem
# E finalizou a exceção.
begin
raise StandardError
rescue NoMethodError => ex
puts "NoMethodError: #{ex.message}"
else
puts "GenericError"
ensure
puts "E finalizou a exceção."
end
# GenericError
# E finalizou a exceção.
Utilizando o rescue
em um método, classe ou módulo.
class Patrick; end
class SpongeBob; end
class Squidward; end
class NotSpongeBobError < StandardError; end
class NotPatrickError < StandardError; end
def is_squarepants?(name)
raise NotSpongeBobError if !name.is_a? SpongeBob
rescue
raise NotPatrickError if !name.is_a? Patrick
else
puts "O nome da classe é: {Squidward.name}."
ensure
puts "Ele também é um personagem."
end
is_squarepants?(Squidward)
# O nome da classe é: Squidward
# Ele também é um personagem.
Utilizando o rescue
como modificador:
puts is_squarepants?(Patrick)
# NoMethodError
puts is_squarepants?(SpongeBob) rescue true
# true
Definindo um método:
class SpongeBob; end
def is_squarepants?(name)
name.is_a?(SpongeBob) ? true : false
end
Invocando um método:
class SpongeBob; end
def is_squarepants?(name)
name.is_a?(SpongeBob) ? true : false
end
is_squarepants?(SpongeBob.new)
# true
Definindo um método Singleton:
bob = 'SpongeBob'
def bob.is_squarepants?
true
end
bob.is_squarepants?
# true
Indefinindo um método:
def is_squarepants?
true
end
is_squarepants?
# true
undef is_squarepants?
is_squarepants?
# NoMethodError
Por convensão nomes de métodos começam sempre com letra minúscula, podem começar com letra maiúscula mas irão se parecer com uma constante.
Quando o nome do método é maior que uma palavra, por convensão utiliza-se "_" para separa as palavras: "is_squarepants?".
A convensão para métodos com ?
no final, são métodos cujo valor retornado sempre será um boleano.
A conversão para métodos com !
no final, são métodos cuja utilização deve ser com cautela, por exemplo, o método sort
de um Array
, ele copia
o Array
e ordena, já o método sort!, efetua o
sort!` no mesmo array o redefinindo.
class SpongeBob
def is_squarepants?
@squarepants || false
end
def is_squarepants!
@squarepants = true
end
end
bob = SpongeBob.new
puts bob.is_squarepants?
# false
bob.is_squarepants!
puts bob.is_squarepants?
# true
Redefinindo os Métodos Operadores:
class SpongeBob
def +(value)
"SpongeBob #{value}"
end
end
puts SpongeBob.new + "SquarePants"
# SpongeBob SquarePants
Definindo "alias" para os Métodos: (Não é possível fazer "Overloading" em um "alias")
def is_squarepants?
true
end
alias is_sp? is_squarepants?
puts is_squarepants?
# true
puts is_sp?
# true
Lista de Argumentos como Parâmetros:
def is_squarepants?(name, *args)
puts "Name: #{name}"
puts "Qualquer outro parâmetro informado: #{args}"
end
is_squarepants?('SpongeBob', true, 'Patrick')
# Name: SpongeBob
# Qualquer outro parâmetro informado: [true, "Patrick"]
Hash como parâmetro:
def is_squarepants?(name = 'SpongeBob', options = { squarepants: true })
puts name
puts options[:squarepants]
end
puts is_squarepants?
# SpongeBob
# true
puts is_squarepants?('Patrick', squarepants: false)
# Patrick
# false
Bloco como parâmetro:
Se você prefere um controle explícito sobre o bloco, adicione um parâmetro final com um &
na frente, então esse parâmetro irá referenciar o bloco, se for passado
para o método, o tipo desse bloco sera um Proc
ao invés de usar o yield
você invocará através do método call
.
def is_squarepants?(name, &block)
block.call(name)
end
puts is_squarepants?('SpongeBob') { |name| puts "#{name} SquarePants" }
# SpongeBob SquarePants
puts is_squarepants?('Patrick') { |name| puts "#{name} isn't SquarePants" }
# Patrick isn't SquarePants
Se você prefere um controle mais específico ainda, defina um parâmetro como sendo o do bloco, o tipo deste parâmetro será um Proc
e será invocado através do método call
.
def is_squarepants?(name, block)
block.call(name)
end
puts is_squarepants?('SpongeBob', proc { |name| puts "#{name} SquarePants" })
# SpongeBob SquarePants
puts is_squarepants?('Patrick', proc { |name| puts "#{name} isn't SquarePants" })
# Patrick isn't SquarePants
Blocos são estruturas sintáticas em Ruby, não são objetos e não tem como os manipular como objetos.
Então é possível criar um objeto representante de um bloco. Dependendo de como é criado o objeto,
ele é chamado de proc
ou lambda
. Procs
tem um comportamento como o de um bloco, e Lambdas
tem
um comportamento como um método. No entando os dois são instâncias da classe Proc
.
Criando Procs:
p = Proc.new { |adjective| "SpongeBob #{adjective}" }
p.call('SquarePants')
# SpongeBob SquarePants
p = proc { |adjective| "SpongeBob #{adjective}" }
p.call('SquarePants')
# SpongeBob SquarePants
Criando Lambdas:
l = lambda { |adjective| "SpongeBob #{adjective}" }
l.call('SquarePants')
# SpongeBob SquarePants
l = ->(adjective) { "SpongeBob #{adjective}" }
l.call('SquarePants')
# SpongeBob SquarePants
Descubrindo a quantidade de parâmetros obrigatórios de uma Proc
:
p = proc { |adjective| "SpongeBob is #{adjective}" }
puts p.arity
# 1
l = ->(adjective) { "SpongeBob is #{adjective}" }
l.arity
# 1
Como diferenciar um lambda
de um proc
:
O proc
se parece como um bloco e tem um comportamento de bloco, o lambda
é levemente modificado para parecer como um método. Para descobrir se o
objeto é um lambda
ou proc
existe o método lambda?
que retorna true
se for um lambda
e false
se for um proc
.
O proc
funciona como um bloco, seu retorno é propagado no contexto e não para o próprio proc
.
O lambda
funciona como um método, seu retorno é propagado para o próprio lambda
.
def is_squarepants?
p = proc { puts 'SpongeBob is SquarePants'; return }
p.call
puts ' and Patrick also'
end
# SpongeBob is SquarePants
def is_squarepants?
p = ->{ puts 'SpongeBob is SquarePants'; return }
p.call
puts " and Patrick isn't"
end
# SpongeBob is SquarePants and Patrick isn't
class SpongeBob; end
class SpongeBob; end
sb = SpongeBob.new
puts sb.class
# SpongeBob
puts sb.is_a?(SpongeBob)
# true
class SpongeBob
def initialize(squarepants)
@squarepants = squarepants
end
end
Provendo os acessos a uma váriavel de instância de forma manual:
class SpongeBob
def initialize(squarepants)
@squarepants = squarepants
end
def squarepants; @squarepants; end
def squarepants=(value)
@squarepants = value
end
end
sb = SpongeBob.new(true)
sb.squarepants = true
puts sb.squarepants
# true
Para prover esses acessos de formá automática o ruby
fornece os métodos para serem definidos:
- attr_reader - Cria o acesso de leitura
class SpongeBob
attr_reader :squarepants
def initialize
@squarepants = false
end
end
sb = SpongeBob.new
puts sb.squarepants
# false
sb.squarepants = true
# Erro
- attr_writer - Cria o acesso de escrita
class SpongeBob
attr_writer :squarepants
def initialize
@squarepants = false
end
end
sb = SpongeBob.new
sb.squarepants = true
puts sb.squarepants
# Erro
- attr_accessor - Cria o acesso de leitura e escrita
class SpongeBob
attr_accessor :squarepants
def initialize
@squarepants = false
end
end
sb = SpongeBob.new
sb.squarepants = true
puts sb.squarepants
# true
Em ruby
você pode redefinir os operadores de uma classe:
- Alguns operadores que podem ser definidos:
+
-
*
/
%
-@
+@
~
!
=
==
===
class SpongeBob
def initialize
@he = 'SpongeBob'
end
def +(value)
"#{@he} #{value}"
end
def !
"#{@he} isn't SquarePants"
end
end
sb = SpongeBob.new
puts sb + 'SquarePants'
# SpongeBob SquarePants
puts !sb
# SpongeBob isn't SquarePants
Métodos de Classe são métodos dos quais não dependem de uma instância ativa da classe.
class SpongeBob
def Point.is_squarepants?(instance)
instance.is_a?(SpongeBob)
end
def self.is_squarepants?(instance)
instance.is_a?(SpongeBob)
end
class << self
def is_squarepants?(instance)
instance.is_a?(SpongeBob)
end
end
end
puts SpongeBob.is_squarepants?(SpongeBob.new)
# true
Método de Instância são os métodos dos quais dependem de uma instância ativa da classe.
class SpongeBob
def is_squarepants?(instance)
instance.is_a?(SpongeBob)
end
end
puts SpongeBob.new.is_squarepants?(SpongeBob.new)
# true
- Métodos de classe podem ser definidos públicos com este comando:
public_class_method :squarepants
- Todos métodos normalmente são públicos, exceto o
initialize
que é sempre privado. Os métodos públicos podem ser invocados por qualquer um, não existe restrições.
class SpongeBob
def is_squarepants?
self.is_a? SpongeBob
end
end
sb = SpongeBob.new
sb.is_squarepants?
# true
- Métodos de classe podem ser definidos privados com este comando:
private_class_method :squarepants
- Os métodos privados, são métodos que só podem ser acessados internamente.
class KrustyKrab
private
def is_employee?(instance)
instance.is_a?(SpongeBob)
end
# Pode ser definido privado desta maneira também:
# private :is_employee?
end
class SpongeBob < KrustyKrab
def job
puts is_employee?(self)
puts self.is_employee?(self) rescue puts 'Não pode chamar o método privado por uma referência.'
puts SpongeBob.new.is_employee?(SpongeBob.new) rescue puts 'Não pode chamar o método privado externamente.'
end
end
SpongeBob.new.job
# true
# Não pode chamar o método privado por uma referência.
# Não pode chamar o método privado externamente.
- Os métodos protegidos, são métodos iguais ao privados, só difere na medida em que pode ser explicitamente chamado em qualquer instância da classe.
class KrustyKrab
protected
def is_employee?(instance)
instance.is_a?(SpongeBob)
end
# Pode ser definido protegido desta maneira também:
# protected :is_employee?
end
class SpongeBob < KrustyKrab
def job
puts is_employee?(self)
puts self.is_employee?(self)
puts 'Pode chamar o método protegido por uma referência.'
puts SpongeBob.new.is_employee?(SpongeBob.new)
puts 'Pode chamar o método protegido externamente.'
end
end
SpongeBob.new.job
# true
# Pode chamar o método protegido por uma referência
# true
# Pode chamar o método protegido externamente.
Herança não é muito diferente em ruby
, quando você tem um classe SpongeBob
que herda de Ocean
dizemos que SpongeBob
é uma subclass
e Ocean
é uma superclass
.
Uma classe pode ter infinitas subclasses
mas apenas uma superclass
. ruby
não possui herança múltipla.
Variáveis de instância, classe e constantes são herdadas e podem ser modificadas. O detalhes se for uma constante é que se estivermos
criando alguma instância na superclass
dela, a instância da subclass
será diferente, pois sera criada uma nova.
- Herança de métodos:
class Ocean
def has_squarepants_here?
true
end
end
class SpongeBob < Ocean; end
puts SpongeBob.new.has_squarepants_here?
# true
- Sobrescrevendo métodos:
class Ocean
def has_squarepants_here?
false
end
end
class SpongeBob > Ocean
def has_squarepants_here?
true
end
end
puts SpongeBob.new.has_squarepants_here?
# true
- Sobrescrevendo métodos privados e protegidos:
class Ocean
private
def has_squarepants_here?
false
end
protected
def whoiam?
'Ocean'
end
end
class SpongeBob > Ocean
def has_squarepants_here?
true
end
def whoiam?
'SpongeBob'
end
end
puts SpongeBob.new.has_squarepants_here?
# true
puts SpongeBob.new.whoiam?
# SpongeBob
- Algumas vezes necessitamos sobrescrever um método mas continuar com a implementação antiga, então usamos o
super
: (chaining)
class Ocean
def initialize(whoiam = 'Ocean')
@whoiam = "I am #{whoiam}"
end
end
class SpongeBob < Ocean
attr_accessor :presentation
def initialize
super('SpongeBob')
@presentation = "#{@whoiam}, and I live in the ocean."
end
end
puts SpongeBob.new.presentation
# I am SpongeBob, and I live in the ocean.
Módulos são um coleção de módulos, constantes, classes e variáveis de classe.
Um módulo não é instânciavel e não possui herança.
Módulos usam namespaces
e mixins
, classes podem usar namespaces
assim como os módulos, mas classes não usam mixins
.
module Ocean
def self.spongebob_live_here?
true
end
def self.patrick_live_here?
true
end
class SpongeBob
def whoiam?
'SpongeBob'
end
end
class Patrick
def whoiam?
'Patrick'
end
end
end
puts Ocean::SpongeBob.new.whoiam?
# SpongeBob
puts Ocean.spongebob_live_here?
# true
puts Ocean::Patrick.new.whoiam?
# Patrick
puts Ocean.patrick_live_here?
# true
A diferença de mixins e herança é apenas que quando uma classe inclui um módulo ela não se torna filha deste módulo, apenas implementa os seus métodos. Os módulos e os mixins:
O include
implementa os métodos do módulo como um método de instância na classe.
module Ocean
def self.whoiam?
'Ocean'
end
def i_live_in_ocean?
true
end
end
class SpongeBob
include Ocean
def whoiam?
'SpongeBob'
end
end
sb = SpongeBob.new
puts sb.whoiam?
# SpongeBob
puts sb.i_live_in_ocean?
# true
puts Ocean.whoiam?
# Ocean
O extend
implementa os métodos do módulo como um método de classe na classe.
module Ocean
def self.whoiam?
'Ocean'
end
def i_live_in_ocean?
true
end
end
class SpongeBob
extend Ocean
def whoiam?
'SpongeBob'
end
end
sb = SpongeBob.new
puts sb.whoiam?
# SpongeBob
puts SpongeBob.i_live_in_ocean?
# true
puts Ocean.whoiam?
# Ocean
-
$LOAD_PATH ou $ :- Variável global que contém um array com referência dos arquivos.
-
require
- Faz a inclusão e a leitura do arquivo.
require 'some_file'
- require_relative
- É utilizando quando existe a necessidade de referênciar um diretório/arquivo. Faz a leitura do arquivo.
require_relative 'some_path/some_file'
- load
- Tem um comportamento semelhante ao
require
, a diferença é que necessita da extensão do arquivo, e pode ser executada diversas vezes.
- Tem um comportamento semelhante ao
load 'some_path/some_file.rb'
- autoload
- Tem um comportamento semelhante ao
require
, porém só faz a leitura do arquivo quando acessado pela primeira vez.
- Tem um comportamento semelhante ao
autoload :SomeClass, 'some_class'
Todo objeto do Ruby está associado a duas classes: a classe que a instanciou e uma classe anônima, escondida, específica do objeto. Esta classe anônima é chamada de Singleton Class, mas antes de ter um nome oficial também era chamada de anonymous class, metaclass, eigenclass ou ghost class. A sintaxe mais comum para acessar a classe Singleton é:
class SpongeBob
class << self
def whoiam?
'SpongeBob'
end
end
end
puts SpongeBob.whoiam?
# SpongeBob
puts SpongeBob.singleton_methods
# [:whoiam?]
Toda vez que injeta métodos em um objeto, eles são adicionados como métodos singleton. O que é realmente importante saber é que estes métodos pertecem unicamente ao objeto em que foram definidos, não afetando nenhum outro objeto da hieraquia.