attr_accessor does not work on the following code. The error says "undefined method 'things' for Parent:Class (NoMethodError)":
class Parent
##things = []
attr_accessor :things
end
Parent.things << :car
p Parent.things
However the following code works
class Parent
##things = []
def self.things
##things
end
def things
##things
end
end
Parent.things << :car
p Parent.things
attr_accessor defines accessor methods for an instance. If you want class level auto-generated accessors you could use it on the metaclass
class Parent
#things = []
class << self
attr_accessor :things
end
end
Parent.things #=> []
Parent.things << :car
Parent.things #=> [:car]
but note that this creates a class level instance variable not a class variable. This is likely what you want anyway, as class variables behave differently than you might expect when dealing w/ inheritance. See "Class and Instance Variables In Ruby".
attr_accessor generates accessors for instance variables. Class variables in Ruby are a very different thing, and they are usually not what you want. What you probably want here is a class instance variable. You can use attr_accessor with class instance variables like so:
class Something
class << self
attr_accessor :things
end
end
Then you can write Something.things = 12 and it will work.
Just some clarification: class variables won't be accessible using attr_accessor. It's all about instance variables:
class SomeClass
class << self
attr_accessor :things
end
#things = []
end
because in Ruby, class is an instance of the class "Class" (God, I love to say that) and attr_accessor sets accessor methods for instance variables.
This is probably the simplest way.
class Parent
def self.things
##things ||= []
end
end
Parent.things << :car
p Parent.things
Аlso note that a singleton method is a method only for a single object. In Ruby, a Class is also an object, so it too can have singleton methods! So be aware of when you might be calling them.
Example:
class SomeClass
class << self
def test
end
end
end
test_obj = SomeClass.new
def test_obj.test_2
end
class << test_obj
def test_3
end
end
puts "Singleton methods of SomeClass"
puts SomeClass.singleton_methods
puts '------------------------------------------'
puts "Singleton methods of test_obj"
puts test_obj.singleton_methods
Singleton methods of SomeClass
test
Singleton methods of test_obj
test_2
test_3
Parent.class_variable_get(:##things)
That would be the built-in way. In most cases this should be sufficient I think. No need to have a class variable accessor in the instance.
class Parent
#things = []
singleton_class.send(:attr_accessor, :things)
end
This pattern is most useful when you are defining accessors dynamically or creating them inside a method:
class Foo
def self.add_accessor(name)
singleton_class.send(:attr_accessor, name)
end
end
Foo.add_accessor :things
Foo.things = [:car]
Foo.things # => [:car]
Related
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).
Can somebody help me distinguish When we create methods inside class << self block and when we define normal methods.
I saw somewhere code like this, but I don't know concisely the use cases of them
class Foo
def initialize
end
def bar
end
class << self
def foobar
end
end
end
The methods defined right inside a class block are instance methods:
class Foo
def bar
end
end
Methods defined within class << self inside a class block are class methods:
class Foo
class << self
def baz
end
end
end
Instance methods become available to any instance of a given class:
foo = Foo.new
foo.bar
Whereas class methods can be called directly on the class:
Foo.baz
Attempting to call instance methods on the class or vice versa results in an error:
Foo.bar #=> NoMethodError: undefined method `bar' for Foo:Class
foo.baz #=> NoMethodError: undefined method `baz' for #<Foo:0x00007ffe20055a20>
Another way to define class methods is by prefixing the method name with self.:
class Foo
def self.baz
end
end
You could also define them outside the class block, although this is rarely seen:
def Foo.baz
end
Or likewise:
class << Foo
def baz
end
end
Note that defining methods this way is not limited to classes. You can add methods to arbitrary objects, e.g.:
o = Object.new
def o.hello
"hello from o"
end
o.hello
#=> "hello from o"
Or via:
class << o
def hello
"hello from o"
end
end
Internally, these methods are added to the object's singleton class. It's a special purpose class to hold methods for just that instance:
o.singleton_class.instance_methods(false)
#=> [:hello]
For the Foo class above:
Foo.instance_methods(false) #=> [:bar]
Foo.singleton_class.instance_methods(false) #=> [:baz]
So technically, a class method is just an instance method defined on the class' singleton class.
You may need to read up on Ruby's instance and class methods.
But personally, I'd do
class Foo
class << self
def foobar
end
end
end
instead of
class Foo
def self.foobar
end
end
whenever I want to add some class level attributes, or make a method private etc as
class Foo
private
def self.foobar
end
end
wouldn't work the same as
class Foo
class << self
private
def foobar
end
end
end
I want to extend Enumerable methods to my class, so that I can call them directly on my class, and not on class's instance variable i.e. X.objects
class X
include Enumerable
extend Forwardable
#objects ||= []
# tried this, but it doesn't work
# def_delegator self, :count, #objects.count
#def_delegators self, #objects
class << self
attr_accessor :objects
end
def initialize(name)
#name = name
end
def save
self.class.objects << self
end
def self.show
objects
end
end
y = X.new('a')
y.save
z = X.new('b')
z.save
n = X.new('c')
n.save
q = X.new('d')
q.save
p X.show
#p X.count # I want this to work.
I assume you're trying to define delegator on the class level
You may try to define it like this:
class << self
extend Forwardable
attr_accessor :objects
def_delegator :objects, :count
end
I mean, the only difference between your code and this code is extending by Forwardable inside class << self
I am learning Ruby Singletons and i found some ways to define and get list of Class and Object singleton methods.
Class Singleton Methods
Ways to define class singleton methods:
class MyClass
def MyClass.first_sing_method
'first'
end
def self.second_sing_method
'second'
end
class << self
def third_sing_method
'third'
end
end
class << MyClass
def fourth_sing_method
'fourth'
end
end
end
def MyClass.fifth_sing_method
'fifth'
end
MyClass.define_singleton_method(:sixth_sing_method) do
'sixth'
end
Ways to get list of class singletons methods:
#get singleton methods list for class and it's ancestors
MyClass.singleton_methods
#get singleton methods list for current class only
MyClass.methods(false)
Object Singleton Methods
Ways to define object singleton methods
class MyClass
end
obj = MyClass.new
class << obj
def first_sing_method
'first'
end
end
def obj.second_sing_method
'second'
end
obj.define_singleton_method(:third_sing_method) do
'third'
end
Way to get list of object singleton methods
#get singleton methods list for object and it's ancestors
obj.singleton_methods
#get singleton methods list for current object only
obj.methods(false)
Are there other ways to do this?
First of all, the way to list singleton methods is with singleton_methods. The methods method returns a list of the names of public and protected methods of the object. Also, it is defined in the Object class. Try extending an instance. It is one of the most elegant ways, as it supports code reuse and seems to me very object-oriented:
class Foo
def bar
puts "Hi"
end
end
module Bar
def foo
puts "Bye"
end
end
f = Foo.new
f.bar
#=> hi
f.extend Bar
f.foo
#=> bye
f.methods(false)
#=> []
# the module we extended from is not a superclass
# therefore, this must be empty, as expected
f.singleton_methods
#=> ["foo"]
# this lists the singleton method correctly
g = Foo.new
g.foo
#=> NoMethodError
Edit: In the comment you asked why methods(false) returns nothing in this case. After reading through the C code it seems that:
methods returns all the methods available for the object (also the ones in included modules)
singleton_methods returns all the singleton methods for the object (also the ones in included modules) (documentation)
singleton_methods(false) returns all the singleton methods for the object, but not those declared in included modules
methods(false) supposedly returns the singleton methods by calling singleton_methods, but it also passes the parameter false to it; whether this is a bug or a feature - I do not know
Hopefully, this clarifies the issue a little. Bottom line: call singleton_methods, seems more reliable.
Some more ways:
Class singleton methods
class << MyClass
define_method :sixth_sing_method do
puts "sixth"
end
end
class MyClass
class << self
define_method :fourth_sing_method do
puts "seventh"
end
end
end
Object singleton methods
class << obj
define_method :fourth_sing_method do
puts "fourth"
end
end
Next, you can use define_method and define_singleton_method in combination with send, e.g.
obj.send :define_singleton_method, :nth_sing_method, lambda{ puts "nth" }
and all possible combinations thereof. To use define_method, you need to capture the singleton class first as in this (the same works for class objects, too)
singleton_class = class << obj; self end
singleton_class.send :define_method, :nth_sing_method, lambda{ puts "nth" }
Another ways is using class_eval on the singleton class objects:
singleton_class.class_eval do
def nth_sing_method
puts "nth"
end
end
And then you once again may combine send with class_eval...
There are myriads of ways, I guess :)
Object singleton methods
instance_eval
class A
end
a = A.new
a.instance_eval do
def v
"asd"
end
end
a.singleton_methods
=> [:z, :v]
How can I add new attributes to a particular instance ?
For example here I want to add attr_acessors methods to attributes "m1","m2" in object e1 and "m4".."m6" to e2
e1=Element.new("e1",["m1","m2"])
e2=Element.new("e2",["m4","m5","m6"])
e1.m1 = "try"
puts e2.m4
To allow this :
class Element
attr_accessor :name
def initialize name,meths=[]
#name=name
meths.each do |m|
#??
end
end
end
Why not use a simple OpenStruct instead?
require 'ostruct'
e1 = OpenStruct.new
e1.m1 = 'try'
Alternatively, you can add attribute to any object using:
a.instance_eval('def m2; #m2; end; def m2=(x); #m2=x;end')
If you want to add attributes to all instances of specific class you can also:
a.class.instance_eval('attr_accessor :mmm')
Try this:
meths.each do |m|
singleton_class().class_eval do
attr_accessor m
end
end
where the singleton_class() method is defined as:
def singleton_class
class << self
self
end
end
(you probably want to make it private).
This will create the accessors only on the specific instance rather than on the Element class.
Here is a simpler solution:
methods.each do |method|
class << self
attr_accessor method
end
end
This way, you get rid of the extra method definition and class_eval because class << self already puts you into the scope of the eigenclass, where you add singleton methods.