Announcing the Constant Library: Importing Ruby Namespaces Without Mixing Them In
Announcing the Constant library. A new release from the Eventide Project:
github.com/eventide-project/constant.
Purpose
In Ruby, importing a namespace and mixing in a module are the same mechanism. If you include a module to get shortcut access to its inner constants, you also get a mixin that might not be what you’d intended — and the ancestry change adds constant-resolution paths that can be surprising.
It doesn’t matter in most code, but in reflection-heavy code, like in framework internals, the extra lookup paths can become subtle, frustrating bugs — the kind that turn up when building general-purpose tools and frameworks.
Import, without the mixin
Constant::Import does the import without the mixin. Instead of including the origin module, it puts a reference to the imported namespace into the receiver, so the origin’s inner constants are reachable without qualification — and the receiver’s ancestry is left untouched:
module SomeReceiver
include Constant::Import
import SomeOrigin
import SomeOrigin::SomeInnerModule, alias: :Something
end
The inner constants of SomeOrigin are reachable from SomeReceiver without qualification, and alias: rebinds an import to a name of your choosing. The import macro is activated by including Constant::Import. There’s also a direct-call form if you’d rather skip the macro: Constant::Import.(origin, destination=self).
A domain object for constants
Import is the headline, but the library also ships an entity for working with constants generally. A Constant wraps a Ruby constant and answers questions about it — its name, its namespace, its value, whether a name is defined within it, and what inner constants it contains.
constant = Constant.get(SomeNamespace)
constant.name # => "SomeNamespace"
constant.constant_names # => ["SomeInnerModule"]
constant.defined?(:SomeInnerModule) # => true
constant.constants(include_literal_constants: true)
# => [#<Constant::Module value=SomeNamespace::SomeInnerModule>,
# #<Constant::Literal SomeNamespace::SomeLiteralConstant = "some value">]
Constant.get takes a module, or a name resolved within a namespace. Constant::Define creates and assigns a module to a name in a namespace; it’s the same mechanism Import uses to build an alias target, and it’s useful on its own.
Where it fits
Constant is a foundation utility. It’s the sort of thing that quietly underpins protocol discovery and reflection work in the Eventide ecosystem. Its only dependency is Eventide’s Initializer library; it’s otherwise standalone, and useful anywhere Ruby constants are handled programmatically. If you’ve used include purely for its import side effect, this is a tool built to do exactly that, and nothing more.
Get it
evt-constant is MIT licensed.
gem install evt-constant
Source and full documentation are at github.com/eventide-project/constant.