Continuing on the topic of local variable scoping rules, we come to the scoping rules for methods.
Check this code:
a = 5 def some_method a = 3 end puts a # => 5 (Why 5 and not 3?
When the code executes “puts a”, it displays 5 and not 3. The reason is our 2nd rule.
Rule #2 – Methods create their own scope outside of execution flow.
We’ll visit methods a little more later in the blog.
Blocks and Nests
Rule #3 – A block creates a new scope for local variables, nested blocks created nested scopes, and the scope of a variable is determined by where it is initiated.
Let’s take a look at the following code for illustration.
2.times do a = 'hello' # a is initialized inside the 2.times block. puts a end loop do puts a # => uh oh NameError break end puts a # => man! another NameError
The variable “a” is initialized in the first block and therefore can’t be accessed the second block or the last “puts a” line. That also means that peer blocks cannot access variables initialized in other blocks. Which also then means we could reuse “a” for another variable in the second block. We really shouldn’t use letters for variable names since they should be a lot more descriptive.
Nested blocks create nested scopes. If you initiate a variable outside of any blocks it will be accessible by blocks and inner blocks. If a variable is initiated in a block it is accessible by blocks created inside the block it was created in. Confused? I guess this concept is better served with actual code.
a = 1 # first level variable accessible by blocks and nested blocks loop do b = 2 # second level variable loop do c = 3 # third level not accessible to first or second level puts a # => 1 puts b # => 2 puts c # => 3 break end puts a # => 1 puts b # => 2 puts c # => NameError because c is on 3rd level break end puts a # => 1 puts b # => NameError because b is on 2nd level puts c # => NameError because c is on 3rd level
Variable Shadowing
Basically if you have an outer variable and an inner variable with the same name, you prevent the outer variable from being modified by the code where the inner variable exists.
n = 10 [1, 2, 3].each do |n| puts n # will use the block 'n' and ignore the outer scoped 'n' end
n = 10 1.times do |n| n = 11 end puts n # => 10 because the outer scope 'n' isn't changed
Rule #4 – The simplest way to avoid variable shadowing is to use descriptive variable names.
Variables and Methods
In contrast to blocks that leak from outer to inner blocks, methods are self contained.
a = 'hello' def some_method puts a end # invoke the method some_method # => NameError because puts can't access 'a'
def some_method(a) puts a end some_method(5) # => 5
Rule #5 – Variables can’t leak into a method from outside the method, they have to be passed in explicitly.
Blocks Within Methods
Rule #6 – The rules of block scope remain in full effect inside of a method.
Constants
USERNAME = 'Boss' def welcome puts "Welcome, #{USERNAME}!" end welcome # => Welcome, Boss!
Rule #7 – Constants are accessible by methods and can be initiated in an inner scope.