Ruby variables' visibility - ruby

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'

Related

Why does using class_eval cause toplevel class variable to be set?

I wrote some code to learn about the class_eval method. My understanding is that Foo.class_eval will set self to "Foo" and evaluate the block below in that context, just as saying "class Foo" will do. However, the code below shows that setting a class variable using class_eval will assign the class variable at the toplevel: to "Object". Why does this happen?
class Foo
end
class Foo
puts self
# => Foo
##class_var = "hello"
end
puts Object.class_variables
# => []
Foo.class_eval do
puts self
# => Foo
##class_var = "hi"
# => warning: class variable access from toplevel
end
puts Object.class_variables
# => ##class_var
As pointed out in comments a single # would do what you want, now if you were running the code from inside another class ## would work but it would set a class variable for both Foo, and whatever class you running from:
class Foo
puts self
# => Foo
##class_var = "hello"
end
class Bar
def self.main
puts Object.class_variables
# => []
Foo.class_eval do
puts self
# => Foo
##class_var = "hi"
end
end
end
Bar.main
p [Bar.class_variables]
#[[:##class_var]]
p [Foo.class_variables]
#[[:##class_var]]
That has to do with proc or block scope, I was expecting it to set only ##class_var only on Bar class but it actually sets on both, weird.

Code not evaluated in default argument value specification

(At least some kind of) Ruby code is accepted and evaluated within the default value specification of a method. In below, "foo" * 3 is evaluated:
def bar baz = "foo" * 3; baz end
bar # => "foofoofoo"
def bar baz: "foo" * 3; baz end
bar # => "foofoofoo"
However, when I try to evaluate a local variable/method under a certain scope in the default value description as follows, the local variable/method is evaluated under lexical scope:
MAIN = TOPLEVEL_BINDING.eval('self')
foo = 3
def bar baz = MAIN.instance_eval{foo}; end
bar # => undefined local variable or method `foo' for main:Object
def bar baz: MAIN.instance_eval{foo}; end
bar # => undefined local variable or method `foo' for main:Object
Why is foo above not evaluated within MAIN scope and is evaluated in lexical scope?
This seems there is some limitation to what Ruby expressions can be evaluated in the default value description. What exactly can be put there?
foo is local variable for main. Your attempt to access local variable from outside might be shorten to:
▶ MAIN = TOPLEVEL_BINDING.eval('self')
▶ foo = 3
▶ MAIN.foo
#⇒ NoMethodError: undefined method `foo' for main:Object
The analogue of this code in less tangled manner is:
class A
foo = 5
end
class B
def a_foo
A.new.foo
end
end
▶ B.new.a_foo
#⇒ NoMethodError: undefined method `foo' for #<A:0x00000002293bd0>
If you want to provide access from the universe to your local variable you are to implement getter:
def MAIN.foo ; 5 ; end
def bar baz = MAIN.instance_eval{foo}; baz; end
▶ bar
#⇒ 5
Hope it helps.

Inheritence & Scope Resolution in Ruby

I'm trying to understand why I need the scope resolution operator to access constants in an inherited module but not methods. Assuming:
module Foo
SOMETHING = {:one => 1, :two => 2}
def showit
p SOMETHING
end
end
and
class Bar
include Foo
def initialize
# why doesn't method name need scope resolution but const does?
showit
p Foo::SOMETHING
end
Also related, can you access instance or local variables declared in Foo from Bar? I tried something like this:
module Foo
#myar = "some string"
myvar2 = "some other string"
def showit
p #myvar
p myvar2
end
end
and calling showit from within the Bar class...printing #myvar resulted in nil and myvar2 was undefined. I would expect both strings to print...what's going on?
You actually don't have to scope the constant.
This should run just fine.
class Bar
include Foo
def initialize
showit
p SOMETHING # instead of Foo::SOMETHING
end
end
Bar.new

Confused about instance variable scope, top-level vs. class

I've been playing around with Ruby mostly in the top level and I typically write code like:
#x = 'foo'
def show_var
puts #x
end
show_var # => foo
I thought that instance variables were available to both the Class and the Object, based off how this example works.
Today I ran into this, and it looks like my understanding of instance variables is incorrect:
class Test
#x = "foo" #you would need to define this inside 'initialize' for this to be available to show_var
def show_var
puts #x
end
end
Test.new.show_var # => nil
It looks like the second example is how instance variables work. If you define an instance variable inside the Class, then it only exists inside that scope, and does not exist for instance methods.
Then my question is... why does the first case output 'foo' when the variable #x shouldn't exist inside the scope of an instance method? Also, what is the proper way for defining variables in the top-level Class that you want to use for your top-level methods?
The method in the first example is at the top level. Which means it belongs to a special top level object main. You can't really create more copies of main, so self inside and outside of that method is the same. Check this out:
self # => main
def show_var
self # => main
end
class Test
self # => Test
def show_var
self # => #<Test:0x007fdf9c892d70>
end
end
Look below:
#x = 'foo'
def show_var
puts #x,self
p defined? #x
end
show_var
# >> foo
# >> main
# >> "instance-variable"
In the first case you defined #x in the main scope. And then when call the method show_var,from the main,thus you got the output of #x,which is not the case of the other.
class Test
#x = "foo"
def show_var
p #x,self
p defined? #x
end
end
Test.new.show_var
# >> nil
# >> #<Test:0x9b6fcd4>
# >> nil

How to reference global variables and class variables?

I'm new to programming. Right now I'm studying Ruby. To my understanding, global variables are defined in the global namespace (so outside of any classes or functions). I'm reading something and it says global variables have a $ sign before them. What does that mean? Does it mean when I define a function or class and want to reference my global variable (let's say it is edmund = 123) I would have to reference it like this: $edmund?
so:
edmund = 123
def my_function()
456 + $edmund
end
Also are class variables (the ones that begin with ##) like instance variables (#) where you can access them by calling them through Class.classvariable? What is their purpose?
Global scope is scope that covers the entire program. Global scope is enjoyed by global variables, which are recognizable by their initial dollar-sign ($) character. They’re available everywhere and creating your own global variables can be tempting, especially for beginning programmers. But they’re not always a good idea.
$gvar = "I'm a global!"
class C
def examine_global
puts $gvar
end
end
c = C.new
c.examine_global # I'm a global!
Class variables begin with two at signs: ##var, for example. Despite their name, class variables aren’t class scoped. Rather, they’re class-hierarchy scoped. At its simplest, the idea behind a class variable is that it provides a storage mechanism that’s shared between a class and instances of that class, and that’s not visible to any other objects.
class Parent
##value = 100
end
class Child < Parent
##value = 200
end
class Parent
puts ##value
end
What gets printed is 200. The Child class is a subclass of Parent, and that means Parent and Child share the same class variables—not different class variables with the same names, but the same actual variables. When you assign to ##value in Child, you’re setting the one and only ##value variable that’s shared throughout the hierarchy—
that is, by Parent and Child and any other descendant classes of either of them.
And to give credit where its due - This explanation comes from "The Well Grounded Rubyist" by David A Black, one of the best resources to learn about Ruby.
Excellent question. Unfortunately, you just jumped down a rabbit hole, but it's one that you have to fall through eventually in ruby to start understanding the real intricacies.
For your first question, regarding the $-prefixed global variables. They are truly global:
def mk_foo() $foo ||= "foo"; end
$foo # => nil
mk_foo # => "foo"
$foo # => "foo"
mk_foo.object_id # => 70299647799620
$foo.object_id # => 70299647799620
As you can see, when $foo is defined within the mk_foo method, it is defined in the global space, and you can access it anywhere:
class CanSeeFoo
def see_foo() $foo; end
end
CanSeeFoo.new.can_see_foo
# => "foo"
CanSeeFoo.new.can_see_foo.object_id
# => 70299647799620
As for the class variable question, this is where the rabbit-hole begins. First, you are correct that ##-prefixed variables are referred to as "class variables" and #-prefixed variables are referred to as "instance variables".
Class variables are static across all subclasses (at all sub-levels of the inheritance tree) of the defining class. The implication here is that if any subclass changes the class variable, it will change in all related subclasses and up to the defining class.
class A; end
class B < A; ##foo = "foo"; end
B.class_variable_get(:##foo) # => "foo"
A.class_variable_get(:##foo)
# => raises NameError "uninitialized class variable ##foo in A"
class C < B; end
C.class_variable_get(:##foo) # => "foo"
class D < C
def self.change_foo(); ##foo = "bar"; end
def change_foo(); ##foo = "baz"; end
end
D.class_variable_get(:##foo) # => "foo"
class E < D; end
E.class_variable_get(:##foo) # => "foo"
D.change_foo # => "bar"
D.class_variable_get(:##foo) # => "bar"
E.class_variable_get(:##foo) # => "bar"
C.class_variable_get(:##foo) # => "bar"
B.class_variable_get(:##foo) # => "bar"
D.new.change_foo # => "baz"
D.class_variable_get(:##foo) # => "baz"
E.class_variable_get(:##foo) # => "baz"
C.class_variable_get(:##foo) # => "baz"
B.class_variable_get(:##foo) # => "baz"
A.class_variable_get(:##foo)
# => raises NameError "uninitialized class variable ##foo in A"
As for accessing class and instance variables, neither is accessible without the use of #instance_variable_get or ::class_variable_get until an accessor is defined. At present, ruby only has methods for defining accessors on instance variables, but it is simple enough to define the appropriate methods for the class variables:
class A
##foo = "foo"
# the second argument `true` adds the writer method `#bar=`
attr :bar, true
def self.foo(); ##foo; end
def self.foo=(v); ##foo = v; end
def initialize()
#bar = "bar"
end
end
class B < A; end
A.foo # => "foo"
B.foo = "foobar"
A.foo # => "foobar"
B.foo # => "foobar"
a = A.new
a.bar # => "bar"
a.bar = "baz"
a.bar # => "baz"
a.foo
# => raises NoMethodError: undefined method `foo' for #<A:0x ...
You can see the attribute accessor methods here in the ruby core docs: http://www.ruby-doc.org/core-1.9.3/Module.html#method-i-attr. Also, ActiveSupport (http://rubygems.org/gems/activesupport) has "cattr" methods for defining class variable accessors http://api.rubyonrails.org/v3.2.5/classes/Class.html#method-i-cattr_accessor.
That's the simple stuff. The next step is understanding the "singleton class" also known as the "eigenclass" or "metaclass" (Wikipedia: Metaclass) (remember, everything in ruby is an Object, including the Class and Module constructs). Here I will point you to an excellent post by Yehuda Katz: Metaprogramming in Ruby: It’s All About the Self, and another Stack Overflow question: class << self idiom in Ruby.
As a preview: The singleton class (not to be confused with the singleton design pattern) allows you to access methods and instance data for a specific class or module. For some related documentation, see the core docs: http://www.ruby-doc.org/core-1.9.3/Object.html#method-i-singleton_class
class A; end
class B < A;
class << self
def foo() #foo end
def foo=(v) #foo = v; end
end
end
B.foo = "foo"
class C < B; end
A.foo
# => raises NoMethodError: undefined method `foo' for A:Class
B.foo # => "foo"
C.foo # => nil
B.foo = "baz"
B.foo # => "baz"
C.foo # => nil
C.foo = "foo"
C.foo # => "foo"
B.foo # => "baz"
Lastly, remember to make use of the Ruby-Core docs. Most useful for understanding the above are:
http://www.ruby-doc.org/core-1.9.3/Object.html
http://www.ruby-doc.org/core-1.9.3/Module.html
http://www.ruby-doc.org/core-1.9.3/Class.html
The dollar sign is part of the variable name, so it has to be declared like this:
$edmund = 123
This is the same things for the instance and class variables: their names begin with # or ##.

Resources