Include Namespace Module
Video transcript & code
In the last episode, we took a look at how to organize generally useful utility methods into a module, using the module_function
macro to expose them at both the module and instance level.
module ROFLWeb
module ModelUtils
module_function
def unique_id(object)
if object.respond_to?(:to_unique_id)
object.to_unique_id
else
"#{object.class.name}+#{object.hash.to_s(16)}"
end
end
end
module UrlHelpers
include ModelUtils
# ...
end
class MemoryRepository
include ModelUtils
# ...
end
end
One possible objection to this approach is that if there are only a few such "utility functions", creating a module just for them may seem like overkill. Another is that for a client coder, who is using our framework, to find these methods he or she has to dig a couple layers deep into our module hierarchy. If they find the methods at all, they might legitimately feel like they are depending on implementation details of our library.
In order to make these utility methods more accessible to client coders, we might move them to the outermost "namespace" module, like this:
module ROFLWeb
module_function
def unique_id(object)
if object.respond_to?(:to_unique_id)
object.to_unique_id
else
"#{object.class.name}+#{object.hash.to_s(16)}"
end
end
# ...
end
Now client coders reading the documentation or code of our library can easily see these helper methods right at the top level. And through the magic of module_function
, these methods are available both as included instance methods, and as fully-qualified module-level methods:
puts ROFLWeb.unique_id("Hello, world")
include ROFLWeb
puts unique_id("Hello, world")
But now our own internal classes and modules have no utility module to include!
…or do they? It may seem counter-intuitive, but in fact Ruby has no problem with including a "containing" module into a nested module or class.
module ROFLWeb
module_function
def unique_id(object)
if object.respond_to?(:to_unique_id)
object.to_unique_id
else
"#{object.class.name}+#{object.hash.to_s(16)}"
end
end
module UrlHelpers
include ROFLWeb
# ...
end
class MemoryRepository
include ROFLWeb
# ...
end
end
This isn't an excuse for filling a top-level namespace module with dozens of utility methods. But when there are only a few, this is a low-ceremony way to make them readily accessible to both internal code and client code alike.
That's all I have today. Happy hacking!
Responses