3.days.from_now). Fica bem difícil lembrar o que cada parâmetro significa e pior,
qual é a ordem desses parâmetros. Ou mais além, no Rails, por exemplo, é possível
passar algumas opções para mudar algum comportamento do método. Este tipo de
problema é o chamado acoplamento por posição, ou seja, o código torna-se com-
plicado devido à grande dependência da posição dos parâmetros.
Por isso, é bem comum usar hashes como parâmetros para tornar a chamada de
método mais legível e evitar que o programador fique consultando manuais a todo
momento:
search_flights(:from => 'SAO', :to => 'NYC')
search_flights(:from
=> 'SAO',
:to
=> 'NYC',
:max_stops => 3,
:class
=> :first)
Resumindo, os principais usos para hashes como parâmetros são:
Independência de ordem de parâmetros, reduzindo acoplamento por posição;
Quando há muitos parâmetros;
Quando há parâmetros opcionais;
Quando é possível alterar o comportamento através de opções.
43
2.5. Funções, blocos, lambdas e closure
Casa do Código
Saiba mais: acoplamento por posição
Para entender melhor sobre este tipo de dependência e acoplamento, re-
comendo a leitura do artigo Connascence as a Software
Design Metric
[4] do Gregory Brown e também ver a palestra Building Blocks of Mo-
dularity [10], do Jim Weirich.
Nesses casos, é bastante comum métodos serem construídos da seguinte ma-
neira:
# Se nada for passado, todos os parâmetros serão os default
def search_flights(options={})
from
= options.fetch(:from, 'SAO')
to
= options.fetch(:to, '')
max_stops
= options.fetch(:max_stops, 9999)
flight_class = options.fetch(:class, :any)
# ...
end
Blocos associados a métodos
Vimos no final da seção 2.4 o funcionamento de blocos. Agora veremos como
construir funções que possuem comportamento semelhante. O procedimento é bas-
tante simples, basta usarmos duas expressões: yield e block_given?.
O yield passa o fluxo de execução do programa para o bloco associado ao mé-
todo ou função. Usamos o block_given? para verificar se algum bloco foi passado
de fato ao método. Observe os exemplos a seguir:
def announce_it
puts "Legen..."
yield
puts "Dary!"
end
announce_it { puts "Wait for it!" }
# Legen...
# Wait for it!
# Dary!
44
Casa do Código
Capítulo 2. Conhecendo Ruby
announce_it
# Legen...
# LocalJumpError: no block given (yield)
Agora, usando o block_given?:
def announce_it
puts "Legen..."
# yield não executado caso não haja um bloco!
yield if block_given?
puts "Dary!"
end
announce_it { puts "Wait for it!" }
# Legen...
# Wait for it!
# Dary!
announce_it
# Legen...
# Dary!
Sintaxe alternativa para blocos
Existe uma forma de se usar blocos que não usa o par yield/block_given?. Essa
forma é explicitar o bloco com um parâmetro da função através do operador &, que
sempre deverá vir por último na lista de parâmetros:
def announce_it(name, &block)
puts "Hey #{name}, it's gonna be... "
puts "Legen..."
# Se o bloco não for passado, block será nil
block.call if block
puts "Dary!"
end
announce_it("Ted")
announce_it("Ted") { puts "Wait for it..." }
45
2.5. Funções, blocos, lambdas e closure
Casa do Código
Apesar de não recomendado, mesmo usando esta forma é possível usar yield e
block_given?. A grande vantagem de usar esta forma é para repassar o bloco para
outros métodos, sendo impossível com o yield:
def header(&block)
puts "Começa..."
# Precisamos transformar a variável
# em bloco novamente, por isso colocamos o &
[1,2,3].each(&block)
puts "Termina!"
end
header { |num| puts num }
# Começa...
# 1
# 2
# 3
# Termina!
# => nil
Sintaxe de blocos
A sintaxe de blocos mais usada por desenvolvedores que trabalham com
Rails é a mesma dos desenvolvedores do próprio Rails: explicitar os blo-
cos no parâmetro com &. O motivo é simples: documentação, ou seja,
fica claro para o leitor do código que aquele método pode receber um
bloco.
procs
Uma coisa interessante na sintaxe usando o operador & (apelidado também de
operador pretzel) é que o bloco é transformado em um objeto da classe Proc, que
responde ao método #call:
def block_it(&block)
puts block.class
end
46
Casa do Código
Capítulo 2. Conhecendo Ruby
block_it {} # Proc
Procs são estruturas amplamente utilizadas em projetos Ruby por diversas ra-
zões, sendo a possibilidade de transformá-las em blocos é apenas uma delas. É im-
portante ressaltar esse fato: blocos não são procs e vice-versa, mas são facilmente
transformados um no outro usando o &. Vejamos alguns exemplos:
logger = proc { |x| puts "#{Time.now} -- #{x}" }
logger.call("Teste!") # 2012-05-08 15:52:58 -0300 -- Teste!
[1,2,3].each(&logger) # O proc vira bloco para o each com o &
# 2012-05-08 15:53:46 -0300 -- 1
# 2012-05-08 15:53:46 -0300 -- 2
# 2012-05-08 15:53:46 -0300 -- 3
Um uso extremamente útil desses conceitos é que símbolos implementam o mé-
todo #to_proc, que é chamado pelo operador
&. Este proc resultante chama o mé-
todo do argumento cujo nome é o símbolo. Para ficar mais claro, vejamos os exem-