I'm trying to create an array of all the superclasses for the given class. I tried to solve this problem by using a loop. Why is this not working?
class Object
def superclasses
array = []
klass = self.superclass
unless klass == nil
array << klass
klass = klass.superclass
end
array
end
end
class Bar
end
class Foo < Bar
end
p Foo.superclasses # should be [Bar, Object, BasicObject]
unless isn't a loop. What you're looking for is until:
class Object
def superclasses
array = []
klass = self.superclass
until klass == nil
array << klass
klass = klass.superclass
end
array
end
end
class Bar
end
class Foo < Bar
end
p Foo.superclasses # Prints "[Bar, Object, BasicObject]"
Furthermore, you don't need a new method for this. There's already a method called Module#ancestors which does basically what you want:
class Bar
end
class Foo < Bar
end
p Foo.ancestors # Prints "[Foo, Bar, Object, Kernel, BasicObject]"
Note that the return value of ancestors includes Foo itself, and modules which have been included in the inheritance chain, like Kernel. If you don't want that, you can define superclasses like this:
class Module
def superclasses
ancestors[1..-1].select{|mod| mod.is_a? Class }
end
end
class Bar
end
class Foo < Bar
end
p Foo.superclasses # Prints "[Bar, Object, BasicObject]"
You need a loop to continue looking up superclasses:
def superclasses
array = []
klass = self.superclass
while klass
array << klass
klass = klass.superclass
end
array
end
I think it looks a bit nicer to use recursion
def superclasses
return [] if superclass.nil?
[superclass].concat superclass.superclasses
end
And really this method should be defined in Class because not every Object responds to superclass.
Ruby's built-in way of doing this is Module#ancestors. It's not exactly the same though, because that also takes into account included modules. The built-in way is the correct way to do this, however, since the strict chain of superclasses doesn't really give you the whole picture in Ruby.
Another way to get superclasses
class Object
def superclasses
ObjectSpace.each_object(Class).select { |klass| klass < self }
end
end
Related
Actually i am new to the oops concepts in ruby, here i want to call the instance variables from one function to another function in same class.but im getting undefined method for second function "one_object"
module User
class Object
class << self
def add_object(items)
#data={}
#data.merge!(items)
end
# i want to use above #data = {} for next one_object method after adding items to the add_object method
def one_object
#data.merge!({"ext"=>"1.0"})
end
end
end
end
a = User::Object.add_object({"txt"=>"text_file","csv"=>"csv_file"})
p a.one_object
Expected Output:
{"txt"=>"text_file", "csv"=>"csv_file", "ext"=>"1.0"}
In Ruby, Object is the root class of all objects. Although you can have your own Object class within User, it could cause a lot of confusion.
Let's simplify your problem by removing the User module (it's not relevant to the example) and by renaming Object to Foo (you'll find a better name). To initialize instance variables you can use the initialize method which is invoked by default every time you construct an object via new:
class Foo
def initialize
#data = {}
end
end
foo = Foo.new
#=> #<Foo:0x00007fb551823d78 #data={}>
# ^^^^^^^^
That hash you assign to #data will be shared among all instance methods. In addition, each Foo instance will have its own #data hash. To merge! another hash into it, you can use:
class Foo
def initialize
#data = {}
end
def add(hash)
#data.merge!(hash)
end
end
foo = Foo.new
#=> #<Foo:0x00007fbc80048230 #data={}>
foo.add({"abc"=>123})
#=> {"abc"=>123}
foo.add({"def"=>456})
#=> {"def"=>456}
foo
#=> #<Foo:0x00007fbc80048230 #data={"abc"=>123, "def"=>456}>
In order to chain multiple add calls (a so-called fluent interface), you have to return self from within the method:
class Foo
def initialize
#data = {}
end
def add(hash)
#data.merge!(hash)
self # <- like this
end
end
foo = Foo.new
#=> #<Foo:0x00007ff7408003d8 #data={}>
foo.add({"abc"=>123}).add({"def"=>456})
#=> #<Foo:0x00007ff7408003d8 #data={"abc"=>123, "def"=>456}>
Finally, to add static data, you could simply call your own method:
class Foo
def initialize
#data = {}
end
def add(hash)
#data.merge!(hash)
self
end
def add_more
add({"more" => 789})
end
end
foo = Foo.new
#=> #<Foo:0x00007f99b20f8590 #data={}>
foo.add({"abc"=>123}).add({"def"=>456}).add_more
#=> #<Foo:0x00007f99b20f8590 #data={"abc"=>123, "def"=>456, "more"=>789}>
You assign the result of add_object to a (i.e. your a is now a Hash), but next you are going to call one_object on a (but one_object is part of User::Object and not of your Hash instance).
If you add to add_object another line, containing self, you will receive your expected output.
With the change, add_object will return User::Object, and you won't run into your initial issue.
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).
I'm currently doing some metaprogramming with ruby, and I'm trying to isolate the methods of class (that class is in another file, that I get by a require). I can get all the methods, thanks to klass.public_instance_methods(false), but I in the sametime, the array given also have all the attributes of the class. How could I isolate them ? In others related questions on SO, they suggest to use klass.instance_variables but when I do that, it only returns an empty array.
I can't seem to wrap my head around that one. I don't understand why there isn't a method specifically for that already...
For example:
I have in a file this class :
class T
attr_reader:a
def initialize(a)
#a = a
end
def meth
#code here
end
end
And, in another file, i have
require_relative 'T.rb'
class meta
def initialize
methods = T.public_instance_methods(false) #=> here methods = [:a,:meth] but I would want only to have [:meth]
#rest of code
end
end
For class defined like this:
class Klass
attr_accessor :variable
def initialize(variable)
#variable = variable
end
def method
end
end
you can find public non-attr instance methods using public_instance_methods and instance_variables methods.
public_instance_methods = Klass.public_instance_methods(false)
# [:method, :variable, :variable=]
instance_variables = Klass.new(nil).instance_variables
# [:#variable]
getters_and_setters = instance_variables
.map(&:to_s)
.map{|v| v[1..-1] }
.flat_map {|v| [v, v + '=']}
.map(&:to_sym)
# [:variable, :variable=]
without_attr = public_instance_methods - getters_and_setters
# [:method]
This is impossible. Ruby's "attributes" are completely normal methods. There is no way to distinguish them from other methods. For example, these two classes are completely indistinguishable:
class Foo
attr_reader :bar
end
class Foo
def bar
#bar
end
end
You can try to be clever and filter them out based on instance variables, but that is dangerous:
class Foo
# can filter this out using #bar
attr_writer :bar
def initialize
#bar = []
end
end
class Foo
def initialize
#bar = []
end
# this looks the same as above, but isn't a normal attribute!
def bar= x
#bar = x.to_a
end
end
In order to ask something like:
MyClass::create().empty?
How would I set up empty within MyClass?
Empty (true/false) depends on whether a class variable #arr is empty or not.
The question mark is actually part of the method name, so you would do this:
class MyClass
def empty?
#arr.empty? # Implicitly returned.
end
end
Exactly the same as I showed in the last post, but with a different method name.
First, create must return something with an empty? method. For example:
class MyClass
def self.create
[]
end
end
If you want to be operating on instances of MyClass as per your last question:
class MyClass
def self.create
MyClass.new
end
def initialize
#arr = []
end
def empty?
#arr.empty?
end
def add x
#arr << x
self
end
end
Here MyClass acts as a simple wrapper around an array, providing an add method.
pry(main)> MyClass.create.empty?
=> true
You might also need to check whether #arr is nil or not. This depends on your class definition of empty.
def empty?
!#arr || #arr.empty?
end
You could use Forwardable to delegate empty? from your class to the array:
require "forwardable"
class MyClass
extend Forwardable
def_delegators :#arr, :empty?
def initialize(arr)
#arr = arr
end
end
my_object = MyClass.new([])
my_object.empty? # => true
This question already has answers here:
When to use `self.foo` instead of `foo` in Ruby methods
(3 answers)
Closed 9 years ago.
When do you use self.property_name in Ruby?
Use self when calling a class's mutator. For example, this won't work:
class Foo
attr_writer :bar
def do_something
bar = 2
end
end
The problem is that 'bar = 2' creates a local variable named 'bar', rather than calling the method 'bar=' which was created by attr_writer. However, a little self will fix it:
class Foo
attr_writer :bar
def do_something
self.bar = 2
end
end
self.bar = 2 calls the method bar=, as desired.
You may also use self to call a reader with the same name as a local variable:
class Foo
attr_reader :bar
def do_something
bar = 123
puts self.bar
end
end
But it's usually better to avoid giving a local variable the same name as an accessor.
self references the current object. This lends itself to many uses:
calling a method on the current object
class A
def initialize val
#val = val
end
def method1
1 + self.method2()
end
def method2
#val*2
end
end
Here running A.new(1).method1() will return 3. The use of self is optional here - the following code is equivalent:
class A
def initialize val
#val = val
end
def method1
1 + method2()
end
def method2
#val*2
end
end
self is not redundant for this purpose though - operator overloading makes it neccessary:
class A
def initialize val
#val = val
end
def [] x
#val + x
end
def method1 y
[y] #returns an array!
end
def method2 y
self.[y] #executes the [] method
end
end
This shows how self must be used if you want to call the current object's [] method.
referencing attributes
You can generate the methods to read and write to instance variables using attr_accessor and co.
class A
attr_accessor :val
def initialize val
#val = val
end
def increment!
self.val += 1
end
end
Using self is redundant here because you can just reference the variable directly, eg. #val.
Using the previous class, A.new(1).increment! would return 2.
method chaining
You can return self to provide a form of syntactical sugar known as chaining:
class A
attr_reader :val
def initialize val
#val = val
end
def increment!
#val += 1
self
end
end
Here, because we are returning the current object, methods can be chained:
A.new(1).increment!.increment!.increment!.val #returns 4
creating class methods
You can define class methods using self:
class A
def self.double x
x*2
end
def self.quadruple x
self.double(self.double(x))
end
end
This will enable you to call A.double(2) #= 4 and A.quadruple(2) #=8. Note that in a class method, self references that class because the class is the current object.
how the value of self is determined
The current value of self in a particular method is set to the object that that method was called upon. Normally this uses the '.' notation. When you run some_object.some_method(), self is bound to some_object for the duration of some_method, meaning that some_method can use self in one of the ways mentioned above.
Using self is used will reference the current object accessible within a program. Therefore, self.property is used when accessing a variable through a attr_accessor of some sort. In must cases, it can be used in place of #property from within an object.