Skip to content

dtcristo/boxwerk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

227 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“¦ Boxwerk

Boxwerk is a tool for creating modular Ruby and Rails applications. It enables you to organize code into packages of Ruby files with clear boundaries and explicit dependencies. Boxwerk is heavily inspired by Packwerk but provides more robust enforcement at runtime using Ruby::Box, ensuring that only public constants from direct dependencies are accessible. Violations raise NameError, turning architectural rules into runtime guarantees.

As your application grows, Boxwerk helps prevent accidental coupling, enforces modularity, and makes it easier to understand and modify code without breaking other parts of the system.

Usage Guide Β· API Documentation Β· Changelog

Features

  • Boxwerk reads standard Packwerk package.yml files, supporting both dependency and privacy enforcement. Packwerk itself is not required.
  • Packages in a Boxwerk application share a set of global gems but may also define package-local ones. Multiple packages can depend on different versions of the same gem.
  • Ruby::Box provides monkey patch isolation between packages.
  • Boxwerk uses Zeitwerk to automatically load constants in packages with conventional file structure although manual loading is also supported.

Goals

  • Enforce boundaries at runtime. Ruby::Box turns architectural rules into runtime guarantees. Undeclared dependencies and privacy violations raise NameError.
  • Enable gradual modularization. Add package.yml files around existing code and declare dependencies incrementally.
  • Feel Ruby-native. Integrates with Bundler, gems.rb/Gemfile, and standard Ruby tools. boxwerk exec rake test feels like any other Ruby command.

Ruby::Box

Ruby::Box (Ruby 4.0+) provides in-process isolation of classes, modules, and constants. Each box has its own top-level Object, isolated $LOAD_PATH and $LOADED_FEATURES, and independent monkey patches. Boxwerk creates one box per package and wires cross-package constant resolution through const_missing.

Set RUBY_BOX=1 before starting Ruby. See the official documentation for details. See ARCHITECTURE.md for how Boxwerk uses Ruby::Box internally.

Quick Start

Create packages with package.yml files:

my_app/
β”œβ”€β”€ package.yml
β”œβ”€β”€ main.rb
└── packs/
    β”œβ”€β”€ foo/
    β”‚   β”œβ”€β”€ package.yml
    β”‚   └── lib/foo.rb
    └── bar/
        β”œβ”€β”€ package.yml
        └── lib/bar.rb
# package.yml (root)
enforce_dependencies: true
dependencies:
  - packs/foo
  - packs/bar

Install and run:

gem install boxwerk
RUBY_BOX=1 boxwerk run main.rb

No Bundler or Gemfile required for basic usage. To use global or per-package gems, see USAGE.md.

CLI

boxwerk run <script.rb>             Run a Ruby script in a package box
boxwerk exec <command> [args...]    Execute a command in the boxed environment
boxwerk console                     Interactive console in a package box
boxwerk info                        Show package structure and dependencies
boxwerk install                     Install gems for all packages

Options: --package <package>, --all, --global. See USAGE.md for details.

Limitations

  • Ruby::Box is experimental in Ruby 4.0
  • No constant reloading (restart required for code changes)
  • IRB autocomplete disabled in console

See TODO.md for plans to address these and other planned features.

Examples

Development

bundle install                        # Install dependencies
RUBY_BOX=1 bundle exec rake           # Run all tests (unit, e2e, examples)
bundle exec rake format               # Format code

License

Available as open source under the MIT License.

About

πŸ“¦ Ruby package system with Box-powered boundary enforcement

Resources

License

Stars

Watchers

Forks

Contributors

Languages