0

Why is Ruby forcing me to instantiate/store these classes inside of local variables instead of instance variables?

Before I changed my code to make it functional, I had this:

require 'test/unit'
require 'converter'

class TestConverter < Test::Unit::TestCase

  @cv = Convert.new

  def test_celsius
    assert_equal(100.0, @cv.celsius(212))
    assert_equal(0.0, @cv.@celsius(32))
  end

  def test_fahrenheit
    assert_equal(212.0, @cv.fahrenheit(100))
    assert_equal(32.0, @cv.fahrenheit(0))
  end

end

which threw this error:

% ruby -I. converter_test.rb                                                  ✭
Run options: 

# Running tests:

EE

Finished tests in 0.000616s, 3246.7638 tests/s, 0.0000 assertions/s.

  1) Error:
test_celsius(TestConverter):
NoMethodError: undefined method `celsius' for nil:NilClass
    converter_test.rb:9:in `test_celsius'

  2) Error:
test_fahrenheit(TestConverter):
NoMethodError: undefined method `fahrenheit' for nil:NilClass
    converter_test.rb:14:in `test_fahrenheit'

2 tests, 0 assertions, 0 failures, 2 errors, 0 skips

I decided to try instantiating the class (Convert) inside of each method and was successful:

require 'test/unit'
require 'converter'

class TestConverter < Test::Unit::TestCase

  #@cv = Convert.new
  #instantiated the class in each method instead of here

  def test_celsius
    cv = Convert.new
    assert_equal(100.0, cv.celsius(212))
    assert_equal(0, cv.celsius(32))
  end

  def test_fahrenheit
    cv = Convert.new
    assert_equal(212, cv.fahrenheit(100))
assert_equal(32, cv.fahrenheit(0))
end
end
ddouglas@coders:~/Develop/davincicoders$ ruby -I. converter_test.rb
Run options: 

# Running tests:

..

Finished tests in 0.001894s, 1055.9149 tests/s, 2111.8298 assertions/s.

2 tests, 4 assertions, 0 failures, 0 errors, 0 skips

Why wouldn't Ruby recognize the instance variable as an object in the first try?

3
  • Because that's not how you declare instance variables in Ruby. In a class declaration you're in the class, not an instance, so a @variable isn't what you believe it to be. Commented Jun 25, 2012 at 21:44
  • 1
    So basically it didn't work because i DEFINED the instance OUTSIDE of a method. Had I done it inside of an initialize method it would've worked. Right? Umm....inside of a test this doesn't seem to be working...OK, I think I get it though. Thanks guys. Commented Jun 25, 2012 at 22:18
  • Which would be the most efficient code? Using a @@class_var declaration or doing it the way I've done above? Commented Jun 25, 2012 at 22:30

3 Answers 3

3

Declaring @cv outside the tests makes it an instance variable for TestConverter - not an instance of TestConverter!

Probably the easiest way to get around that would be to make it a class variable: @@cv.

If you're still confused, consider this example:

class Foo
  @x = 3
  def initialize
    @y = 4
  end
end

puts Foo.instance_variables
puts Foo.new.instance_variables
Sign up to request clarification or add additional context in comments.

Comments

2

This is often confusing to people (including me) because it works differently than what you would expect from other languages. I'll try to illustrate with a Java example:

class Foo
  @bar = 42
end

Is NOT equivalent to

 public class Foo {
      private int bar = 42;
 }

But actually roughly equivalent to

 public class Foo {
      private static int bar = 42;
 }

Try it out in your IRB:

class Foo
  attr_accessor :bar
  @bar = 42
  class << self
    attr_accessor :bar
  end
end
Foo.bar # => 42
Foo.new.bar # => nil

So why is this? In Ruby, everything is an object! So any class (e.g. Foo) is an instance of the class Class (sounds confusing, I know). Anything inside between class Foo; end is executed in the scope of the instance Foo of the class Class.

That is by no means a complete explanation, so you should really read up on the details.

Comments

0
assert_equal(0.0, @cv.@celsius(32))

Shouldn't this be

assert_equal(0.0, @cv.celsius(32))

2 Comments

Yes, if it was actually an instance variable... But it isn't an instance variable of what the OP believes it is. Ultimately this doesn't answer the real question, which is a misunderstanding of Ruby.
@David "OP" means "Original Poster". It's a sex-agnostic and short way to refer to the person who is asking the question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.