8

What is actually occurring when private/protected is stated within a Ruby class definition? They are not keywords, so that implies they must be method calls, but I cannot find where they are defined. They do not appear to be documented. Are the two different ways of declaring private/protected methods (shown below) implemented differently? (The second way is obviously a method call, but this is not so apparent in the first way.)

class Foo
  private
  def i_am_private; end
  def so_am_i; end
end

class Foo
  def i_am_private; end
  def so_am_i; end
  private :i_am_private, :so_am_i
end
Andrew Grimm
  • 78,473
  • 57
  • 200
  • 338
Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
  • 1
    Have you looked at http://en.wikibooks.org/wiki/Ruby_Programming/Syntax/Classes#Private ? – jtbandes Nov 05 '11 at 20:20
  • Yes but this doesn't state where or how the methods are actually implemented. I'm fully aware of *what* `private` & `protected` do, I'm curious about Ruby's internals (the *how* & *where*). – Andrew Marshall Nov 05 '11 at 20:20

2 Answers2

9

Both are method calls. Quoting from docs:

Each function can be used in two different ways.

  1. If used with no arguments, the three functions set the default access control of subsequently defined methods.
  2. With arguments, the functions set the access control of the named methods and constants.

See documentation here:

  1. Module.private
  2. Access Control

You were looking for how the Module.private method comes into existence. Here is where that happens. And here is some more information about it. You would have to read more into it, starting from rb_define_private_method defined in class.c.

Hope that helps!

Andrew Marshall
  • 95,083
  • 20
  • 220
  • 214
Zabba
  • 64,285
  • 47
  • 179
  • 207
  • I'm curious why this doesn't show up here: http://www.ruby-doc.org/core-1.9.2/Module.html – Andrew Marshall Nov 05 '11 at 20:26
  • I agree it should be there, but I don't know why it's not. I don't really like the documentation too much - it could be improved a lot! – Zabba Nov 05 '11 at 20:30
  • Do you know where the method is actually defined though? That's part of what I'm really looking for. I figured it'd probably be in `class.c` but I can't seem to find it. – Andrew Marshall Nov 05 '11 at 20:32
  • These are instance methods of the Module class. All classes are modules, and inside a class definition (but outside method definitions), self refers to class being defined. Thus, public, private, and protected may be used bare as if they were keywords of the language - they behaves like 'Mimic methods'. For example, try following code `Module.private_methods.each { |m| puts m if m =~ /private|protected/ }` – WarHog Nov 05 '11 at 20:33
  • I think its not in docs because that method is "private to module" and seemingly the docs are only listing public methods. – Zabba Nov 05 '11 at 20:35
  • I can't find it either, still looking. – Zabba Nov 05 '11 at 20:36
  • Awesome, that's what I was looking for. Sad that it's not well documented and buried away in `vm_method.c`. – Andrew Marshall Nov 05 '11 at 23:15
0

I'd like to add something about their keyword-like behavior, because the answers have been more about where than how; the answer lies in the complex metaprogramming capabilities of Ruby. It is possible to use them as keywords making use of the method_added hook; an hook in Ruby is a function that is called when a specific event (i.e. the hook's name) occurs. What's important is that the method_added hook receives as his argument the name of the method that has been defined: this way, it's possible to modify it's behavior.

For example, you could use this hook to define a behavior similar to Python's decorators; the important part is that, differently from the private and protected methods, this decorator-like method should define a method_added that undefines itself:

class Module
  def simple_decorator
    eigenclass = class << self; self; end
    eigenclass.class_eval do
      define_method :method_added do |name|
        eigenclass.class_eval { remove_method :method_added }
        old_name = 'old_' + name.to_s
        alias_method old_name, name
        class_eval %Q{
          def #{name}(*args, &block)
            p 'Do something before call...'
            #{old_name}(*args, &block)
            p '... and something after call.'
          end
        }
      end
    end
  end
end

class UsefulClass
  simple_decorator
  def print_something
    p "I'm a decorated method :)"
  end

  def print_something_else
    p "I'm not decorated :("
  end
end

a = UsefulClass.new
a.print_something
a.print_something_else

simple_decorator looks like a language keyword and behaves like private; however, because it removes the method_added hook, it only applies to the immediately following method definition.

Alberto Moriconi
  • 1,645
  • 10
  • 17