Q_ro
Q_ro

Reputation: 433

rspect problems

Here is some code that i know works:

require 'csv'

class Motorcycle
  attr_reader :name, :weight
  @@count = 0

  def self.find (name)
    found = nil
    ObjectSpace.each_object(Motorcycle) { |o|
      found = o if o.name == name
    }
    return found
  end

  def self.create
    File.new('motorcycles.csv').readlines[1..-1].map{ |line|
        Motorcycle.new( *line.split( ',' )  )
    }
  end

  def initialize (name, weight)
    @name = name
    @weight = weight
    self.class.count += 1
  end

  def self.count
    return @@count
  end

  def self.count=( count ) 
    @@count = count 
  end

  def available_colors

    colors=[]
    colorsFile = File.read('colors.csv').split("\n").map { |line| line.split(',') }
    for i in (0..colorsFile.flatten.length) do
        if (colorsFile.flatten[i].to_s == self.name.to_s)
            colors.push(colorsFile.flatten[i+1])
        end
    end

    return colors
  end

  def contains (name,color)
    if(self.name.to_s == name)
    else
        return color
    end
  end

  def has_abs?
    File.open( 'abs.txt' ) do |io|
    io.each {|line| line.chomp! ; return true if line.include? self.name.to_s}
    end

    return false
  end

end

Motorcycle.create

the code must pass this tests on rspec:

describe Motorcycle do
  describe "loading the motorcycle list" do
    it "should load 2 motorcycles from the CSV" do
      Motorcycle.count.should == 2
    end
  end

  describe "finding a motorcycle by name" do
    it "should return an instance of the Motorcycle class" do
      Motorcycle.find("1200 RT").should be_a Motorcycle
    end    
  end

  describe "#weight" do
    it "should have a weight of 800 pounds for the 1200 RT" do
      Motorcycle.find("1200 RT").weight.should == '800 pounds'
    end

    it "should have a weight of 500 pounds for the 600 GS" do
      Motorcycle.find("600 GS").weight.should == '500 pounds'
    end
  end

  describe "#available colors" do
    it "should find 'red' and 'black' as available colors for the BMW 1200 RT" do
      Motorcycle.find("1200 RT").available_colors.should == [ 'red', 'black' ]
    end

    it "should find 'green' and 'blue' as available colors for the BMW 600 GS" do
      Motorcycle.find("600 GS").available_colors.should == [ 'green', 'blue' ]
        end    
  end

  describe "#has_abs?" do
    it "should be true for a motorcycle that appears in abs_motorcycles.txt" do
      Motorcycle.find("1200 RT").has_abs?.should be_true
    end

    it "should be false for a motorcycle that does not appear in abs_motorcycles.txt" do
      Motorcycle.find("600 GS").has_abs?.should be_false
    end
  end
end

problem is, after the first test (where it counts the amount of motrocicle instances) every instance is a nil, thats t say, every test is failed except for the fist one. here is the output log:

Failures:

  1) Motorcycle finding a motorcycle by name should return an instance of the Motorcycle class
     Failure/Error: Unable to find matching line from backtrace
       expected nil to be a kind of Motorcycle
     # ./motoapp.rb:76

  2) Motorcycle#weight should have a weight of 800 pounds for the 1200 RT
     Failure/Error: Unable to find matching line from backtrace
     NoMethodError:
       undefined method `weight' for nil:NilClass
     # ./motoapp.rb:82

  3) Motorcycle#weight should have a weight of 500 pounds for the 600 GS
     Failure/Error: Unable to find matching line from backtrace
     NoMethodError:
       undefined method `weight' for nil:NilClass
     # ./motoapp.rb:86

  4) Motorcycle#available colors should find 'red' and 'black' as available colors for the BMW 1200 RT
     Failure/Error: Unable to find matching line from backtrace
     NoMethodError:
       undefined method `available_colors' for nil:NilClass
     # ./motoapp.rb:92

  5) Motorcycle#available colors should find 'green' and 'blue' as available colors for the BMW 600 GS
     Failure/Error: Unable to find matching line from backtrace
     NoMethodError:
       undefined method `available_colors' for nil:NilClass
     # ./motoapp.rb:96

  6) Motorcycle#has_abs? should be true for a motorcycle that appears in abs_motorcycles.txt
     Failure/Error: Unable to find matching line from backtrace
     NoMethodError:
       undefined method `has_abs?' for nil:NilClass
     # ./motoapp.rb:102

  7) Motorcycle#has_abs? should be false for a motorcycle that does not appear in abs_motorcycles.txt
     Failure/Error: Unable to find matching line from backtrace
     NoMethodError:
       undefined method `has_abs?' for nil:NilClass
     # ./motoapp.rb:106

Finished in 0.01223 seconds
8     examples, 7 failures

i have been inclined to think this is some kind of bug or something due to my result doing the manual test like this:

puts Motorcycle.count
puts Motorcycle.find("1200 RT")
puts Motorcycle.find("1200 RT").weight
puts Motorcycle.find("600 GS").weight
puts Motorcycle.find("1200 RT").available_colors
puts Motorcycle.find("600 GS").available_colors
puts Motorcycle.find("1200 RT").has_abs?
puts Motorcycle.find("600 GS").has_abs?

which give me this output:

2
#<Motorcycle:0x7fd8bffcfd88>
800 pounds
500 pounds
red
black
green
blue
true
false

so i'm really pretty much on a dead end, ¿does anyone have a clue as to what could be happening?.

Upvotes: 1

Views: 232

Answers (1)

riffraff
riffraff

Reputation: 2447

it seems to me you are not storing the Motorcycle objects anywhere when doing the create call, so if the GC gets called the instances will just go away. Probably loading loads of more ruby code (rspec) causes the invocation of the GC to happen sooner.

Why don't you just store the instances in a class-level Set? (e.g. replacing the count call with instances << self) or Hash(storing the instances with their name as key)

Also it seems to me you are relying on the rspec order of execution when you should have a before block in which you read from the CSV.

Example code:

Class Motorcycle
 # use a constant, this will always point to the same object even though
 # the content of the object changes. 
 # Using @@instances would also be ok
 Instances = Set.new # or you can use Instances = Hash.new
 def initialize (name, weight)
   @name = name
   @weight = weight
   # 
   Instances << self # or store the name/instance mapping Instances[name] = self
 end

Upvotes: 2

Related Questions