Search code examples
rubynamespacesruby-2.0pundit

How does Ruby namespace the parent class of a nested class


While working with the Ruby gem Pundit, I realized I was unsure of the way some namespacing works within Ruby and I do not like mysteries/uncertainties in my mind.

Pundit suggests you set up an application_policy.rb as so:

class ApplicationPolicy
  class Scope
     .
     .
     .
  end
end

What actually is happening inside of the class doesn't matter, just the structure of the classes do.

Then you specify a policy for a particular resource that inherits from ApplicationPolicy, say post_policy.rb as so:

class PostPolicy < ApplicationPolicy
  class Scope < Scope
  end
end

My general question is, inside my PostPolicy, when I declare that Scope < Scope, what does the parent Scope refer to? Does it automatically get namespaced within the parent of the outer class? So is it essentially the same as inheriting from ApplicationPolicy::Scope? I am having trouble finding a way to answer this on my own, thanks!


Solution

  • Short answer

    You are right on both counts. You can check it with Class#ancestors :

    class ApplicationPolicy
      class Scope
      end
    end
    
    class PostPolicy < ApplicationPolicy
      class Scope < Scope
      end
    end
    
    p PostPolicy::Scope.ancestors
    #=> [PostPolicy::Scope, ApplicationPolicy::Scope, Object, Kernel, BasicObject]
    

    Equivalent code

    The above code is exactly the same as :

    class ApplicationPolicy
    end
    
    class ApplicationPolicy::Scope
    end
    
    class PostPolicy < ApplicationPolicy
    end
    
    class PostPolicy::Scope < ApplicationPolicy::Scope
    end
    
    p PostPolicy::Scope.ancestors
    #=> [PostPolicy::Scope, ApplicationPolicy::Scope, Object, Kernel, BasicObject]
    

    Note that PostPolicy::Scope doesn't inherit from PostPolicy. They are independant classes, the former just happens to be defined in the namespace of the latter.

    Another way to check

    class A
      class Scope
      end
    end
    
    class B < A
      class Scope < Scope
      end
    end
    
    class C
      class Scope < Scope
      end
    end
    

    fails with :

    namespace.rb:26:in `<class:C>': uninitialized constant C::Scope (NameError)
        from namespace.rb:25:in `<main>'
    

    It means that Scope must come from A namespace.