how to change instance variables in class in ruby - ruby

I have some legacy code and completely new to Ruby. I want to change the value of a class instance in Ruby.
class CoffeeMachine
attr_reader :water
def initialize
#water = 100
end
end
machine = CoffeeMachine.new
machine.water
I now want to change machine.water to 70. I learned that these instances are protected through something called "Encapsulation". But I'm wondering if there is not any way to change this variable. Following this and this I tried changing it like so:
machine.class_eval {#water = 70}
but it doesn't work. When I print it out like so
puts machine.class_eval '#water' it shows 70 but when I use it in my program it somehow doesn't get stored.

In your Scenario this will be more convenient way to Handle it
class CoffeeMachine
attr_reader :water
def initialize(water=100)
#water = water
end
end
machine = CoffeeMachine.new
machine.water # 100
machine = CoffeeMachine.new(70)
machine.water # 70

As #Surya suggested it's also possible to do:
class CoffeeMachine
attr_accessor :water
def initialize
#water = 100
end
end
and then do this:
machine = CoffeeMachine.new
machine.water #100
machine.water = 70
machine.water #70

Related

save instances in global variable

I need to save all instances of an object in a global variable, so I can access that instances from another object. There is no need to pass them like parameteres.
In my solution I have a mixin with a method that puts the instance in a variable, also I used an open class technique to include that mixin in Object, so other objects use that method (and not only ONE class).
class Object
include Favourite
end
module Favourite
def favourite_it
#if the variable its not initialized:
#favourites.class == Array.class ? #favourites.push(self) :
#favourites = [].push(self)
end
def get_favourites
#favourites
end
end
#this class is only an example
class Dog
def initialize age
#age = age
end
end
class Dog_T
#all instances of this class will be saved in the variable
def initialize age
#age = age
favourite_it
end
end
class Handler
def do_something
#here I need to access the variable with all the instances of favourites to do something to them
end
end
And here is a simple test
handler = Handler.new
d1 = Dog_T.new(10)
d2 = Dog_T.new(12)
all_f = Handler.get_favourites
expect(all_f[0].age).to eq 10
expect(all_f[1].age).to eq 12
d3 = Dog_T.new(15)
all_f = Handler.get_favourites
expect(all_f[3].age).to eq 15
I tried to do this, but only each instance save itself in a different list (it makes sense because I'm not using global variables yet).
How can I do to have only one list, add the instances when are created and have the ability to empty and manipulate that list from Handler?
Ruby supports using a class variable in a Module. You also need a reader method for the Dog_T object to be able to access the instance variable. Since Favourite doesn't know anything about the object, you will want to use respond_to? to guard against calling an non-existent method in the list. For example, if there were a Dog_R class that did not have a method age but did add itself, you would get a runtime error blindly calling the age method on members of the Favourite array.
module Favourite
##favourites = [] # you can use a class variable in module
def self.favourite_it(obj) # singleton method of the class
##favourites.push(obj)
end
def self.get_favourites # singleton method of the class, see below for usage example
##favourites
end
end
class Object
include Favourite
end
class Dog
def initialize age
#age = age
end
end
class Dog_T
attr_reader :age # you need a reader to able to access it
def initialize age
#age = age
Favourite.favourite_it(self)
end
end
d1 = Dog_T.new(10)
d2 = Dog_T.new(12)
all_f = Favourite.get_favourites
all_f.each do |obj|
puts "#{obj.class}: #{obj.age if obj.respond_to?(:age)}"
end
puts '-' * 20
d3 = Dog_T.new(15)
all_f = Favourite.get_favourites
all_f.each do |obj|
puts "#{obj.class}: #{obj.age if obj.respond_to?(:age)}"
end
The output of this program is:
Dog_T: 10
Dog_T: 12
--------------------
Dog_T: 10
Dog_T: 12
Dog_T: 15

How to access instance variable of other object of same class

This is a job interview problem. I'm supposed to create a data structure for a time in seconds and milliseconds, then create two Time objects, and then write a function that can return the difference between the two Times. This is my code:
class Time
def initialize (sec, milli_sec)
#sec = sec
#milli_sec = milli_sec
end
def difference(time_2)
puts #sec.to_i*1000 + #milli_sec.to_i + time_2.#sec
end
end
time_1 = Time.new('5','30')
time_2 = Time.new('6','40')
time_1.difference(time_2)
This is the error:
syntax error, unexpected tIVAR, expecting '('
I am having a problem accessing the #sec, #milli_sec variables of time_2 passed as time_1.difference(time_2). I think that the syntax is time_2.#sec.to_i or time_2.##sec.to_i, but those return errors. time_2.sec returns uninitialized time, even though it looks like it's been initialized. I would like to know the solution to this problem.
#sec and #milli_sec are instance variables of your Time class. This means that unless you do something else only the instance itself has access to them. Other parts of your code can create an instance, but are only able to access the methods you specify. The point of this is so that you can change the underlying implementation without affecting other parts of your code that are using this class.
There are a variety of ways to do this. You could define the following two methods in your Time class:
def sec
#sec
end
def milli_sec
#milli_sec
end
These methods are public (by default) so you can now do this:
t = Time.new(1, 2)
puts t.sec # prints 1
puts t.milli_sec # prints 2
A more Ruby-ish way would be to add this line to the top of your class:
attr_reader :sec, :milli_sec
Doing this accomplishes the same thing as defining the two methods above. You might also want to look at attr_accessor.
P.S. Time is a poor choice of class name for your own code as it's already defined by Ruby itself.
In ruby, every interface is a group of methods. You can't just obj.#var to access instance variables since they are not methods thus not interfaces. If you want to expose instance variables, then create public methods to access them. attr_reader and attr_accessor are simply handy ways to create those methods.
Here is my final solution:
class Moment
def initialize (sec, milli_sec)
#sec = sec
#milli_sec = milli_sec
end
def sec
#sec
end
def milli_sec
#milli_sec
end
def difference(time_2)
return ((#sec.to_i*1000 + #milli_sec.to_i) - (time_2.sec.to_i*1000 + time_2.milli_sec.to_i)).abs
end
end
time_1 = Moment.new('1','300')
time_2 = Moment.new('11','20')
time_1.difference(time_2)
The same objective can be achieved in much lesser lines of code using approach as below:-
class MyTime
attr_accessor :seconds, :milli_seconds
def initialize(entity={})
#seconds = entity['seconds']
#milli_seconds = entity['milli_seconds']
end
def difference(obj)
ms= ((#seconds.to_i*1000+#milli_seconds.to_i)-(obj.seconds.to_i*1000+obj.milli_seconds.to_i))
secs = ms/1000
msecs = ms%1000
return MyTime.new({'seconds'=> secs,
'milli_seconds'=> msecs})
end
end
time_1 = MyTime.new({'seconds'=> '20',
'milli_seconds'=> '100'})
time_2 = MyTime.new({'seconds'=> '10',`enter code here`
'milli_seconds'=> '20'})
diff_Obj = time_1.difference(time_2)
puts "Difference is : #{diff_Obj.seconds} Seconds #{diff_Obj.milli_seconds} milliseconds"

Error working with oop (classes)

I just started with ruby, and just started learning oop today, after making a class, I am trying to print to console yet I keep getting this error. Does anyone know what's wrong?
undefined method `set_brand_name=' for # (NoMethodError)
Here is the code causing this error:
class Laptop
def set_brand_name(brand_name)
#brand = brand_name
end
def get_brand_name
return #brand
end
def set_color(color)
#color = color
end
def get_color
return #color
end
def set_processor(processor)
#processor = processor
end
def get_processor
return #processor
end
def set_storage(hard_drive)
#storage = hard_drive
end
def get_storage
return #storage
end
def set_memory(ram)
#memory = ram
end
def get_memory
return #memory
end
end
my_laptop = Laptop.new
my_laptop.set_brand_name = "HP"
my_laptop.set_processor = 'i7-4700k'
my_laptop.set_memory = '16gb'
my_laptop.set_storage = '750gb'
my_laptop.set_color = 'Silver'
brand = my_laptop.get_brand_name
color = my_laptop.get_color
processor = my_laptop.get_processor
memory = my_laptop.get_memory
storage = my_laptop.get_storage
This should output the message:
"""The Laptop I want is an #{brand}, it has a #{processor},
#{memory} of ram, a #{storage}, and it #{color}!!!"""
What am I doing wrong?
The problem is that you are not calling the method names as you've defined them. You defined set_brand_name without an equal sign so use:
my_laptop.set_brand_name("HP")
I would simply the getters and setters like so:
class Laptop
def brand_name=(brand_name)
#brand_name = brand_name
end
def brand_name
#brand_name
end
end
Or even better:
class Laptop
attr_accessor :brand_name
end
Then you can use it the same way:
my_laptop = Laptop.new
my_laptop.brand_name = "HP"
puts my_laptop.brand_name # => "HP"
In line 45, you are calling the method set_brand_name=, but your Laptop class doesn't have a method with that name. You need to either call the method which you do have (set_brand_name), or rename the set_brand_name method to set_brand_name=.
Note that neither of those two is idiomatic, though. Idiomatically, the method should be named brand_name= (without the set_ prefix, the "setting" part is already implied by the = sign), and you shouldn't define it manually, but programmatically using the Module#attr_writer method.
Your entire code can be condensed to:
Laptop = Struct.new(:brand_name, :color, :processor, :storage, :memory)
my_laptop = Laptop.new('HP', 'Silver', 'i7-4700k', '750gb', '16gb')
brand = my_laptop.brand_name
color = my_laptop.color
processor = my_laptop.processor
memory = my_laptop.memory
storage = my_laptop.storage
puts "The Laptop I want is an #{brand}, it has a #{processor}, #{memory} of ram, a #{storage}, and it's #{color}!!!"
Your setter methods are defined incorrectly.
Here's your definition of the set_brand_name method:
def set_brand_name(brand_name)
#brand = brand_name
end
And here's how you're calling it:
my_laptop.set_brand_name = "HP"
You're calling the method incorrectly. If you'd like to keep your definition, you should be calling it like this:
my_laptop.set_brand_name("HP")
Or, if you'd like to use the equals sign, you should define your method like this:
def set_brand_name=(brand_name)
#brand = brand_name
end
Notice the equals in the method definition? You're required to use it when you want the setter to look like a regular assignment.
However, for most trivial cases you don't need to define getters and setters manually. You can just use attr_accessor on the class and pass it the properties you want to define. Here's what your class would look like with attr_accessor:
class Laptop
attr_accessor: :brand_name, :color, :processor, :storage, :memory
end
my_laptop = Laptop.new
my_laptop.brand_name = "HP"
my_laptop.processor = 'i7-4700k'
my_laptop.memory = '16gb'
my_laptop.storage = '750gb'
my_laptop.color = 'Silver'
brand = my_laptop.brand_name
color = my_laptop.color
processor = my_laptop.processor
memory = my_laptop.memory
storage = my_laptop.storage
puts """The Laptop I want is an #{brand}, it has a #{processor},
#{memory} of ram, a #{storage}, and it #{color}!!!"""
I encourage you to try it.

Why can't I puts an instance variable of a Ruby class if it contains the value of a variable in a module?

When I try to run this code, nothing or nil shows up. I can't seem to understand why, since I thought classes that include modules can access its instance/class variables. I can print out the value just fine if I don't use garbtest and just use the garb= method to assign it a different value. It works fine without assigning it another value since I initialized it to 16 too. Is there something about the instance/class variables in the module Test that makes it equal to nil? Furthermore, when I try to assign garb to #myg + ##vit it says there is no such method for the nil class. I think this further confirms my suspicion that those variables are somehow nil. Thank you.
module Test
RED = "rose"
BLUE = "ivy"
#myg = 9
##vit = 24.6
end
class Xy
include Test;
def initialize(n)
#garb = n
end
attr_accessor :garb;
def garbTest
#garb = #myg;
end
def exo
return 50;
end
end
ryu = Xy.new(16);
ryu.garbTest;
puts "#{ryu.garb}";
Because #myg is not shared variable. It is private property of module Test, thus while you included Test, #myg didn't come into Xy due to the mixin, it wouldn't come also by default. But, "Why nil?" - Because, instance variable, class variables are like that. Before initialising/defining them, if you attempt to use them, it will simply give you nil.
Small program to prove myself and Ruby :-
module Test
#x = 10
##y = 11
end
class Foo
include Test
end
Foo.instance_variable_defined?(:#x) # => false
Test.instance_variable_defined?(:#x) # => true
Foo.class_variable_defined?(:##y) # => true
Test.class_variable_defined?(:##y) # => true
You can define reader method inside Test singleton class, and then you can use it. Look below
module Test
class << self
attr_reader :myg
end
RED = "rose"
BLUE = "ivy"
#myg = 9
##vit = 24.6
end
class Xy
include Test
def initialize(n)
#garb = n
end
attr_accessor :garb
def garbTest
#garb = Test.myg
end
def exo
return 50
end
end
ryu = Xy.new(16)
ryu.garbTest # => 9

Passing parent class initialized variables to an instance of a child class

(working in Ruby)
First off, I apologize in advance. I'm not a programmer by training, I've simply come to it by dint of convenience and a changing world. What I needed to do involved programming, and I got tired of waiting around for others to do my programming for me. As such, I've missed some basic concepts along the way, and when I ask these questions I sometimes make a fool of myself by consequence.
Let's say I want to define a species/job relationship in classes. I want to define a superclass "BlackAnt" and have subclasses "Worker" "Fighter" and "Queen"
To me, intuitively, this looks something like this:
class BlackAnt
#legs = 6
end
class Worker < BlackAnt
#jaws = 'small'
end
but if I then try
ant1 = Worker.new
puts ant1.legs
I get an error. If I amend class BlackAnt to:
class BlackAnt
attr_accessor :legs
#legs = 6
end
ant1.legs returns 'nil'
I've tried the method outlined here: http://railstips.org/blog/archives/2006/11/18/class-and-instance-variables-in-ruby/
and this allows Worker.legs to return '6', but... alas:
ant1 = Worker.new
Worker.legs => '6'
ant1.legs => 'nil'
At a guess, the values of those parent variables are not being initialized each time a new child is spawned.
I feel I'm being silly about this somehow, and the reply is no doubt going to make me curse the day I discovered caffeine-driven all-nighters. What I need to do is arrange things so that I can create objects like so:
ant1 = Worker.new
ant2 = Queen.new
ant3 = Fighter.new
and have them each acquire the appropriate quantity of legs, along with whatever special characteristics were assigned in the child class. The worker/queen/fighter classifications will be bound by namespaces such that the actual calls will be:
ant1 = AntBlack::Worker.new
ant2 = AntRed::Worker.new
ant3 = AntRed::Queen.new
etc.
I'd like to then be able to check the quantity of legs of an individual ant using:
ant1.legs #=> 6
I may be going around my elbow to get to my thumb. If so, feel free to offer alternate suggestions for ways to achieve the same result, I will greatly appreciate the insights.
///updated re:response///
class AntRed
attr_accessor :legs
def initialize
#legs = 6
end
end
class Worker < AntRed
#strength = 1
end
result:
irb(main):009:0> ant1 = Worker.new
#=> #<Worker:0x87616ac #strength=1>
irb(main):010:0> ant1.legs
#=> nil
Define your BlackAnt class as follows:
class BlackAnt
attr_accessor :legs
def initialize
#legs = 6
end
end
And your Worker class as follows:
class Worker < BlackAnt
attr_accessor :strength
def initialize
super
#strength = 1
end
end
The way you currently define the BlackAnt class, you are storing legs as a class level instance variable.
At a guess, the values of those parent variables are not being
initialized each time a new child is spawned.
In Ruby instance variables belong to a single object. Look at point 7) in my answer starting
To address your "If true that they are variables"
in Why are symbols in Ruby not thought of as a type of variable?
I don't understand why you get irb(main):010:0> ant1.legs #=> nil. For me it works (at least as script).
class AntRed
attr_accessor :legs
def initialize
#legs = 6
end
end
class Worker < AntRed
end
ant1 = Worker.new
print 'ant1.legs : '; puts ant1.legs
Execution :
$ ruby -w test.rb
ant1.legs : 6
you mean you want a class's instance varieble which has been initialed!
so you can do it like this:
class BlackAnt
attr_accessor :legs
#legs = 6
def initialize
#legs = self.class.instance_variable_get(:#legs)
end
end
newObj = BlackAnt.new
newObj.legs

Resources