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

(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

Related

Why can't self be replaced in Ruby?

EDIT: For those criticizing my intentions with replacing self, you are free to click the back button, continue developing with your own opinions, and leave me to develop with mine :)
I was wondering if there is a way to completely remove the object that self references and replace it with a new instance.
Example:
def refresh_from_server!
self = newly_fetched_object_from_server
end
I don't want to return the new object.
It seems like I would have to build my own copying interface and call self.copy_from(other_object) but maybe someone has a cool ruby bit to share that works better!
--EDIT
Since some people seem unclear on the question, I want instance.my_method! to completely replace instance with a new instance of that class
For example lets imagine we have a class
class Counter
attr_accessor :count
def initialize
count = 0
end
def reset!
# This is what I want to achieve.
# Obviously in this case it would be trivial to write `self.count = 0`
# Or return a new value
# But I have a much more complex object in real life
# which copying would not be trivial
# All I'm looking for is a bit of stylistic sugar to make my code look cooler
# If it doesn't exist, I would love to know why
self = Counter.new
end
def up
count += 1
end
end
No, you can't replace self. You can only change some/all of its state, but the object reference will remain the same.
Why would you want to do this, anyway? If you just want to piggyback on your initialization logic (as it seems to me to be the case), some refactoring will help: just call a shared method from both places.
class Counter
attr_accessor :count
def initialize
init_state
end
def reset!
init_state
end
def up
self.count += 1
end
private
def init_state
self.count = 0
end
end
As already noted by others, self can't be replaced from enclosed instance. If replacement of instance with a new instance is required, it need to be done from outside, like in a class factory which registers its class instances.
Bellow is a simplest example using a delegator, demonstrating what I mean. SimpleDelegator represents a simple wrapper around Counter instance:
require 'delegate'
class Counter
attr_accessor :count
def initialize
#count = 0
end
end
class CounterDecorator < SimpleDelegator
def reset!
__setobj__(__getobj__.class.new)
end
end
c = CounterDecorator.new(Counter.new)
p c.__getobj__.object_id
c.count = 123
p c.count
c.reset!
p c.__getobj__.object_id
p c.count
# produces following output
20131160
123
20130900
0
Though the question is old, it is still visited. I will attempt to elaborate more on the "why" in "Why can't self be replaced in Ruby?".
usage of self in which context
https://web.archive.org/web/20191217060940/https://www.honeybadger.io/blog/ruby-self-cheat-sheet/
There are various contexts in which self can be used. You question uses it in the context of an instance method, so I will focus on that.
E.g. this context:
class SomeClass
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
a = SomeClass.new
a.some_method
# prints : SomeClass - 47013616336320 - #<SomeClass:0x000055846bcd7b80>
Note that there are other usages of self: e.g. where it reference the Class object in scope of a class definition. E.g.
class SomeClass
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
# prints : Class - 47102719314940 - SomeClass
the intended effect of replacing self
Below code a demonstration of what you expected / wished (as I understand it):
class Counter
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
def refresh!
self = Counter.new # not possible
# results in error : "Can't change the value of self"
end
end
a = Counter.new
a.some_method
# prints : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish a to point to a different object
But what about other references? E.g. assuming you wanted:
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter - 47013616336320 - #<Counter:0x000055846bcd7b80>
a.refresh!
# now you wish both a and b to point to the same (new) object
If stated as such it gives a hint on the why not.
why we can't replace self
The short answer is that it is simply not something that the language / interpreter offers. As to the reasoning: in a way #matthewd answers that in this answer:
All ruby variable references are essentially pointers (but not
pointers-to-pointers), in C parlance.
You can mutate an object (assuming it's not immutable), and all
variables that reference it will thus be pointing at the same (now
mutated) object. But the only way to change which object a variable is
referring to is with direct assignment to that variable -- and each
variable is a separate reference; you can't alias a single reference
with two names.
In short: there may be other references to that object in variables that are not in the scope of the instance method. These cannot be manipulated by that instance method.
a way to achieve the intended effect
If you want this effect and only want to touch the code of Counter you might move all methods and state to an inner class Counter::Inner and make Counter behave like a decoupled reference. The only 'state' of Counter would be the reference to the Counter::Inner object and Counter can delegate all calls it receives to that reference in a method_missing method. In case of your refresh! you can replace the reference in Counter same as you now intent to replace self. All outside code will now use indirectly the new Counter:Inner instance.
class Counter
class Inner
def some_method
puts "#{self.class} - #{self.object_id} - #{self.inspect}"
end
end
def initialize(*args)
#reference = Inner.new(*args)
end
def method_missing(method_id, *args)
#reference.send(method_id, *args)
end
def refresh!
#reference = Inner.new
end
end
a = Counter.new
b = a
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238242100 - #<Counter::Inner:0x0000557a00203e68>
a.refresh!
a.some_method
b.some_method
# both print same : Counter::Inner - 46991238240000 - #<Counter::Inner:0x0000557a00202e00>
Just one more answer for the archives :-) I hope this gives useful insights to future visitors.

Why is my second class not allowing me to call my methods from first class?

I am creating a code for a small game of 4 horses running a set distance across my terminal. I have it to where it is outputting my horses that I have added and my users horse, but when I go to my next class to build the race it self, I keep getting method undefined errors. I searched for something similar but couldn't find anything. learningruby.com has some roundabout answers to it, but not showing me what im missing.
class Horses
##list_of_horses = []
attr_accessor :name
attr_accessor :position
def initialize
self.name = nil
self.position = 0
end
def self.add_horse(*horse_variables)
horse = Horses.new
horse.name = horse_variables[0]
##list_of_horses.push horse
end
def self.add_user(*user_variables)
add_user = Horses.new
add_user.name = user_variables[0]
##list_of_horses.push add_user
end
def self.display_data
puts "*" * 60
##list_of_horses.each do |racer|
print "-" * racer.position
puts racer.name
end
end
def move_forward
self.position += rand(1..5)
end
def self.display_horses
##list_of_horses
end
end
horse1 = Horses.add_horse ("Jackrabbit")
horse2 = Horses.add_horse ("Pokey")
horse3 = Horses.add_horse ("Snips")
user1 = Horses.add_user ("Jim")
Horses.display_data
Now when I run just this file, It will give me the printout in my terminal of
Jackrabbit
Pokey
Snips
Jim
But when I start trying to call the methods I have created in my Horses class in my next class of Race even outside of the Race class itself, Im returning method undefined.
require_relative 'Horses_class.rb'
no_winner = true
class Race
def begin_race
puts "And the Race has begun!"
end
end
while no_winner == true
puts begin_race
racing = Race.new
racing.Horses.display_data
end
So why am I not allowed to call my other methods? should I be using a splat or is there something more simplistic that im missing? Thank you in advanced.
Jim
Your begin_race method seems to be out of scope when you're calling it. You need to use either the . or the :: (scope) operator to access it.
class Race
def self.begin_race
puts "And the race has begun!"
end
end
Race::begin_race
# or
Race.begin_race
Also, when you call racing.Horses.display_data you must make sure that your Horses class is a sub-class of you racing class. You can not call a sub-class via an object, you must call it through the class constant.
class Race
class Horses
def self.display_data
puts "The Data"
end
end
end
# Access 'display_data'
Race::Horses.display_data
So in this case your require_relative should be within your Race class and your while block should look like
while no_winner == true
Race.begin_race
Race::Horses.display_data
end

How to count instances of class without counting reassignment?

I am working on class methods.
I am trying to count the number of created instances of a class. I am able to do this by creating a counter variable in the initialize method.
The problem arises when I reassign the variable originally assigned to one class instance. Because the initialize method is called twice, it does not recognize that the variable is simply being reassigned to another class instance.
class Ticket
attr_accessor :price
attr_reader :event, :venue
##count = 0
##tickets = {}
def initialize(event, venue)
#event = event
#venue = venue
##count += 1
end
def self.count
##count
end
end
a = Ticket.new("Michael Buble", "Staples")
a = Ticket.new("Frank Sinatra", "Madison Square Garden")
puts "Ticket count of #{Ticket.count}"
When I run the above code in IRB, it gives me a Ticket count of 2 (as expected). How do I change my code so that it recognizes the overwrite?
NOTE: I know that this question has been asked before for Objective C, but the reassignment aspect of the question adds a different element to the problem. Let me know otherwise.
ObjectSpace.each_object(Ticket).count
Will give you the count of object currently in memory. On testing in IRB I find it runs into the problem you describe, objects persist in memory even though you have assigned a new one to the variable. Technically the object still exists, even though you assign a new instance to the variable "a".
See this article: Deleting an object in Ruby The answers have plenty of info about what you are trying to do.
In the real world you wouldn't be counting instances in memory, you'd be asking a database how many exist. You need to think in terms of a database.
Your use of a to repeatedly contain the Ticket instance is wrong. You should be using an Array, Hash or Set to maintain the list, then ask the container how many exist:
require 'set'
class Ticket
attr_accessor :price
attr_reader :event, :venue
##tickets = Set.new
def initialize(event, venue)
#event = event
#venue = venue
##tickets << self
end
def delete
##tickets.delete(self)
end
def self.count
##tickets.size
end
end
a = Ticket.new("Michael Buble", "Staples")
b = Ticket.new("Frank Sinatra", "Madison Square Garden")
puts "Ticket count of #{Ticket::count}"
b.delete
puts "Ticket count of #{Ticket::count}"
You can build this out by adding ways to retrieve a particular instance from ##tickets, add a to_s so you can list them, but, in the end, you'll want to use a real database. If your code were to crash for any reason, your entire list of tickets would disappear, which would be unacceptable in real life.
If you really want to count live instances of the Ticket class (for reasons I cannot fathom), #Beartech has the right idea:
class Ticket
attr_reader :event, :venue
def initialize(event, venue)
#event = event
#venue = venue
end
def self.count_live_instances
ObjectSpace.garbage_collect
ObjectSpace.each_object(self).to_a.size
end
end
a = Ticket.new("Michael Buble", "Staples")
b = Ticket.new("Cher", "Canadian Tire Center")
a = Ticket.new("Frank Sinatra", "Madison Square Garden")
puts "Ticket instances count = #{Ticket.count_live_instances}" # => 2
It is essential to garbage-collect before invoking ObjectSpace#each_object. If you are skeptical, insert p ObjectSpace.each_object(self).to_a.size as the first line of self.count_live_instances. It will print 3.
(There is also a method ObjectSpace#count_objects. This method returns a hash like this one: {:TOTAL=>56139,..., :T_ARRAY=>3139,..., :T_ICLASS=>32}. Unfortunately, the keys are "object types"; you won't find :TICKET among them.)
class Gs
def self.method1
code...
end
def self.method2
code...
end
def self.method3
code...
end
end
Gs.new
p Gs.singleton_methods.count
The Gs.singleton_methods.count will print 3
it will count the singleton methods,if we use the self keyword or
classname.method name..

When are class variables actually useful?

If you look around you'll find some comparisons of class variables to class instance variables, and the best I ever hear of class variables are "that might be what you want", but I've never heard anybody say that is what they want, or when the situation of sharing a variable across an entire inheritance tree might be more useful.
So in practice, when is a class variable a better choice than a class instance variable?
Another example(you want unique name for every object)
class Foobar
##uniqueId = 0
def initialize
##uniqueId += 1
end
def unique_name
"Foobar_" + ##uniqueId.to_s
end
end
a = Foobar.new
puts a.unique_name
a = Foobar.new
puts a.unique_name
This will output
Foobar_1
Foobar_2
Edit: Singleton pattern is also good example for static variables link
The easiest example would be that you use class variable to store some overview states of that class or shared states of all instances.
class Foobar
##total = 0
def initialize
##total += 1
end
class << self
def all
##total
end
end
end
5.times { Foobar.new }
Foobar.all #=> 5
So here ##total will show you how many instances have been created as a sum.
Rails' ActiveRecord#FinderMethods#all simply returns the array's size. But if all your instances of a class are not wrapped in a array, using class variable to return "all" is also a solution.

What is the right way to initialize an object that invoke an instance method of its mother?

The question is as of the title. Say I have a simple example below:
class Vehicle
attr_accessor :wheels
end
class Car < Vehicle
def initialize
self.wheels = 4
end
end
class Truck < Vehicle
def initialize
#wheels = 16
end
end
I am curious which way is considered correct or better to invoke
wheels writer method of mother Vehicle?
self.wheels = 4 is more flexible because under the hood it is calling a setter method: self.wheels=(4)
So if you ever wanted to do something with the value before it is placed into #wheels, you could define that function:
def wheels=(val)
# do something
#wheels = new_val
end

Resources