tags:

views:

85

answers:

2

Hi all, I'm writing a Ruby library which has a module with a bunch of classes inside it. Many of these classes need to be usable and modifiable by calling scripts, but I don't want (some of) the initializers to be visible/callable:

module MyLib
  class Control
    def initialize
      # They can use this
    end

    def do_stuff
      Helper.new('things')
    end
  end

  class Helper
    # Shouldn't be visible 
    def initialize(what)
      @what = what
    end

    def shout
      @what
    end
  end
end

c = MyLib::Control.new
h = c.do_stuff
p h.shout
# => "things"
# ^ All of this is desired

# v This is undesirable
p MyLib::Helper.new('!')
# => <MyLib::Helper @what='!'>

If it's a simple thing, then I'd also appreciate the generated RDoc not even include the .new method for the Helper class either. Any ideas?

Thanks for reading!

+1  A: 

Ruby has access control.

Matthew Flaschen
Wouldn't defining it as private prevent me from calling `Helper.new` from the `Control` class?
JP
+3  A: 

My original answer was completely wrong, as @Matthew pointed out. But there are other workarounds. For instance, you can assign an anonymous class to a class variable on Control, and still define methods as normal by using class_eval:

module MyLib
  class Control
    def initialize
    end

    def do_stuff
      @@helper.new('things')
    end

    @@helper = Class.new
    @@helper.class_eval do
      def initialize(what)
        @what = what
      end

      def shout
        @what
      end
    end
  end
end

The snippet

c = MyLib::Control.new
h = c.do_stuff
p h.shout

still writes "things", but now there's no way to access @@helper except through the class variable. If someone really wants to access it my reopening the Control class or using class_eval, there's nothing to stop them, but that's just something you have to deal with in a dynamic language.

I chose to assign the anonymous class to a class variable so that it would only be created once; but if you don't care about redefining the anonymous class many times, there's no reason it couldn't be an instance variable.

Mark Rushakoff
@Mark, unfortunately this is broken. It does raise a syntax error because Helper is now in Control. However `p MyLib::Control::Helper.new('!')` now works despite the private.
Matthew Flaschen
@Matthew: You're right. That was a really dumb error on my part. I've written another workaround that isn't victim to such a dumb error but that still solves the problem.
Mark Rushakoff