I need to disable a class method for some time, then enable it again. How could I do that? I know that I can remove a method:
class Foo
def Foo.bar
puts "bar"
end
end
Foo.bar # => bar
class <<Foo
remove_method :bar
end
Foo.bar # => undefined method `bar' for Foo:Class (NoMethodError)
Now I need Foo.bar again. How could I do that? I tried to save the method in a proc
m = Proc.new { Foo.bar }
and then define it again:
class Foo
define_method(:bar, &m)
end
but I get
NameError: undefined local variable or method 'm' for...
So I flattened the scope
P = Class.new(Foo) do
define_method(:bar, &m)
end
but I get undefined method if I run it
P.bar
Foo.bar
Is it possible to save a method, undefine it, and then define it back?
Instead of keeping a method body in a proc, you should keep methods as methods. Constantly keep it defined under some different name, and switch Foo.bar between alias of it or not.
class Foo
def Foo.temporal_bar
puts "bar"
end
end
Foo.singleton_class.class_eval{alias bar temporal_bar}
Foo.bar # => bar
Foo.singleton_class.class_eval{remove_method bar}
Foo.bar # => Undefined local variable or method `bar' for #<Class:Foo>
Foo.singleton_class.class_eval{alias bar temporal_bar}
Foo.bar # => bar
You can try out instance_eval:
(class << Foo; self; end).instance_eval do
define_method(:bar, &m)
end
Related
In the below code snippet, I monkey patch Foo#bar with a define_method block. After I initialize a new instance of Foo, I overwrite it with a bind call to the parent class bar method, but when I call the method the block defined by the define_method block is called. Why doesn't the bind call change the behavior of the method?
class OriginalFoo
def bar
puts 'in OriginalFoo!'
end
end
class Foo < OriginalFoo
def bar
puts 'in Foo'
end
end
class Foo < OriginalFoo
old_bar = instance_method(:bar)
define_method(:bar) do
puts 'in define_method block'
old_bar.bind(self).call
end
end
foo_instance = Foo.new # => #<Foo:0x00007fe3ff037038>
OriginalFoo.instance_method(:bar).bind(foo_instance) # => #<Method: OriginalFoo#bar>
foo_instance.bar
# >> in define_method block
# >> in Foo
You're misunderstanding how UnboundMethod#bind works. You call Module#instance_method to get an UnboundMethod (i.e. a method without a self):
OriginalFoo.instance_method(:bar)
# #<UnboundMethod: ... >
Then you call UnboundMethod#bind to attach a self to that method, that returns a Method instance:
m = OriginalFoo.instance_method(:bar).bind(foo_instance)
# => #<Method: ...>
But that won't alter the method in foo_instance, all it does is make self your foo_instance when (or if) you said m.call: um.bind(obj) doesn't do anything to obj, it just gives you um as a Method that has obj as its self.
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.
I noticed in Ruby when you undefine a method, it does NOT undefine aliases:
class Foo
end
Foo.instance_eval do
def bar
puts 'bar'
end
end
Foo.bar # => bar
Foo.instance_eval {alias :baar :bar }
Foo.instance_eval { undef :bar }
Foo.bar # => NoMethodError: undefined method `bar' for Foo:Class
Foo.baar # => bar
What is the correct approach to undefine all aliases associated with a method when that method is deleted via undef?
You can list all methods aliased to a specific method by selecting all methods equal to it (see here), so you could define a method like this:
class Class
def undef_with_aliases(method_name)
methods.select { |m| method(m) == method(method_name) }.each { |m| instance_eval "undef #{m}" }
end
end
Foo.instance_eval { undef_with_aliases :bar }
Foo.bar # => NoMethodError: undefined method `bar' for Foo:Class
Foo.baar # => NoMethodError: undefined method `baar' for Foo:Class
I have a method in a superclass from a third-party gem which I want to hide. I would prefer if it is impossible to call this method at all, so not just override it and leave the body empty.
I believe this may be what you're looking for:
undef_method :foo
This will prevent any calls to the method foo.
In contrast, this will not achieve the same effect:
remove_method :foo
That will remove the method from the child, but will still pass through up the inheritance chain.
Docs: undef_method and remove_method
Use the undef keyword.
class A
def foo
5
end
end
class B < A
undef foo
end
A.new.foo #=> 5
B.new.foo #=> NameError: undefined local variable or method `foo'
That is wrong OOP that you're trying to do there. I suggest you use composition instead of inheritance.
require 'forwardable'
class SomeBaseClass
def foo
puts 'foo'
end
def bar
puts 'bar'
end
def quux
puts 'quux'
end
end
class MyClass
def initialize
#base = SomeBaseClass.new
end
extend Forwardable
def_delegators :#base, :foo, :bar # but not quux
end
mc = MyClass.new
mc.foo
mc.bar
mc.quux
# >> foo
# >> bar
# ~> -:32:in `<main>': undefined method `quux' for #<MyClass:0x007febcc155210> (NoMethodError)
I must be making a n00b mistake here. I've written the following Ruby code:
module Foo
def bar(number)
return number.to_s()
end
end
puts Foo.bar(1)
test.rb:6:in <main>': undefined methodbar' for Foo:Module (NoMethodError)
I wish to define a single method on a module called Foo.bar. However, when I try to run the code, I get an undefined method error. What am I doing wrong?
You could do with:
module Foo
def self.bar(number)
number.to_s
end
end
puts Foo.bar(1)
Every module in Ruby can be mixed in an object. Once a class is an object, you could mix the methods in a class using the word extend:
module Foo
def bar
'bar'
end
end
class MyInstanceMethods
include Foo
end
class MyClassMethods
extend Foo
end
## Usage:
MyInstanceMethods.new.bar
=> "bar"
MyClassMethods.bar
=> "bar"
If you wish calling the bar method directly from the Foo module, you could do in the same way #xdazz wrote, but since the extend word mixes to a Class:
MyInstanceMethods.class
=> Class
MyClassMethods.class
=> Class
Module.class
=> Class # Hey, module is also a class!!!!!
The trick:
module Foo
extend self # self of Foo is the Module!
def bar
# .....
end
end
Now you can see Foo.bar returning the expected result :P