Ruby API doc and Module

The Ruby API doc is a great source for information about my programming language of choice. Even after years of writing ruby code i learn new tricks and features.
Lately i’ve been looking into the Module class in more detail.

I did not know that there is a callback for methods being added to a class. Not that i missed them much or that i even know what i could use them for. Similar exists for removal of methods.

class Foo
 def self.method_added(method)
   puts method
 end

 def hello_world
 end
end

# => "hello_world"

Because there is also a callback for methods that are undef’d (no documentation for this method though) i started to wonder what the difference between removing and undefining a method is. Consider the following classes:

class Base
 def hello_world
   puts "Hello World from #{self.class.name}"
 end

 def self.method_removed(name)
   puts "removed #{name} from #{self.class.name}"
 end

 def self.method_undefined(name)
   puts "undefined #{name} from #{self.class.name}"
 end
end

class Undefined < Base
 def hello_world
   puts "Hello World from #{self.class.name}"
 end

 undef_method(:hello_world)
end

class Removed < Base
 def hello_world
   puts "Hello World from #{self.class.name}"
 end

 remove_method(:hello_world)
end

If you run the code there will be some output from the callbacks:

undefined hello_world from Class
removed hello_world from Class

But the interesting part starts when you call those methods:

Removed.new.hello_world
# => Hello World from Removed

Undefined.new.hello_world
# => undefined method `hello_world' for #<Undefined:0x007f8dd488a8d8> (NoMethodError)

undef_method prevents the class from responding to a method, even if it is present in a superclass or mixed in module. remove_method only removes it from the current class hence it will still respond to the call if the method is defined in superclass or mixed in.

Something that i’ve seen in other people’s source code already but don’t use myself: the ability to pass a list of Strings/Symbols to the visibility modifiers such as private, public and protected:

class Foo
 def a_method
 end
 private(:a_method)
end

Foo.new.a_method

# => private method `a_method' called for #<Foo:0x007fb169861c90> (NoMethodError)

Note that those visibility modifiers are methods and not part of the language syntax. This is different from other languages like Java where public/private/protected are language keywords (and no modifier is also supported and leads to default visibility).

Actually i prefer the Java syntax over the ruby one: having the visibility part of the method signature makes it easy to spot what visibility a method has. Especially for long classes this might be difficult in Ruby.
It is actually possible to have a similar style in ruby. Ruby allows to write multiple statements on one line as long as they are separated by a semicolon:

class Foo
 private; def hello_world
   puts "hello world"
 end
end

Looks awkward and modifies the visibility for all following methods as well.

For newer Rubies (2.1+) you can omit the semicolon as def is not void anymore but returns a Symbol (the method name):

class Foo
 private def hello_world
   puts "hello world"
 end
end

(Thanks to Thomas Ritter for the hint).

Now lets look at how you would make a private class method:

class Foo
  private
  def self.hello_world
    puts "hello World"
  end
end

You would expect hello_world to be private, right? Not exactly: you can still call it:

Foo.hello_world
# => hello_world

So why is that? Because to change the visibility of class methods you need to use private_class_method like so:

class Foo
  def self.hello_world
    puts "hello World"
  end
  private_class_method :hello_world
end

Note that confusingly private_class_method does not set the visibility for class methods following that call like private does. You need to pass the method name as an argument!

So i stick to grouping methods by visibility and write small classes to make sure i don’t lose track of what visibility the methods are in.

Learned something new today? Then go pick a class of ruby core and read up on it on the API doc. Chances are you are learning something new.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s