Private class (not class method) in a Ruby module?

独自空忆成欢 提交于 2019-11-29 01:26:27

I haven't seen such concept so far in Ruby, but I guess you could simulate that by creating private method which would return a class created as a local variable (remember that in Ruby, a class is an object just like any other, and can be instantiated in a method and returned by it).

BTW, even private methods in Ruby aren't as private as in other languages - you can always access them using send method. But doing that implies you know what you are doing.

The most important thing to realize is that a class is nothing special. It's just an object. Per convention, classes are assigned to constants, but there is nothing that says they have to be.

And since classes are just objects like any other object, you make them private the same way that you make any other object private.

Here are the possibilities I can think of, in the order of increasing privateness:

  1. Just nest them inside a namespace (i.e. module). In Ruby, it is generally expected that all of a library's modules and classes live inside a namespace with the same name as the library (i.e. my_awesome_libraryMyAwesomeLibrary), but in general, everything which is nested below that namespace, is considered private. In fact, besides Test::Unit::TestCase, I cannot think of a single example of a three-level deep namespace that is actually expected to be used by client code.
  2. same as 1., but name it something obvious, like MyAwesomeLibrary::Internal::FfiStruct
  3. same as 1. or 2., and mark it with the :nodoc: RDoc tag.
  4. similar to 3., but use a more modern documentation system like YARD, which actually lets you explicitly mark up private APIs.
  5. Use a method instead of a constant. Methods can be made private. (For consistency's sake, you can have the method name start with an uppercase letter, to make it resemble a constant. There's nothing to prevent that, the snake_case convention is just that: a convention.)
  6. Use an instance variable. They are always private. Note that both private methods and instance variables can be trivially accessed using reflection. send seems to be in more widespread use than instance_variable_get, though, which is why I consider instance variables to have a higher level of privacy than methods.
  7. Really the only way to get actual privacy or encapsulation is using local variables and closures, though. Note however that this might preclude you from using Ruby's module, class or method definition syntax, because those create new scopes. In all cases where you need access to the class, you need to use Module.new, Class.new or Module#define_method.

Ex.:

module MyAwesomeLibrary
  struct = Class.new(FFI::Struct) do
    # ...
  end

  PublicInterface = Class.new do
    define_method(:initialize) do |bar|
      @foo = struct.new(bar)
    end
  end
end

And yes, this is the only way of achieving true 100% information hiding and encapsulation in Ruby.

However, the normal Ruby way would be to simply document the stuff as being private (maybe push it down a level of namespacing) and trust your fellow developers. In the Ruby community, this is sometimes summarized under the Python slogan "We are all consenting adults".

Taking this information straight from this blog post, but since Ruby 1.9.3 you can create a private class within a module using private_constant:

class Person
  class Secret
    def to_s
      "1234vW74X&"
    end
  end
  private_constant :Secret

  def show_secret
    Secret.new.to_s
  end
end
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!