ruby ruby中tap方法的优点
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/17493080/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
advantage of tap method in ruby
提问by Kyle Decot
I was just reading a blog article and noticed that the author used tapin a snippet something like:
我只是在阅读一篇博客文章,并注意到作者tap在一个片段中使用了类似的内容:
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
My question is what exactly is the benefit or advantage of using tap? Couldn't I just do:
我的问题是使用 的好处或优势究竟是什么tap?我不能只做:
user = User.new
user.username = "foobar"
user.save!
or better yet:
或者更好:
user = User.create! username: "foobar"
采纳答案by sawa
When readers encounter:
当读者遇到:
user = User.new
user.username = "foobar"
user.save!
they would have to follow all the three lines and then recognize that it is just creating an instance named user.
他们必须遵循所有三行,然后才能认识到它只是在创建一个名为user.
If it were:
如果是:
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
then that would be immediately clear. A reader would not have to read what is inside the block to know that an instance useris created.
那么这将立即清楚。读者不必阅读块内的内容即可知道实例user已创建。
回答by megas
Another case to use tap is to make manipulation on object before returning it.
使用 tap 的另一种情况是在返回对象之前对其进行操作。
So instead of this:
所以而不是这个:
def some_method
...
some_object.serialize
some_object
end
we can save extra line:
我们可以节省额外的行:
def some_method
...
some_object.tap{ |o| o.serialize }
end
In some situation this technique can save more then one line and make code more compact.
在某些情况下,这种技术可以节省多于一行并使代码更紧凑。
回答by Rebitzele
Using tap, as the blogger did, is simply a convenience method. It may have been overkill in your example, but in cases where you'd want to do a bunch of things with the user, tap can arguably provide a cleaner looking interface. So, perhaps it may be better in an example as follows:
正如博主所做的那样,使用点击只是一种方便的方法。在您的示例中,这可能有点矫枉过正,但在您想与用户一起做很多事情的情况下,点击可以说提供了一个更简洁的界面。因此,也许在以下示例中可能会更好:
user = User.new.tap do |u|
u.build_profile
u.process_credit_card
u.ship_out_item
u.send_email_confirmation
u.blahblahyougetmypoint
end
Using the above makes it easy to quickly see that all those methods are grouped together in that they all refer to the same object (the user in this example). The alternative would be:
使用上面的方法可以很容易地快速看到所有这些方法都组合在一起,因为它们都引用同一个对象(本例中的用户)。替代方案是:
user = User.new
user.build_profile
user.process_credit_card
user.ship_out_item
user.send_email_confirmation
user.blahblahyougetmypoint
Again, this is debatable - but the case can be made that the second version looks a little messier, and takes a little more human parsing to see that all the methods are being called on the same object.
同样,这是有争议的 - 但可以证明第二个版本看起来有点混乱,并且需要更多的人工解析才能看到所有方法都在同一个对象上被调用。
回答by Gupta
This can be useful with debugging a series of ActiveRecordchained scopes.
这对于调试一系列链式作用域非常有用。ActiveRecord
User
.active .tap { |users| puts "Users so far: #{users.size}" }
.non_admin .tap { |users| puts "Users so far: #{users.size}" }
.at_least_years_old(25) .tap { |users| puts "Users so far: #{users.size}" }
.residing_in('USA')
This makes it super easy to debug at any point in the chain without having to store anything in in a local variable nor requiring much altering of the original code.
这使得在链中的任何点进行调试变得非常容易,而无需在局部变量中存储任何内容,也不需要对原始代码进行大量更改。
And lastly, use it as a quick and unobtrusive way to debug without disrupting normal code execution:
最后,将其用作一种快速且不引人注目的调试方式,而不会中断正常的代码执行:
def rockwell_retro_encabulate
provide_inverse_reactive_current
synchronize_cardinal_graham_meters
@result.tap(&method(:puts))
# Will debug `@result` just before returning it.
end
回答by montrealmike
If you wanted to return the user after setting the username you'd need to do
如果您想在设置用户名后返回用户,则需要执行以下操作
user = User.new
user.username = 'foobar'
user
With tapyou could save that awkward return
有了tap你就可以省去那个尴尬的回报
User.new.tap do |user|
user.username = 'foobar'
end
回答by SystematicFrank
Visualize your example within a function
在函数中可视化您的示例
def make_user(name)
user = User.new
user.username = name
user.save!
end
There is a big maintenance risk with that approach, basically the implicit return value.
这种方法存在很大的维护风险,基本上是隐式返回值。
In that code you do depend on save!returning the saved user. But if you use a different duck (or your current one evolves) you might get other stuff like a completion status report. Therefore changes to the duck might break the code, something that would not happen if you ensure the return value with a plain useror use tap.
在该代码中,您确实依赖于save!返回保存的用户。但是如果你使用不同的鸭子(或者你当前的鸭子进化),你可能会得到其他东西,比如完成状态报告。因此,对duck 的更改可能会破坏代码,如果您user使用普通或使用tap确保返回值,则不会发生这种情况。
I have seen accidents like this quite often, specially with functions where the return value is normally not used except for one dark buggy corner.
我经常看到这样的事故,特别是那些通常不使用返回值的函数,除了一个黑暗的马车角落。
The implicit return value tends to be one of those things where newbies tend to break things adding new code after the last line without noticing the effect. They do not see what the above code really means:
隐式返回值往往是新手倾向于在最后一行之后添加新代码而没有注意到效果的事情之一。他们没有看到上述代码的真正含义:
def make_user(name)
user = User.new
user.username = name
return user.save! # notice something different now?
end
回答by Wand Maker
It results in less-cluttered code as the scope of variable is limited only to the part where it is really needed. Also, the indentation within the block makes the code more readable by keeping relevant code together.
由于变量的范围仅限于真正需要它的部分,因此它可以减少代码的混乱。此外,块内的缩进通过将相关代码放在一起使代码更具可读性。
Yields self to the block, and then returns self.The primary purpose of this method is to “tap into” a method chain, in order to perform operations on intermediate results within the chain.
将 self 交给块,然后返回 self。此方法的主要目的是“接入”方法链,以便对链中的中间结果执行操作。
If we search rails source code for tapusage, we can find some interesting usages. Below are few items (not exhaustive list) that will give us few ideas on how to use them:
如果我们搜索 rails 源代码的tap用法,我们可以找到一些有趣的用法。下面是一些项目(不是详尽的列表),它们会给我们一些关于如何使用它们的想法:
Append an element to an array based on certain conditions
%w( annotations ... routes tmp ).tap { |arr| arr << 'statistics' if Rake.application.current_scope.empty? }.each do |task| ... endInitializing an array and returning it
[].tap do |msg| msg << "EXPLAIN for: #{sql}" ... msg << connection.explain(sql, bind) end.join("\n")As syntactic sugar to make code more readable - One can say, in below example, use of variables
hashandservermakes the intent of code clearer.def select(*args, &block) dup.tap { |hash| hash.select!(*args, &block) } endInitialize/invoke methods on newly created objects.
Rails::Server.new.tap do |server| require APP_PATH Dir.chdir(Rails.application.root) server.start endBelow is an example from test file
@pirate = Pirate.new.tap do |pirate| pirate.catchphrase = "Don't call me!" pirate.birds_attributes = [{:name => 'Bird1'},{:name => 'Bird2'}] pirate.save! endTo act on the result of a
yieldcall without having to use a temporary variable.yield.tap do |rendered_partial| collection_cache.write(key, rendered_partial, cache_options) end
根据特定条件将元素追加到数组中
%w( annotations ... routes tmp ).tap { |arr| arr << 'statistics' if Rake.application.current_scope.empty? }.each do |task| ... end初始化一个数组并返回它
[].tap do |msg| msg << "EXPLAIN for: #{sql}" ... msg << connection.explain(sql, bind) end.join("\n")至于语法糖使代码更易读-可以说,在下面的例子,使用的变量
hash,并server使得意图的代码更清晰。def select(*args, &block) dup.tap { |hash| hash.select!(*args, &block) } end在新创建的对象上初始化/调用方法。
Rails::Server.new.tap do |server| require APP_PATH Dir.chdir(Rails.application.root) server.start end以下是测试文件中的示例
@pirate = Pirate.new.tap do |pirate| pirate.catchphrase = "Don't call me!" pirate.birds_attributes = [{:name => 'Bird1'},{:name => 'Bird2'}] pirate.save! end对
yield调用结果采取行动而不必使用临时变量。yield.tap do |rendered_partial| collection_cache.write(key, rendered_partial, cache_options) end
回答by Giuseppe
A variation on @sawa's answer:
@sawa 答案的变体:
As already noted, using taphelps figuring out the intent of your code (while not necessarily making it more compact).
如前所述,使用tap有助于弄清代码的意图(但不一定使它更紧凑)。
The following two functions are equally long, but in the first one you have to read through the end to figure out why I initialized an empty Hash at the beginning.
以下两个函数同样长,但在第一个函数中,您必须通读结尾才能弄清楚为什么我在开头初始化了一个空 Hash。
def tapping1
# setting up a hash
h = {}
# working on it
h[:one] = 1
h[:two] = 2
# returning the hash
h
end
Here, on the other hand, you know right from the start that the hash being initialized will be the block's output (and, in this case, the function's return value).
在这里,另一方面,你从一开始就知道被初始化的散列将是块的输出(在这种情况下,是函数的返回值)。
def tapping2
# a hash will be returned at the end of this block;
# all work will occur inside
Hash.new.tap do |h|
h[:one] = 1
h[:two] = 2
end
end
回答by Pushp Raj Saurabh
It's a helper for call chaining. It passes its object into the given block and, after the block finishes, returns the object:
它是调用链的帮手。它将其对象传递给给定的块,并在块完成后返回该对象:
an_object.tap do |o|
# do stuff with an_object, which is in o #
end ===> an_object
The benefit is that tap always returns the object it's called on, even if the block returns some other result. Thus you can insert a tap block into the middle of an existing method pipeline without breaking the flow.
好处是 tap 总是返回它被调用的对象,即使块返回一些其他结果。因此,您可以在现有方法管道的中间插入一个 tap 块,而不会中断流程。
回答by gylaz
I would say that there is no advantage to using tap. The only potential benefit, as @sawa points outis, and I quote: "A reader would not have to read what is inside the block to know that an instance user is created." However, at that point the argument can be made that if you're doing non-simplistic record creation logic, your intent would be better communicated by extracting that logic into its own method.
我会说使用tap. 正如@sawa 指出的,唯一潜在的好处是,我引用:“读者不必阅读块内的内容即可知道实例用户已创建。” 但是,在这一点上,可以提出这样的论点:如果您正在执行非简单的记录创建逻辑,那么通过将该逻辑提取到其自己的方法中会更好地传达您的意图。
I hold to the opinion that tapis an unnecessary burden on the readability of the code, and could be done without, or substituted with a better technique, like Extract Method.
我坚持认为这tap对代码的可读性造成了不必要的负担,可以不用或用更好的技术代替,例如Extract Method。
While tapis a convenience method, it's also personal preference. Give tapa try. Then write some code without using tap, see if you like one way over another.
虽然tap是一种方便的方法,但也是个人喜好。给tap一试。然后在不使用 tap 的情况下编写一些代码,看看您是否喜欢一种方式。

