I have created a class for example
class Result
##min = 0
##max = 0
def initialize(min, max)
##max.min = min
##max.max = max
end
end
result = Result.new(1, 10)
result.max
Same as other lang. like php, C# etc I have created a class and pass a value and since it has initialize method it will should now contains the object values but when I try to print out
puts result.min
puts result.max
it says undefined method min
In Ruby, ## before a variable means it's a class variable. What you need is the single # before the variable to create an instance variable. When you do Result.new(..), you are creating an instance of the class Result.
You don't need to create default values like this:
##min = 0
##max = 0
You can do it in the initialize method
def initialize(min = 0, max = 0)
This will initialize min and max to be zero if no values are passed in.
So now, your initialize method should like something like
def initialize(min=0, max=0)
#min = min
#max = max
end
Now, if you want to be able to call .min or .max methods on the instance of the class, you need to create those methods (called setters and getters)
def min # getter method
#min
end
def min=(val) # setter method
#min = val
end
Now, you can do this:
result.min #=> 1
result.min = 5 #=> 5
Ruby has shortcuts for these setters and getters:
attr_accessor: creates the setter and getter methods.
attr_reader: create the getter method.
attr_writer: create the setter method.
To use those, you just need to do attr_accessor :min. This will create both methods for min, so you can call and set min values directly via the instance object.
Now, you code should look like this
class Result
attr_accessor :min, :max
def initialize(min=0, max=0)
#min = min
#max = max
end
end
result = Result.new(1, 10)
result.max #=> 10
Without knowing the context here it's hard to say exactly what you're looking to do. I suspect what you actually want is an instance variable. In which case you would do:
class Result
attr_accessor :min, :max
def initialize(min, max)
#max = min
#max = max
end
end
Class variables in Ruby and are best avoided unless you really need them. If you actually do you could do this:
class Result
##min = 0
##max = 0
def self.min
##min
end
def self.min=(new_min)
##min = new_min
end
def self.max
##max
end
def self.max=(new_max)
##max = new_max
end
def initialize(min, max)
##min = min
##max = max
end
def min
##min
end
def max
##max
end
end
puts Result.min
puts Result.max
result = Result.new(1, 10)
puts result.min
puts result.max
puts Result.min
puts Result.max
Be warned though, class variables are tricky.
Related
This question already has answers here:
Why do Ruby setters need "self." qualification within the class?
(3 answers)
Closed 5 years ago.
I was trying to get an understanding of Ruby classes and the auto generated getters and setters that come with attr_accessor. How come for the code below, I'm able to get it but not set it? Moreover, setting works for my store instance variable later in code (not shown). From what I read here, it seems with an attr_accessor I should be ble to read and write.
class HashMap
attr_accessor :store, :num_filled_buckets, :total_entries
def initialize(num_buckets=256)
#store = []
#num_filled_buckets = 0
#total_entries = 0
(0...num_buckets).each do |i|
#store.push([])
end
#store
end
def set(key, value)
bucket = get_bucket(key)
i, k, v = get_slot(key)
if i >= 0
bucket[i] = [key, value]
else
p num_filled_buckets # <- this works
num_filled_buckets = num_filled_buckets + 1 if i == -1 # this does not
# spits out NoMethodError: undefined method `+' for nil:NilClass
total_entries += 1
bucket.push([key, value])
end
end
...
What attr_accessor :num_filled_buckets gives you is two methods, a reader and a writer
def num_filled_buckets
#num_filled_buckets
end
def num_filled_buckets=(foo)
#num_filled_buckets = foo
end
A reader method that returns the instance variable #num_filled_buckets.
A write method that takes an argument which will write to #num_filled_buckets
I've a simplified version of your class below.
class HashMap
attr_accessor :num_filled_buckets
def initialize(num_of_buckets=256)
#num_filled_buckets = 0
end
def set
p num_filled_buckets #calls the reader method num_filled_buckets
p #num_filled_buckets #instance variable #num_filled_buckets
#num_filled_buckets = #num_filled_buckets + 1
p #num_filled_buckets
end
end
hm = HashMap.new
hm.set
# will output
# 0
# 0
# 1
I'm trying to convert Celsius temperatures to Fahrenheit and vice versa. As in the following, instance variable #temperature is defined in the methods celsius= and fahrenheit= respectively.
class Temperature
def self.ctof(temp)
(temp * 9 / 5.0) + 32
end
def self.ftoc(temp)
(temp - 32) * (5 / 9.0)
end
def initialize(options)
if options[:f]
self.fahrenheit = options[:f]
else
self.celsius = options[:c]
end
end
def fahrenheit=(temp)
#temperature = self.class.ftoc(temp)
end
def celsius=(temp)
#temperature = temp
end
def in_fahrenheit
self.class.ctof(#temperature)
end
def in_celsius
#temperature
end
end
It is confusing to me because I've never seen instance variables defined outside of the initialize method. I'm hoping someone can help me understand what is going on here.
When you call Temperature.new(c: 0), this will set the celsius= accessor, which sets the instance variable #temperature (which is meant to always be in Celsius) to 0.
When you call Temperature.new(f: 32), this will set the fahrenheit= accessor, which sets the instance variable #temperature to Temperature.ftoc(32), or 0.0.
Calling in_celsius simply returns #temperature, or 0 in the example.
Calling in_fahrenheit returns Temperature.ctof(0), or 32.0.
There is nothing magical about an instance variable being defined outside the constructor. The key point is that it is a variable that is available throughout the instance methods.
It is perfectly fine to set instance variables outside of initialize. This is exactly what setters do. You probably already know attr_accessor – when calling attr_accessor :foo, it creates a getter foo and a setter foo= for the instance variable #foo, i.e. two methods equivalent to:
def foo
#foo
end
def foo=(value)
#foo = value
end
In your code, in_celsius and celsius= and just that: getters and setters for #temperature.
But your code does indeed look a bit convoluted. I think this is because Temperature has to handle both, Fahrenheit and Celsius. You can simplify it by providing separate classes for each temperature scale:
class Celsius
attr_reader :value
def initialize(value)
#value = value
end
def to_celsius
self
end
def to_fahrenheit
Fahrenheit.new((value * 9 / 5.0) + 32)
end
end
class Fahrenheit
attr_reader :value
def initialize(value)
#value = value
end
def to_celsius
Celsius.new((value - 32) * (5 / 9.0))
end
def to_fahrenheit
self
end
end
Now each class has a single instance variable #value which is being set within initialize.
temperature = Celsius.new(0)
#=> #<Celsius:0x007fb83d8b33a8 #value=0>
temperature.to_fahrenheit
#=> #<Fahrenheit:0x007fb83d8b3128 #value=32.0>
I am trying to have sushiable? return an instance variable set in initialise but I can't seem to get it to work. What am I doing wrong?
attr_accessor :weight, :value, :sushiable?
def initialize (weight,value, sushiable?)
#weight = weight
#value = value
#sushiable = sushiable?
end
# def sushiable?
# false
# end
Using ? is only valid for methods names, not for variables. So, the correct way would be:
attr_accessor :weight, :value, :sushiable
def initialize (weight, value, sushiable)
#weight = weight
#value = value
#sushiable = sushiable
end
def sushiable?
sushiable
end
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
Say I have a class that defines a collection of my days and how wacky they are. Is it better to initialize my #scores variable in the initialize function like so:
class WackyDayScorer
attr_reader :scores
def initialize(start = Time.now, max = 90.days)
#start = start
#limit = start - max
#scores = get_scores
end
private
def get_scores
result = []
t = #start.clone
until t < max
result << score_wackiness(t)
t -= 1.day
end
result
end
end
or initialize it in the get_scores method like so:
class WackyDayScorer
attr_reader :scores
def initialize(start = Time.now, max = 90.days)
#start = start
#limit = start - max
get_scores
end
private
def get_scores
#scores = []
t = #start.clone
until t < max
#scores << score_wackiness(t)
t -= 1.day
end
end
end
The first one is just plain wrong. You are saying
#scores = get_scores
But get_scores does not return a useful value, so this is nuts. Instead, get_scores sets #scores directly and internally as a side effect.
The second one is at least coherent.
Personally, though, I would not do either of the things you do. I would say what you say the first time:
#scores = get_scores
But my implementation of get_scores would not touch any ivar. It would return an actual value:
def get_scores
scores = []
# do stuff to scores
return scores # or simply, scores
end
If you want to handle something in initialization, create a private method to handle the calculation and then set the instance variable in #initialize. I wouldn't set it in the method in case you want to reuse that calculation elsewhere in the class without wiping out your instance variable.
class WackyDayScorer
attr_accessor :scores
def initialize(start = DateTime.now, max = 90.days)
#start = start
#limit = start - max
#scores = calculate_scores
end
private
def calculate_scores
(#limit..#start).to_a.map { |date_time| score_wackiness(date_time) }
end
end
In #calculate_scores, we are creating a range between the limit date and the start (counting backwards, as you were doing) and sending each result to the #score_wackiness method.
I'd recommend explicitly defining the getter for scores, memoizing the calculation and skipping all the ceremony:
class WackyDayScorer
def initialize(start = Time.now, max = 90.days)
#start = start
#limit = start - max
end
def scores
#scores ||= begin
result = []
t = #start.clone
until t < max
result << score_wackiness(t)
t -= 1.day
end
result
end
end
end
This is basically what you are doing anyway, but it's more direct.