Ruby - accessing instance methods / variables from anonymous class - ruby

I have following Ruby code:
class Baz
def foo()
qux = Class.new() {
def call()
bar()
end
}.new()
qux.call()
end
def bar()
puts "bar"
end
end
b = Baz.new()
b.foo()
How can I access method bar from the anonymous class, that means from qux.call? Is it possible?
I'm keeping getting this message:
classes-test.rb:5:in `call': undefined method `bar' for #<#<Class:0x00000002d9c248>:0x00000002d9c1a8> (NoMethodError)
I'm new to Ruby, so any advice or even deeper explanation of the problem will be appreciated. Thanks in advance.

Since .bar is an instance method of Baz, you need to have an instance of Baz available in your context to call .bar. You can do that by passing the instance object to the class on initialization, so you can call its .bar method on it.
This works:
class Baz
def foo
qux = Class.new do
def initialize(a)
#a = a
end
def call
#a.bar
end
end.new(self)
qux.call
end
def bar
puts "bar"
end
end
b = Baz.new
b.foo
=> 'bar'
If you need to pass a class to Class.new() as you mention in the comments, you can override the initializer method like this (please note that you may have to consider the arguments that your Closure class initialize needs for super:
qux = Class.new(Fiddle::Closure) do
def initialize(baz)
#baz = baz
super
end
def call
#baz.bar
end
end.new(self)
On a side note, you don't need all those (), it's Ruby style to omit them if not needed.

Related

When to use self for Ruby Class

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

Variables in Ruby method names created by attr_accessor

I have an object that is using attr_accessor. I want to be able to call a method on that object with a variable with #send. My problem is that the = method doesn't seem to work.
class Foo
attr_accessor :bar
end
class Test_class
def test_method(x)
f = Foo.new
f.send(x)
f.send(x) = "test" #this doesnt work
f.send("#{x} =") "test" #this also doesn't work
# How can I set bar?
end
end
t = Test_class.new
t.test_method("bar")
You want f.send "#{x}=", "test". In Ruby, method names may include punctuation, such as = or !. The methods created by attr_accessor :bar are simply named bar and bar=. In fact, attr_accessor :bar is just shorthand for:
def bar
#bar
end
def bar=(value)
#bar = value
end
When you're calling foo.bar = "baz", you're actually calling the #bar= method with foo as the receiver and "bar" as the first parameter to the function - that is, foo.bar=("baz"). Ruby just provides syntactic sugar for methods ending in = so that you can write the more natural-looking form.

accessing instance methods from class method proc

I am trying to call a proc on a class but access instance methods from it with an inherited class. I think some mock code will make more sense :)
class Bar
def self.foo &block
#foo ||= block
end
def foo; self.class.foo.call(); end
def bar; 'bar'; end
end
class Foo < Bar
foo do
bar
end
end
Foo.new.foo
# NameError: undefined local variable or method `bar' for Foo:Class
I want to be able to access the bar instance method on the Bar class. The reason for calling the foo class method with a block from the inherited class is part of the DSL requirement, but any suggestions for a better design would be appreciated.
Blocks are lexically scoped, including the value of self. At the point where the block is defined, self is Bar and Bar doesn't respond to bar. You need to evaluate the block in the context of the object (in this case an instance of Bar and not Bar itself) whose methods you want to call. That's what instance_eval does:
class Bar
def self.foo(&block) #foo ||= block end
def foo; instance_eval(&self.class.foo) end
def bar; 'bar' end
end
class Foo < Bar; foo do bar end end
Foo.new.foo
# => 'bar'
Note that all the usual disclaimers about instance_eval apply: since you change the value of self methods and instance variables that the block author may expect to be available won't be.

Strange SimpleDelegator behavior

There is my code using SimpleDelegator
require 'delegate'
class Foo < SimpleDelegator
def meth
p 'new_meth'
end
end
class Bar
def meth
p 'old_meth'
end
def bar_meth
meth
end
end
bar = Bar.new
foo = Foo.new(bar)
foo.meth #=> "new_meth"
foo.bar_meth #=> "old_meth"
Why the last line gives "old_meth"???!!! Thanks!
Delegator saying:
This library provides three different ways to delegate method calls to an object. The easiest to use is SimpleDelegator. Pass an object to the constructor and all methods supported by the object will be delegated. This object can be changed later.
Okay,so now look at the output,which is to the right of the symbol # =>.
require 'delegate'
class Foo < SimpleDelegator
def meth
p 'new_meth'
end
end
class Bar
def meth
p 'old_meth'
end
def bar_meth
self.method(:meth)
end
end
bar = Bar.new # => #<Bar:0x8b31728>
foo = Foo.new(bar)
foo.__getobj__ # => #<Bar:0x8b31728>
foo.bar_meth # => #<Method: Bar#meth>
foo.method(:meth) # => #<Method: Foo#meth>
So when I used the line foo.method(:meth), then output(#<Method: Foo#meth>) confirms that whenever you will call foo.meth,then meth method of the Foo class will be called.But the line foo.bar_meth outputs(#<Method: Bar#meth>) simply saying that inside the method bar_meth,if you call meth method then, Bar#meth will be invoked.
SimpleDelegator saying that:
A concrete implementation of Delegator, this class provides the means to delegate all supported method calls to the object passed into the constructor and even to change the object being delegated to at a later time with #setobj.
Yes,in your case foo object has been set to bar object,by using #__setobj__. The output of the line foo.__getobj__ is showing that.

Remove/undef a class method

You can dynamically define a class method for a class like so:
class Foo
end
bar = %q{def bar() "bar!" end}
Foo.instance_eval(bar)
But how do you do the opposite: remove/undefine a class method? I suspect Module's remove_method and undef_method methods might be able to be used for this purpose, but all of the examples I've seen after Googling for hours have been for removing/undefining instance methods, not class methods. Or perhaps there's a syntax you can pass to instance_eval to do this as well.
Thanks in advance.
class Foo
def self.bar
puts "bar"
end
end
Foo.bar # => bar
class <<Foo
undef_method :bar
end
# or
class Foo
singleton_class.undef_method :bar
end
Foo.bar # => undefined method `bar' for Foo:Class (NoMethodError)
When you define a class method like Foo.bar, Ruby puts it Foo's singleton class. Ruby can't put it in Foo, because then it would be an instance method. Ruby creates Foo's singleton class, sets the superclass of the singleton class to Foo's superclass, and then sets Foo's superclass to the singleton class:
Foo -------------> Foo(singleton class) -------------> Object
super def bar super
There are a few ways to access the singleton class:
class <<Foo,
Foo.singleton_class,
class Foo; class << self which is commonly use to define class methods.
Note that we used undef_method, we could have used remove_method. The former prevents any call to the method, and the latter only removes the current method, having a fallback to the super method if existing. See Module#undef_method for more information.
This also works for me (not sure if there are differences between undef and remove_method):
class Foo
end
Foo.instance_eval do
def color
"green"
end
end
Foo.color # => "green"
Foo.instance_eval { undef :color }
Foo.color # => NoMethodError: undefined method `color' for Foo:Class
You can remove a method in two easy ways. The drastic
Module#undef_method( )
removes all methods, including the inherited ones. The kinder
Module#remove_method( )
removes the method from the receiver, but it
leaves inherited methods alone.
See below 2 simple example -
Example 1 using undef_method
class A
def x
puts "x from A class"
end
end
class B < A
def x
puts "x from B Class"
end
undef_method :x
end
obj = B.new
obj.x
result -
main.rb:15:in
': undefined methodx' for # (NoMethodError)
Example 2 using remove_method
class A
def x
puts "x from A class"
end
end
class B < A
def x
puts "x from B Class"
end
remove_method :x
end
obj = B.new
obj.x
Result -
$ruby main.rb
x from A class
I guess I can't comment on Adrian's answer because I don't have enough cred, but his answer helped me.
What I found: undef seems to completely remove the method from existence, while remove_method removes it from that class, but it will still be defined on superclasses or other modules that have been extened on this class, etc.
If you would like to remove method with name what calculate dinamically, you should use eigenclasses like:
class Foo
def self.bar
puts "bar"
end
end
name_of_method_to_remove = :bar
eigenclass = class << Foo; self; end
eigenclass.class_eval do
remove_method name_of_method_to_remove
end
this way is better than others answers, becouse here i used class_eval with block. As you now block see current namespace, so you could use your variables to remove methods dinamically
Object.send(:remove_const, :Foo)

Resources