Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scope resolution works differently in ruby when written in different ways

I faced this issue while developing a feature.Lets say there is following code:

case 1:
module Person
  module Employee 
    class Officer
      def self.print_class(obj)
        obj.is_a? Employee
      end
    end
  end
end

case 2:
class Person::Employee::Officer
  def self.print_class(obj)
       puts obj.is_a? Employee
  end
end

class Employee < ActiveRecord::Base
end

emp = Employee.last

and we have model Employee . Now

For case 1: Person::Employee::Officer.print_class(emp) gives "false"

For case 2: Person::Employee::Officer.print_class(emp) gives "true"

Why is this happening?

like image 757
Manish Chauhan Avatar asked Jul 16 '20 12:07

Manish Chauhan


1 Answers

:: is the scope resolution operator. Unlike the class and module keywords it does not reopen the module and properly set the module nesting.

For example:

TEST = "I'm in the global scope"

module Foo
  TEST = "I'm scoped to foo"
end


module Foo::Bar
  def self.test
    TEST 
  end

  def self.nesting
    Module.nesting
  end
end

puts Foo::Bar.test # "I'm in the global scope"
puts Foo::Bar.nesting.inspect [Foo::Bar]

This is because the module nesting is resolved lexically at the point of definition. When you do module Foo::Bar that module nesting is the global scope - when you reference TEST it is not resolved to Foo::TEST since Foo is not in the module nesting.

In your case 2 Employee is resolved to ::Employee not Person::Employee.

Therefore you should always explicitly nest classes and modules as it will set the correct module nesting and avoid these very unexpected module lookups.

TEST = "I'm in the global scope"

module Foo
  TEST = "I'm scoped to foo"
  module Bar
    def self.test
      TEST 
    end

    def self.nesting
      Module.nesting
    end
  end
end

puts Foo::Bar.test # "I'm scoped to foo"
puts Foo::Bar.nesting.inspect [Foo::Bar, Foo]
like image 103
max Avatar answered Nov 04 '22 19:11

max