0

I had this code passing ALL the specs the other day, so I moved it into another folder (for completed assignments). When I tried to run the specs again on it from there, I couldn't get the file to load despite typing the path exactly (several times) as I saw it. Hmmmm. Is there some special way to run rspecs from a subfolder?? Anyways, So I moved it back to the main directory it was in, and tried from there, and suddenly it would't pass!! I must have accidentally deleted something. But for the life of me after almost two days...I just DON'T SEE IT. I've gone through it a million times and it seems like it should be working, and when I take the methods out of the class they return as expected. I need fresh eyes. Why is this code suddenly not passing?

class :: Timer
  attr_accessor :seconds, :padded, :time_string

  def initialize(seconds = 0)
    @seconds = seconds
  end 

  def padded(num)
    num = num.to_s
    num.length == 1 ? "0#{num}" : "#{num}"
  end

  def time_string=(seconds)
    @seconds = seconds
    mins = 0
    hours = 0

    if seconds == 0 
      return "00:00:00"
    elsif seconds < 60
      return "00:00:#{padded(seconds)}"
    end

    while seconds > 59
      mins += 1
      seconds -= 60
      if mins > 59
         hours += 1
         mins -= 60
      end  
    end#while
  return "#{padded(hours)}:#{padded(mins)}:#{padded(seconds)}"
  end
end#class

Here are the specs:

require_relative '09_timer'

describe "Timer" do
  before(:each) do
    @timer = Timer.new
  end


  it "should initialize to 0 seconds" do
    @timer.seconds.should == 0
  end

  describe 'time_string' do
    it "should display 0 seconds as 00:00:00" do
      @timer.seconds = 0
      @timer.time_string.should == "00:00:00"
    end

    it "should display 12 seconds as 00:00:12" do
      @timer.seconds = 12
      @timer.time_string.should == "00:00:12"
    end

    it "should display 66 seconds as 00:01:06" do
      @timer.seconds = 66
      @timer.time_string.should == "00:01:06"
    end

    it "should display 4000 seconds as 01:06:40" do
      @timer.seconds = 4000
      @timer.time_string.should == "01:06:40"
    end
  end



  # One way to implement the Timer is with a helper method.
  # Uncomment these specs if you want to test-drive that
  # method, then call that method from inside of time_string.
  #
  describe 'padded' do
     it 'pads zero' do
       @timer.padded(0).should == '00'
     end
     it 'pads one' do
       @timer.padded(1).should == '01'
     end
     it "doesn't pad a two-digit number" do
       @timer.padded(12).should == '12'
     end
   end

end
HolyMoly
  • 2,020
  • 3
  • 22
  • 35
  • In the first line, you have `class :: Timer`, I've never seen a lone double colon by itself before a class name like that. Is this a mistake or something cool that I don't know about? – Rob Wise Apr 12 '15 at 05:00
  • Honestly it's just the way I learned to do it in my coursework. I'm not even totally sure it is best practice. Here is a link that explains it: [http://stackoverflow.com/questions/3009477/what-is-rubys-double-colon-all-about] maybe somebody will chime in and give a better answer. – HolyMoly Apr 12 '15 at 05:04
  • 1
    `attr_accessor :time_string` creates a getter method `time_string` and a setter method `time_string=`. Later you overwrite the setter. I don't know if that was your intent, but it's not good practice. Better would be `attr_reader :time_string`. Note [Module#attr_accessors](http://ruby-doc.org/core-2.2.0/Module.html#method-i-attr_accessor) is a method. Also, notice that you never invoked `time_string=`. After you get your code working I suggest you post it on [Code Review](http://codereview.stackexchange.com/). – Cary Swoveland Apr 12 '15 at 05:51
  • @CarySwoveland I have been having some confusion about setting up classes and what should be initialized, what should have getter/setter methods etc...and when it is just overkill or detremential. Rspecs and creating classes are very new to me, and any resources that explain the aforementioned(in regard to setting up classes) in a clear concise and simple way is greatly appreciated;) as it is I am getting bits and pieces from different online sources but have yet to find one that really brings it home for me. I will post my new code on code review and see what they say. Yikes. lol. – HolyMoly Apr 12 '15 at 06:37
  • 1
    If you've not been to Code Review before, I think you will be impressed. Read through some of the other q&a's before posting. – Cary Swoveland Apr 12 '15 at 06:55
  • Will do :) I'm eager to get past the bottom of this Ruby learning curve lol...so I'll take anything I can get my hands on – HolyMoly Apr 12 '15 at 07:01

1 Answers1

1
class :: Timer
  attr_accessor :seconds

  def initialize(seconds = 0)
    @seconds = seconds
  end

  def padded(num)
    num = num.to_s
    num.length == 1 ? "0#{num}" : "#{num}"
  end

  def time_string
    seconds = @seconds
    mins = 0
    hours = 0

    if seconds == 0
      return "00:00:00"
    elsif seconds < 60
      return "00:00:#{padded(seconds)}"
    end

    while seconds > 59
      mins += 1
      seconds -= 60
      if mins > 59
         hours += 1
         mins -= 60
      end
    end#while
  return "#{padded(hours)}:#{padded(mins)}:#{padded(seconds)}"
  end
end#class

Specs

require_relative '09_timer'

describe "Timer" do
  before(:each) do
    @timer = Timer.new
  end


  it "should initialize to 0 seconds" do
    expect(@timer.seconds).to eq 0
  end

  describe 'time_string' do
    it "should display 0 seconds as 00:00:00" do
      @timer.seconds = 0
      expect(@timer.time_string).to eq "00:00:00"
    end

    it "should display 12 seconds as 00:00:12" do
      @timer.seconds = 12
      expect(@timer.time_string).to eq "00:00:12"
    end

    it "should display 66 seconds as 00:01:06" do
      @timer.seconds = 66
      expect(@timer.time_string).to eq "00:01:06"
    end

    it "should display 4000 seconds as 01:06:40" do
      @timer.seconds = 4000
      expect(@timer.time_string).to eq "01:06:40"
    end
  end



  # One way to implement the Timer is with a helper method.
  # Uncomment these specs if you want to test-drive that
  # method, then call that method from inside of time_string.
  #
  describe 'padded' do
     it 'pads zero' do
       expect(@timer.padded(0)).to eq '00'
     end
     it 'pads one' do
       expect(@timer.padded(1)).to eq '01'
     end
     it "doesn't pad a two-digit number" do
       expect(@timer.padded(12)).to eq '12'
     end
   end

end

I don't really know how your code got messed up, but the time_string method isn't taking any arguments in your specs, and I don't see why it would just from a logical perspective. Therefore I took out the =(seconds) part from its method signature.

You were also setting the instance variable @seconds to the passed argument, but really we want to use a local seconds variable that starts out as the instance variable, so that just gets flipped around to seconds = @seconds.

Lastly, I changed your specs to use the new expect syntax instead of should which is now deprecated.

Rob Wise
  • 4,930
  • 3
  • 26
  • 31
  • ...and `time_string=` was never invoked. – Cary Swoveland Apr 12 '15 at 05:45
  • Thank you @Rob W. I am going through your solution now...the rspecs are using depreciated syntax and I have changed some of the files, but I started getting myself in trouble doing that so I stopped. I just started learning rspecs in the last couple days so am still pretty fuzzy on it. – HolyMoly Apr 12 '15 at 05:52
  • @Cary Swoveland - I'm not sure what you mean by it was never invoked? I'm having a hard time understanding exactly how classes should be set up (what needs be initialized, and/or have getter/setter methods and why). I must have invoked it in my original code without even realizing it because it ran, but I'm not clear on what you mean by 'invoked' to go back and duplicate it, maybe that's what got messed up. – HolyMoly Apr 12 '15 at 05:55
  • 1
    No problem, besides the syntax change the specs are the same so you can ignore that if necessary. There are some weird behaviors that can crop up if you use `foobar.should` instead of `expect(foobar).to`. Also, I agree with @CarySwoveland about the use of `attr_accessor` for `time_string` (and `padded`, for that matter). I would recommend eliminating it entirely. I will update my answer accordingly. If you're confused about the equals sign, check out [this post](http://stackoverflow.com/questions/5398919/what-does-the-equal-symbol-do-when-put-after-the-method-name-in-a-method-d). – Rob Wise Apr 12 '15 at 05:58
  • 1
    Consider `it "should display 0 seconds as 00:00:00" do; @timer.seconds = 0; expect(@timer.time_string).to eq "00:00:00"; end`. `@timer.seconds = 0` is equivalent to `@timer.send(:seconds=, 0)`. The method `seconds=` has been invoked on (or sent to) `@timer`. Next we have `expect@timer.send(time_string)...`. The method `time_string` is the getter created by `attr_accessor`. However, the instance variable `@time_string` has not been initialized, so `@timer.send(time_string) returns `nil`. – Cary Swoveland Apr 12 '15 at 06:13
  • That worked @Rob W. thank you! It was really driving me absolutely crazy!! lol. *sighs in relief* but I will definitely be going over yours and Cary's suggestions so that I know better. When you only half understand what your doing (but it works) - when something like this happens its hard to recreate what you did right the first time!! Thanks again! – HolyMoly Apr 12 '15 at 06:16
  • Your worship, if you half-understand something, it's probably better if it doesn't work. – Cary Swoveland Apr 12 '15 at 06:18
  • 1
    As @CarySwoveland is pointing out, the problem was that your method was a setter method, but your specs didn't use an equals sign, so you were actually calling a totally different method—the getter method provided by the `attr_accessor`. Therefore, your method never got invoked. I would suggest [reading up on attr_accessor](http://stackoverflow.com/questions/4370960/what-is-attr-accessor-in-ruby) in addition to the post I linked about using equals signs in method signatures. – Rob Wise Apr 12 '15 at 06:22
  • Thanks Rob as I was just reading through Cary's last message with a little confusion and was about to ask him for more clarification - but you pretty much just clarified it for me:) Cary is probably happy about that because I tend to badger him into feeding me everything in morsel sizes..lol. Thank you both so much! – HolyMoly Apr 12 '15 at 06:26