Sunday, July 18, 2010

module_function (where have you been all my life?)

I recently discovered a feature of Ruby that surprised me. Really, I was surprised that I hadn't heard of it before (for as long as I have been a fan of the language).

module_function can be used to make one or more Module methods accessible as type-scoped methods (in the same way that declaring them as self methods would). For example:

module Foo
def self.bar
'heynow'
end
end
# is equivalent to
module Foo
def bar
'heynow'
end
module_function :bar
end
view raw snippet.rb hosted with ❤ by GitHub

In this form, the methods of the module must be declared before the line containing module_function. You can pass one or more method names (as symbols) in the call. In fact, you can even re-write this as follows:

module Foo
module_function
def bar
'heynow'
end
end
view raw snippet.rb hosted with ❤ by GitHub

This form makes all methods declared after module_function behave as type-scoped functions (callable in the form of: Foo.bar or Foo::bar). You can "turn-off" the behavior of what is given type-scoped calling access by declaring a visibility modifier after the last method you want to expose in this way.

module Foo
module_function
def foo
'hey'
end
def bar
'now'
end
public
def baz
'hooba'
end
end
view raw snippet.rb hosted with ❤ by GitHub

Now the Foo functions can be called either as type-scoped functions or Foo can be included in a class or added to an object.

class Something
include Foo
def heynow
'foo' + bar + baz
end
end
puts Something.new.foo # "hey"
puts Something.new.bar # "now"
puts Something.new.baz # "hooba"
puts Something.new.heynow # "foonowhooba"
view raw snippet.rb hosted with ❤ by GitHub

One thing you now can't do, though, is refer to the module_function scoped methods as instance methods of classes or objects into which the Foo module is included. IOW, trying to call: Something.new.foo in the above sample code blows up with a warning about trying to access a 'private' scoped variable.