Estaba leyendo un artículo de blog y noté que el autor usó tap
en un fragmento algo como:
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
Mi pregunta es ¿cuál es exactamente el beneficio o la ventaja de usar tap
? ¿No podría simplemente hacer:
user = User.new
user.username = "foobar"
user.save!
o mejor aún:
user = User.create! username: "foobar"
User.new.tap &:foobar
user = User.create!(username: 'foobar')
sería más claro y más corto en este caso :) - el último ejemplo de la pregunta.user
". Además, el argumento de que "Un lector no tendría que leer lo que hay dentro del bloque para saber queuser
se crea una instancia ". no tiene peso, porque en el primer bloque de código, el lector también necesita leer la primera línea "para saber queuser
se crea una instancia ".Otro caso para usar tap es realizar manipulación en el objeto antes de devolverlo.
Entonces en lugar de esto:
podemos ahorrar línea extra:
En alguna situación, esta técnica puede ahorrar más de una línea y hacer que el código sea más compacto.
fuente
some_object.tap(&:serialize)
Usar tap, como hizo el bloguero, es simplemente un método conveniente. Puede haber sido exagerado en su ejemplo, pero en los casos en los que quisiera hacer un montón de cosas con el usuario, podría decirse que el toque puede proporcionar una interfaz de aspecto más limpio. Entonces, tal vez sea mejor en un ejemplo como sigue:
El uso de lo anterior facilita ver rápidamente que todos esos métodos están agrupados en el sentido de que todos se refieren al mismo objeto (el usuario en este ejemplo). La alternativa sería:
Nuevamente, esto es discutible, pero se puede argumentar que la segunda versión parece un poco más desordenada y requiere un poco más de análisis humano para ver que todos los métodos se invocan en el mismo objeto.
fuente
user = User.new, user.do_something, user.do_another_thing
... ¿podría explicar por qué uno podría hacer esto?tap
nunca ha agregado beneficios en mi experiencia. Crear y trabajar con unauser
variable local es mucho más limpio y legible en mi opinión.u = user = User.new
y luego lo usarau
para las llamadas de configuración, estaría más en línea con el primer ejemplo.Esto puede resultar útil para depurar una serie de
ActiveRecord
ámbitos encadenados.Esto hace que sea muy fácil depurar en cualquier punto de la cadena sin tener que almacenar nada en una variable local ni requerir mucha alteración del código original.
Y, por último, utilícelo como una forma rápida y discreta de depurar sin interrumpir la ejecución normal del código :
fuente
Visualiza tu ejemplo dentro de una función
Existe un gran riesgo de mantenimiento con ese enfoque, básicamente el valor de retorno implícito .
En ese código, depende de
save!
devolver el usuario guardado. Pero si usa un pato diferente (o el actual evoluciona), es posible que obtenga otras cosas como un informe de estado de finalización. Por lo tanto, los cambios en el pato podrían romper el código, algo que no sucedería si se asegura el valor de retorno con un simpleuser
toque o toque.He visto accidentes como este con bastante frecuencia, especialmente con funciones en las que el valor de retorno normalmente no se usa, excepto en una esquina oscura con errores.
El valor de retorno implícito tiende a ser una de esas cosas en las que los novatos tienden a romper cosas agregando código nuevo después de la última línea sin notar el efecto. No ven lo que realmente significa el código anterior:
fuente
user
?User.new.tap{ |u| u.username = name; u.save! }
Si desea devolver el usuario después de configurar el nombre de usuario, debe hacerlo
Con
tap
tu podrías salvar ese incómodo regresofuente
Object#tap
para mí.user = User.new.tap {|u| u.username = 'foobar' }
Da como resultado un código menos desordenado, ya que el alcance de la variable se limita solo a la parte donde realmente se necesita. Además, la sangría dentro del bloque hace que el código sea más legible al mantener el código relevante junto.
Descripción de
tap
dice :Si buscamos el código fuente de rails para su
tap
uso , podemos encontrar algunos usos interesantes. A continuación se muestran algunos elementos (lista no exhaustiva) que nos darán algunas ideas sobre cómo usarlos:Agregar un elemento a una matriz en función de ciertas condiciones
Inicializar una matriz y devolverla
Como azúcar sintáctico para hacer el código más legible: se puede decir, en el siguiente ejemplo, el uso de variables
hash
yserver
aclara la intención del código.Inicializar / invocar métodos en objetos recién creados.
A continuación se muestra un ejemplo del archivo de prueba
Actuar sobre el resultado de una
yield
llamada sin tener que utilizar una variable temporal.fuente
Una variación de la respuesta de @ sawa:
Como ya se señaló, el uso
tap
ayuda a descubrir la intención de su código (aunque no necesariamente lo hace más compacto).Las siguientes dos funciones son igualmente largas, pero en la primera tienes que leer hasta el final para descubrir por qué inicialicé un Hash vacío al principio.
Aquí, por otro lado, sabe desde el principio que el hash que se inicializa será la salida del bloque (y, en este caso, el valor de retorno de la función).
fuente
tap
convierte en un argumento más convincente. Estoy de acuerdo con otros en que cuando vesuser = User.new
, la intención ya está clara. Sin embargo, una estructura de datos anónima podría usarse para cualquier cosa, y eltap
método al menos deja en claro que la estructura de datos es el foco del método.def tapping1; {one: 1, two: 2}; end
programas.tap
es aproximadamente un 50% más lento en este casoEs un ayudante para el encadenamiento de llamadas. Pasa su objeto al bloque dado y, una vez finalizado el bloque, devuelve el objeto:
El beneficio es que tap siempre devuelve el objeto al que se llama, incluso si el bloque devuelve algún otro resultado. Por lo tanto, puede insertar un bloque de derivación en el medio de una tubería de método existente sin interrumpir el flujo.
fuente
Yo diría que usar
tap
. El único beneficio potencial, como señala @sawa es, y cito: "Un lector no tendría que leer lo que hay dentro del bloque para saber que se crea un usuario de instancia". Sin embargo, en ese punto se puede argumentar que si está utilizando una lógica de creación de registros no simplista, su intención se comunicaría mejor extrayendo esa lógica en su propio método.Sostengo la opinión de que
tap
es una carga innecesaria para la legibilidad del código, y podría prescindirse o sustituirse por una técnica mejor, como el método de extracción .Si bien
tap
es un método de conveniencia, también es una preferencia personal. Pruébalotap
. Luego escriba un código sin usar el tap, vea si le gusta una forma sobre otra.fuente
Podría haber varios usos y lugares donde podríamos usar
tap
. Hasta ahora solo he encontrado los siguientes 2 usos detap
.1) El propósito principal de este método es aprovechar una cadena de métodos para realizar operaciones sobre resultados intermedios dentro de la cadena. es decir
2) ¿Alguna vez se encontró llamando a un método en algún objeto y el valor de retorno no era el que deseaba? Tal vez quisiera agregar un valor arbitrario a un conjunto de parámetros almacenados en un hash. Lo actualizas con Hash. [] , Pero obtienes back bar en lugar del hash params, por lo que debes devolverlo explícitamente. es decir
Para superar esta situación aquí,
tap
entra en juego el método. Simplemente instálelo en el objeto, luego toque un bloque con el código que desea ejecutar. El objeto se cederá al bloque y luego se devolverá. es decirHay docenas de otros casos de uso, intente encontrarlos usted mismo :)
Fuente:
1) API Dock Object Tap
2) Five-Ruby-methods-you-should-be-using
fuente
Tienes razón: el uso de
tap
en tu ejemplo es un poco inútil y probablemente menos limpio que tus alternativas.Como señala Rebitzele,
tap
es solo un método de conveniencia, que a menudo se usa para crear una referencia más corta al objeto actual.Un buen caso de uso
tap
es para la depuración: puede modificar el objeto, imprimir el estado actual y luego continuar modificando el objeto en el mismo bloque. Vea aquí por ejemplo: http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions .De vez en cuando me gusta usar
tap
métodos internos para regresar condicionalmente temprano mientras devuelvo el objeto actual de lo contrario.fuente
Existe una herramienta llamada flog que mide qué tan difícil es leer un método. "Cuanto mayor sea la puntuación, más doloroso será el código".
y según el resultado de flog, el método con el que
tap
es más difícil de leer (y estoy de acuerdo con él)fuente
Puede hacer que sus códigos sean más modulares usando tap y puede lograr una mejor gestión de las variables locales. Por ejemplo, en el siguiente código, no es necesario asignar una variable local al objeto recién creado, en el alcance del método. Tenga en cuenta que la variable de bloque, u , tiene un alcance dentro del bloque. En realidad, es una de las bellezas del código ruby.
fuente
En rieles podemos usar
tap
para incluir parámetros en la lista blanca explícitamente:fuente
Daré otro ejemplo que he utilizado. Tengo un método user_params que devuelve los parámetros necesarios para guardar para el usuario (este es un proyecto de Rails)
Puede ver que no devuelvo nada, pero ruby devuelve el resultado de la última línea.
Luego, después de algún tiempo, necesitaba agregar un nuevo atributo condicionalmente. Entonces, lo cambié a algo como esto:
Aquí podemos usar tap para eliminar la variable local y eliminar el retorno:
fuente
En el mundo donde el patrón de programación funcional se está convirtiendo en una mejor práctica ( https://maryrosecook.com/blog/post/a-practical-introduction-to-functional-programming ), puede ver
tap
, como unmap
valor único, de hecho , para modificar sus datos en una cadena de transformación.No es necesario declarar
item
varias veces aquí.fuente
¿Cuál es la diferencia?
La diferencia en términos de legibilidad del código es puramente estilística.
Recorrido por el código:
Puntos clave:
u
variable ahora se usa como parámetro de bloque?user
variable ahora debería apuntar a un Usuario (con un nombre de usuario: 'foobar', y que también está guardado).Documentación de API
Aquí hay una versión fácil de leer del código fuente:
Para obtener más información, consulte estos enlaces:
https://apidock.com/ruby/Object/tap
http://ruby-doc.org/core-2.2.3/Object.html#method-i-tap
fuente