3

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!

mackshkatz
  • 861
  • 8
  • 19

2 Answers2

4

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.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • Appreciate you showing how you determined it, thanks! – mackshkatz Dec 25 '16 at 19:14
  • One thing that's interesting and slightly confusing is the following scenario: [1] pry(main)> class C1 [1] pry(main)* end => nil [2] pry(main)> class C2 < C1 [2] pry(main)* class Nested < Nested [2] pry(main)* end [2] pry(main)* end NameError: uninitialized constant C2::Nested from (pry):4:in `' [3] pry(main)> – mackshkatz Dec 25 '16 at 19:58
  • So, by not defining C1::Nested, the error message is NameError: uninitialized constant C2::Nested, does this make sense to you? – mackshkatz Dec 25 '16 at 19:58
4

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.

Eric Duminil
  • 52,989
  • 9
  • 71
  • 124
  • 1
    Thank you for the quick reply, and sorry for taking a while to respond. Your answer was awesome an super thorough! Thank you. For some reason, the shorthand of `class Scope < Scope` really referring to `ApplicationPolicy::Scope` doesn't seem natural to me or what I would expect, but I guess it's just the way it is and at least now I understand! – mackshkatz Dec 25 '16 at 19:10
  • One thing that's interesting and slightly confusing is the following scenario: [1] pry(main)> class C1 [1] pry(main)* end => nil [2] pry(main)> class C2 < C1 [2] pry(main)* class Nested < Nested [2] pry(main)* end [2] pry(main)* end NameError: uninitialized constant C2::Nested from (pry):4:in `' [3] pry(main)> – mackshkatz Dec 25 '16 at 19:40
  • By not defining `C1::Nested`, the error message is `NameError: uninitialized constant C2::Nested`. – mackshkatz Dec 25 '16 at 19:42
  • You're welcome. I agree the syntax is a bit too convoluted. I wouldn't use it for anything that should be read by other people. – Eric Duminil Dec 25 '16 at 19:47