Ruby local variable scope - ruby

I'm struggling with variable scope in ruby. I was under the impression that local variables were accessible by methods below them. Looking at the following code however I'm getting an undefined variable error.
a = Array.new
def testing()
a.push("test")
end
testing
Using global variables it works just fine, how can I avoid using global variables?

There isn't much to say here except that local variables in Ruby are only accessible in the scope in which they are defined and any blocks (closures) defined in that scope that capture them. Since in Ruby, unlike some other dynamic languages, method are not closures, they do not capture local variables.
If you tried, say, this:
irb(main):001:0> a = 3
=> 3
irb(main):002:0> define_method(:testing) do
irb(main):003:1* puts a
irb(main):004:1> end
=> :testing
irb(main):005:0> testing
3
It works, since the code is in a block instead of a method.

Defining a method in the top-level can be quite confusing. Let's wrap your code in a class instead:
class Foo
a = []
def testing
a << 'test'
end
end
(I've shortened Array.new to [] and a.push(...) to a << ...)
Foo#testing can be called via:
foo = Foo.new
foo.testing
#=> undefined local variable or method `a'
Apparently, this doesn't work. The first a is a local variable in the scope of the class body, whereas the second a is a local variable within an instance method.
Moving the variable initialization out of the class body into the initialize method doesn't work either, because local variables are not shared across methods:
class Foo
def initialize
a = [] # <- one 'a'
end
def testing
a << 'test' # <- another 'a'
end
end
To get this working, you have to use an instance variable:
class Foo
def initialize
#a = []
end
def testing
#a << 'test'
end
end
foo = Foo.new
foo.testing
#=> ["test"]
foo.testing
#=> ["test", "test"]

You could use instance variables. Any variable whose name begins with # is an instance variable and is available anywhere in the class or method in which it is defined. For example, the variable #A defined within class B will be available to any methods in B.

2.3.3 :007 > def testing()
2.3.3 :008?> [].push("test")
2.3.3 :009?> end
=> :testing
2.3.3 :010 > testing
=> ["test"]
You can't let local variables accessible by methods below them , you can use block like the answer by #Linuxios, or use the way that it easy work.

Related

How to Initialize Class Arrays in Ruby

I want to create an empty array as a class instance variable in Ruby. However, my current method does not seem to work.
Here is my code:
class Something
#something = []
def dosomething
s = 5
#something << s
end
end
When I call the function, it gives me an undefined method traceback.
However, if I do something similar with class variables, i.e.:
class Something
##something = []
def dosomething
s = 5
##something << s
end
end
This works perfectly.
I know I can use the initialize method to actually create an empty list for #something, but is there another way of doing this without using the initialize method? And why does this work for class variables?
EDIT: Fixed typo
You need to use initialize as a constructor as below code and is there any reason why not to use initialize/constructor. And please fix a typo error in class definition Class Something to class Something no camel case or first letter capitalize while in class
class Something
def initialize
#something = Array.new
end
def dosomething
s = 5
#something << s
end
end
class variable ## are available to the whole class scope. so they are working in the code and if you want to use instance variable # you need to initialize it as above. The instance variable is share with instance/objects of a class
for more details visit the link Ruby initialize method
At first you have a typo. Change Classto class. Next I suggest to use the initialize method. While creating a new object this is the perfect place to initialize instance variables.
class Something
##my_class_variable = [1]
def initialize
#something = []
end
def dosomething
s = 5
#something << s
end
def self.get_my_class_variable
##my_class_variable
end
end
Your script will be read and executed from top to bottom and after this,
you can access the class Something. While the parser reads your script/class/module you can define class variables (##), execute mixins and extend the class with other modules. This is why you can define a class variable, but you can not define an instance variable. Because actually you have no instance object from your class. You only have a class object. In ruby everything is an object. And your class object has a defined class variable now:
Something.get_my_class_variable
# => [1]
Now you can create an instance from your class. With Something.new the initialize method will be invoked and your instance variable will be defined.
something = Something.new
something.dosomething
# => [5]
Later, if you are familar with this you can define getter and setter methods with attr_reader, attr_writer and attr_accessor for instance objects or cattr_reader, cattr_writer and cattr_accessor for class objects. For example:
class Something
attr_reader :my_something
def initialize
#my_something = []
end
def dosomething
s = 5
#my_something << s
end
end
something = Something.new
something.my_something
# => []
something.dosomething
# => [5]
something.my_something
# => [5]
Your problem in trying to access #something in your instance method is that, in the scope of instance methods, # variables refer to instance variables, and your #something is a class instance variable.
# variables are instance variables of the instance that is self when they are created. When #something was created, self was the class Something, not an instance of Something, which would be the case inside an instance method.
How then to access a class instance variable in an instance method? Like regular instance variables, this must be done via a method, as in attr_accessor. One way to do this is to use class << self to tell the Ruby interpreter that the enclosed code should be evaluated with the class (and not the instance) as self:
class C
#foo = 'hello'
class << self
attr_accessor :foo # this will be a class method
end
def test_foo # this is, of course, an instance method
puts self.class.foo # or puts C.foo
end
end
We can show that this works in irb:
2.3.0 :005 > C.foo
=> "hello"
2.3.0 :006 > C.new.test_foo
hello
You have correctly created a class instance variable, #something, and initialized it to an empty array. There are two ways for instances to obtain or change the value of that variable. One is to use the methods Object#instance_variable_get and Object#instance_variable_set (invoked on the class):
class Something
#something = []
def dosomething
s = 5
self.class.instance_variable_get(:#something) << s
end
end
sthg = Something.new
sthg.dosomething
Something.instance_variable_get(:#something)
#=> 5
The other way is to create an accessor for the variable. There are several ways to do that. My preference is the following:
Something.singleton_class.send(:attr_accessor, :something)
Something.something #=> [5]
In your dosomething method you would write:
self.class.something << s

Create blank binding in the scope of an object

class Foo
def self.run(n,code)
foo = self.new(n)
#env = foo.instance_eval{ binding }
#env.eval(code)
end
def initialize(n)
#n = n
end
end
Foo.run( 42, "p #n, defined? foo" )
#=> 42
#=> "local-variable"
The sample program above is intended to evaluate arbitrary code within the scope of a Foo instance. It does that, but the binding is "polluted" with the local variables from the code method. I don't want foo, n, or code to be visible to the eval'd code. The desired output is:
#=> 42
#=> nil
How can I create a binding that is (a) in the scope of the object instance, but (b) devoid of any local variables?
The reason that I am creating a binding instead of just using instance_eval(code) is that in the real usage I need to keep the binding around for later usage, to preserve the local variables created in it.
so like this? or did i miss something important here?
class Foo
attr_reader :b
def initialize(n)
#n = n
#b = binding
end
def self.run(n, code)
foo = self.new(n)
foo.b.eval(code)
end
end
Foo.run(42, "p #n, defined?(foo)")
# 42
# nil
or move it further down to have even less context
class Foo
def initialize(n)
#n = n
end
def b
#b ||= binding
end
def self.run(n, code)
foo = self.new(n)
foo.b.eval(code)
end
end
Foo.run(42, "p #n, defined?(foo), defined?(n)")
# 42
# nil
# nil
Answer:
module BlankBinding
def self.for(object)
#object = object
create
end
def self.create
#object.instance_eval{ binding }
end
end
Description:
In order to get a binding with no local variables, you must call binding in a scope without any of them. Calling a method resets the local variables, so we need to do that. However, if we do something like this:
def blank_binding_for(obj)
obj.instance_eval{ binding }
end
…the resulting binding will have an obj local variable. You can hide this fact like so:
def blank_binding_for(_)
_.instance_eval{ binding }.tap{ |b| b.eval("_=nil") }
end
…but this only removes the value of the local variable. (There is no remove_local_variable method in Ruby currently.) This is sufficient if you are going to use the binding in a place like IRB or ripl where the _ variable is set after every evaluation, and thus will run over your shadow.
However, as shown in the answer at top, there's another way to pass a value to a method, and that's through an instance variable (or class variable, or global variable). Since we are using instance_eval to shift the self to our object, any instance variables we create in order to invoke the method will not be available in the binding.

Changing the binding of a block

I am trying to write a class that can capture a block of code and subsequently change the variables in the scope/closure.
class BlockCapture
attr_reader :block
def initialize(&block)
#block = block
#scope_additions = {}
end
def merge_into_scope(values = {})
#scope_additions = #scope_additions.merge(values)
end
def mutated_block
# some code that merges the scope additions into the binding
# and returns the context
end
end
captured_block = BlockCapture.new do
future_variable.upcase
end
captured_block.block.call # This should trigger a NameError, as `future_variable` is not in scope.
captured_block.merge_into_scope(future_variable: "I was added to the scope")
captured_block.mutated_block.call # This should work
=> "I WAS ADDED TO THE SCOPE"
I don't know how to go about merging the variables into the scope.
I have tried calling the binding method on the block variable, but it returns a new binding each time, so it appears to be a copy of rather than a reference to the binding of the original block.
irb(main):078:0> capture.block.binding
=> #<Binding:0x007fa38292e398>
irb(main):079:0> capture.block.binding
=> #<Binding:0x007fa382925f18>
irb(main):080:0> capture.block.binding
=> #<Binding:0x007fa38291d908>
Positing as an answer, even if I am not sure it would be helpful, because there is no space in the comment. Looks like you can run eval with the specific binding, for example with the current binding, created after the lambda was defined:
bind_lambda = -> (bind, x) { bind.eval x }
#=> #<Proc:0x007fec943b85e8#(pry):1 (lambda)>
First try ends with an error, as expected:
bind_lambda.call binding, 'future_var'
NameError: undefined local variable or method `future_var' for main:Object
from (pry):2:in `__pry__'
But after declaration of the local variable:
future_var = "here be dragons"
#=> "here be dragons"
We can reach it:
bind_lambda.call binding, 'future_var'
#=> "here be dragons"
This is because binding is the current environment.

Understanding ruby TOPLEVEL_BINDING

I understand that TOPLEVEL_BINDING is the Binding object for main. The following code confirms it:
def name
:outer
end
module Test
class Binder
def self.name
:inner
end
def self.test_it
eval 'name', TOPLEVEL_BINDING
end
end
end
p Test::Binder.test_it # => :outer
I got confused while looking at the source for rack. The problem was in understanding this code in the file lib/rack/builder.rb
def self.new_from_string(builder_script, file="(rackup)")
eval "Rack::Builder.new {\n" + builder_script + "\n}.to_app",
TOPLEVEL_BINDING, file, 0
end
def run(app)
end
The new_from_string method is passed the contents of a config.ru file which will be something like
run DemoApp::Application
Here it seems like TOPLEVEL_BINDING is referring to a Builder object, since the method run is defined for Builder but not for Object. However the earlier experiment establishes that TOPLEVEL_BINDING refers to main's binding. I do not understand how run method is working here. Please help me in understanding this code.
TOPLEVEL_BINDING is the top level binding.
That method is passing the string "run ..." into Builder.new { run ... }
Builder.new then does an instance_eval (https://github.com/rack/rack/blob/df1506b0825a096514fcb3821563bf9e8fd52743/lib/rack/builder.rb#L53-L55) on the block, thereby giving the code inside the block direct access to the instance's methods.
def initialize(default_app = nil,&block)
#use, #map, #run, #warmup = [], nil, default_app, nil
instance_eval(&block) if block_given?
end
run is an instance method of the Builder class, defined here -> https://github.com/rack/rack/blob/df1506b0825a096514fcb3821563bf9e8fd52743/lib/rack/builder.rb#L103-L105
def run(app)
#run = app
end
In short, "run DemoApp::Application" becomes:
Rack::Builder.new {
run DemoApp::Application
}.to_app
Edit: A simple example to illustrate the point:
class Builder
def initialize(&block)
instance_eval(&block)
end
def run(what)
puts "Running #{what}"
end
end
TOPLEVEL_BINDING.eval "Builder.new { run 10 }"
prints
Running 10
TOPLEVEL_BINDING gives you access to context in which the first file was executed:
a.rb:
a = 1
p TOPLEVEL_BINDING.local_variables #=> [:a]
require_relative 'b'
b.rb:
b = 1
p TOPLEVEL_BINDING.local_variables #=> [:a]
$ ruby a.rb
What rack developers are apparently trying to achieve is to isolate the builder line/script from all what is accessible from Rack::Builder.new_from_string (local variables, methods, you name it).
There's also a comment there these days:
# Evaluate the given +builder_script+ string in the context of
# a Rack::Builder block, returning a Rack application.
def self.new_from_string(builder_script, file = "(rackup)")
# We want to build a variant of TOPLEVEL_BINDING with self as a Rack::Builder instance.
# We cannot use instance_eval(String) as that would resolve constants differently.
binding, builder = TOPLEVEL_BINDING.eval('Rack::Builder.new.instance_eval { [binding, self] }')
eval builder_script, binding, file
return builder.to_app
end

ruby class collections

how does this work?
in irb:
>> class A
>> b = [1, 2,3]
>> end
=> [1, 2, 3]
Is b an instance variable? class variable? how would I access b from
outside the class? Is it used for meta-programming?
Is b an instance variable? class variable?
No, it's a local variable inside the class ... end scope.
how would I access b from outside the class?
You wouldn't. It goes out of scope (and is thus inaccessible) once it reaches the end.
Is it used for meta-programming?
It can be. Example:
class A
b = [1,2,3]
b.each do |i|
define_method("foo#{i}") do end
end
end
I've now defined the methods foo1, foo2 and foo3.
Of course this wouldn't behave any differently if I didn't create the variable b and just did [1,2,3].each directly. So creating a local variable by itself does nothing, it allows you to write cleaner code (the same as using local variables in a method).
b is a simple block variable, you cannot access it from outside of the block.
You can use class like this :
class Building
##count=0 #This is a class variable
MIN_HEIGHT=50 #This is a constant
attr_accessor :color, :size #grant access to instance variables
def initialize options
#color=options[:color] ##color is an instance variable
#size=options[:size] ##size too
##count=##count+1
end
def self.build options #This is a class method
# Adding a new building
building=Building.new options
end
end
#[...]
Building.build({:color=>'red', :size=>135})
blue_building=Building.new({:color=>'blue', :size=>55})
puts blue_building.color # How to use an instance variable
# => 'blue'
puts "You own #{Building.count.to_s} buildings !" # How to use a class variable
# => 'You own 2 buildings !'
puts Building::MIN_HEIGHT # How to use a constant
# => 50

Resources