return command from a newbie in ruby - ruby

I'm trying out the return function for the first time. The following lines of code show no output. I'm trying to figure out what's wrong with my code. I'd appreciate your input.
def favourite_drink name
if name == "tea"
return "I love tea too!"
end
if name == "lemonade"
return "Stuff's refreshing, isn't it?"
end
if name == "coffee"
return "Dude, don't have too much of that stuff!"
end
"So what exactly is it that you like? (scratches head)"
end
favourite_drink "tea"

There's no output because you don't output the result of your function.
puts favourite_drink("tea")
outputs:
"I love tea too!"
You've probably experimented with Ruby in irb, which is a REPL -- a read-eval-print loop. In irb, if you entered your code, you'd see:
=> "I love tea too!"
because irb automatically shows you the value of whatever you type. When actually running your program, you need to specifically ask to output whatever you want printed.

I'm no Ruby wizz by far, but I think you are missing a piece of code that will actually do the output for you. You have some strings, but they remain just that: string. To actually send them to the screen you need a command like puts or print.
see: http://en.wikibooks.org/wiki/Ruby_Programming/Strings
puts 'Hello world'

To target your method, in order to display out the string "I love tea too!" to the output screen(your terminal) you need to give accurate instructions to your method. i.e, you need to instruct your method 'favourite_drink' to take the argument "tea" and paly with it according to the structure described inside the method 'favourite_drink'
puts favourite_drink "tea"
the above will solve your issue.

Related

How can I stop Ruby if/elsif statement from looping?

I am teaching myself Ruby by making a small game in order to test out how I feel about the language. My code was going rather smoothly until I encountered a problem in which the first decision of the game loops instead of progressing forwards.
This code is what I have been using for a short part in the Exposition of my game.
def getup_or_sleep?
puts"Cole";
puts"----";
puts"Will you get up or go back to sleep?";
decision = gets
if decision == "sleep";
puts"Cole";
puts"----";
puts"You decide to go back sleep. It is far too early.";
elsif decision == "get up";
Exposition.stretch
else;
Exposition.getup_or_sleep?
end
This is the expected result I was hoping to achieve:
Cole
Will you get up or go back to sleep?
If player chooses 'sleep'
1)Cole
You decide to go back to sleep, it is far too early.
*I plan to make a new method to direct the user to, but I first want to fix this issue.
**if player chooses 'get up'
->>> to stretch method which is inside of the same class.
I'm new to coding so I may be confused on a few things. Thanks in advance! =)
Your method calls itself recursively because it all conditions fail, and it always falls back to the else branch.
This happens because gets reads input from the user and returns the input including the invisible newline character that is added when the user hits the enter key. But your conditions do not include such new line characters.
A common Ruby idiom is to call gets.chomp to get user input, where the chomp removes the newline character from the input.
Just change this line decision = gets to
decision = gets.chomp
to fix your issue.
Apart from that, your code isn't following Ruby idioms, for example, Ruby does not require a ; at the end of a line, or you usually add a whitespace between a method name and its argument, like in puts "Cole". Therefore, I suggest formatting your code like this:
def getup_or_sleep?
puts 'Cole'
puts '----'
puts 'Will you get up or go back to sleep?'
decision = gets.chomp
if decision == 'sleep'
puts 'Cole'
puts '----'
puts 'You decide to go back sleep. It is far too early.'
elsif decision == 'get up'
Exposition.stretch
else
Exposition.getup_or_sleep?
end
end
Or with a case block and some duplication extracted into a method:
def greeting
puts 'Cole'
puts '----'
end
def getup_or_sleep?
greeting
puts 'Will you get up or go back to sleep?'
case gets.chomp
when 'sleep'
greeting
puts 'You decide to go back sleep. It is far too early.'
when 'get up'
Exposition.stretch
else
Exposition.getup_or_sleep?
end
end

Case statement don't work

I'm learning Ruby from "Programming Ruby, The Pragmatic Programmers(2nd, 2005)" and I'm stuck in the Case statement chapter.
So i copy-paste some code in my version from book:
def kind
puts "Type year and I'll tell you genre: "
ask = gets.chomp
kind = case ask
when 1850..1889 then "Blues"
when 1890..1909 then "Ragtime"
when 1910..1929 then "New Orleans Jazz"
when 1930..1939 then "Swing"
when 1940..1950 then "Bebop"
else "Jazz"
end
puts "You typed year #{ask}. Genre of music in that period is
#{kind}."
end
kind
Hence, whatever year I'm put, output is "Jazz"...What am I working incorrectly?
gets.chomp returns a string, and you are comparing that with integers.
You can inspect ask after you assigned it:
ask = gets.chomp
p ask
When you run the script and enter a number (e.g. 1940), you should see "1940" printed in the terminal. The quotes around the number show you that the variable holds a string, not a number. (FYI don't use puts here, since it won't show the quotes.)
As mudasobwa wrote in his comment, the way to fix this is to cast the input to a number before you compare it:
ask = gets.chomp.to_i
If you add p ask again, you should now see that only the number is printed to the terminal, without any " surrounding it. This shows you that the variable holds an integer.

What is this Rspec syntax exactly asking for?

TL;DR I am going to a bootcamp next year and one their assessments to get in is learning ruby. I have a background in JS and am very new to ruby. I have this assessment in which case I am not familiar with what the test wants me to do. Below is the test case(Rspec) and right below that is my answer.
describe "some silly block functions" do
describe "reverser" do
it "reverses the string returned by the default block" do
result = reverser do
"hello"
end
expect(result).to eq("olleh")
end
it "reverses each word in the string returned by the default block" do
result = reverser do
"hello dolly"
end
expect(result).to eq("olleh yllod")
end
end
This is my answer code:
def reverser sentence
words = sentence.split(" ")
result = []
words.length.times do |i|
result.push(yield(words[i]))
end
result.join(" ")
end
reverser("hello dolly") {|n| n.reverse} #=> 'olleh yllod'
As I mentioned above I am new to ruby and the idea of yielding is like a callback function for JS. I am having a hard time figuring out what expected code the test case wants me to write. It says that 'it reverses each word in the string returned by the default block' from the statement I just created a block outside of the function where the words are being reversed. I appreciate the help and guidance from whoever can give advice.
This is really more about TDD than about Ruby. The approach would be much the same in any other language as well.
The point of TDD is to write the simplest test that could possibly fail, and then write the simplest code that could possibly change the error message. Step #1 is already provided to you in this example, so this is really about step #2: write the simplest code that could possibly change the message.
Let's run the tests! The message says:
NoMethodError:
undefined method `reverser' …
What's the simplest code that could possibly change a message that says a method doesn't exist? Well, make the method exist, of course!
def reverser; end
Now, the message is:
expected: "olleh"
got: nil
Okay, so it expected us to return the string 'olleh', but we actually returned nil. That's easy:
def reverser; 'olleh' end
Great! The first test passes. But the second still fails. It says:
expected: "olleh yllod"
got: "olleh"
Hmm … apparently, it is not enough to just return something statically. We have to return something different every time. The obvious source would be an argument, but our method doesn't have any parameters. Or does it? Yes! In Ruby, all methods have an implicit block parameter! You can evaluate it using the yield keyword, so let's try that:
def reverser; yield end
Damn! We're back to two errors! That was a step backwards. Or was it? Let's look at the first error message:
expected: "olleh"
got: "hello"
Do you notice something? The expected value is exactly the reverse of the value we are currently returning. So, all we need to do is reverse the return value:
def reverser; yield.reverse end
Hooray! We're back to 1 error:
expected: "olleh yllod"
got: "yllod olleh"
Again, can you spot what is happening? The order of the words is reversed! We need to separate the words and then reverse them, then put them back together:
def reverser; yield.reverse.split.reverse.join end
So close!
expected: "olleh yllod"
got: "ollehyllod"
We just need to put the space back in:
def reverser; yield.reverse.split.reverse.join(' ') end
Yippieh!
2 examples, 0 failures
The important thing is: at no point did we actually have to think. Every step of the way, the tests told us what to do, what to do next, and when we were done. That's what TDD is about: the tests drive the development.
I think the point of the question should be to explain about RSpec and TDD if you're going on a bootcamp. I think giving you the answer to the problem is only part of what you need to know in this case. So...
One of the principles of TDD is to write the least amount of code to get the tests to pass. If I go back to the start.
You have written a reverser method that takes sentence as an argument:
def reverser sentence
'olleh'
end
Run the test and the test should fail with an error: wrong number of arguments. Remove the argument and try that and run RSpec again:
def reverser
'olleh'
end
The first test should pass, but it will fail the second test as we've hard coded the return value of olleh. Clearly it's no good returning a hard coded value — it just helped the test to pass — so I need to figure out how to yield a return value.
def reverser
yield.reverse
end
And this perhaps will get the second test to pass...
This is the guiding principle of TDD, take small steps and get your code to pass. Once it's passing then go back and refactor and improve your code.
As you probably know already TDD is invaluable for writing good quality code - it makes writing code fun, it helps with refactoring.
I found a page with some resources and I would recommend codeschool but they've retired their course which is a pity but there is a PluralSight TDD course that might be useful and also there is a Udemy course on TDD that might be useful too in giving you a head start.
yield means "execute the code inside the block and give me the result". Your reverser method can be simply written like this:
def reverser
yield.split.map(&:reverse).join(' ') if block_given?
end
result = reverser do
'hello world'
end
puts result
# => olleh dlrow
Ruby automatically returns the last thing called as the return value, so the return value of the block given to reverser is hello world which is what will be assigned to yield.
You could check the documentation for block_given? here. Take a look at this answer for deeper explanation of blocks

Can variables be passed after a do/end block?

I am working with a custom testing framework and we are trying to expand some of the assert functionality to include a custom error message if the assert fails. The current assert is called like this:
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end
and we want something with the functionality of:
assert_compare(first_term, :neq, second_term, error_message) do
puts 'foobar'
end
so that if the block fails the error message will describe the failure. I think this is ugly, however, as the framework we are moving away from did this and i have to go through a lot of statements that look like:
assert.compare(variable_foo['ARRAY1'][2], variable_bar['ARRAY2'][2], 'This assert failed because someone did something unintelligent when writing the test. Probably me, since in am the one writing this really really long error statement on the same line so that you have to spend a quarter of your day scrolling to the side just to read it')
This type of method call makes it difficult to read, even when using a variable for the error message. I feel like a better way should be possible.
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end on_fail: 'This is a nice error message'
This, to me, is the best way to do it but i don't know how or if it is even possible to accomplish this in ruby.
The goal here is to make it as aesthetic as possible. Any suggestions?
You could make on_fail a method of whatever assert_compare returns and write
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end.on_fail: 'This is a nice error message'
In short, no. Methods in ruby take a block as the final parameter only. As Chuck mentioned you could attempt to make the on_fail method a method of whatever assert_compare returns and that is a good solution. The solution I've come up with is not what you are looking for, but it works:
def test block, param
block.call
puts param
end
test proc { puts "hello"}, "hi"
will result in
"hello"
"hi"
What I've done here is create a Proc (which is essentially a block) and then passed it as a regular parameter.

Making sense of the need for as_null_object in RSpec

While working my way through The RSpec Book, I came across the as_null_object method. I don't understand the explanation the book provides as to why it's needed:
... the simplest way is to tell the double output to only listen for the messages we tell it to expect and ignore any other messages.
But why does the example code fail? When we call double('output') in each example, are we not creating a new double object for each example and sending it a single message?
What I would like is an deeper explanation (than the book) of why the example code fails, and how as_null_object addresses the issue.
it "sends a welcome message" do
output = double('output')
game = Game.new(output)
output.should_receive(:puts).with('Welcome to Codebreaker!')
game.start
end
it "prompts for the first guess" do
output = double('output')
game = Game.new(output)
output.should_receive(:puts).with('Enter guess:')
game.start
end
The book tries to explain the reason for the error in an earlier section, but again I don't understand the explanation.
We've told the double in the first example to expect puts "Welcome to Codebreaker!" and we've satisfied that requirement, but we've only told it to expect "Welcome to Codebreaker!" It doesn't know anything about "Enter guess:"
Similarly, the double in the second example expects "Enter guess:" but the first message it gets is "Welcome to Codebreaker".
When you create a double with output = double('output') and then pass it to the new game in Game.new(output), that double will receive every message that it is passed in the codebreaker game code. You haven't included it, but the start method has the following code:
module Codebreaker
class Game
...
def start
#output.puts 'Welcome to Codebreaker!'
#output.puts 'Enter guess:'
end
end
end
Here, remember that the double output has been assigned to the instance variable #output in the game's initialize method, so in each spec it is called with two messages, first with 'Welcome to Codebreaker!', then with 'Enter guess:'.
Without as_null_object, the output double will fail when it receives anything other than what it expects, i.e. in the first spec anything other than 'Welcome to Codebreaker!' and in the second spec anything other than 'Enter guess:'. By using as_null_object on the double, you tell it to sit and wait and ignore anything other than what it expects. This avoids the problem above.
Hope that helps.
I agree the explanation is not crystal clear. Here is what I understand it to be, perhaps that in combination with shioyama's answer will make this click.
When you are creating specs, you are saying the output will include the expected phrases, but not that it will include ONLY the expected phrase. So lets say you got around the error by putting all of the expectations in one example, like this:
it "gets expected output" do
output = double('output')
game = Game.new(output)
output.should_receive(:puts).with('Welcome to Codebreaker!')
output.should_receive(:puts).with('Enter guess:')
game.start
end
This will pass. The problem is, if you decide later on to have the game start by saying "Hi Joe!", it will then fail. Then you will have to go back and fix your specification when in reality, it is meeting the specification already. You need a mechanism for the output to react to unexpected input with no behavior. That way, you can have specific examples of output that are expected without them failing when something unexpected appears. This seems like very basic programming and unit test assertion, but in RSpec they implement it in this manner that might seem unclear to you and I. The phrase "null object" carries a lot of baggage, and doesn't seem to describe what it is doing, but it is an implementation of the null object pattern (http://en.wikipedia.org/wiki/Null_Object_pattern).

Resources