How do I dynamically add an attr_reader - ruby

I expect the following code to work as expected but it gives me a NoMethodError (private method `foo' called for #<MyClass...)
class MyClass
end
my_object = MyClass.new
my_object.instance_variable_set(:#foo, "bar")
MyClass.send("attr_reader", :foo)
puts my_object.foo
The problem is I'm using literally identical code in a larger application and it works exactly as I expect, but when I simplify it to this most basic example it fails.
(I understand there are many other ways to do what I'm doing in Ruby)

Use Module#class_eval:
By doing this, the block puts you in the context of MyClass, thus allowing you to call attr_reader
ruby-1.9.2-p136 :028 > my_object.instance_variable_set(:#foo, "bar")
=> "bar"
ruby-1.9.2-p136 :029 > MyClass.class_eval{attr_reader :foo}
=> nil
ruby-1.9.2-p136 :030 > my_object.foo
=> "bar"

Let's assume for an instant that you want to create an accessor for that particular instance of the class (which I assume is the case since you are operating on the instance.
You can simply open up the instance's singleton class and use instance_eval to add the accessor
class MyClass
end
my_instance = MyClass.new
my_instance.singleton_class.instance_eval { attr_accessor :foo }
my_instance.foo = :bar
my_instance.foo # => :bar

Interesting problem, I found this solution works fine:
MyClass.class_eval("attr_reader :foo")

The answer by #MikeLewis is nice, but why not just re-open the class?
irb(main):001:0> class MyClass; end
#=> nil
irb(main):002:0> m = MyClass.new
#=> #<MyClass:0x2d43ae0>
irb(main):003:0> class MyClass; attr_accessor :foo; end
#=> nil
irb(main):004:0> m.foo = 42
#=> 42

Just force the method to become public:
MyClass.send("public", :foo)
I have no idea why it is private in some cases.

I'm puzzled at this, but one solution that works is to make the attribute_reader explicitly public using MyClass.send(:public, :foo), i.e.
class MyClass
end
my_object = MyClass.new
my_object.instance_variable_set(:#foo, "bar")
MyClass.send(:attr_reader, :foo)
MyClass.send(:public, :foo)
puts my_object.foo

Related

Are all singleton methods public?

Is a singleton method necessarily public? If not, when would a private/protected singleton method be useful?
Singleton methods do not necessarily need to be public. Private/protected singleton methods are useful in the same situations as regular private/protected methods - for example as a helper method that you do not intend to be called outside of the class.
class Foo
end
f = Foo.new
class << f
def foo
helper
# other stuff
end
private
def helper
end
end
You can make a singleton method private if you want:
class Foo
end
f = Foo.new
def f.bar
"baz"
end
f.singleton_class.send :private, :bar
f.bar # => NoMethodError: private method `bar' called for #<Foo:0x007f8674152a00>
f.send :bar # => "baz"
whether or not this is actually useful depends on what you're doing.
You can make a singleton method private if you want:
class Foo
def self.bar
# ...
end
private_class_method :bar
end
Could they be useful? Hmmm. For Ruby 2.2
ObjectSpace.each_object(Module).flat_map { |m|
m.singleton_class.private_methods(false) }.size
#=> 900
Most are private methods of classes' singleton classes:
ObjectSpace.each_object(Class).flat_map { |c|
c.singleton_class.private_methods(false) }.size
#=> 838
[Edit: The following is an edit of my original post, to present more useful information.)
I am puzzled by one thing. Let:
a = ObjectSpace.each_object(Class).map { |c|
[c, c.singleton_class.private_methods(false)] }.to_h
b = ObjectSpace.each_object(Class).map { |c|
[c, c.private_methods(false)] }.to_h
def diff(a,b)
a.map {|k,v| b.key?(k) ? [k,v-b[k]] : [k,v] }.reject { |_,a| a.empty?}.to_h
end
I expected diff(a,b) == diff(b,a) == {}. Let's see:
diff(a,b)
#=> {}
diff(b,a)
#=> {Gem::Specification=>[:skip_during, :deprecate],
# Complex=>[:convert],
# Rational=>[:convert],
# Random=>[:state, :left],
# Time=>[:_load]}
Hmmmm.

How to create an instance with part of the methods of a class in Ruby?

I have class A with methods X and Y. Now I want to create an instance but only want it to have method X from class A.
How should I do it? Should it be by deleting method Y for the instance when creating it? Your help is appreciated!
You should not do this. You should instead share the problem you're solving and find a better pattern for solving it.
An example for solving this problem a little differently:
class A
def x; end
end
module Foo
def y; end
end
instance_with_y = A.new
instance_with_y.send :include, Foo
instance_with_y.respond_to? :y #=> true
Here is one way to solve the problem :
class X
def a
11
end
def b
12
end
end
ob1 = X.new
ob1.b # => 12
ob1.singleton_class.class_eval { undef b }
ob1.b
# undefined method `b' for #<X:0x9966e60> (NoMethodError)
or, you could write as ( above and below both are same ) :
class << ob1
undef b
end
ob1.b
# undefined method `b' for #<X:0x93a3b54> (NoMethodError)
It's possible to do what you want with ruby, as ruby can be very malleable like that, but there are much better ways. What you want to achieve seems like a really bad idea.
The problem you just described a problem inheritance is designed to solve. So really, you have two classes. Class A and also class B which inherits from class A.
class A
def foo
'foo'
end
end
# B inherits all functionality from A, plus adds it's own
class B < A
def bar
'bar'
end
end
# an instance of A only has the method "foo"
a = A.new
a.foo #=> 'foo'
a.bar #=> NoMethodError undefined method `bar' for #<A:0x007fdf549dee88>
# an instance of B has the methods "foo" and "bar"
b = B.new
b.foo #=> 'foo'
b.bar #=> 'bar'

In Ruby, how do I implement a class whose new method creates subclasses of itself?

In Ruby, the Struct class's new method creates a subclass of Struct that behaves differently based on the parameters passed to it. How do I do something similar with my own class in Ruby? (I would have just copied Struct's source code, except it's written in C.)
irb(main):001:0> Foo = Struct.new(:foo, :bar)
=> Foo
irb(main):002:0> x = Foo.new
=> #<struct Foo foo=nil, bar=nil>
irb(main):003:0> Foo.superclass
=> Struct
class A
def self.new; Class.new(self) end
end
A.new # => #<Class:0x007f009b8e4200>
Edit This might better fit the OP's intention.
class A
singleton_class.class_eval{alias :old_new :new}
def self.new
Class.new(self){singleton_class.class_eval{alias :new :old_new}}
end
end

Get all instance variables declared in class

Please help me get all instance variables declared in a class the same way instance_methods shows me all methods available in a class.
class A
attr_accessor :ab, :ac
end
puts A.instance_methods #gives ab and ac
puts A.something #gives me #ab #ac...
You can use instance_variables:
A.instance_variables
but that’s probably not what you want, since that gets the instance variables in the class A, not an instance of that class. So you probably want:
a = A.new
a.instance_variables
But note that just calling attr_accessor doesn’t define any instance variables (it just defines methods), so there won’t be any in the instance until you set them explicitly.
a = A.new
a.instance_variables #=> []
a.ab = 'foo'
a.instance_variables #=> [:#ab]
If you want to get all instances variables values you can try something like this :
class A
attr_accessor :foo, :bar
def context
self.instance_variables.map do |attribute|
{ attribute => self.instance_variable_get(attribute) }
end
end
end
a = A.new
a.foo = "foo"
a.bar = 42
a.context #=> [{ :#foo => "foo" }, { :#bar => 42 }]
It's not foolproof - additional methods could be defined on the class that match the pattern - but one way I found that has suited my needs is
A.instance_methods.grep(/[a-z_]+=/).map{ |m| m.to_s.gsub(/^(.+)=$/, '#\1') }
If you want to get a hash of all instance variables, in the manner of attributes, following on from Aschen's answer you can do
class A
attr_accessor :foo, :bar
def attributes
self.instance_variables.map do |attribute|
key = attribute.to_s.gsub('#','')
[key, self.instance_variable_get(attribute)]
end.to_h
end
end
a = A.new
a.foo = "foo"
a.bar = 42
a.context #=> {'foo' => 'foo', 'bar' => 42}
Building on the answer from #Obromios , I added .to_h and .to_s to a class to allow for pleasant, flexible dumping of attributes suitable for display to an end user.
This particular class (not an ActiveRecord model) will have a variety of attributes set in different situations. Only those attribs that have values will appear when printing myvar.to_s, which was my desire.
class LocalError
attr_accessor :product_code, :event_description, :error_code, :error_column, :error_row
def to_h
instance_variables.map do |attribute|
key = attribute.to_s.gsub('#', '')
[key, self.instance_variable_get(attribute)]
end.to_h
end
def to_s
to_h.to_s
end
end
This allows me to put this simple code in a mailer template:
Data error: <%= #data_error %>
And it produces (for example):
Data error: {"event_description"=>"invalid date", "error_row"=>13}
This is nice, as the mailer doesn't have to be updated as the LocalError attributes change in the future.

Method declared out side the class is included automatically?

Can someone help me make sense of the following? I have the following code in test.rb:
class Dog
end
// bark is declared outside of Dog class
def bark
puts 'Woof!'
end
then in irb:
>> source 'test.rb'
>> a = Dog.new
=> #<Dog:0x117f614>
>> a.bark
Woof!
=> nil
Why does method bark exist in Dog instance even though it is declared outside of the class? Because it's in the same file? Thanks!
When you create a method in the "global" scope (outside of any class), that method is made a private method of Object:
#!/usr/bin/ruby1.8
class Dog
end
p Object.respond_to?(:bark, true) # => false
def bark
puts "Woof!"
end
p Object.respond_to?(:bark, true) # => true
Object is in the ancestry chain of all objects, including Dog:
dog = Dog.new
p dog.class.name # => "Dog"
p dog.class.superclass.name # => "Object"
Therefore dogs (and indeed all objects) now know how to bark. However, the method being private, you'll have to use instance_eval to call it with an explicit receiver:
dog.instance_eval { bark } # => "Woof!"
Or you can call it with an implicit receiver with no gymnastics needed:
bar # => "Woof!"
Your exact example doesn't work in Ruby 1.9. (Apart from the bad comment syntax.)
However, declaring a method in the top level scope will make it a private method on Object, apparently:
>> Object.private_methods.include? :bark
=> true
Perhaps in your Ruby (1.8?), this is a public method?

Resources