Ruby - Lexical scope vs Inheritance - ruby

This is a continuation this original SO question: Using "::" instead of "module ..." for Ruby namespacing
In the original SO question, here is the scenario presented which I'm still having trouble understanding:
FOO = 123
module Foo
FOO = 555
end
module Foo
class Bar
def baz
puts FOO
end
end
end
class Foo::Bar
def glorf
puts FOO
end
end
puts Foo::Bar.new.baz # -> 555
puts Foo::Bar.new.glorf # -> 123
Can someone provide some explanation behind why the first call is returning 555 and why the second call is returning 123?

You can think of each appearance of module Something, class Something or def something as a “gateway” into a new scope. When Ruby is searching for the definition of a name that has been referenced it first looks in the current scope (the method, class or module), and if it isn’t found there it will go back through each containing “gateway” and search the scope there.
In your example the method baz is defined as
module Foo
class Bar
def baz
puts FOO
end
end
end
So when trying to determine the value of FOO, first the class Bar is checked, and since Bar doesn’t contain a FOO the search moves up through the “class Bar gateway” into the Foo module which is the containing scope. Foo does contain a constant FOO (555) so this is the result you see.
The method glorf is defined as:
class Foo::Bar
def glorf
puts FOO
end
end
Here the “gateway” is class Foo::Bar, so when FOO isn’t found inside Bar the “gateway” passes through the Foo module and straight into the top level, where there is another FOO (123) which is what is displayed.
Note how using class Foo::Bar creates a single “gateway”, skipping over the scope of Foo, but module Foo; class Bar ... opens two separate “gateways”

wow, great question. The best answer I can come up with is in this case you're using the module to define a namespace.
Check this out:
FOO = 123
module Foo
FOO = 555
end
module Foo
class Bar
def baz
puts FOO
end
def glorf3
puts ::FOO
end
end
end
class Foo::Bar
def glorf2
puts Foo::FOO
end
def glorf
puts FOO
end
end
puts Foo::Bar.new.baz # -> 555
puts Foo::Bar.new.glorf # -> 123
puts Foo::Bar.new.glorf2 # -> 555
puts Foo::Bar.new.glorf3 # -> 123
So my thought is that when you define:
module Foo
FOO = 555
end
you are creating FOO in the namespace of Foo. So when you use it here:
module Foo
class Bar
def baz
puts FOO
end
end
end
you are in the Foo namespace. However, when you refer to it in:
class Foo::Bar
def glorf
puts FOO
end
end
FOO is coming from the default namespace (as illustrated by ::FOO).

the first call:
puts Foo::Bar.new.baz # -> 555
prints the result of invoking method baz of an instance of class Foo::Bar
notice that Foo::Bar#baz definition is actually a closure on FOO. Following ruby's scope rules:
FOO is searched for in Foo::Bar (the class, not the instance) scope, it is not found,
FOO is searched for in the enclosing scope Foo (because we are within the module definition) and it is found there (555)
the second call:
puts Foo::Bar.new.glorf # -> 123
prints the result of invoking method glorf of an instance of class Foo::Bar
notice that Foo::Bar#glorf definition this time is also a closure on FOO, but if we follow ruby's scope rules you'll notice that the value closed upon this time is ::FOO (top level scope FOO) in the following way:
FOO is searched for in Foo::Bar (the class, not the instance) namespace, it is not found
FOO is searched in the enclosing scope ('top level') and it is found there (123)

glorf is a method of class Foo, in => [Foo, Module, Object, Kernel, BasicObject]
within that scope (i.e. in default/main module), FOO is assigned 123
module Foo is defined as
module Foo
FOO = 555
class Bar
def baz
puts FOO
end
end
end
where method baz belongs to class Bar in module Foo => [Bar, Foo, Object, Kernel, BasicObject]
and in that scope FOO was assigned 555

Related

How to call a method defined in another module in Ruby?

class Foo
include Bar
include Baz
end
module Bar
def do_something
end
end
module Baz
do_something
end
Baz module does not have access to Bar. Is there a way for it to call a method in Bar?
One approach is to extend Baz with Bar, but I want to include them all in Foo instead.
If for whatever reason you don't want to extend Baz with Bar, you can create an object that extends Bar inside Baz:
module Bar
def do_something
puts 42
end
end
module Baz
Object.new.extend(Bar).do_something # prints 42
end

How do I access an instance variable from a module that was included dynamically?

I am dynamically including a module into the Baz class in the foobarbaz method.
However, when I execute this in ruby, I get a nil puts. Doesn't the module have access to Foo's instance variables?
class Foo
attr_accessor :current_session
def initialize(current_session)
#current_session = current_session
end
def foobarbaz
Baz.send(:include, Bar) # For Ruby < 2.1
# Baz.include(Bar) # For Ruby > 2.1
end
end
class Baz
end
module Bar
def foobar
#current_session
# 'foobar'
end
end
puts Foo.new('current_session').foobarbaz.new.foobar # nil
NOTE, for this, I was using Ruby 2.0.0. The following also does not puts desired result in Ruby 2.1.2.
Here is a meta programming for you :
#!/usr/bin/env ruby
class Foo
attr_accessor :current_session
def initialize(current_session)
#current_session = current_session
end
def foobarbaz
session = current_session
Bar.module_eval { #current_session = session }
Baz.send(:include, Bar)
end
end
module_eval says
Evaluates the string or block in the context of mod, except that when a block is given, constant/class variable lookup is not affected....
Thus inside Bar.module_eval { #current_session = session }, #current_session is the instance variable of Bar only and I am setting the value of it to the instance variable value of the class Foo, which is #current_session.
Baz.send(:include, Bar) is helpfull, which returns class/module itself, which is including the other module. include(module, ...) → self.
class Baz
end
Read this post to understand the below stuff.
module Bar
class << self
attr_reader :current_session
end
def foobar
Bar.current_session
end
end
puts Foo.new('current_session').foobarbaz.new.foobar
# >> current_session
Update
As #Christian Fazzin gave a good suggestion :-
If you want Bar module to have write method also, then you have to do 2 changes -
Bar should contain then attr_accesor :current_session, instead of what it has now.
You don't need to use the power of module_eval there, rather use syntactic sugraness of write methods, like put Bar.current_session = current_session inside the method foobarbaz . Remove the lines session = current_session and Bar.module_eval { #current_session = session }.
After creating an instance of Foo (foo, say) and in doing so initializing foo's instance variable #current_session to 'current session', it appears to me that you want foo.foobarbaz to do the following:
cause Baz to include the module Bar
create an instance of Baz (baz, say)
create an instance variable named #current_session for baz and assign it the value of foo's instance variable of the same name
invoke baz.foobar to return the value of baz's instance variable #current_session.
If my understanding is correct, we can perform these four steps with four lines in Foo#foobarbaz:
class Baz
end
module Bar
def foobar
#current_session + ' in Baz'
end
end
class Foo
attr_accessor :current_session
def initialize(current_session)
#current_session = current_session
end
def foobarbaz
Baz.include(Bar)
baz = Baz.new
baz.instance_variable_set(:#current_session, self.current_session)
baz.foobar
end
end
foo = Foo.new('current session')
foo.foobarbaz
#=> "current session in Baz"
I've slightly modified what foobarbaz returns to show where it is coming from.
Note that the third line of foobarbaz could be changed to either of the following
baz.instance_variable_set(:#current_session, #current_session)
baz.instance_variable_set(:#current_session,
instance_variable_get(:#current_session))
If the latter of these were used, #current_session's accessor would not be needed.
You'd just need to set instance variable (not class instance variable!) #current_session of class Baz.
With slightest modification of your code without need of additional class/module methods the most straightforward way is to define initialization method that sets the required variable:
class Foo
attr_accessor :current_session
def initialize(current_session)
#current_session = current_session
end
def foobarbaz
  # define Baz#initialize on-the-fly, alternatively with define_method
Baz.class_eval "def initialize; #current_session = '#{#current_session}';end"
Baz.send(:include, Bar) # For Ruby < 2.1
end
end
class Baz
end
module Bar
def foobar
#current_session
# 'foobar'
end
end
puts Foo.new('current_session').foobarbaz.new.foobar
# current_session

inheritance changes class of method

The following prints Bar twice:
class Foo
def foo
p self.class # => prints Bar
end
end
class Bar < Foo
def foo
p self.class # => prints Bar
super
end
end
b = Bar.new
b.foo
How do I get it to print
Bar
Foo
? i.e. I want to know what class each method is defined on.
To capture the context in which a method was originally defined, you can use define_method instead of def to get the appropriate closure. A simple example:
class Foo
klass = self
define_method(:foo){p klass}
end
class Bar < Foo
def foo
p self.class
super
end
end
b = Bar.new
b.foo
You could change Foo#foo like so (provided there is just one subclass level):
class Foo
def foo
if self.class == Foo
p self.class
else
p self.class.superclass
end
end
end
class Bar < Foo
def foo
p self.class
super
end
end
Foo.new.foo
Foo
Bar.new.foo
Bar
Foo
You can use
b.class.superclass <= "Foo"
The problem you are having there is that self is the instance of Bar, b.
b.class <= always going to be Bar
self.class <= always going to be Bar if you are invoking Bar.
You say that you are defining a method at runtime, and that you don't know the class name. I don't really know what you mean ... the way I would handle this would be something like
class Bar
def initialize
puts 'In BAR class'
end
def foo
p self.class.name # => prints Bar
end
end
and then
Bar.class_eval do
def brand_new_method
# do something new
p "Still in Bar, but this is dynamically added"
end
end
Maybe you are talking about dynamically adding methods to classes higher in the inheritance chain ... to "Foo" in your example ... based on some conditional happening in an instance of "Bar". If that is the case, then why don't you use a single module to define your inherited methods:
module Foo
def foo
p self.class
end
end
and then use module_eval the same way as class_eval?

How can I override a module's singleton method?

Given a module with a singleton method like this:
module Foo
class << self
def bar
puts "method bar from Foo"
end
end
end
How can I override Foo.bar using another module?
This code
module Foo
class << self
def bar
puts "method bar from Foo"
end
end
end
is equal to
class << Foo
def bar
puts "method bar from Foo"
end
end
that is also equal to
def Foo.bar
puts "method bar from Foo"
end
So, you can call it to redefine this method everywhere where Foo is defined (ever withing another module, without including Foo).
Extend and alias
My problem was that I forgot to think through the inheritance chain. I was
looking for a way to override the method by modifying the inheritance chain, but
that's not possible.
The reason is that bar is defined on Foo itself, so it never looks up its inheritance chain for the method. Therefore, to change bar, I have to change it on Foo itself.
While I could just re-open Foo, like this:
module Foo
def self.bar
puts "new foo method"
end
end
... I prefer a way to be able to wrap the original bar method, as though I
were subclassing and could call super. I can achieve that by setting up an
alias for the old method.
module Foo
class << self
def bar
"method bar from Foo"
end
end
end
puts Foo.bar # => "method bar from Foo"
module FooEnhancement
# Add a hook - whenever a class or module calls `extend FooEnhancement`,
# run this code
def self.extended(base)
# In the context of the extending module or class
# (in this case, it will be Foo), do the following
base.class_eval do
# Define this new method
def self.new_bar
"#{old_bar} - now with more fiber!"
end
# Set up aliases.
# We're already in the context of the class, but there's no
# `self.alias`, so we need to call `alias` inside this block
class << self
# We can call the original `bar` method with `old_bar`
alias :old_bar :bar
# If we call `bar`, now we'll get our `new_bar` method
alias :bar :new_bar
end
end
end
end
# This will fire off the self.extended hook in FooEnhancement
Foo.extend FooEnhancement
# Calls the enhanced version of `bar`
puts Foo.bar # => 'method bar from Foo - now with more fiber!'
You can do the following:
module Foo
class << self
def bar
puts "method bar from Foo"
end
def baz
puts "method baz from Foo"
end
end
end
module Foo2
def Foo.bar
puts "new version of bar"
end
end
include Foo
Foo.baz #=> method baz from Foo
Foo.bar #=> new version of bar
Or instead of naming it Foo2 simply re-open Foo.
module Foo
class << self
def bar
puts "method bar from Foo"
end
def baz
puts "method baz from Foo"
end
end
end
module Foo
class << self
def bar
puts "new version of bar"
end
end
end
include Foo
Foo.baz #=> method baz from Foo
Foo.bar #=> new version of bar

Naming a Ruby method with an existing constant

module Foo
def self.bar
"Module.bar"
end
end
def Foo
"main.Foo"
end
puts Foo # Foo
puts Foo.bar # Module.bar
puts Foo() # main.Foo
puts Foo.class # Module
Why is it possible for the module Foo and the method Foo to coexist in the same namespace? Why doesn't the definition of the method Foo overwrite the module?
They don't exist in the same namespace. Constants and methods are in different namespaces. That's how they coexist. The language looks in the constant namespace unless you use the method-call parens, which unambiguously say "This is supposed to be a method."

Resources