I was just reading a blog article and noticed that the author used tap in a snippet something like:
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:
user = User.new
user.username = "foobar"
user.save!
or better yet:
user = User.create! username: "foobar"
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.
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 user is created.
Another case to use tap is to make manipulation on object before returning it.
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.
This can be useful with debugging a series of ActiveRecord chained scopes.
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
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.
If you wanted to return the user after setting the username you'd need to do
user = User.new
user.username = 'foobar'
user
With tap you could save that awkward return
User.new.tap do |user|
user.username = 'foobar'
end
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.
Description of tap says:
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.
If we search rails source code for tap usage, we can find some interesting usages. Below are few items (not exhaustive list) that will give us few ideas on how to use them:
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|
...
end
Initializing 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 hash and server makes the intent of code clearer.
def select(*args, &block)
dup.tap { |hash| hash.select!(*args, &block) }
end
Initialize/invoke methods on newly created objects.
Rails::Server.new.tap do |server|
require APP_PATH
Dir.chdir(Rails.application.root)
server.start
end
Below 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!
end
To act on the result of a yield call without having to use a temporary variable.
yield.tap do |rendered_partial|
collection_cache.write(key, rendered_partial, cache_options)
end
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 user or use 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
A variation on #sawa's answer:
As already noted, using tap helps figuring out the intent of your code (while not necessarily making it more compact).
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.
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
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.
I would say that there is no advantage to using tap. The only potential benefit, as #sawa points out is, 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.
I hold to the opinion that tap is an unnecessary burden on the readability of the code, and could be done without, or substituted with a better technique, like Extract Method.
While tap is a convenience method, it's also personal preference. Give tap a try. Then write some code without using tap, see if you like one way over another.
There is a tool called flog that measures how difficult it is to read a method. "The higher the score, the more pain the code is in."
def with_tap
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
end
def without_tap
user = User.new
user.username = "foobar"
user.save!
end
def using_create
user = User.create! username: "foobar"
end
and according on flog's result the method with tap is the most difficult to read (and I agree with it)
4.5: main#with_tap temp.rb:1-4
2.4: assignment
1.3: save!
1.3: new
1.1: branch
1.1: tap
3.1: main#without_tap temp.rb:8-11
2.2: assignment
1.1: new
1.1: save!
1.6: main#using_create temp.rb:14-16
1.1: assignment
1.1: create!
There could be number of uses and places where we may be able to use tap. So far I have only found following 2 uses of tap.
1) The primary purpose of this method is to tap into a method chain, in order to perform operations on intermediate results within the chain. i.e
(1..10).tap { |x| puts "original: #{x.inspect}" }.to_a.
tap { |x| puts "array: #{x.inspect}" }.
select { |x| x%2 == 0 }.
tap { |x| puts "evens: #{x.inspect}" }.
map { |x| x*x }.
tap { |x| puts "squares: #{x.inspect}" }
2) Did you ever find yourself calling a method on some object, and the return value not being what you wanted it to? Maybe you wanted to add an arbitrary value to a set of parameters stored in a hash. You update it with Hash.[], but you get back bar instead of the params hash, so you have to return it explicitly. i.e
def update_params(params)
params[:foo] = 'bar'
params
end
In order to overcome this situation here, tap method comes into play. Just call it on the object, then pass tap a block with the code that you wanted to run. The object will be yielded to the block, then be returned. i.e
def update_params(params)
params.tap {|p| p[:foo] = 'bar' }
end
There are dozens of other use cases, try finding them yourself :)
Source:
1) API Dock Object tap
2) five-ruby-methods-you-should-be-using
You're right: the use of tap in your example is kind of pointless and probably less clean than your alternatives.
As Rebitzele notes, tap is just a convenience method, often used to create a shorter reference to the current object.
One good use case for tap is for debugging: you can modify the object, print the current state, then continue modifying the object in the same block. See here for example: http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions.
I occasionally like to use tap inside methods to conditionally return early while returning the current object otherwise.
You can make your codes more modular using tap, and can achieve a better management of local variables. For example, in the following code, you don't need to assign a local variable to the newly created object, in the scope of the method. Note that the block variable, u, is scoped within the block. It is actually one of the beauties of ruby code.
def a_method
...
name = "foobar"
...
return User.new.tap do |u|
u.username = name
u.save!
end
end
In rails we can use tap to whitelist parameters explicitly:
def client_params
params.require(:client).permit(:name).tap do |whitelist|
whitelist[:name] = params[:client][:name]
end
end
I will give another example which I have used. I have a method user_params which returns the params needed to save for the user (this is a Rails project)
def user_params
params.require(:user).permit(
:first_name,
:last_name,
:email,
:address_attributes
)
end
You can see I dont return anything but ruby return the output of the last line.
Then, after sometime, I needed to add a new attribute conditionally. So, I changed it to something like this:
def user_params
u_params = params.require(:user).permit(
:first_name,
:last_name,
:email,
:address_attributes
)
u_params[:time_zone] = address_timezone if u_params[:address_attributes]
u_params
end
Here we can use tap to remove the local variable and remove the return:
def user_params
params.require(:user).permit(
:first_name,
:last_name,
:email,
:address_attributes
).tap do |u_params|
u_params[:time_zone] = address_timezone if u_params[:address_attributes]
end
end
In the world where functional programming pattern is becoming a best practice (https://maryrosecook.com/blog/post/a-practical-introduction-to-functional-programming), you can see tap, as a map on a single value, indeed, to modify your data on a transformation chain.
transformed_array = array.map(&:first_transformation).map(&:second_transformation)
transformed_value = item.tap(&:first_transformation).tap(&:second_transformation)
No need to declare item multiple times here.
What is the difference?
The difference in terms of code readability is purely stylistic.
Code Walk through:
user = User.new.tap do |u|
u.username = "foobar"
u.save!
end
Key points:
Notice how the u variable is now used as block parameter?
After the block is done, the user variable should now point to a User ( with a username: ‘foobar’, and who is also saved).
It's just pleasant and easier to read.
API Documentation
Here’s an easy to read version of the source code:
class Object
def tap
yield self
self
end
end
For more info, see these links:
https://apidock.com/ruby/Object/tap
http://ruby-doc.org/core-2.2.3/Object.html#method-i-tap
Apart from the above answers, I have used tap in stubbing and mocking while writing RSpecs.
Scenario: When I have a complex query to stub and mock with multiple arguments which shouldn't go missed. The alternative here is to use receive_message_chain (but it lacks the details).
# Query
Product
.joins(:bill)
.where("products.availability = ?", 1)
.where("bills.status = ?", "paid")
.select("products.id", "bills.amount")
.first
# RSpecs
product_double = double('product')
expect(Product).to receive(:joins).with(:bill).and_return(product_double.tap do |product_scope|
expect(product_scope).to receive(:where).with("products.availability = ?", 1).and_return(product_scope)
expect(product_scope).to receive(:where).with("bills.status = ?", "paid").and_return(product_scope)
expect(product_scope).to receive(:select).with("products.id", "bills.amount").and_return(product_scope)
expect(product_scope).to receive(:first).and_return({ id: 1, amount: 100 })
end)
# Alternative way by using `receive_message_chain`
expect(Product).to receive_message_chain(:joins, :where, :where, :select).and_return({ id: 1, amount: 100 })
Related
I have an array to which I keep adding blocks of code at different points of time. When a particular event occurs, an iterator iterates through this array and yields the blocks one after the other.
Many of these blocks are the same and I want to avoid executing duplicate blocks.
This is sample code:
#after_event_hooks = []
def add_after_event_hook(&block)
#after_event_hooks << block
end
Something like #after_event_hooks.uniq or #after_event_hooks |= block don't work.
Is there a way to compare blocks or check their uniqueness?
The blocks can not be checked for uniqueness since that will mean to check whether they represent the same functions, something that is not possible and has been researched in computer science for a long time.
You can probably use a function similar to the discussed in "Ruby block to string instead of executing", which is a function that takes a block and returns a string representation of the code in the block, and compare the output of the strings you receive.
I am not sure if this is fast enough to be worthy to compare them, instead of executing them multiple times. This also has the downside you need to be sure the code is exactly the same, even one variable with different name will break it.
As #hakcho has said, it is not trivial to compare blocks. A simple solution might be having the API request for named hooks, so you can compare the names:
#after_event_hooks = {}
def add_after_event_hook(name, &block)
#after_event_hooks[name] = block
end
def after_event_hooks
#after_event_hooks.values
end
Maybe use something like this:
class AfterEvents
attr_reader :hooks
def initialize
#hooks = {}
end
def method_missing(hook_sym, &block)
#hooks[hook_sym] = block
end
end
Here is a sample:
events = AfterEvents.new
events.foo { puts "Event Foo" }
events.bar { puts "Event Bar" }
# test
process = {:first => [:foo], :sec => [:bar], :all => [:foo, :bar]}
process.each { |event_sym, event_codes|
puts "Processing event #{event_sym}"
event_codes.each { |code| events.hooks[code].call }
}
# results:
# Processing event first
# Event Foo
# Processing event sec
# Event Bar
# Processing event all
# Event Foo
# Event Bar
I'm currently trying to test a basic method that receives some input from the user (gets) and outputs it (puts). After a bit of research I found a good way to test the standard output stream which is the below:
def capture_standard_output(&block)
original_stream = $stdout
$stdout = mock = StringIO.new
yield
mock.string.chomp
ensure
$stdout = original_stream
end
The method I'm testing is the one below, output and input refer to ivars which I initialize in the beginning and point to the equivalent $stdout & $stdin:
def ask_for_mark
ouput.puts 'What shall I call you today?'
answer = input.gets.chomp.capitalize
answer
end
Now I've seen some solutions for STDIN but haven't really understood any of them and I definitely don't want to copy & paste. The only one I got to "work" is the one below, but it's not really working since when I run rspec it pauses and waits for input and by simply pressing enter, it passes:
it "takes user's name and returns it" do
output = capture_standard_output { game.ask_for_name }
expect(output).to eq "What shall I call you today?"
game.input.stub(:gets) { 'joe' }
expect(game.ask_for_name).to eq 'Joe'
end
What would be a good way to test STDIN? I've been staring at a screen for most of the day (not good, I know) so a fresh perspective and some help would be greatly appreciated :-)
Update
For anyone facing similar issues, I followed different (simpler) approaches. First, I would separate the IO operations in their own methods.
In the case of input, in the tests it can be pre-populated with the desired data so when the gets message is send, it will return that data which are separated by the newline character \n like so:
input = StringIO.new("one\ntwo\n")
=> #<StringIO:0x007f88152f3510>
input.gets
=> "one\n"
input.gets
=> "two\n"
input.gets
=> nil
This helps with keep the internals of the method under test, private without coupling the tests to the implementation details.
Another approach is to simply use polymorphism and pass in a Fake or a Spy object that conforms to the same api but instead of making a call to stdin or stdout, it returns canned data instead or in the case of the Spy, it registers the call.
class SpyIO
def initialize
was_called? = false
end
...
def ask_for_name
# call to stdout would normally take place
was_called? = true
end
...
end
class FakeIO
def initialize(data = [some, data])
#data = data
end
def get_user_input
# call to stdin would normally happen
#data.shift
end
...
end
There's tradeoffs in each approach but I thought I'd put these here in case anyone was having similar issues or considering options.
You can simply stub STDIN:
it "takes user's name and returns it" do
output = capture_standard_output { game.ask_for_name }
expect(output).to eq "What shall I call you today?"
allow(STDIN).to receive(:gets) { 'joe' }
expect(game.ask_for_name).to eq 'Joe'
end
Actually, you can do the same with STDOUT, without needing to change $stdout:
it "takes user's name and returns it" do
expect(STDOUT).to receive(:puts).with("What shall I call you today?")
allow(STDIN).to receive(:gets) { 'joe' }
expect(game.ask_for_name).to eq 'Joe'
end
Well I can't really be sure, but one problem I had (still have) when working with $stdin / $stdout / StringIO is making sure to run #rewind on them. Are you doing that?
input = StringIO.new
input.puts "joe"
input.rewind
input.gets #=> "joe\n"
I am new to ruby. I tried to do a simple method(with parameter) call.
class MeowEncoder
def method(c)
puts c
end
end
print "please enter the thing you want"
s = gets.chomp()
MeowEncoder.method(s)
It is only passing parameter and prints it out. But the terminal keep giving me errors like
:MeowEncoder.rb:9: undefined method `toBinary' for MeowEncoder:Class (NoMethodError)
what is going on here?
I made some enhancement.
class MeowEncoder
def encode(n)
toBianry(?n)
puts ""
end
def toBinary(n)
if n < 2
print n
else
toBinary(n / 2)
print n % 2
end
end
end
o = MeowEncoder.new
print "please enter the thing you want: "
s = gets.chomp()
s.each_char{|c| o.encode(c)} #this doesn't work
o.toBinary(212) # this works
I made some enhancement here. I try to convert a char to its ASCII value then to its binary form. I can made the single toBinary works. But the Encode method also gave me same error. What happened?
You defined an instance method, but you're trying to call it on a class object. Try this:
MeowEncoder.new.method(s)
Also, method is a bad name for a method. It will cause a name clash.
To expand on Sergio's answer, if you actually wanted the method defined on the class, there are several ways to accomplish that, but the most straightforward is to prepend the method definition with self like so:
def self.method(c)
puts c
end
That will allow you to invoke the method the way you are currently.
The reason this works is, in the context of defining the method, self is set to the MeowEncoder class. It's equivalent to saying:
def MeowEncoder.method(c)
puts c
end
This is actually another valid way to declare class methods, but using self is better practice, as refactoring becomes easier if you ever change the name of your class.
Instead of each_char use each_byte and no need of encode method.
s.each_byte{|c| o.toBinary(c)}
Book (title, author)
Author (pseudonym, first_name, last_name)
Book_catalog => collection of books
methods
add_book(book)
remove_book(book)
borrow_book(borrower, book) => voeg boek toe aan borrower.books_borrowed
return_book(borrower, book) => verwijder boek uit borrower.books_borrowed
book_available?(book)
search(title) => geeft gevonden book-object terug (anders nil)
Book_borrowing
book (read-only), date_of_borrowing (read-only), date_of_return (read-only)
borrow_book(book_to_borrow) : #date_of_borrowing = systeem-datum+uur
return_book(book_to_return) : #date_of_return = systeem-datum+uur
Borrower
member_nbr, first_name, last_name, books_borrowed = collection of Book_borrowing
has_book_by_title(title) => geeft true of false terug
has_book(book) => geeft true of false terug
Person(first_name, last_name)
Very often in Ruby (and Rails specifically) you have to check if something exists and then perform an action on it, for example:
if #objects.any?
puts "We have these objects:"
#objects.each { |o| puts "hello: #{o}"
end
This is as short as it gets and all is good, but what if you have #objects.some_association.something.hit_database.process instead of #objects? I would have to repeat it second time inside the if expression and what if I don't know the implementation details and the method calls are expensive?
The obvious choice is to create a variable and then test it and then process it, but then you have to come up with a variable name (ugh) and it will also hang around in memory until the end of the scope.
Why not something like this:
#objects.some_association.something.hit_database.process.with :any? do |objects|
puts "We have these objects:"
objects.each { ... }
end
How would you do this?
Note that there's no reason to check that an array has at least one element with any? if you're only going to send each, because sending each to an empty array is a no-op.
To answer your question, perhaps you are looking for https://github.com/raganwald/andand?
Indeed, using a variable pollutes the namespace, but still, I think if (var = value).predicate is is a pretty common idiom and usually is perfectly ok:
if (objects = #objects.some_association.hit_database).present?
puts "We have these objects: #{objects}"
end
Option 2: if you like to create your own abstractions in a declarative fashion, that's also possible using a block:
#objects.some_association.hit_database.as(:if => :present?) do |objects|
puts "We have these objects: #{objects}"
end
Writing Object#as(options = {}) is pretty straigthforward.
What about tap?
#objects.some_association.something.hit_database.process.tap do |objects|
if objects.any?
puts "We have these objects:"
objects.each { ... }
end
end
Edit: If you're using Ruby 1.9, the Object#tap method provides the same functionality as the code listed below.
It sounds like you just want to be able to save a reference to an object without polluting the scope, correct? How about we open up the Object class and add a method do, which will just yield itself to the block:
class Object
def do
yield self if block_given?
return self # allow chaining
end
end
We can then call, for example:
[1,2,3].do { |a| puts a.length if a.any? }
=> 3
[].do { |a| puts a.length if a.any? }
=> nil
So if I understand correctly Object#tap uses yield to produce a temporary object to work with during the execution of a process or method. From what I think I know about yield, it does something like, yield takes (thing) and gives (thing).dup to the block attached to the method it's being used in?
But when I do this:
class Klass
attr_accessor :hash
def initialize
#hash={'key' => 'value'}
end
end
instance=Klass.new
instance.instance_variable_get('#hash')[key] # => 'value', as it should
instance.instance_variable_get('#hash').tap {|pipe| pipe['key']=newvalue}
instance.instance_variable_get('#hash')[key] # => new value... wut?
I was under the impression that yield -> new_obj. I don't know how correct this is though, I tried to look it up on ruby-doc, but Enumerator::yielder is empty, yield(proc) isn't there, and the fiber version... I don't have any fibers, in fact, doesn't Ruby actually explicitly require include 'fiber' to use them?
So what ought have been a read method on the instance variable and a write on the temp is instead a read/write on the instance variable... which is cool, because that's what I was trying to do and accidentally found when I was looking up a way to deal with hashes as instance variables (for some larger-than-I'm-used-to tables for named arrays of variables), but now I'm slightly confused, and I can't find a description of the mechanism that's making this happen.
Object#tap couldn't be simpler:
VALUE
rb_obj_tap(VALUE obj)
{
rb_yield(obj);
return obj;
}
(from the documentation). It just yields and then returns the receiver. A quick check in IRB shows that yield yields the object itself rather than a new object.
def foo
x = {}
yield x
x
end
foo { |y| y['key'] = :new_value }
# => {"key" => :new_value }
So the behavior of tap is consistent with yield, as we would hope.
tap does not duplicate the receiver. The block variable is assigned the very receiver itself. Then, tap returns the receiver. So when you do tap{|pipe| pipe['key']=newvalue}, the receiver of tap is modified. To my understanding,
x.tap{|x| foo(x)}
is equivalent to:
foo(x); x
and
y.tap{|y| y.bar}
is equivalent to:
y.bar; y