How does Ruby namespace the parent class of a nested class

1.5k views Asked by At

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!

2

There are 2 answers

4
Eric Duminil On BEST ANSWER

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.

3
Aleksei Matiushkin On

It is quite easy to get youself:

class C1; class Nested; end; end
class C2<C1; class Nested<Nested; end; end

C2::Nested.ancestors
#⇒ [
#  [0] C2::Nested < C1::Nested,
#  [1] C1::Nested < Object,
#  [2] Object < BasicObject,
#  [3] PP::ObjectMixin,
#  [4] Kernel,
#  [5] BasicObject
# ]

So, yes, It’s implicitly resolved to ApplicationPolicy::Scope.