Eu estava lendo um artigo de blog e percebi que o autor usou tap
em um snippet algo como:
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
Minha pergunta é qual é exatamente o benefício ou vantagem de usar tap
? Eu não poderia simplesmente fazer:
user = User.new
user.username = "foobar"
user.save!
ou melhor ainda:
user = User.create! username: "foobar"
User.new.tap &:foobar
user = User.create!(username: 'foobar')
seria o mais claro e mais curto neste caso :) - o último exemplo da pergunta.user
". Além disso, o argumento de que "Um leitor não teria que ler o que está dentro do bloco para saber que uma instânciauser
foi criada." não tem peso, porque no primeiro bloco de código, o leitor também precisa ler apenas a primeira linha "para saber que uma instânciauser
foi criada".Outro caso de uso da torneira é fazer manipulação no objeto antes de devolvê-lo.
Então, em vez disso:
podemos economizar linha extra:
Em algumas situações, essa técnica pode economizar mais de uma linha e tornar o código mais compacto.
fonte
some_object.tap(&:serialize)
Usar a torneira, como o blogueiro fez, é simplesmente um método de conveniência. Pode ter sido um exagero em seu exemplo, mas nos casos em que você deseja fazer um monte de coisas com o usuário, o toque pode fornecer uma interface de aparência mais limpa. Então, talvez seja melhor em um exemplo como segue:
Usar o acima torna fácil ver rapidamente que todos esses métodos estão agrupados de forma que todos eles se referem ao mesmo objeto (o usuário neste exemplo). A alternativa seria:
Novamente, isso é discutível - mas pode-se argumentar que a segunda versão parece um pouco mais confusa e exige um pouco mais de análise humana para ver que todos os métodos estão sendo chamados no mesmo objeto.
fonte
user = User.new, user.do_something, user.do_another_thing
... poderia expandir por que alguém poderia fazer isso?tap
nunca adicionou nenhum benefício em minha experiência. Criar e trabalhar com umauser
variável local é muito mais limpo e legível na minha opinião.u = user = User.new
e, em seguida, usouu
para as chamadas de configuração, estaria mais de acordo com o primeiro exemplo.Isso pode ser útil para depurar uma série de
ActiveRecord
escopos encadeados.Isso torna muito fácil depurar em qualquer ponto da cadeia sem ter que armazenar nada em uma variável local nem exigir muitas alterações do código original.
E, por último, use-o como uma maneira rápida e discreta de depurar sem interromper a execução normal do código :
fonte
Visualize seu exemplo dentro de uma função
Existe um grande risco de manutenção com essa abordagem, basicamente o valor de retorno implícito .
Nesse código você depende de
save!
retornar o usuário salvo. Mas se você usar um pato diferente (ou o atual evoluir), poderá obter outras coisas, como um relatório de status de conclusão. Portanto, mudanças no pato podem quebrar o código, algo que não aconteceria se você garantisse o valor de retorno com umuser
toque simples ou de uso.Tenho visto acidentes como este com bastante frequência, especialmente com funções em que o valor de retorno normalmente não é usado, exceto para um canto escuro do buggy.
O valor de retorno implícito tende a ser uma daquelas coisas em que os novatos tendem a quebrar coisas adicionando novo código após a última linha sem perceber o efeito. Eles não veem o que o código acima realmente significa:
fonte
user
?User.new.tap{ |u| u.username = name; u.save! }
Se você quiser retornar o usuário após definir o nome de usuário, você precisa fazer
Com
tap
você poderia salvar aquele retorno estranhofonte
Object#tap
para mim.user = User.new.tap {|u| u.username = 'foobar' }
Isso resulta em um código menos confuso, pois o escopo da variável é limitado apenas à parte em que é realmente necessário. Além disso, a indentação dentro do bloco torna o código mais legível, mantendo o código relevante junto.
Descrição de
tap
diz :Se pesquisarmos o código-fonte do Rails para
tap
uso , podemos encontrar alguns usos interessantes. Abaixo estão alguns itens (lista não exaustiva) que nos darão algumas idéias sobre como usá-los:Anexar um elemento a uma matriz com base em certas condições
Inicializando uma matriz e retornando-a
Como açúcar sintático para tornar o código mais legível - Pode-se dizer, no exemplo abaixo, o uso de variáveis
hash
eserver
torna a intenção do código mais clara.Inicializa / invoca métodos em objetos recém-criados.
Abaixo está um exemplo do arquivo de teste
Para atuar no resultado de uma
yield
chamada sem ter que usar uma variável temporária.fonte
Uma variação da resposta de @ sawa:
Como já observado, o uso
tap
ajuda a descobrir a intenção do seu código (embora não necessariamente o torne mais compacto).As duas funções a seguir são igualmente longas, mas na primeira você deve ler até o final para descobrir por que inicializei um Hash vazio no início.
Aqui, por outro lado, você sabe desde o início que o hash sendo inicializado será a saída do bloco (e, neste caso, o valor de retorno da função).
fonte
tap
torna um argumento mais convincente. Concordo com outros que quando você vêuser = User.new
, a intenção já está clara. Uma estrutura de dados anônima, entretanto, pode ser usada para qualquer coisa, e otap
método pelo menos deixa claro que a estrutura de dados é o foco do método.def tapping1; {one: 1, two: 2}; end
shows usando.tap
é cerca de 50% mais lento neste casoÉ um auxiliar para encadeamento de chamadas. Ele passa seu objeto para o bloco fornecido e, após o bloco terminar, retorna o objeto:
O benefício é que o toque sempre retorna o objeto em que é chamado, mesmo se o bloco retornar algum outro resultado. Assim, você pode inserir um bloco de derivação no meio de um pipeline de método existente sem interromper o fluxo.
fonte
Eu diria que não há vantagem em usar
tap
. O único benefício potencial, como @sawa aponta, é, e cito: "Um leitor não teria que ler o que está dentro do bloco para saber que um usuário de instância foi criado." No entanto, nesse ponto, pode-se argumentar que, se você estiver fazendo uma lógica de criação de registro não simplista, sua intenção seria melhor comunicada extraindo essa lógica em seu próprio método.Defendo a opinião de que
tap
é um fardo desnecessário para a legibilidade do código e pode ser feito sem, ou substituído por uma técnica melhor, como Extrair Método .Embora
tap
seja um método de conveniência, também é uma preferência pessoal. Dêtap
uma chance. Em seguida, escreva algum código sem usar o toque, veja se você gosta de um jeito em vez de outro.fonte
Pode haver vários usos e lugares onde podemos usar
tap
. Até agora, encontrei apenas 2 usos detap
.1) O objetivo principal deste método é explorar uma cadeia de métodos, a fim de realizar operações em resultados intermediários dentro da cadeia. ie
2) Você já se pegou chamando um método em algum objeto e o valor de retorno não era o que você queria? Talvez você queira adicionar um valor arbitrário a um conjunto de parâmetros armazenados em um hash. Você o atualiza com Hash. [] , Mas obtém a barra de volta ao invés do hash de parâmetros, então você tem que retorná-lo explicitamente. ie
Para superar essa situação aqui, o
tap
método entra em jogo. Basta chamá-lo no objeto e, em seguida, tocar em um bloco com o código que você deseja executar. O objeto será cedido ao bloco e então devolvido. ieExistem dezenas de outros casos de uso, tente encontrá-los você mesmo :)
Fonte:
1) API Dock Object tap
2) five-ruby-methods-you-should-using
fonte
Você está certo: o uso de
tap
em seu exemplo é meio inútil e provavelmente menos limpo do que suas alternativas.Como Rebitzele observa,
tap
é apenas um método de conveniência, frequentemente usado para criar uma referência mais curta para o objeto atual.Um bom caso de uso
tap
é para depuração: você pode modificar o objeto, imprimir o estado atual e continuar modificando o objeto no mesmo bloco. Veja aqui, por exemplo: http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions .Ocasionalmente, gosto de usar
tap
métodos internos para retornar condicionalmente antecipadamente enquanto, caso contrário, retorne o objeto atual.fonte
Existe uma ferramenta chamada flog que mede o quão difícil é ler um método. "Quanto mais alta a pontuação, maior o problema do código."
e de acordo com o resultado do flog o método com
tap
é o mais difícil de ler (e eu concordo com isso)fonte
Você pode tornar seus códigos mais modulares usando tap e conseguir um melhor gerenciamento das variáveis locais. Por exemplo, no código a seguir, você não precisa atribuir uma variável local ao objeto recém-criado, no escopo do método. Observe que a variável de bloco, u , tem como escopo dentro do bloco. Na verdade, é uma das belezas do código Ruby.
fonte
Em trilhos, podemos usar
tap
para permitir parâmetros explicitamente:fonte
Darei outro exemplo que usei. Eu tenho um método user_params que retorna os parâmetros necessários para salvar para o usuário (este é um projeto Rails)
Você pode ver que eu não retornei nada além do Ruby retornar a saída da última linha.
Então, depois de algum tempo, precisei adicionar um novo atributo condicionalmente. Então, eu mudei para algo assim:
Aqui, podemos usar o toque para remover a variável local e remover o retorno:
fonte
No mundo onde o padrão de programação funcional está se tornando uma prática recomendada ( https://maryrosecook.com/blog/post/a-practical-introduction-to-functional-programming ), você pode ver
tap
, como ummap
valor único, de fato , para modificar seus dados em uma cadeia de transformação.Não há necessidade de declarar
item
várias vezes aqui.fonte
Qual é a diferença?
A diferença em termos de legibilidade do código é puramente estilística.
Código passo a passo:
Pontos chave:
u
variável agora é usada como parâmetro de bloco?user
variável agora deve apontar para um usuário (com um nome de usuário: 'foobar', e que também está salvo).Documentação API
Esta é uma versão fácil de ler do código-fonte:
Para obter mais informações, consulte estes links:
https://apidock.com/ruby/Object/tap
http://ruby-doc.org/core-2.2.3/Object.html#method-i-tap
fonte