Search code examples
rubyrspecrspec-rails

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.


Solution

  • 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.