Ruby API doc and Module
24.04.2015 - Pascal BetzThe 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.