Ruby Instance Methods and Variables

2019-07-22 21:52发布

问题:

There is something that i don't understand about ruby class instance variable or methods**. So i have this code that keeps on giving me this error and i cant understand

Looks ruby thinks that i am trying to call for Float.in_celsius but I want to make this call within my class instance.

#-----------------------------------
def ftoc(fr)
fr = fr.to_f
if (fr == 32)
    c = 0
elsif (fr == 212)
    c = 100
else 
    c = (fr-32.0)*(5.0/9.0)
end

return c
end

def ctof (cl)
cl = cl.to_f
f = (cl*(9.0/5.0))+32.0
return f
end
#-----------------------------------

class Temperature
attr_accessor :in_celsius, :in_fahrenheit 

#class metods 
def self.from_celsius(cel)
    puts "from celsious\n"
    puts "cel: #{cel}\n"
    @in_fahrenheit = cel
    @in_celsius = ctof(cel)
    puts "==============================\n"
    return @in_celsius
end

def self.in_celsius
    @in_celsius
end


end


puts "==============================\n"
puts Temperature.from_celsius(50).in_celsius
puts Temperature.from_celsius(50).in_fahrenheit

and Error is test.rb:54: in '<main>' : undefined method 'in_celsius' for 122.0:float (noMethod Error) enter code here

回答1:

You have a fundamental misunderstanding of how classes work in Ruby. Right now all of your variables and methods are defined at class level. That means that everything you do in the methods is acting directly on the class itself. Instead, you should create instances of Temperature.

class Temperature
  # special method called when creating a new instance
  def initialize celsius
    @in_celsius = celsius
    @in_fahrenheit = celsius * 9 / 5.0 + 32
  end

  def self.from_celsius celsius
    new celsius # built in method to create an instance, passes argument to initialize
  end

  # we defined initialize using celsius, so here we must convert
  def self.from_fahrenheit fahrenheit
    new((fahrenheit - 32) * 5 / 9.0)
  end

  private_class_method :new # people must use from_celsius or from_fahrenheit

  # make instance variables readable outside the class
  attr_accessor :in_celsius, :in_fahrenheit
end

Temperature.from_celsius(50).in_celsius

This code isn't perfect (from_fahrenheit does a redundant conversion) but it should give you the idea of how to redesign your class.



回答2:

from_celsius is returning a float which doesn't have an in_celsius method. You need it to return an instance of Temperature which would have that method.

Got to say your intent is a tad confusing, unless you have some other uses for the class Temperature, so it's bit hard to say which way you should go.



回答3:

Let's see the code puts Temperature.from_celsius(50).in_celsius in details:

  1. Call to singleton method ::from_celsius of Temperature class. That is ok (with some stranges), and t returns as instance of Float class because of result of #ctof method.

  2. Call to instance method #in_celsius of object, which is returned from the previous method. Since it was the Float, the ruby interpreter searches for its instance method #in_celsius, hasn't find it out, and throws the NoMethodError exception.

How to fix.

Since you treat ::from_celsius as a constructor of Temperature class, I believe, that you shell to pass the floating value into the new method, and return created object. You will have class code as follows:

def initialize( value )
   @in_fahrenheit = cel
   @in_celsius = ctof(cel)
end

def self.from_celsius(cel)
   puts "from celsious\n"
   puts "cel: #{cel}\n"
   temp = Temperature.new( cel )
   puts "==============================\n"
   return temp
end