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.
Related
This question already has answers here:
In ruby how to use class level local variable? (a ruby newbie's question)
(4 answers)
Closed 7 years ago.
I have noticed the following code is syntactically correct:
class Foo
bar = 3
end
Now, I know that instance variables are accessed by #, and class variables by ##, but I couldn't figure out where bar is stored in this case or how to access it.
How can I find bar's scope?
The body of a class in Ruby is just executable Ruby code. These are indeed local variables (no quotation needed) and follow the "regular" rules being local variables. You can access them in the body of the class. If you literally want the scope where bar is defined, you can use Kernel.binding:
class Foo
bar = 42
##scope = binding
def self.scope
##scope
end
end
Foo.scope.local_variables # => [:bar]
Foo.scope.local_variable_get(:bar) # => 42
A thing to note - using def changes the scope, therefore, they won't be visible inside methods defined using def.
It is accessible from the same class body.
class Foo
bar = 3
bar # => 3
end
It is lexically scoped, so it is accessible from within a block:
class Foo
bar = 3
pr = ->{p bar}
pr.call # => 3
end
but it is not accessible even in the same class once the class body has been closed:
class Foo
bar = 3
end
class Foo
bar # => error
end
nor can it be accessed from within a method definition:
class Foo
bar = 3
def baz; bar end
new.baz # => error
end
The bar variable will be accessible until you close the definition of the class. It will not be accessible inside the methods you define.
You can try to run the code in irb:
$ irb
irb(main):001:0> class Test
irb(main):002:1> bar = 1
irb(main):003:1> puts bar
irb(main):004:1> end
1
=> nil
irb(main):005:0> puts bar
NameError: undefined local variable or method `bar' for main:Object
from (irb):5
from /usr/bin/irb:11:in `<main>'
irb(main):006:0> class Test
irb(main):007:1> puts bar
irb(main):008:1> end
NameError: undefined local variable or method `bar' for Test:Class
from (irb):7:in `<class:Test>'
from (irb):6
from /usr/bin/irb:11:in `<main>'
irb(main):009:0>
Check for the availability in the instance methods:
irb(main):018:0> class Test
irb(main):019:1> bar = 1
irb(main):020:1> def test
irb(main):021:2> puts bar
irb(main):022:2> end
irb(main):023:1> end
=> :test
irb(main):024:0> a = Test.new
=> #<Test:0x00000000f447a0>
irb(main):025:0> a.test
NameError: undefined local variable or method `bar' for #<Test:0x00000000f447a0>
from (irb):21:in `test'
from (irb):25
from /usr/bin/irb:11:in `<main>'
Check for availability in the class methods:
irb(main):026:0> class Test
irb(main):027:1> bar = 1
irb(main):028:1> def self.test
irb(main):029:2> puts bar
irb(main):030:2> end
irb(main):031:1> end
=> :test
irb(main):032:0> Test.test
NameError: undefined local variable or method `bar' for Test:Class
from (irb):29:in `test'
from (irb):32
from /usr/bin/irb:11:in `<main>'
You could make it a constant and use it instance and class methods:
class Foo
Bar = 3
def local_bar(param = Bar)
param
end
end
p Foo.new.local_bar
#=> 3
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
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
Here is an example:
class MyClass
end
obj = MyClass.new
obj.instance_eval do
def hello
"hello"
end
end
obj.hello
# => "hello"
obj.methods.grep "hello"
# => ["hello"]
MyClass.instance_methods.grep "hello"
# => []
MyClass's instance methods don't contain 'hello' method, so My question is where Ruby stores the method defined in instance_eval()?
Look at this:
obj = MyClass.new
def obj.hello
"hello"
end
obj.hello #=> "hello"
obj.singleton_methods #=> [:hello]
obj.methods.grep :hello #=> [:hello]
obj.instance_eval do
def hello2 ; end
end #
obj.singleton_methods #=> [:hello, :hello2]
As you can see instead of using instance_eval you can also define a method directly on an object. In both cases they end up in the object's singleton class (eigenclass), which can be accessed via obj.singleton_class in Ruby 1.9 and the class << self ; self; end idiom in Ruby 1.8.
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.