user7452246
user7452246

Reputation:

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.

Upvotes: 0

Views: 116

Answers (3)

ArashM
ArashM

Reputation: 1409

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

Upvotes: 0

Jörg W Mittag
Jörg W Mittag

Reputation: 369594

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.

Upvotes: 4

user115014
user115014

Reputation: 922

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.

Upvotes: 0

Related Questions