What I'll show is a fulish function, but it's only to explain my problem in a simple way. If I solve the problem in this function, I solved the problem in the original code too.
The problem is simple, and probably the solution too, but I'm new in Ruby and have this doubt. I want to print the values in a range:
def test
(0...5).each do |i|
puts i
end
end
When I call the function, the result that I want is
0
1
2
3
4
but the result that I have is
0
1
2
3
4
0...5
Why this 0...5 is printed together? How can I avoid that?
i don't think the 0..5 is being produced as part of the puts call. Rather, when you call this in your REPL (irb, pry, rails console, etc), you're seeing because it's the last returned value in your code.
Let me show you an example.
Say I save a file called test.rb with the following content:
1.upto(5).each { |i| puts i }
If I call ruby test.rb, I see the expected output,
0
1
2
3
4
If I open irb and run require("./test.rb"), I see the same output.
It's only when I paste the code into irb that I see the additional output (=> 0...5). So I would just ignore this.
In addition to #Max's answer
Whenever any expression is executed in IRB sessions, it will also print the value returned by every expression executed.
In case of method definition it returns the method_name just defined.
> def my_method
?> puts 'this is my method'
?> end
=> :my_method
You see, the :my_method is printed
When the method is invoked, it should print the value returned by the method execution i.e. response of the last expression in the method i.e. puts
> my_method
this is my method
=> nil
but it printed nil because puts always returns nil. I mentioned this because normally developers are astonished when they see their methods returning nil unexpectedly.
Related
I have a sample code:
def print_stuff(first_num, second_num)
puts 'hello'
(first_num..second_num).to_a.each do |num|
puts 'The current number is: '
puts "#{num}"
end
end
I and using rspec, I would like to test to see if the output is correct or not. My attempt are as follows:
expect(initialize_program(1, 3)).to output(
"The current number is:
1
The current number is:
2
The current number is:
3").to_stdout
But instead, I get a expected block to output to stdout but not a block error since the initialize_program(1,3) is outputting the texts, but it is done inside a .each block thus the method itself returns the array of range of numbers.
How can I test for the texts inside the block, to see if the outputted texts are correct?
Thanks!
Todd's answer is fine and I'd strongly recommend you read it carefully: refactor your app in a way that UI (CLI in your case) is minimal and easy to test. But when you want full coverage you'd need to test that std output eventually.
The way you're using it now:
expect(initialize_program(1, 3)).to output("whatever").to_stdout
Means that initialize_program(1, 3) is evaluated immediately when the matcher is called, and it's too soon - it has to do it's magic(*) first, and then run your code. Try like this:
expect { initialize_program(1, 3) }.to output("whatever").to_stdout
Now, instead passing results of calling initialize_program(1, 3) into the matcher, you pass a block that "knows how" to call initialize_program(1, 3). So what the matcher does:
saves the block for later
does it magic to capture whatever goes to the STDOUT (see below)
calls the block, calling the initialize_program(1, 3), capturing whatever was supposed to go to STDOUT
compares it with what you've set up in your expectation (the output("whatever") part)
https://relishapp.com/rspec/rspec-expectations/docs/built-in-matchers/output-matcher
The mentioned magic is not that magical anyway:
https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L49
and
https://github.com/rspec/rspec-expectations/blob/44d90f46a2654ffeab3ba65eff243039232802ce/lib/rspec/matchers/built_in/output.rb#L141
It just creates StringIO, and replaces global var $stdout with it.
Refactor to Inspect a String, Not Standard Output
This type of code is why you should write your tests first. You're essentially testing Kernel#puts, which always returns nil, rather than validating that you've constructed the String you expect. Don't do that. Instead, refactor like so:
def print_stuff(num1, num2)
str =
(num1..num2).map { |num|"The current number is: #{num}" }
.join "\n"
puts str
str
end
print_stuff 1, 3
#=> "The current number is: 1\nThe current number is: 2\nThe current number is: 3"
This will not only print what you expect on standard output:
The current number is: 1
The current number is: 2
The current number is: 3
but will also use the implicit return of the last line of your method to return a value that you can use to compare to the expectations in your spec.
You might also refactor the method to return an Array of String objects, or whatever else you might explicitly want to test. The more your real method reflects what you plan to test, the better.
RSpec Examples
RSpec.describe '#print_stuff' do
it 'prints the expected message' do
expected_string = <<~EOF
The current number is: 1
The current number is: 2
The current number is: 3
EOF
expect(print_stuff 1, 3).to eql(expected_string.chomp)
end
# Even without the collection matchers removed in RSpec 3,
# you can still validate the number of items returned.
it 'returns the expected number of lines' do
lines = print_stuff(1, 3).split("\n").count
expect(lines).to eql(3)
end
end
Testing RSpec Examples in IRB
In irb, you can validate your specs like so:
require 'rspec'
include RSpec::Matchers
expected_string = <<~EOF
The current number is: 1
The current number is: 2
The current number is: 3
EOF
# String#chomp is needed to strip the newline from the
# here-document
expect(print_stuff 1, 3).to eql(expected_string.chomp)
# test the returned object in other ways, if you want
lines = print_stuff(1, 3).split("\n").count
expect(lines).to eql(3)
A method can invoke an associated block of code one or more times using the Ruby yield statement. we can also pass values to the block by giving parameters to yield in vertical bars (|). Just like I have done below.
1 def print_name
2 puts "Hello "
3 yield "Vikram"
4 end
5
6 animals = %w( ant bee cat dog elk )
7 animals.each {|animal| puts animal }
8 animals.each
9
10 print_name {|name| puts name}
11 print_name
In my code line number 11 is giving an error that :
`print_name': no block given (yield) (LocalJumpError)
This means we cannot use yield in a method without passing a block of code while calling the method.
My question is that, how in my above code "animals.each" (refer line 8) is working without giving any error, if there is a "yield" statement present inside "each" method of ruby?
If it is not present then
animals.each {|animal| puts animal }
this should not have worked.
Ruby allows you to check whether a block was passed to the current method by using Kernel#block_given?. Array#each, as the documentation says, returns an enumerator if no block is given (which is checked using block_given?).
Unlike each, your print_name method tries to yield regardless of whether a block was given, leading to the error on line 11.
When I run the following code:
class GermanShepard
attr_accessor :name
def bark
puts "Loud Bark"
end
end
max = GermanShepard.new
max.name = "Max"
puts "#{max.name} goes #{max.bark}"
the result is:
Loud Bark
Max goes
When I change puts to return at the GermanShepard. It gives:
Max goes Loud Bark
I don't understand the difference between the two command in the class.
Expression
In most languages, return in a method means giving a value to the caller.
In Ruby, everything is an expression. In an essence, the last line of a method is automatically called with return. As an example, the following methods are equivalent:
def bark_a
'Woof!'
end
def bark_b
return 'Woof!'
end
However, some methods may not be returning anything.
def bark_c
end
In this case, ruby is actually returning a nil object. An example would be the
puts method. The puts method simply displays whatever you've given it.
So in your example,
def bark
puts "Loud Bark"
end
is actually doing 2 things.
It's calling the puts method (displaying Loud Bark to the terminal screen)
then it's giving a nil value back to the method caller.
You can try running puts nil and see what's printed out!
"#{}"
Calling #{} in ruby is called interpolation, which is basically putting the variables together in their closest string representation value. The following statements are roughly equivalent:
puts "One plus one equals #{1 + 1}"
puts "One plus one equals " + (1 + 1).to_s
Your example
With all the information above, we can now go through your example step-by-step.
The line puts "#{max.name} goes #{max.bark}" can be separated into a few steps
Calls the name method
The value returned is converted into the closest string representation (In this case we don't need to do anything since name is already a string)
The value is then saved to a temporary variable
At this point, our temporary variable is Max goes.
Calls the bark method
The line puts "Loud Bark" gets executed since we're calling it in the bark method.
Terminal(console) displays "Loud Bark"
Since the puts method returns a nil value, the bark method is also going to return a nil value because it's the last line of the bark method. (Read "Expression" above)
nil is then converted to the closest string representation (which is "")
It is then saved to a temporary variable
The temporary variable is now Max goes
Now, the temporary variable is passed into the puts method, and the terminal(console) displays "Max goes "
Program finishes
Therefore, your example prints out
Loud Bark
Max goes
However, if we change the puts inside the bark method to return,
Step 6 will not happen.
Instead of returning a nil value in step 7, it will return "Load bark"
Step 8 will not happen since "Loud bark" already a string value
and the temporary value after step 9 will become Max goes Loud bark
Hope this helps!
Ray's answer is probably correct, but since it is too verbose (and I didn't even feel like reading it), I will post a concise answer.
There are two things to have in mind.
All arguments passed to a method are evaluated prior to the method call.
All methods in Ruby have a return value. In addition, some methods have side effect, an example of which is to output on the terminal. In any case, returning a value is done after a method has done all its side effects.
With your code as is, the argument of puts in
puts "#{max.name} goes #{max.bark}"
is first evaluated. In order to do so, max.bark (among other things like max.name) would have to be evaluated in advance. The method bark prints "Loud Bark", then returns nil. So the argument mentioned above becomes "#{"Max"} goes #{nil}", and eventually to "Max goes ". That is printed after "Loud Bark".
When you change the puts in the method to return, the method bark will have no side effect, and returns "Loud Bark"; that is the function of return (and by the way, return is not a method but is a keyword). So the argument mentioned above becomes "#{"Max"} goes #{"Loud Bark"}", and eventually to "Max goes Loud Bark". That would be the only thing printed.
puts(string) in ruby writes the string value into $stdout. For example if you run ruby console in terminal, string will be written into your terminal. At the same time every method in ruby returns something and method puts returns nil. So, when we want to interpolate string like "Hello #{some_method}" it will interpolate a returning value of some_method, in case of puts it will interpolate nil value.
Loud Bark # output of max.bark method
Max goes # output of last puts
puts "Loud Bark" # => returns nil
return "Loud Bark" # => returns "Loud Bark"
My question involves Ruby's find method and why it works in one situation but not in another. NOTE: I'm using this method in a Cucumber step definition written in Ruby.
This code block works the way I want it to:
Then(/^I expect the bolt service children for CrossrefDepositsDaf to include only: IngestUpdateWorkflow$/) do
#boltworkflowservice ||= BoltWorkflowService.new
Watir::Wait.until(60) do
#job_family = #boltworkflowservice.get_workflows_job_family(#job_id).json
#job_family.find { |job| job['type'] == "IngestUpdateWorkflow" }
end
expect(#job_family.find{|job| job['type'] == "IngestUpdateWorkflow"}.nil?).to eq(false), "IngestUpdateWorkflow child not found"
end
But I wanted to rewrite this code block to get rid of the instance variable and reduce the block by one line. I tried the following:
Then(/^I expect the bolt service children for CrossrefDepositsDaf to include only: IngestUpdateWorkflow$/) do
#boltworkflowservice ||= BoltWorkflowService.new
Watir::Wait.until(60) do
puts #boltworkflowservice.get_workflows_job_family(#job_id).json
#boltworkflowservice.get_workflows_job_family(#job_id).json.find {|job| job['type'] == "IngestUpdateWorkflow"}
end
expect(#job_family.find{|job| job['type'] == "IngestUpdateWorkflow"}.nil?).to eq(false), "IngestUpdateWorkflow child not found"
end
I just added the puts to see what the code would return. According to the puts, it returns an array that contains three hash objects but I still get an error. Here is what I get when I try to run the second block:
[{"id"=>914295, "type"=>"CrossrefDepositsDaf", "subType"=>"", "parentId"=>0, "state"=>"SUCCESS", "subState"=>"SUCCESS", "dataPointer"=>"s3://sequoia-install/app-resources/test.cirrostratus.org/workflows/CrossrefDepositsDaf/2015/7/29/148c7ebc-3da4-4650-a22e-2e8962d448fc", "created"=>"2015-07-29 01:58:14", "metadata"=>[]}, {"id"=>914296, "type"=>"CrossRefDeposits", "subType"=>"BoltCatDaf", "parentId"=>914295, "state"=>"SUCCESS", "subState"=>"SUCCESS", "dataPointer"=>"s3://sequoia-install/app-resources/test.cirrostratus.org/workflows/CrossRefDeposits/2015/7/29/3a703736-d4a2-456e-baa9-7bf2c0f4a0e2", "created"=>"2015-07-29 01:58:17", "metadata"=>[]}, {"id"=>914297, "type"=>"IngestUpdateWorkflow", "subType"=>"CrossRefDeposits", "parentId"=>914296, "state"=>"SUCCESS", "subState"=>"CREATED", "dataPointer"=>"s3://sequoia-install/app-resources/test.cirrostratus.org/workflows/IngestUpdateWorkflow/2015/7/29/9282591b-f93c-49d6-b656-4afbd14156ef", "created"=>"2015-07-29 01:58:23", "metadata"=>[]}]
NoMethodError: undefined method `find' for nil:NilClass
./features/step_definitions/catdaf_integration_crossref_deposits_steps.rb:42:in `/^I expect the bolt service children for CrossrefDepositsDaf to include only: IngestUpdateWorkflow$/'
C:\Users\Dugan23\workspace\cloudy_clouds\cucumber\features\content_management\daf\integration\crossref_deposits.feature:11:in `And I expect the bolt service children for CrossrefDepositsDaf to include only: IngestUpdateWorkflow'
1 scenario (1 failed)
6 steps (1 failed, 5 passed)
9m30.221s
Process finished with exit code 1
Can somebody please explain to me why the second block of code doesn't work? The puts shows that I should be feeding the find method an array so why does it say that I"m trying to run the find method on a nil:NilClass. Thank you in advance for any and all of your help.
NOTE: When I added a puts statement to the first block of code to print out #job_family it returned the same exact array as the second block of code. Does this mean that in the second block of code Ruby is trying to run the find method before evaluating the code before it? I tried to remedy this by adding parentheses as follows, but to no avail:
(#boltworkflowservice.get_workflows_job_family(#job_id).json).find {|job| job['type'] == "IngestUpdateWorkflow"}
In the second version, you have avoided using #job_family inside the block, but still you are referring to it on the expect, which obviously would be nil unless you have defined it elsewhere
When I am in irb or in rails and I create some iteration with each, I am getting the whole struct printed again in my terminal or inside the browser. Example:
a = [1,2,3,4]
a.each do |number|
puts n
end
The result in irb terminal or inside the browser:
1
2
3
4
=> [1,2,3,4]
Why does this => [1,2,3,4] appear inside the browser? I can't create a single list in my page because the whole structure appears.
Every expression in Ruby returns a value; in irb, the value returned by the expression you've just executed is displayed after =>.
The return value of Enumerable::each is the object that called each - in this case, the array [1,2,3,4]
You see them printed twice: once as side effect, second time as the return value. irb always puts the last return value. The return value for each is its receiver. You cannot avoid that when using irb, but they will not show up when you run the script as standalone software. The First 1, ..., 4 are the outputs of your puts. They are called side effects.
Because ruby returns in everything even blocks. So your each statement is returning. You're seeing it output because irb shows you the return of everything. In a script it would only output once.
Run it outside irb, and you shall be enlightened.
ruby -e `[1,2,3,4].each {|ele| puts ele}`