Usando Keyword Arguments

Vamos falar um pouco sobre como passar parâmetros utilizando Ruby.

No Code Saga temos um desafio em que surgem muitas dúvidas sobre como solucioná-lo.

Uma característica introduzida no Ruby 2.1 foi o Keyword Arguments e ela pode nos ajudar nesse desafio. Vamos ver como isso funciona.

Suponha que você quer um parâmetro opcional. Para isso você faria:

def pagar(valor, forma_pagto='boleto')
 puts "Pagando R$#{valor} com #{forma_pagto}"
end

pagar(1000)
#=> Pagando R$1000 com boleto

O código acima não tem nenhum problema, mas requer do programador que vai usá-lo conhecer, no mínimo, quais parâmetros e em qual ordem eles são esperados:

pagar('cartão', 1000)
#=> Pagando R$cartão com 1000

Nesse caso o keyword arguments ajuda muito, veja o exemplo:

def pagar(valor:, forma_pagto: 'boleto')
  puts "Pagando R$#{valor} com #{forma_pagto}"
end

pagar(1000)
#=>BOOOOM! ArgumentError: wrong number of arguments (given 1, expected 0)

pagar(valor: 1000)
#=> Pagando R$1000 com boleto

pagar(forma_pagto: 'Cartão', valor: 1300)
#=> Pagando R$1300 com Cartão

Veja que na segunda execução do método #pagar nós passamos os parâmetros em ordem diferente da qual eles foram declarados e ainda assim o método funcionou como esperado.

Podemos esperar argumentos adicionais, porém também “nomeados”, veja:

def pagar(valor:, forma_pagto:, **opcoes)
  puts "Pagando R$#{valor} com #{forma_pagto}"
  puts "Opcoes #{opcoes}"
end

pagar(forma_pagto: 'Cartão', valor: 1300, limite:3000, autorizado: true)
#=> Pagando R$1300 com Cartão
Opcoes {:limite=>3000, autorizado: true}

pagar(forma_pagto: 'Cartão', valor: 1300, 3000, true)
#=> BOOOOM!!! SyntaxError: (irb):26: syntax error, unexpected ',', expecting =>

A segunda execução do exemplo acima nos mostra que o uso de keyword arguments exige a nomeação de qualquer argumento, até os adicionais!

Keyword Arguments X Hash arguments

Você já deve ter visto algo como:

def algum_metodo(params)
  arg1 = params[:arg1]
  arg2 = params[:arg2]
  arg3 = params[:arg3]
  puts "valor arg1: #{arg1} arg2: #{arg2} arg3: #{arg3}"
end

algum_metodo(arg1: 1431, arg2: 'valor', arg3: :symb)
#=> valor arg1: 1431 arg2: valor arg3: :symb

algum_metodo(arg1: 1431, arg2: 'valor', arg1: :symb)
#=> valor arg1: symb arg2: valor arg3:

Consegue achar o erro na segunda execução do exemplo acima? Difícil, né? Vamos implementar o mesmo código com o keywords arguments:

def algum_metodo(arg1:, arg2:, arg3:)
  puts "valor arg1: #{arg1} arg2: #{arg2} arg3: #{arg3}"
end

algum_metodo(arg1: 1431, arg2: 'valor', arg3: :symb)
#=> valor arg1: 1431 arg2: valor arg3: :symb

algum_metodo(arg1: 1431, arg2: 'valor', arg1: :symb)
#=> BOOOOM! ArgumentError: missing keyword: arg3

Keyword Arguments X Argumentos Posicionais

Aqui a coisa complica um pouco… Você já escreveu muito código com argumentos posicionais e não viu nenhum problema nisso.

Pois é, o problema mora quando outro programador (você, de amanhã) precisa usar esse código que escreveu:

def meu_link_to(url_or_path, text, css, *options)
 # implentacao por conta da imaginação
end

meu_link_to('Texto', posts_path, "post_link")
#=> <a class="post_link" href="Texto">/posts</a>

Um pouco estranho, né? Você, durante a implementação, achou que colocar o path ou url como primeiro argumento faria todo sentido, mas esqueceu disso quando foi utilizar o método. É a vida, não é mesmo?!

Considerações

Não há reais motivos para você sair refatorando todos seus códigos agora, mas é bom considerar keyword arguments quando implementar aquele métodos que a ordem dos argumentos não são lá tão intuitivos quanto parecem à primeira vista.

Uma boa regra é: poucos argumentos (1 ou 2) que façam sentido na ordem que você implementou não são necessariamente candidatos para keyword arguments. Métodos com 3 ou mais argumentos podem ser candidatos a um belo Refactoring, mas isso é assunto para outro post ;).

E você, o que acha dos keyword arguments? Quando devemos usá-los? Deixe seu comentário.

Até mais!

Referências

Faça login para comentar.

Entrar

2 Comentários

matheusam

matheusam há 7 meses

No quarto snipet foi usado "def pagar(valor:, formapagto:, **opcoes)" e no último "def meulinkto(urlor_path, text, css, options)" Qual a diferença entre * e *? Ambos indicam um valor opcional, correto? ** seria para mais de um argumento?


matheusam

matheusam há 7 meses

def meulinkto(urlorpath, text, css, *options)

Faltou o asterisco no segundo.


vinicdasilva

vinicdasilva há 7 meses

Olá matheusam. Essa documentação tem as informações que você deseja: https://ruby-doc.org/core-2.3.0/doc/syntax/callingmethodsrdoc.html

Dica: Não exite em consultar a documentação oficial do Ruby. Ela é excelente e provavelmente responderá a maioria das suas dúvidas.


Luis Zambon

Luis Zambon há 8 meses

Maneiro! Vou experimentar nos próximos códigos.