How to get the instance in ruby? - ruby

I have such a case:
class Person
#a class that wraps to some db table
def initialize(attributes=nil)
populate_with_attributes(attributes) if !attributes.nil?
end
def self.find(id)
#the_db.execute('query here where id....')
end
def save
#save logic and queries here
#the_db.execute('save query here')
end
# other methods .....
end
class SuperPerson
#another class that wraps to some db table
end
class SpTh < Thread
def initialize(thread_id, *other_params)
super
#thread_id = thread_id
#db = SQLite3.Database.new("./db_#{#thread_id}.sqlite3")
#....
end
def start
person = Person.find(55)
person.age = 27
person.save
end
# other methods .....
end
class Sp
def initialize
#threads_amount = 5
#threads = []
#...
raise_threads
#...
end
def raise_threads
#threads_amount.times{|thread_id|
#threads << SpTh.new(thread_id, *other_params){}
}
end
# other methods .....
end
My problem is:
How do I set the value of the #the_db variable in the Person and SuperPerson classes to the value of #db from SpTh class so that each thread has its own database?

You're accessing the class's #the_db from both the instance (in save) and the class (in self.find): wrap it in a class method, so from the instance you can access it on the class by calling self.class.connection (see the db method):
class Person
def self.connect(connection)
#the_db = connection
end
def self.connection
#the_db
end
def self.find(id)
#the_db.execute("...")
end
def db
self.class.connection
end
end
You can use singleton classes to set different connections:
db = SQLite3.Database.new("./db_#{#thread_id}.sqlite3")
person_class = Class.new(Person){ self.connect(db) }
person_class.find(1)

Related

How to Best Factor Out Common Class Methods

I am building an in-memory instance model in Ruby. There are a bunch of classes that each get instantiated and managed by class methods on that class. There are a bunch of those class methods, e.g. list all instances, retrieve all instances, etc.
The code for these methods is common across all classes and does not need to take any account of any particularities of those classes. Hence, I would like that code to live in a common place. See the list method below. My question: How to best achieve this.
class A
attr_reader :value
##instances = []
def initialize(value:)
#value = value; ##instances << self
end
def self.list
##instances.each { |i| puts "#{i.value}"}
end
end
class B
attr_reader :value
##instances = []
def initialize(value:)
#value = value; ##instances << self
end
def self.list
##instances.each { |i| puts "#{i.value}"}
end
end
A.new(value: '100')
A.new(value: '101')
B.new(value: '200')
B.new(value: '201')
A.list
B.list
Ideally, I define the list method only once. I have also tried moving that to a super-class:
class Entity
def self.list
##instances.each { |i| puts "AB: #{i.value}"}
end
end
class A < Entity
attr_reader :value
##instances = []
def initialize(value:)
#value = value; ##instances << self
end
end
class B < Entity
attr_reader :value
##instances = []
def initialize(value:)
#value = value; ##instances << self
end
end
...but as one would expect the super-class cannot access the ##instances array of its sub-classes. Moving the ##instances array to the super-class results in the array being common to all classes, which is not what I need.
The main change you need to make is to use class instance variables rather than class variables. For reasons explained here class variables should be used sparingly; class instance variables are generally a better choice, as is illustrated nicely by this question.
class Entity
attr_reader :value
class << self
attr_reader :ins
end
def self.inherited(klass)
klass.instance_variable_set(:#ins, [])
end
def initialize(value:)
#value = value
self.class.ins << self
end
def self.list
#ins.each { |i| puts "#{i.value}"}
end
end
class A < Entity; end
class B < Entity; end
A.new(value: '100')
#=> #<A:0x00005754a59dc640 #value="100">
A.new(value: '101')
#=> #<A:0x00005754a59e4818 #value="101">
A.list
# 100
# 101
B.new(value: '200')
#=> #<B:0x00005754a59f0910 #value="200">
B.new(value: '201')
#=> #<B:0x00005754a59f8b88 #value="201">
B.list
# 200
# 201
I defined a getter for the class instance variable #ins in Entity's singleton class1:
class << self
attr_reader :ins
end
When subclasses of Entity are created the callback method Class::inherited is executed on Entity, passing as an argument the class that has been created. inherited creates and initializes (to an empty array) the class instance variable #ins for the class created.
Another way of doing that, without using a callback method, is as follows.
class Entity
attr_reader :value
class << self
attr_accessor :ins
end
def initialize(value:)
#value = value
(self.class.ins ||= []) << self
end
def self.list
#ins.each { |i| puts "#{i.value}"}
end
end
The fragment:
(self.class.ins ||= [])
sets #ins to an empty array if #ins equals nil. If #ins is referenced before it is created, nil is returned, so either way, #ins is set equal to []. In order to execute this statement I needed to change attr_reader :ins to attr_accessor :ins in order to perform the assignment #ins = [] (though I could have used instance_variable_set instead).
Note that if I were to add the line #ins = [] to Entity (as th first line, say), the instance variable #ins would be created for every subclass when the subclass is created, but that instance variable would not be initialized to an empty array, so that line would serve no purpose.
1. Alternatively, one could write, singleton_class.public_send(:attr_reader, :ins).

Ruby - Access instance variables of called class

I have two classes Book::Utils, Table::Utils and I calling one class from the other which are not parent-child classes.
If I call class2 from class1 -> In class2, can we access already present class1 instance variables?
module Table
attr_accessor :account_id
class Utils
def initialize(params)
#account_id = params[:account_id]
end
def calculate
book = Book.new
final_account_id = book.get_account_id
return final_account_id
end
end
end
module Book
class Utils
def get_account_id
# Here I want to access Table's instance variables
# Like #account_id + 20
end
end
end
I am calling Table::Utils.new({account_id: 1}).calculate
Expected result : 21
Can we achieve this?
You need to pass instance of the class you need to call and then you can use accessors:
module Table
attr_accessor :account_id
class Utils
def initialize(params)
#account_id = params[:account_id]
end
def calculate
book = Book.new
final_account_id = book.get_account_id(self)
return final_account_id
end
end
end
module Book
class Utils
def get_account_id(table)
table.account_id + 20
end
end
end
or just pass the value that is needed
module Table
attr_accessor :account_id
class Utils
def initialize(params)
#account_id = params[:account_id]
end
def calculate
book = Book.new
final_account_id = book.get_account_id(account_id)
return final_account_id
end
end
end
module Book
class Utils
def get_account_id(other_id)
other_id + 20
end
end
end

Is it possible to access a method without creating an instance?

I have a class within a module and it has methods:
module D
class Dog
#name = 'pluto'
def setName( n )
#name = n
end
def getName ()
return #name
end
end
end
Can I access getName without creating an instance of Dog like the static method in C++? Something like:
D::Dog.getName ()
instead of:
d = D::Dog.new
d.getName()
I believe you're looking for what is known as a class method in Ruby:
module SomeModule
class SomeClass
#class_variable = "some_string" # An instance variable on a class
def self.some_class_method
#class_variable # Return can be omitted in Ruby
end
# This is how setter methods are usually written in Ruby
def self.some_class_method= new_value
#class_variable = new_value
end
end
end
SomeModule::SomeClass.some_class_method
#=> "some_string"

How to get all instances variables in ruby class?

I have a ruby class, and in one of the methods, it calls an external function, and pass in all instance variables, and continue with the return value. Here is the code:
class MyClass
attr_accessor :name1
attr_accessor :name2
...
attr_accessor :namen
def inner_func():
all_vars = ???? # how to collect all my instance variables into a dict/Hash?
res = out_func(all_vars)
do_more_stuff(res)
end
end
The problem is the instance variables might vary in subclasses. I can't refer them as their names. So, is there a way to do this? Or Am I thinking in a wrong way?
You can use instance_variables to collect them in an Array. You will get all initialized instance variables.
class MyClass
attr_accessor :name1
attr_accessor :name2
...
attr_accessor :namen
def inner_func():
all_vars = instance_variables
res = out_func(all_vars)
do_more_stuff(res)
end
end
You could keep track of all accessors as you create them:
class Receiver
def work(arguments)
puts "Working with #{arguments.inspect}"
end
end
class MyClass
def self.attr_accessor(*arguments)
super
#__attribute_names__ ||= []
#__attribute_names__ += arguments
end
def self.attribute_names
#__attribute_names__
end
def self.inherited(base)
parent = self
base.class_eval do
#__attribute_names__ = parent.attribute_names
end
end
def attributes
self.class.attribute_names.each_with_object({}) do |attribute_name, result|
result[attribute_name] = public_send(attribute_name)
end
end
def work
Receiver.new.work(attributes)
end
attr_accessor :foo
attr_accessor :bar
end
class MySubclass < MyClass
attr_accessor :baz
end
Usage
my_class = MyClass.new
my_class.foo = 123
my_class.bar = 234
my_class.work
# Working with {:foo=>123, :bar=>234}
my_subclass = MySubclass.new
my_subclass.foo = 123
my_subclass.bar = 234
my_subclass.baz = 345
my_subclass.work
# Working with {:foo=>123, :bar=>234, :baz=>345}

Ruby loop through instance methods and run them

I have the following class:
module StatCalculators
class Passing
def initialize(user_id, game_id)
#user_id = user_id
#game_id = game_id
end
def save_completion_percentage
completions = StatType.find_by_name("Completions").stats.where(athlete_id: #user_id).sum(:float_value)
attempts = StatType.find_by_name("Pass Attempts").stats.where(athlete_id: #user_id).sum(:float_value)
value = completions/attempts
stat = Stat.new(value: value, game_id: #game_id, athlete_id: #user_id, float_value: value)
stat.save(validate: false)
end
end
end
The class above has the potential to have a lot more methods that need to be run without having to call each method individually... is there a way to run all instance methods in the initialize method?
It is possible:
module StatCalculators
class Passing
def initialize(user_id, game_id)
#user_id = user_id
#game_id = game_id
klass = self.class
klass.instance_methods(false).each do |method|
klass.instance_method(method).bind(self).call
end
end
...
end
end

Resources