p parent.class #=> NilClass # ok.
p !!parent # => false # as expected.
p parent.object_id # => 17006820 # should be 4
p parent && parent.foo # => NoMethodError foo # should be nil-guarded
Where does this object come from?
Possibly something like this:
class BlankSlate
instance_methods.each do |m|
# Undefine all but a few methods. Various implementations leave different
# methods behind.
undef_method(m) unless m.to_s == "object_id"
end
end
class Foo < BlankSlate
def method_missing(*args)
delegate.send(*args)
end
def delegate
# This probably contains an error and returns nil accidentally.
nil
end
end
parent = Foo.new
p parent.class
#=> NilClass
p !!parent
#=> false
p parent.object_id
#=> 2157246780
p parent && parent.foo
#=> NoMethodError: undefined method `foo' for nil:NilClass
Creating BlankSlate or BasicObject is a common pattern (before it was added to core Ruby as of version 1.9). It serves to create objects that will do something special with any method they are sent, or heavily delegate their behaviour to a different class. The downside is that it may introduce strange behaviour like this.
Related
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'
I was trying if I can call Class instance methods by the instances of class or not. Accordingly tried the below:
class Foo
def show; p "hi" ; end
def self.display ; p "hello" ; end
end
#=> nil
Foo.display
#"hello"
#=> "hello"
Foo.new.show
#"hi"
#=> "hi"
Foo.show
#NoMethodError: undefined method `show' for Foo:Class
#from (irb):7
#from C:/Ruby200/bin/irb:12:in `<main>'
But in the below call I expect the same error as NoMethodError: undefined method `display'. But why is it not the case?
Foo.new.display
#<Foo:0x538020> #=> nil
foo = Foo.new
#=> #<Foo:0x22bc438>
foo.display
#<Foo:0x22bc438> #=> nil
There is an existing method display on all objects.
class Bar
end
Bar.new.methods.grep(/disp/) # => [:display]
Bar.methods.grep(/disp/) # => [:display]
Your code just overwrites it for instances of Foo. Choose another name (display1, for example) and you'll see expected error.
I was playing with the local,class variable and instance variable creation inside the class block as below. But I found something which I failed to explain myself. My confusion has been posted between the two codes below.
class Foo
def self.show
##X = 10 if true
p "hi",##X.object_id,x.object_id
end
end
#=> nil
Foo.show
#NameError: undefined local variable or method `x' for Foo:Class
# from (irb):4:in `show'
# from (irb):7
# from C:/Ruby193/bin/irb:12:in `<main>'
The above erros is expected. But in the below code I have assigned the class variable ##X to 10. But in the p statement I used instance variable #X.Why did the error not throw up like the above code ?
class Foo
def self.show
##X = 10 if true
p "hi",#X.object_id
end
end
#=> nil
Foo.show
"hi"
4
#=> ["hi", 4]
Because of everything is object and no explicit variable declaration is required in Ruby, you code
p #X.object_id
silently introduces an instance variable #X (#X.nil? == true). You can see this magic in irb:
~ irb
> p #x.object_id
# 8
# ⇒ 8
What is the point of defining respond_to_missing? as opposed to defining respond_to?? What goes wrong if you redefine respond_to? for some class?
Without respond_to_missing? defined, trying to get the method via method will fail:
class Foo
def method_missing name, *args
p args
end
def respond_to? name, include_private = false
true
end
end
f = Foo.new
f.bar #=> []
f.respond_to? :bar #=> true
f.method :bar # NameError: undefined method `bar' for class `Foo'
class Foo
def respond_to? *args; super; end # “Reverting” previous redefinition
def respond_to_missing? *args
true
end
end
f.method :bar #=> #<Method: Foo#bar>
Marc-André (a Ruby core committer) has a good blog post on respond_to_missing?.
It's a good practice to create respond_to_missing? if you are overriding method_missing. That way, the class will tell you the method you are calling exists, even though it's not explicitly declared.
respond_to? should probably not be overriden :)
I am trying to find a way that I can override a method, do something, and then revert without leaving any artifacts around.
I have implemented this using mocha but obviously this is not going to fly in a production app. Notice the new method has parameters and the old one does not.
Example as follows
require 'rubygems'
require 'mocha'
class Example
def to_something
self.stubs(:attribs => other(1))
r = attribs_caller
self.unstub(:attribs)
r
end
def other(int)
{"other" => int }
end
def attribs_caller
attribs
end
def attribs
{"this" => 1 }
end
end
a1 = Example.new
puts a1.attribs_caller #=> this1
puts a1.to_something #=> other1
puts a1.attribs_caller #=> this1
class String
alias orig_reverse reverse
def reverse(n)
'fooled you. '*n
end
end
puts "ab".reverse(2)
#=> fooled you fooled you
# clean up:
class String
alias reverse orig_reverse
remove_method(:orig_reverse)
end
puts "ab".reverse #=> ba
Another way to do that, without creating an extra method, is this:
class Foo
def bar
:old_method
end
end
Foo.new.bar # => :old_method
$old_method = Foo.new.method(:bar)
class Foo
def bar
:new_method
end
end
Foo.new.bar # => :new_method
class Foo
define_method($old_method.name, &$old_method)
end
Foo.new.bar # => :old_method
I think that this is better than using an alias method. In Ruby methods are, also, objects. I just take the reference of the object before destructing the association of the object (the method) with the class. After I add the same method. It also works if you use the undef keyword to remove the method from the class. The bad point is that you have to have an object of the class to take the reference of the method.