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..
Related
So, I'm trying to initialize multiple objects and add them to a list. What I'm wanting to happen is by running Market.new, I want every item from the api added as an object. Below is the code I thought might work. But, it's adding the same object to the list 100x. Is there a way to accomplish this?
def initialize
data = JSON.parse(open(BASE_URL + "markets?vs_currency=usd").read)
i = 0
# looping until we hit the end of the list. adding them all as objects.
while i < data.length
#id = data[i]["id"].to_s
#name = data[i]["name"].to_s
#symbol = data[i]["symbol"].to_s
#price = data[i]["current_price"].to_s
#price_movement_24h = data[i]["price_change_percentage_24h"].to_s
#market_cap = data[i]["market_cap"].to_s
##market << self
i += 1
end
end
This gives me the same object added to the ##market list 100x.
=> [#<Market:0x0000561b3f853f30
#id="iostoken",
#market_cap="43969067",
#name="IOST",
#price="0.0036523",
#price_movement_24h="-0.76702",
#symbol="iost">,
#<Market:0x0000561b3f853f30
#id="iostoken",
#market_cap="43969067",
#name="IOST",
#price="0.0036523",
#price_movement_24h="-0.76702",
#symbol="iost">,
I want to start by saying this is very strange Ruby code and not something you'd typically do. That's not meant as an insult, just to say that Ruby devs tend to follow the same or similar guidelines on structuring objects and this chunk of code feels like it's ported from another language.
The issue you're seeing is due to the fact that within the initialize method you're not creating any new objects but instead updating the instance variables and pushing self into a class variable. self is referencing this instance directly which means the class variable array is filling up with references to the same object. If you're adamant on keeping the code the same then you should instead push a duplicate of your object after you've updated the instance variables.
##market << self.dup
This creates a duplicate object that has a different memory address and reference.
If you're looking to write more idiomatic code you'd want to use multiple objects and not rely on class variables at all. If you're not interpolating a variable in a string use single quotes instead of double quotes. Keep object methods simple and focused on specific tasks. These are just a few things Ruby developers consider when writing code, but find what works best for you.
Take something like this for instance:
class Market
attr_accessor :id, :name, :symbol, :price, :price_movement_24h, :market_cap
def initialize(data = {})
#id = data['id'].to_s
#name = data['name'].to_s
#symbol = data['symbol'].to_s
#price = data['current_price'].to_s
#price_movement_24h = data['price_change_percentage_24h'].to_s
#market_cap = data['market_cap'].to_s
end
end
class ImportService
def self.from_api(url)
response = JSON.parse(open(url).read) || []
response.map { |data| Market.new(data) }
end
end
You could then call this as such:
#market_data = ImportService.from_api(BASE_URL + 'markets?vs_currency=usd')
When adding self to the ##market, you should change the code to this
##market << self.dup
However, I don't think it's a good practice to use a class variable here and add self to init an array of object. Instead, you should create a new class (for example MarketImporter)
class Market
attr_accessor :id, :name, :symbol, :price, :price_movement_24h, :market_cap
def initialize(data = {})
#id = data["id"].to_s
#name = data["name"].to_s
#symbol = data["symbol"].to_s
#price = data["current_price"].to_s
#price_movement_24h = data["price_change_percentage_24h"].to_s
#market_cap = data["market_cap"].to_s
end
end
class MarketImporter
attr_accessor :markets
def initialize
data = JSON.parse(open(BASE_URL + "markets?vs_currency=usd").read)
#markets = data.collect { |item| Market.new(item) }
end
end
Then you can init the collection by
MarketImporter.new
I keep trying to reason upon the functionality of a method within a subclass that inherits functionality of a parents class. But it seems that I keep getting into a mental loop of: one cannot behave without the other but the other cannot come before the one... My brain hurts...
Ok heres my relevant code in the parent class
class BankAccount
# method to initialize and other methods etc...
def withdraw(amount)
if (amount <= #balance)
#balance -= amount
else
'Insufficient funds'
end
end
end
And heres my relevant code in the subclass
class CheckingAccount < BankAccount
# methods to initialize and other methods etc...
def withdraw
super
end
end
According to the tutorial im learning from - what I am trying to accomplish is
"CheckingAccount methods #withdraw increments 'number_of_withdrawals' by one after a successful withdrawal"
So if I create a variable number_of_withdrawals inside of my BankAccount class (as tutorial examples hint towards) then how is it that when I call super from the subclass version of withdraw that it would know to increment number_of_withdrawals based on the if else statement executing a withdraw or not.
Shouldn't a variable number_of_withdrawals be declared in the BankAccount class, not the CheckingAccount class (even though tutorial examples hint towards putting it in the CheckingAccount class). For a full picture of this here is a gist with the test specs() below of my current code state:
Test Specs / Code Attempt
If someone can provide a working example of
"CheckingAccount methods #withdraw increments 'number_of_withdrawals' by one after a successful withdrawal"
With modified code I have provided in the GIST - I would really appreciate it. Im very new to ruby.(1 week)
With the code as it's currently written, CheckingAccount#withdraw could check the return value of super to determine whether the withdrawal was successful or not.
For example:
def withdraw(n)
result = super
if result != 'Insufficient funds'
#number_of_withdrawals += 1
end
result
end
Your method does way too much and internalizes too many assumptions. A better approach to this is to break things up a little:
class BankAccount
attr_reader :balance
def initialize
#balance = 0
end
def withdraw(amount)
if (can_withdraw?(amount))
credit(amount)
after_withdraw
else
false
end
end
def can_withdraw?(amount)
amount <= balance
end
def after_withdraw
# No default behaviour
end
def debit(amount)
#balance += amount
end
def credit(amount)
#balance -= amount
end
end
Then you can make the subclass specialize very specific methods instead of having to lean on super so hard:
class CheckingAccount < BankAccount
attr_reader :overdraft
def initialize
super
#overdraft = 0
#withdrawals = 0
end
def can_withdraw?(amount)
amount <= balance + overdraft
end
def after_withdraw
#withdrawals += 1
end
end
With inheritance, the parent can be thought of as a 'template' for the child. That is to say, you can instead of using a parent at all simply write everything into the child class (not that you should). The point is that everything from the parent class can be thought of as getting copied on to the child class, so if you make an instance variable on the parent and change it from the child, there is only one instance variable defined since there is only one object instantiated. In other words, when you say CheckingAccount.new there is no separate BankAccount getting instantiated - it's all the same object.
So, for every method that is defined in both the parent and child, you need to call super or else the parent method won't be called. Here's an example with your code:
class BankAccount
def initialize
#balance = 0
#number_of_withdrawals = 0
end
def withdraw(amount)
if amount <= #balance
#balance -= amount
#number_of_withdrawals += 1
else
'Insufficient funds'
end
end
end
class CheckingAccount < BankAccount
MAX_FREE_WITHDRAWALS = 3
def withdraw(amount)
if #number_of_withdrawals >= self.class::MAX_FREE_WITHDRAWALS
amount += 5
super(amount)
end
end
I just skimmed the requirement document, so make sure to double check (e.g. don't just take my code and hand it is as homework :D)
The behavior you're seeing -- or expected to provide in order to complete the exercise -- is due to Ruby's dynamic nature. Since your program is "interpreted" as it's run (and is subject to change), there's no way for Ruby to know that the instance variable in question won't exist until the method is actually executed.
Here's a contrived example which (hopefully) demonstrates why you're seeing/hoping to see this behavior:
class Foo
def say_something_that_doesnt_exist
# Foo is free to try to make use of #nothing,
# in case it's been provided by a child class'
# instance, but if it's not present, its value
# will just be nil
puts "say_something_that_doesnt_exist, like #{#nothing}!"
end
def say_something_that_does_exist
puts "say_something_that_does_exist, like #{#bar}!"
end
end
class Bar < Foo
attr_reader :bar
def initialize
super
#bar = "bar"
end
end
bar = Bar.new
bar.say_something_that_doesnt_exist # say_something_that_doesnt_exist, like !
bar.say_something_that_does_exist # say_something_that_does_exist, like bar!
You should have a look at this question and its answers for a more detailed discussion about the distinction between static/dynamic languages and the early/late binding of values.
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.
Ok so I just started learning ruby and I'm making a Yhatzee game, now this is where I'm currently at:
class Yhatzee
def dices
#dices.to_a= [
dice1=rand(1..6),
dice2=rand(1..6),
dice3=rand(1..6),
dice4=rand(1..6),
dice5=rand(1..6)
]
end
def roll_dice
#dices.to_a.each do |dice|
puts dice
end
end
end
x = Yhatzee.new
puts x.roll_dice
Now the reason i typed .to_a after the array is i kept getting a "uninitialized variable #dices" error, and that seemed to fix it, i have no idea why.
anyways on to my question, i currently don't get any errors but my program still won't print anything to the screen. I expected it to print out the value of each dice in the array... any idea what I'm doing wrong? It seems to work when i do it in a procedural style without using classes or methods so i assumed it might work if i made the 'dices' method public. But no luck.
There are a few issues here. Firstly #dices is nil because it is not set anywhere. Thus when you call #dices.to_a you will get []. Also the dices method will not work either because nil does not have a to_a= method and the local variables you are assigning in the array will be ignored.
It seems a little reading is in order but I would do something like the following: (Not the whole game just refactor of your code)
class Yhatzee
def dice
#dice = Array.new(5){rand(1..6)}
end
def roll_dice
puts dice
end
end
x = Yhatzee.new
puts x.roll_dice
There are alot of additional considerations that need to be made here but this should at least get you started. Small Example of how I would recommend expanding your logic: (I did not handle many scenarios here so don't copy paste. Just wanted to give you a more in depth look)
require 'forwardable'
module Yahtzee
module Display
def show_with_index(arr)
print arr.each_index.to_a
print "\n"
print arr
end
end
class Roll
include Display
extend Forwardable
def_delegator :#dice, :values_at
attr_reader :dice
def initialize(dice=5)
#dice = Array.new(dice){rand(1..6)}
end
def show
show_with_index(#dice)
end
end
class Turn
class << self
def start
t = Turn.new
t.show
t
end
end
attr_reader :rolls
include Display
def initialize
#roll = Roll.new
#rolls = 1
#kept = []
end
def show
#roll.show
end
def roll_again
if available_rolls_and_dice
#rolls += 1
#roll = Roll.new(5-#kept.count)
puts "Hand => #{#kept.inspect}"
show
else
puts "No Rolls left" if #rolls == 3
puts "Remove a Die to keep rolling" if #kept.count == 5
show_hand
end
end
def keep(*indices)
#kept += #roll.values_at(*indices)
end
def show_hand
show_with_index(#kept)
end
def remove(*indices)
indices.each do |idx|
#kept.delete_at(idx)
end
show_hand
end
private
def available_rolls_and_dice
#rolls < 3 && #kept.count < 5
end
end
end
The main problem with this code is that you are trying to use the #dices instance variable inside of the roll_dice method, however you are not defining the instance variable anywhere (anywhere that is being used). You have created the dices method but you are not actually instantiating it anywhere. I have outlined a fix below:
class Yhatzee
def initialize
create_dices
end
def roll_dice
#dices.each do |dice|
puts dice
end
end
private
def create_dices
#dices = Array.new(5){rand(1..6)}
end
end
x = Yhatzee.new
x.roll_dice
I have done some simple refactoring:
Created an initialize method, which creates the #dice instance variable on the class initialization.
Made the 'dices' method more descriptive and changed the method visibility to private so only the class itself is able to create the #dice.
Cleaned up the creation of the dices inside of the #dice instance variable
I have omitted the .to_a from the roll_dice method, now that we create the variable from within the class and we know that it is an array and it will be unless we explicitly redefine it.
UPDATE
Although I cleaned up the implementation of the class, it was kindly pointed out by #engineersmnky that I oversaw that the roll would return the same results each time I called the roll_dice function, I have therefore written two functions which will achieve this, one that defines an instance variable for later use and one that literally just returns the results.
class Yhatzee
def roll_dice
#dice = Array.new(5){rand(1..6)} # You will have access to this in other methods defined on the class
#dice.each {|dice| puts dice }
end
def roll_dice_two
Array.new(5){rand(1..6)}.each {|dice| puts dice } # This will return the results but will not be stored for later use
end
end
x = Yhatzee.new
x.roll_dice
x.roll_dice # Will now return a new result
Let's say I have the following class:
class Vehicle
##total_vehicles = 0
##all_instances = Array.new
def initialize
##total_vehicles += 1
##all_instances << self
end
def total_vehicles #returns total number of Vehicles 'alive'
return ##total_vehicles
end
def all_vehicles #returns an array of all Vehicle objects
return ##all_instances
end
end
Now to keep ##total_vehicles and ##all_instances up-to-date and correct, I want to make sure that they are correctly decremented and updated, respectively, when one of those objects is garbage collected. But here is what happens:
v = Vehicle.new
Vehicle.total_vehicles # => 1
v = nil #no references to Vehicle instance now
ObjectSpace.garbage_collect #instance garbage collected
Vehicle.total_vehicles # => 1 Nope!
Well I could add a finalizer Proc to each instance of the Vehicle class that, when called upon the object's garbage collection, would be called. But according to the documentation, ObjectSpace.define_finalizer(v,someProc) would call someProc after the Vehicle instance is destroyed - meaning I cannot use self or self.class in there (since there would be no class, as there is no object!) I could have the proc call a public accessor method on the Vehicle class, but that takes away the purpose of class variables being accessible only to the class and its instances -> essentially turning the class variables into gvars.
How can I have the equivalent of a destructor method (from C++) that will get a Vehicle instance's affairs in order, as it were, before getting garbage-collected?
P.S.
ObjectSpace#count_objects is no a viable option, as even the Ruby docs are up front about.
What you almost certainly want here is the WeakRef class from the standard library. That handles all the details of object tracking and management without blocking reference counting.
Using a WeakRef that points to the object in your tracking you can delegate the whole finalization work to the library, and simplify your own life. (You may need to flush dead items from the arrays, but that is easily enough wrapped in your parent class.)
eg:
def all_instances
# this will vacuum out the dead references and return the remainder.
##weakrefs_to_vehicles = ##weakrefs_to_vehicles.select(&:weakref_alive?)
end
def total_vehicles
all_instances.count
end
Right now, they will never be garbage collected, as you are holding a reference in ##all_instances. You could use a finalizer to get the result you want:
class Vehicle
class << self
attr_accessor :count
def finalize(id)
#count -= 1
end
def all #returns an array of all Vehicle objects
ObjectSpace.each_object(Vehicle).to_a
end
end
Vehicle.count ||= 0
def initialize
Vehicle.count += 1
ObjectSpace.define_finalizer(self, Vehicle.method(:finalize))
end
end
100.times{Vehicle.new}
p Vehicle.count # => 100
ObjectSpace.garbage_collect
p Vehicle.count # => 1, not sure why
p Vehicle.all # => [#<Vehicle:0x0000010208e730>]
If you run this code, you will see that it "works", except that there remains one Vehicle that is not garbage collected. I'm not sure why that is.
Your count method could be also defined more simply by returning ObjectSpace.each_object(Vehicle).count
Finally, if you really want to maintain a list of existing Vehicles, you need to store their ID and use ObjectSpace._id2ref:
require 'set'
class Vehicle
class << self
def finalize(id)
#ids.delete(id)
end
def register(obj)
#ids ||= Set.new
#ids << obj.object_id
ObjectSpace.define_finalizer(obj, method(:finalize))
end
def all #returns an array of all Vehicle objects
#ids.map{|id| ObjectSpace._id2ref(id)}
end
def count
#ids.size
end
end
def initialize
Vehicle.register(self)
end
end