Confusion with class and instance variable creations - ruby

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

Related

Local variables for a class in ruby [duplicate]

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

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'

confusion with `Class instance methods` call

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.

Ruby variables' visibility

If I declare #var in Ruby, every object of that class would have its own #var.
But what if I miss #? I mean, I declare a variable called var2 without #. Do they share the variable or it is temporarily created?
When variable is declared without scope prefix (# - instance, ## - class or $ - global) then is declared for current scope, i.e:
class Foo
def boo
#boo ||= 'some value'
var ||= 40
puts "boo: ##boo var: #{var}"
end
def foo
var ||= 50
puts "boo: ##boo var: #{var}"
end
end
c = Foo.new
c.boo # => boo: some value var: 40
c.foo # => boo: some value var: 50
def foo
$var ||= 30
puts "$var: #$var"
end
foo # => $var: 30
puts "$var: #$var" # => $var: 30
%w[some words].each do |word|
lol = word # blocks introduce new scope
end
puts lol # => NameError: undefined local variable or method `lol'
for word in %w[some words]
lol = word # but for loop not
end
puts lol # => words
Without an # it's discarded when the method it's in is done running.
class Foo
def initialize
#bing = 123
zing = 456
end
def get_bing
#bing
end
def get_zing
zing
end
end
foo = Foo.new
foo.get_bing #=> 123
foo.get_zing #=> NameError: undefined local variable or method `zing' for #<Foo:0x10b535258 #bing=123>
This shows that an instance variable #bing is saved with that instance. It's value is accessible within any method on that instance.
But a local variable zing is not persisted (in most cases), and as soon as the method is done running any local variables are discarded, and no longer accessible. When get_zing is run, it looks for a local variable or method named zing and doesn't find one, because the zing from initialize is long gone.
It will become a local variable that belongs to the local lexical scope.
Ex.
class Foo
def Bar
#fooz = 1
end
def Barz
fooz = 2
end
def test
puts #fooz
end
end
f = Foo.new
f.Bar
f.test
f.Barz
f.test
Output:
1
1 #not 2
If you use a variable called var2 it is local and is only in scope in the construct where it is declared. That is:
If you declared it within a method, it will be local to that method.
If you try to declare within a class definition but outside any method, it will raise a NameError. For example:
class Foo
bar = 2
end
Foo.new
NameError: undefined local variable or method 'bar'

What happend here? (nil in Ruby)

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.

Resources