Jellicle
Jellicle

Reputation: 30256

Rails autoloader is making private constants public

I'm trying to enforce a boundary in a Rails app: only code within the namespace Boundary should be able to access the class Boundary::Blackbox.

This works alright when it's defined outside of an autoloaded path:

# app/lib/boundary/blackbox.rb
module Boundary
  module Blackbox
    def self.foo
      "from foo"
    end
  end

  private_constant :Blackbox
end

# app/lib/boundary.rb
module Boundary
  def self.bridge
    "from bridge > " + Blackbox.foo
  end
end

# app/lib/outside.rb
module Outside
  def self.violation
    "from violation > " + Boundary::Blackbox.foo
  end
end

I expect the ruby code to make Boundary::Blackbox private, and indeed it is private except when Boundary::Blackbox is defined where the autoloader expects to find it.

Suppose I define it in example.rb instead:

load "example.rb"
Boundary.bridge # => "from bridge > from foo"
Outside.violation # => NameError uninitialized constant Boundary::Blackbox
Boundary::Blackbox.foo # => NameError uninitialized constant Boundary::Blackbox

Now let's test it using app/lib/boundary/blackbox.rb:

load "app/lib/boundary/blackbox.rb"
Boundary.bridge # => "from bridge > from foo"
Outside.violation # => "from violation > from foo"
Boundary::Blackbox.foo # => "from foo"

Now I get no NameError; Boundary::Blackbox is completely accessible.

How can I make my Boundary::Blackbox module private but also keep it in a location where the autoloader expects to find it?

(TMI: I do want it autoloaded so that Boundary.bridge can access it automatically, but I don't want to put it into app/lib/boundary.rb because that file would grow prohibitively unwieldy with the various modules/classes which need to go into the Boundary namespace. And I'm loath to put it into a file that falls outside the autoloader's pattern because I need to uphold conventions in a very large application.)

Upvotes: 0

Views: 65

Answers (0)

Related Questions