vikas95prasad
vikas95prasad

Reputation: 1332

How to allow initializing a class from a particular class in ruby and nowhere else?

module A
  module B
    class Foo
      def initialize(args)
        @args = args
      end
      def call
        puts 'Inside Foo'
      end
    end
  end
end

module C
  class Boo
    def initialize(args)
      @args = args
    end
    def call
      puts 'Inside Boo'
      A::B::Foo.new(@args).call
    end
  end
end

boo = C::Boo.new(nil).call
# Inside Boo
# Inside Foo

A::B::Foo.new(nil).call
# Inside Foo

How can I avoid A::B::Foo.new(nil).call ? It should be accessible only from Boo class.

If anybody wants to access Foo class they will be able to access it from Boo. How can I achieve this ?

Searched Internet but could not find what should be call this concept ?

Upvotes: 1

Views: 59

Answers (1)

Tom Lord
Tom Lord

Reputation: 28285

This is ruby - so there's no such thing as an iron-clad way of making an object "private". (For example, you can access private methods via .send!) But you can at least define a private interface.

However, your interface doesn't actually make much sense from an OOP perspective. Why is A::B::Foo accessible only within C::Boo? If A::B::Foo is private, then it should only be accessible within A::B, and nowhere else.

The key method you're looking for is: private_constant

And you can circumvent a private constant lookup exception by using const_get.

Therefore we can "hack" your current implementation to work as follows:

module A
  module B
    class Foo
      def initialize(args)
        @args = args
      end
      def call
        puts 'Inside Foo'
      end
    end
    private_constant :Foo
  end
end


module C
  class Boo
    def initialize(args)
      @args = args
    end
    def call
      puts 'Inside Boo'
      A::B.const_get(:Foo).new(@args).call
    end
  end
end

# This works:
C::Boo.new(nil).call

# This errors:
A::B::Foo.new(nil).call

Upvotes: 3

Related Questions