ruby-on-railsrubyruby-on-rails-4.2

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?


Solution

  • :: 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]