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.