I have a class Temperature, with two subclasses, Celsius and Fahrenheit, which in their initialize methods take an argument with a Fixnum as the temperature which then calls super initialize with the temperature as entry in a options hash.
However, if I call Celsius.new(50), in the superclass initialization code I'm getting the error "NoMethodError:
undefined method `each' for 50:Fixnum" meaning the original argument from the subclass constructor is not being passed as an options hash.
I can't for the life of me figure out why this is happening!
Please see my code below:
class Temperature
def initialize(opts={})
opts.each { |k,v| instance_variable_set("##{k}", v) }
end
def self.from_celsius(c)
Temperature.new(:c => c)
end
def self.from_fahrenheit(f)
Temperature.new(:f => f)
end
def self.ftoc(n)
(n - 32) * (5.0 / 9.0)
end
def self.ctof(n)
n * (9.0 / 5.0) + 32
end
def in_fahrenheit
#f.nil? ? Temperature.ctof(#c) : #f
end
def in_celsius
#c.nil? ? Temperature.ftoc(#f) : #c
end
end
class Celsius < Temperature
def initialze(n)
super(:c => n)
end
end
class Fahrenheit < Temperature
def initialize(n)
super(:f => n)
end
end
spotted the problem:
class Celsius < Temperature
def initialze(n)
# should be initialize
super(:c => n)
end
end
Related
Unfortunately, I get the following error. I can't quite understand why it doesn't work?
:14:in `convert': undefined method `factors' for 30:Fixnum (NoMethodError)
from question_stack.rb:18:in `<main>'
I try to create the following class:
# Class Wordgame
class Wordgame
WORDGAME_MAP = {
'3' => 'baa',
'5' => 'bar',
'7' => 'bla'
}.freeze
def self.factors
(1..self).select { |n| (self % n).zero? }
end
def self.convert(number)
number.factors.map(&:to_s).each.map { |char| WORDGAME_MAP[char] }.join
end
end
Wordgame.convert(30)
What am I doing wrong? Where is my mental error?
self refers to the class itself in a class method or to the current object in an instance method. In your case it refers to WordGame, the object's class.
If you really want it to refer to 30 into the factors method you have to define it as an instance method, because called on an object (30), not a class (Integer), opening the Integer class
class Integer
def factors
(1..self).select { |n| (self % n).zero? }
end
end
I think you know the alternative:
def self.factors(x)
(1..x).select { |n| (self % n).zero? }
end
def self.convert(number)
factors(number).map(&:to_s).each.map { |char| WORDGAME_MAP[char] }.join
end
Or better, with OOP.
class WordGame
def initialize(n)
#n = n
end
def convert
factors.map(&:to_s).each.map { |char| WORDGAME_MAP[char] }.join
end
private
def factors
(1..#n).select { |m| (#n % m).zero? }
end
end
Wordgame.new(30).convert
I'm working on a TestFirst exercise (temperature_object) and have come to a standstill when it comes to integrating a subclass. So far I've got:
class Temperature
def initialize(opts = {})
#options = opts
#c = #options[:c]
#f = #options[:f]
end
def self.from_celsius(num)
self.new(:c => num)
end
def self.from_fahrenheit(num)
self.new(:f => num)
end
def in_celsius
if #options.has_key?(:c)
#c
elsif #options.has_key?(:f)
ctof(#f)
end
end
def in_fahrenheit
if #options.has_key?(:f)
#f
elsif #options.has_key?(:c)
ftoc(#c)
end
end
def ftoc(num)
(((num * 9) / 5.000) + 32)
end
def ctof(num)
(((num - 32) * 5) / 9.0000)
end
end
class Celsius < Temperature
def initialize(num)
#c = num
end
end
class Fahrenheit < Temperature
def initialize(num)
#f = num
end
end
All of the tests pass until I get to the following:
require "temperature_object"
describe Temperature do
# Here's another way to solve the problem!
describe "Temperature subclasses" do
describe "Celsius subclass" do
it "is constructed in degrees celsius" do
Celsius.new(50).in_celsius.should == 50
Celsius.new(50).in_fahrenheit.should == 122
end
it "is a Temperature subclass" do
Celsius.new(0).should be_a(Temperature)
end
end
describe "Fahrenheit subclass" do
it "is constructed in degrees fahrenheit" do
Fahrenheit.new(50).in_fahrenheit.should == 50
Fahrenheit.new(50).in_celsius.should == 10
end
it "is a Temperature subclass" do
Fahrenheit.new(0).should be_a(Temperature)
end
end
end
end
So, I'm thinking the problem is that I'm trying to go from Temperature.new, which takes a hash, to Celsius.new, which only takes a value. I'm getting an undefined method "has_key?" for nil:NilClass error message. Do I need to set num as a hash value and assign it a key? If so, how do I do that? If not, any suggestions?
Your problem is that you refer to #options, but you don't assign it when creating an instance of Celsius. You should call the super constructor in your inherited classes:
class Celsius < Temperature
def initialize(num)
super(c: num)
end
end
class Fahrenheit < Temperature
def initialize(num)
super(f: num)
end
end
Now, when you call Celsius.new(50) the initialize(opts) will be called as if you called Temperature.new(c: 50), and all members will be properly assigned.
Need help implementing Ruby utility class methods to get this test to pass. Can someone break this down for me please?
My Code
class Temperature
class << self
def from_fahrenheit temp
Temperature.new({f: temp})
end
def from_celsius temp
Temperature.new({c: temp})
end
end
def initialize(options={})
#f = options[:f]
#c = options[:c]
end
def in_fahrenheit
return #f if #f
(#c * (9.0 / 5.0)) + 32
end
def in_celsius
return #c if #c
(#f - 32) * 5.0 / 9.0
end
end
class Celsius < Temperature
def initialize temp
#temp = temp
end
end
class Fahrenheit < Temperature
def initialize temp
#temp = temp
end
end
Failures:
1) Temperature Temperature subclasses Celsius subclass is constructed in degrees celsius
Failure/Error: (#f - 32) * 5.0 / 9.0
NoMethodError:
undefined method `-' for nil:NilClass
# ./ct.rb:1118:in `in_celsius'
# ./ct.rb:1219:in `block (4 levels) in <top (required)>'
2) Temperature Temperature subclasses Fahrenheit subclass is constructed in degrees fahrenheit
Failure/Error: (#c * (9.0 / 5.0)) + 32
NoMethodError:
undefined method `*' for nil:NilClass
# ./ct.rb:1113:in `in_fahrenheit'
# ./ct.rb:1230:in `block (4 levels) in <top (required)>'
Rspec Test
describe "utility class methods" do
end
# Here's another way to solve the problem!
describe "Temperature subclasses" do
describe "Celsius subclass" do
it "is constructed in degrees celsius" do
Celsius.new(50).in_celsius.should == 50
Celsius.new(50).in_fahrenheit.should == 122
end
it "is a Temperature subclass" do
Celsius.new(0).should be_a(Temperature)
end
end
describe "Fahrenheit subclass" do
it "is constructed in degrees fahrenheit" do
Fahrenheit.new(50).in_fahrenheit.should == 50
Fahrenheit.new(50).in_celsius.should == 10
end
it "is a Temperature subclass" do
Fahrenheit.new(0).should be_a(Temperature)
end
end
end
end
You aren't using the Temperature class how you defined it. You take an options hash with :f and :c keys in it in the Temperature class, but don't set those in your sub-classes.
Try this:
class Celsius < Temperature
def initialize temp
super(c: temp)
end
end
class Fahrenheit < Temperature
def initialize temp
super(f: temp)
end
end
Is this for an exercise or something? It's an ... interesting design.
I wondering of how to check the owner of certain method/class from other class.
For example:
class Value
attr_accessor :money
def initialize
#money = 0.0
end
def get_money
return self.money
end
def transfer_money(target, amount)
self.money -= amount
target.money += amount
end
end
class Nation
attr_accessor :value
def initialize
#value = Value.new
end
end
class Nation_A < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_B.value, 500)
end
end
class Nation_B < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_C.value, 200)
end
end
class Nation_C < Nation
def initialize
super
end
def pay_tribute_to_decendant_country
value.transfer_money(Nation_A.value, 300)
end
end
Yea, makes no sense how the decendant goes in a circle, but I'd like to implement the idea that different subclass has different argument.
The list is pretty long (I've installed at least 40 of these already with much more complex desendant branches and much more methods that call transfer_money from Value class). Then I have some idea to implement to the structure. I'd like to add currency, but to override all transfer_money method call would be a tremendous task for me to apply. Therefore I create a hash table that generate the call for me.
class Nation
def self.get_descendants
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end
module Additional_Value
currency_table = {}
min = 50
max = 100
def self.range (min, max)
rand * (max-min) + min
end
Nation.get_descendants.each do |derived_classes|
currency_table[derived_classes] == self.range min, max
end
end
class Value
attr_accessor :currency
def initialize
#money = 0
#currency = Additional_Value::currency_table
end
def transfer_money(target, amount)
self.money -= amount
amount = amount * #currency[self.class.owner] / #currency[target.class.owner]
target.money += amount
end
end
and I need to figure out how to define owner class. I tried using the caller, but it returns me string / array of string instead of object, method or calle work only for the same method, the 'sender' gem gives me an idea, but it's written in C, and I need to use the default library due to my circumstances.
Greatly appreciated.
Edit:
I'll rewrite the problem in a shorter way:
class Slave
def who_is_the_owner_of_me
return self.class.owner unless self.class.owner.nil?
end
end
class Test
attr_accessor :testing
def initialize
#testing = Slave.new
end
end
class Test2 < Test1
end
a = Test.new
b = Test2.new
c = Slave.new
a.testing.who_is_the_owner_of_me #=> Test
b.testing.who_is_the_owner_of_me #=> Test2
c.who_is_the_owner_of_me #=> main
not quite understanding factory method here...
here is the respec line:
Temperature.from_celsius(50).in_celsius.should == 50
Here is what I have now:
getting errors...not quite sure how to satisfy this. thanks
class Temperature
attr_accessor :f
attr_accessor :c
def initialize(args)
#f = args[:f]
#c = args[:c]
end
def in_fahrenheit
#f or
(#c*9.0/5.0)+32
end
def in_celsius
#c or
(#f-32)*(5.0/9.0)
end
def self.from_celsius(c)
new c
end
end
This should help
class Temperature
def initialize c
#c = c
end
def in_celsius
#c
end
def in_fahrenheit
#c *9.0 /5.0 +32
end
# factory pattern typically instantiates a new object
def self.from_celsius(c)
new c
end
end
puts Temperature.from_celsius(50).in_celsius #=> 50
puts Temperature.from_celsius(100).in_fahrenheit #=> 212
I would recommend against attr_accessor :c unless you want users to have public access to temp.c. Without it, users will be forced to use temp.in_celsius or temp.in_fahrenheit
You need to assign to :c in the initialize method. Then you need self.from_celsius to return a new instance of Temperature. You probably want something like this:
class Temperature
attr_accessor :c
def initialize c
#c = c
end
def in_celsius
#c
end
def in_fahrenheit
9/5 * #c + 32
end
def self.from_celsius(num)
Temperature.new(num)
end
def self.from_fahrenheit(num)
Temperature.new((num-32)*5/9)
end
end
Now rspec shows true
1.9.1p378 :047 > Temperature.from_celsius(50).in_celsius.should == 50
=> true
1.9.1p378 :131 > Temperature.from_fahrenheit(32).in_celsius.should == 0
=> true
The reason you're getting "error: Can't covert symbol to integer –" is because you're in your Temperature.from_celsius(50) you're passing it an integer when you're supposed to pass it a key & symbol for the options hash.
initialized
class Temperature
def initialize(opts = {})
#options = opts
end
class factory method
def self.from_celsius(x)
Temperature.new(:c => x)
end
instance method
def in_celsius
if #options[:c] == nil
return (#options[:f]-32) * (5/9.to_f)
else
return #options[:c]
end
end