Skip to content

johnvoloski/ruby

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

58 Commits
 
 
 
 

Repository files navigation

RUBY

Referência técnica da linguagem Ruby, baseado no material preparado por John Voloski (johnvoloski).

Introdução:

John Voloski:

  • @johnvoloski
    • github
    • bitbucket
    • gmail.com
    • cwi.com.br
  • Projeto Atual ( Fábrica RoR )

História do Ruby:

  • Yukihiro "Matz" Matsumoto
  • Se tornou pública em 1995
  • Ganhou muita popularidade com o Rails

Características:

  • Open Source
  • Orientada a Objetos
  • Blocos de Código
  • Mixins
  • RubyGems
  • Linguagem Interpretada

Interpretadores:

  • MRI
  • YARV
  • JRuby
  • Rubinius
  • MagLev
  • MacRuby

Console:

  • irb

Editores:

Links:

O que é uma Gem?

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").

Gems "Famosas":

Quem usa:

RubyGems:

  • 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 - Ajuda
  • gem install GEMNAME - Instala uma gem.
  • gem list GEMNAME - Lista as gems que começam com a GEMNAME
  • gem outdated - Exibe todas as gems que necessitam de update.
  • gem push - Sobe uma gem para o rubygems
  • gem search GEMNAME - Pesquisa uma gem no rubygems 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 no rubygems.

Bundler:

É 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.

Gemfile Gemfile.lock

Rake:

É 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

Rakefile

Instalação:

Hospedagem:

Integração Contínua:

Gerênciadores de Versões Ruby:

Comentários:

# SpongeBob SquarePants.

=begin
  SpongeBob SquarePants.
  Patrick isn't SquarePants.
=end

Tipos de Dados:

True, False e Nil:

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

Numbers:

Integer:

Pode ser utilizado o _ para melhor visualização.

puts 1_000_000_00
# 100000000

puts 1_000_000_00.class
# Fixnum
Fixnum:

São inteiros que se encaixam dentro de 31 bits então sua instância é um Fixnum.

puts 999999999999999999.class
# Fixnum
Bignum:

São inteiros que se encaixam acima de 31 bits então sua instância é um Bignum.

puts 9999999999999999999.class
# Bignum

Float:

São números que utilizam pontos flutuantes, sua instância é um Float.

99.99.class
# Float

Rational:

São números racionais, sua instância é um Rational.

puts Rational(8/4).class
# Rational

Strings:

Aspas Simples:

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

Aspas Duplas:

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

Symbols:

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]

Arrays:

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]

Hashes:

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

Ranges:

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'

Expressões Regulares:

  • Em ruby as expressões regulares são representadas por /
puts 'SpongeBob'.gsub(/[aeiou]/, '')
# SpngBb

Expressões e Operadores:

Variáveis Globais:

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

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

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

  • 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

Constants:

  • 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

Operadores:

Aritimético:

  • +
# 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

Comparação:

  • ==
# 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

Atribuição:

  • =
# 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ção Múltipla:

# Atribui paralelizadamente valores à variáveis seguindo sua ordem de definição.

a, b, c = 10, 20, 30

Lógico:

  • 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
  • ! ou not
# 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

Ternário:

  • ?:
# Cria uma expressão condicional.

condition = false

puts conditidion ? true : false
# true

Intervalo:

  • ..
# 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

Controle de Estruturas e Afirmação:

Condicionais:

if, else afirmação:

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

if modificador:

conditional = true

puts 'Is true.' if conditional
# Is true

unless afirmação:

conditional = 1

unless conditional > 2
  puts 'conditional is less than 2'
else
  puts 'conditional is greater than 2'
end
# conditional is less than 2

unless modificador:

conditional = false

puts 'Is false.' unless conditional
# Is false

case afirmação:

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

Laços

While afirmação:

a = 0
b = 5

while a < b do
  puts a
  a += 1
end
# 0, 1, 2, 3, 4

While modificador:

a = 0
b = 5

begin
  puts a
  a += 1
end while a < b
# 0, 1, 2, 3, 4

Until afirmação:

a = 0
b = 5

until a > b do
  puts a
  a += 1
end
# 0, 1, 2, 3, 4, 5

Until modificador:

a = 0
b = 5

begin
  puts a
  a += 1
end until a > b
# 0, 1, 2, 3, 4, 5

For:

for a in 0..5
  puts a
end
# 0, 1, 2, 3, 4, 5

Blocos:

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

Alteradores de Controle de Fluxo:

Return

for a in 0..5
  return a if a > 2
  puts a
end
# 0, 1, 2

Break:

for a in 0..5
  break if a > 2
  puts a
end
# 0, 1, 2

Next:

for a in 0..5
  next if a < 2
  puts a
end
# 2, 3, 4, 5

Redo:

for a in 0..5
  puts a
  redo if a < 2
end

# Loop infinito

Retry:

a = 0

begin
  a += 1
  raise NoMethodError
rescue
  puts a
  retry
end

# Retry infinito

Exceções e Tratamentos:

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

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

Métodos, Procs e Lambdas:

Definindo métodos simples:

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

Nomes de Métodos:

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

Argumentos em Métodos:

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

Procs e Lambdas:

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 

Classes e Módulos:

Definindo uma classe simples:

Criando uma classe:

class SpongeBob; end

Instânciando a classe:

class SpongeBob; end

sb = SpongeBob.new

puts sb.class
# SpongeBob
puts sb.is_a?(SpongeBob)
# true

Definindo o método inicializador de uma classe:

class SpongeBob
  def initialize(squarepants)
    @squarepants = squarepants
  end
end

Acessos e Atributos:

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

Definindo Operadores:

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étodo de classe:

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:

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

Visibilidade de Métodos: Public, Protected, Private

  • 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ças:

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:

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

Carregamento e Requerimento:

  • $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.
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.
autoload :SomeClass, 'some_class'

Métodos Singleton em classe:

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.

Testes:

Problema!

Releases

No releases published

Packages

No packages published