boxwerk 0.1.0 โ 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/AGENTS.md +24 -0
- data/ARCHITECTURE.md +264 -0
- data/CHANGELOG.md +74 -3
- data/README.md +61 -335
- data/Rakefile +46 -3
- data/TODO.md +317 -0
- data/USAGE.md +505 -0
- data/exe/boxwerk +55 -22
- data/lib/boxwerk/autoloader_mixin.rb +65 -0
- data/lib/boxwerk/box_manager.rb +405 -0
- data/lib/boxwerk/cli.rb +775 -68
- data/lib/boxwerk/constant_resolver.rb +236 -0
- data/lib/boxwerk/gem_resolver.rb +235 -0
- data/lib/boxwerk/gemfile_require_parser.rb +50 -0
- data/lib/boxwerk/global_context.rb +85 -0
- data/lib/boxwerk/package.rb +71 -28
- data/lib/boxwerk/package_context.rb +103 -0
- data/lib/boxwerk/package_resolver.rb +122 -0
- data/lib/boxwerk/privacy_checker.rb +159 -0
- data/lib/boxwerk/setup.rb +123 -36
- data/lib/boxwerk/version.rb +1 -1
- data/lib/boxwerk/zeitwerk_scanner.rb +172 -0
- data/lib/boxwerk.rb +26 -4
- metadata +54 -24
- data/example/Gemfile +0 -6
- data/example/Gemfile.lock +0 -66
- data/example/README.md +0 -130
- data/example/app.rb +0 -101
- data/example/package.yml +0 -6
- data/example/packages/finance/lib/invoice.rb +0 -51
- data/example/packages/finance/lib/tax_calculator.rb +0 -26
- data/example/packages/finance/package.yml +0 -10
- data/example/packages/util/lib/calculator.rb +0 -21
- data/example/packages/util/lib/geometry.rb +0 -26
- data/example/packages/util/package.yml +0 -5
- data/lib/boxwerk/graph.rb +0 -111
- data/lib/boxwerk/loader.rb +0 -277
- data/lib/boxwerk/registry.rb +0 -37
- data/sig/boxwerk.rbs +0 -4
data/README.md
CHANGED
|
@@ -1,376 +1,102 @@
|
|
|
1
|
-
|
|
1
|
+
<div align="center">
|
|
2
|
+
<h1>
|
|
3
|
+
๐ฆ Boxwerk
|
|
4
|
+
</h1>
|
|
5
|
+
</div>
|
|
2
6
|
|
|
3
|
-
Boxwerk is a
|
|
7
|
+
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](https://github.com/Shopify/packwerk) but provides more robust enforcement at runtime using [`Ruby::Box`](https://docs.ruby-lang.org/en/4.0/Ruby/Box.html), ensuring that only public constants from direct dependencies are accessible. Violations raise `NameError`, turning architectural rules into runtime guarantees.
|
|
4
8
|
|
|
5
|
-
|
|
9
|
+
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.
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
- **Explicit Dependencies**: Dependencies are declared in `package.yml` files, forming a validated DAG.
|
|
9
|
-
- **Ergonomic Imports**: Flexible import strategies (namespaced, aliased, selective, renamed).
|
|
11
|
+
**[Usage Guide](USAGE.md)** ยท **[API Documentation](https://dtcristo.github.io/boxwerk/)** ยท **[Changelog](CHANGELOG.md)**
|
|
10
12
|
|
|
11
|
-
##
|
|
13
|
+
## Features
|
|
12
14
|
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
15
|
+
- Boxwerk reads standard Packwerk `package.yml` files, supporting both dependency and privacy enforcement. Packwerk itself is not required.
|
|
16
|
+
- 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.
|
|
17
|
+
- `Ruby::Box` provides monkey patch isolation between packages.
|
|
18
|
+
- Boxwerk uses [Zeitwerk](https://github.com/fxn/zeitwerk) to automatically load constants in packages with [conventional file structure](https://github.com/fxn/zeitwerk#file-structure) although manual loading is also supported.
|
|
17
19
|
|
|
18
|
-
##
|
|
20
|
+
## Goals
|
|
19
21
|
|
|
20
|
-
-
|
|
21
|
-
- `
|
|
22
|
+
- **Enforce boundaries at runtime.** `Ruby::Box` turns architectural rules into runtime guarantees. Undeclared dependencies and privacy violations raise `NameError`.
|
|
23
|
+
- **Enable gradual modularization.** Add `package.yml` files around existing code and declare dependencies incrementally.
|
|
24
|
+
- **Feel Ruby-native.** Integrates with Bundler, `gems.rb`/`Gemfile`, and standard Ruby tools. `boxwerk exec rake test` feels like any other Ruby command.
|
|
22
25
|
|
|
23
|
-
##
|
|
26
|
+
## Ruby::Box
|
|
24
27
|
|
|
25
|
-
|
|
28
|
+
[`Ruby::Box`](https://docs.ruby-lang.org/en/4.0/Ruby/Box.html) (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`.
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
my_app/
|
|
29
|
-
โโโ Gemfile # Your gem dependencies
|
|
30
|
-
โโโ package.yml # Root package
|
|
31
|
-
โโโ app.rb # Your application entrypoint
|
|
32
|
-
โโโ packages/
|
|
33
|
-
โโโ billing/
|
|
34
|
-
โโโ package.yml # Package manifest
|
|
35
|
-
โโโ lib/
|
|
36
|
-
โโโ invoice.rb # Package code
|
|
37
|
-
```
|
|
30
|
+
Set `RUBY_BOX=1` before starting Ruby. See the [official documentation](https://docs.ruby-lang.org/en/4.0/Ruby/Box.html) for details. See [ARCHITECTURE.md](ARCHITECTURE.md) for how Boxwerk uses `Ruby::Box` internally.
|
|
38
31
|
|
|
39
|
-
|
|
32
|
+
## Quick Start
|
|
40
33
|
|
|
41
|
-
|
|
42
|
-
```ruby
|
|
43
|
-
source 'https://rubygems.org'
|
|
34
|
+
Create packages with `package.yml` files:
|
|
44
35
|
|
|
45
|
-
gem 'boxwerk'
|
|
46
|
-
gem 'money' # Example: gems are auto-required and globally accessible
|
|
47
36
|
```
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
37
|
+
my_app/
|
|
38
|
+
โโโ package.yml
|
|
39
|
+
โโโ main.rb
|
|
40
|
+
โโโ packs/
|
|
41
|
+
โโโ foo/
|
|
42
|
+
โ โโโ package.yml
|
|
43
|
+
โ โโโ lib/foo.rb
|
|
44
|
+
โโโ bar/
|
|
45
|
+
โโโ package.yml
|
|
46
|
+
โโโ lib/bar.rb
|
|
55
47
|
```
|
|
56
48
|
|
|
57
|
-
**`packages/finance/package.yml`:**
|
|
58
49
|
```yaml
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
**`packages/finance/lib/invoice.rb`:**
|
|
65
|
-
```ruby
|
|
66
|
-
class Invoice
|
|
67
|
-
def initialize(amount_cents)
|
|
68
|
-
# Money gem is accessible because it's in the Gemfile
|
|
69
|
-
@amount = Money.new(amount_cents, 'USD')
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
def total
|
|
73
|
-
@amount
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
**`packages/finance/lib/tax_calculator.rb`:**
|
|
79
|
-
```ruby
|
|
80
|
-
class TaxCalculator
|
|
81
|
-
# ...
|
|
82
|
-
end
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### 4. Use in Your Application
|
|
86
|
-
|
|
87
|
-
**`app.rb`:**
|
|
88
|
-
```ruby
|
|
89
|
-
# No requires needed - imports are wired by Boxwerk
|
|
90
|
-
invoice = Finance::Invoice.new(10_000)
|
|
91
|
-
puts invoice.total # => #<Money fractional:10000 currency:USD>
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
### 5. Run Your Application
|
|
95
|
-
|
|
96
|
-
```bash
|
|
97
|
-
RUBY_BOX=1 boxwerk run app.rb
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Boxwerk automatically:
|
|
101
|
-
1. Sets up Bundler
|
|
102
|
-
2. Requires all gems from your Gemfile in the root box
|
|
103
|
-
3. Loads and wires all packages
|
|
104
|
-
4. Executes your script in the root package context
|
|
105
|
-
|
|
106
|
-
## Usage
|
|
107
|
-
|
|
108
|
-
### Running Scripts
|
|
109
|
-
|
|
110
|
-
Execute a Ruby script in the root package context:
|
|
111
|
-
|
|
112
|
-
```bash
|
|
113
|
-
boxwerk run script.rb [args...]
|
|
50
|
+
# package.yml (root)
|
|
51
|
+
enforce_dependencies: true
|
|
52
|
+
dependencies:
|
|
53
|
+
- packs/foo
|
|
54
|
+
- packs/bar
|
|
114
55
|
```
|
|
115
56
|
|
|
116
|
-
|
|
117
|
-
- All gems from your Gemfile (automatically required)
|
|
118
|
-
- All imports defined in the root `package.yml`
|
|
119
|
-
|
|
120
|
-
### Interactive Console
|
|
121
|
-
|
|
122
|
-
TODO: This feature is currenly broken and will run IRB from the root box, not the root package as desired.
|
|
123
|
-
|
|
124
|
-
Start an IRB session in the root package context:
|
|
57
|
+
Install and run:
|
|
125
58
|
|
|
126
59
|
```bash
|
|
127
|
-
|
|
60
|
+
gem install boxwerk
|
|
61
|
+
RUBY_BOX=1 boxwerk run main.rb
|
|
128
62
|
```
|
|
129
63
|
|
|
130
|
-
|
|
64
|
+
No Bundler or Gemfile required for basic usage. To use global or per-package gems, see [USAGE.md](USAGE.md).
|
|
131
65
|
|
|
132
|
-
|
|
66
|
+
## CLI
|
|
133
67
|
|
|
134
|
-
```bash
|
|
135
|
-
boxwerk help
|
|
136
68
|
```
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
```yaml
|
|
143
|
-
exports:
|
|
144
|
-
- PublicClass
|
|
145
|
-
- PublicModule
|
|
146
|
-
|
|
147
|
-
imports:
|
|
148
|
-
- packages/dependency1
|
|
149
|
-
- packages/dependency2: Alias
|
|
69
|
+
boxwerk run <script.rb> Run a Ruby script in a package box
|
|
70
|
+
boxwerk exec <command> [args...] Execute a command in the boxed environment
|
|
71
|
+
boxwerk console Interactive console in a package box
|
|
72
|
+
boxwerk info Show package structure and dependencies
|
|
73
|
+
boxwerk install Install gems for all packages
|
|
150
74
|
```
|
|
151
75
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
Constants that should be visible to packages that import this one.
|
|
155
|
-
|
|
156
|
-
### Imports
|
|
157
|
-
|
|
158
|
-
Dependencies this package needs. **Note**: Dependencies are NOT transitive. If package A imports B, and B imports C, then A cannot access C unless it explicitly imports it.
|
|
159
|
-
|
|
160
|
-
## Import Strategies
|
|
161
|
-
|
|
162
|
-
Boxwerk supports four import strategies in `package.yml`:
|
|
76
|
+
Options: `--package <package>`, `--all`, `--global`. See [USAGE.md](USAGE.md) for details.
|
|
163
77
|
|
|
164
|
-
|
|
78
|
+
## Limitations
|
|
165
79
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
imports:
|
|
170
|
-
- packages/finance
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
Result: `Finance::Invoice`, `Finance::TaxCalculator`
|
|
174
|
-
|
|
175
|
-
### 2. Aliased Namespace
|
|
176
|
-
|
|
177
|
-
Import under a custom module name:
|
|
178
|
-
|
|
179
|
-
```yaml
|
|
180
|
-
imports:
|
|
181
|
-
- packages/finance: Billing
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
Result: `Billing::Invoice`, `Billing::TaxCalculator`
|
|
185
|
-
|
|
186
|
-
**Single Export Optimization**: If a package exports only one constant, it's imported directly (not wrapped in a module):
|
|
187
|
-
|
|
188
|
-
```yaml
|
|
189
|
-
# util exports only Calculator
|
|
190
|
-
imports:
|
|
191
|
-
- packages/util: Calc
|
|
192
|
-
```
|
|
80
|
+
- `Ruby::Box` is experimental in Ruby 4.0
|
|
81
|
+
- No constant reloading (restart required for code changes)
|
|
82
|
+
- IRB autocomplete disabled in console
|
|
193
83
|
|
|
194
|
-
|
|
84
|
+
See [TODO.md](TODO.md) for plans to address these and other planned features.
|
|
195
85
|
|
|
196
|
-
|
|
86
|
+
## Examples
|
|
197
87
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
imports:
|
|
202
|
-
- packages/finance:
|
|
203
|
-
- Invoice
|
|
204
|
-
- TaxCalculator
|
|
205
|
-
```
|
|
206
|
-
|
|
207
|
-
Result: `Invoice`, `TaxCalculator` (no namespace)
|
|
208
|
-
|
|
209
|
-
### 4. Selective Rename
|
|
210
|
-
|
|
211
|
-
Import specific constants with custom names:
|
|
212
|
-
|
|
213
|
-
```yaml
|
|
214
|
-
imports:
|
|
215
|
-
- packages/finance:
|
|
216
|
-
Invoice: Bill
|
|
217
|
-
TaxCalculator: Calculator
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
Result: `Bill`, `Calculator`
|
|
221
|
-
|
|
222
|
-
## Gems and Packages
|
|
223
|
-
|
|
224
|
-
### How Gems Work in Boxwerk
|
|
225
|
-
|
|
226
|
-
When you run `boxwerk`, all gems in your `Gemfile` are:
|
|
227
|
-
1. Automatically loaded via Bundler in the root box
|
|
228
|
-
2. Accessible globally in all package boxes (gems are not isolated)
|
|
229
|
-
|
|
230
|
-
This means:
|
|
231
|
-
- You can use any gem from your Gemfile in any package.
|
|
232
|
-
- Gems don't need to be declared in `package.yml`.
|
|
233
|
-
- You do not `require` gems manually.
|
|
234
|
-
|
|
235
|
-
### Isolation Model
|
|
236
|
-
|
|
237
|
-
- **Root Box**: The box where Ruby bootstraps and all builtin classes/modules are defined. In Boxwerk, the root box performs all setup operations (Bundler setup, gem loading, dependency graph building, package box creation, and import wiring).
|
|
238
|
-
- **Main Box**: The first user box created automatically by Ruby (copied from root box). In Boxwerk, it only runs the `exe/boxwerk` executable file, which then calls into the root box to execute the setup. The main box has no other purpose.
|
|
239
|
-
- **Package Boxes**: Each package (including root package) runs in its own isolated `Ruby::Box` (created by copying from root box after gems are loaded).
|
|
240
|
-
- **Box Inheritance**: All boxes are created via copy-on-write from the root box, inheriting builtin classes and loaded gems.
|
|
241
|
-
- **Gems are Global**: All gems from Gemfile are accessible in all boxes (loaded in root box before package boxes are created).
|
|
242
|
-
- **Package Exports are Isolated**: Only explicit imports from packages are accessible.
|
|
243
|
-
- **No Transitive Access**: Packages can only see their explicit imports.
|
|
244
|
-
|
|
245
|
-
For more details on how Ruby::Box works, see the [official Ruby::Box documentation](https://docs.ruby-lang.org/en/master/Ruby/Box.html).
|
|
246
|
-
|
|
247
|
-
## Known Issues
|
|
248
|
-
|
|
249
|
-
These issues are related to the current state of Ruby::Box in Ruby 4.0+. See the [Ruby::Box documentation](https://docs.ruby-lang.org/en/master/Ruby/Box.html) for known issues with the feature itself.
|
|
250
|
-
|
|
251
|
-
### Gem Requiring in Boxes
|
|
252
|
-
|
|
253
|
-
Requiring any gem from within a box (after boot) currently crashes the Ruby VM. This is likely an issue with Ruby::Box itself. As a workaround, Boxwerk automatically requires all gems from the Gemfile in the root box before creating package boxes, so gems are already loaded and accessible everywhere.
|
|
254
|
-
|
|
255
|
-
### Console Context
|
|
256
|
-
|
|
257
|
-
The console does not correctly run in the root package boxโit runs in the context of the root box instead. It should run in the root package box. However, if we attempt to `require 'irb'` in the root package box, the Ruby VM crashes due to the gem requiring issue described above.
|
|
258
|
-
|
|
259
|
-
### IRB Autocomplete
|
|
260
|
-
|
|
261
|
-
Autocomplete is disabled for the console/IRB by default. When enabled, the Ruby VM crashes as soon as any key is pressed. This appears to be an issue with Ruby::Box and IRB's autocomplete feature interacting poorly.
|
|
262
|
-
|
|
263
|
-
## Architecture
|
|
264
|
-
|
|
265
|
-
### Boot Process
|
|
266
|
-
|
|
267
|
-
1. Setup Bundler and require all gems in the root box
|
|
268
|
-
2. Find root `package.yml` (searches up from current directory)
|
|
269
|
-
3. Build dependency graph from package manifests
|
|
270
|
-
4. Validate dependency graph (no circular dependencies)
|
|
271
|
-
5. Boot packages in topological order
|
|
272
|
-
6. Wire imports into each package box
|
|
273
|
-
7. Execute command in root package context
|
|
274
|
-
|
|
275
|
-
### Internal Components
|
|
276
|
-
|
|
277
|
-
Boxwerk consists of several internal components that work together to provide package isolation:
|
|
278
|
-
|
|
279
|
-
#### `Boxwerk::CLI`
|
|
280
|
-
|
|
281
|
-
The command-line interface handler that:
|
|
282
|
-
- Parses commands (`run`, `console`, `help`)
|
|
283
|
-
- Validates the Ruby environment (checks for `RUBY_BOX=1` and Ruby::Box support)
|
|
284
|
-
- Delegates to `Boxwerk::Setup` for the boot process
|
|
285
|
-
- Executes the requested command in the root package's box context
|
|
286
|
-
|
|
287
|
-
#### `Boxwerk::Setup`
|
|
288
|
-
|
|
289
|
-
The setup orchestrator that:
|
|
290
|
-
- Searches up the directory tree to find the root `package.yml`
|
|
291
|
-
- Creates a `Boxwerk::Graph` instance to build and validate the dependency graph
|
|
292
|
-
- Creates a `Boxwerk::Registry` instance to track booted packages
|
|
293
|
-
- Calls `Boxwerk::Loader.boot_all` to boot all packages in topological order
|
|
294
|
-
- Returns the loaded graph for introspection
|
|
295
|
-
|
|
296
|
-
#### `Boxwerk::Graph`
|
|
297
|
-
|
|
298
|
-
The dependency graph builder that:
|
|
299
|
-
- Parses the root `package.yml` and recursively discovers all package dependencies
|
|
300
|
-
- Builds a directed acyclic graph (DAG) of package relationships
|
|
301
|
-
- Validates that there are no circular dependencies
|
|
302
|
-
- Performs topological sorting to determine boot order (dependencies before consumers)
|
|
303
|
-
- Provides access to all packages in the graph
|
|
304
|
-
|
|
305
|
-
#### `Boxwerk::Package`
|
|
306
|
-
|
|
307
|
-
The package manifest parser that:
|
|
308
|
-
- Represents a single package with its configuration
|
|
309
|
-
- Parses `package.yml` files to extract exports and imports
|
|
310
|
-
- Normalizes the polymorphic import syntax (String, Array, Hash)
|
|
311
|
-
- Stores the package path, name, exports, imports, and box reference
|
|
312
|
-
- Tracks which exports have been loaded via `loaded_exports` hash (export name โ file path)
|
|
313
|
-
- Tracks whether the package has been booted
|
|
314
|
-
|
|
315
|
-
#### `Boxwerk::Loader`
|
|
316
|
-
|
|
317
|
-
The package loader that:
|
|
318
|
-
- Creates a new `Ruby::Box` for each package (including the root package)
|
|
319
|
-
- Loads exported constants lazily on-demand when they are imported by other packages
|
|
320
|
-
- Uses Zeitwerk naming conventions to discover file locations for exported constants
|
|
321
|
-
- Caches loaded exports in `package.loaded_exports` to avoid redundant file loading
|
|
322
|
-
- Wires imports by injecting constants from dependency boxes into consumer boxes
|
|
323
|
-
- Implements all four import strategies (default namespace, aliased namespace, selective import, selective rename)
|
|
324
|
-
- Handles the single-export optimization for namespace imports
|
|
325
|
-
- Registers each booted package in the registry
|
|
326
|
-
- Only loads files that define exported constants, never loading non-exported code
|
|
327
|
-
|
|
328
|
-
#### `Boxwerk::Registry`
|
|
329
|
-
|
|
330
|
-
The package registry that:
|
|
331
|
-
- Tracks all booted package instances
|
|
332
|
-
- Allows packages to be retrieved by name during the wiring phase
|
|
333
|
-
- Ensures each package is only booted once
|
|
334
|
-
- Provides a clean interface for package lookup
|
|
335
|
-
|
|
336
|
-
## Example
|
|
337
|
-
|
|
338
|
-
See the [example/](example/) directory for a complete working example with:
|
|
339
|
-
|
|
340
|
-
- Multi-package application
|
|
341
|
-
- Gem usage
|
|
342
|
-
- Transitive dependency demonstration
|
|
343
|
-
- Isolation verification
|
|
344
|
-
- Console usage examples
|
|
345
|
-
|
|
346
|
-
Run it with:
|
|
347
|
-
|
|
348
|
-
```bash
|
|
349
|
-
cd example
|
|
350
|
-
RUBY_BOX=1 boxwerk run app.rb
|
|
351
|
-
```
|
|
352
|
-
|
|
353
|
-
Or explore interactively:
|
|
354
|
-
|
|
355
|
-
```bash
|
|
356
|
-
cd example
|
|
357
|
-
RUBY_BOX=1 boxwerk console
|
|
358
|
-
```
|
|
88
|
+
- [`examples/minimal/`](examples/minimal/) โ Three packages, dependency enforcement, no gems
|
|
89
|
+
- [`examples/complex/`](examples/complex/) โ Namespaced constants, privacy, per-package gems, tests
|
|
90
|
+
- [`examples/rails/`](examples/rails/) โ Usage with Rails
|
|
359
91
|
|
|
360
92
|
## Development
|
|
361
93
|
|
|
362
|
-
After checking out the repo, run `bin/setup` to install dependencies. Then, run the tests:
|
|
363
|
-
|
|
364
94
|
```bash
|
|
365
|
-
|
|
95
|
+
bundle install # Install dependencies
|
|
96
|
+
RUBY_BOX=1 bundle exec rake # Run all tests (unit, e2e, examples)
|
|
97
|
+
bundle exec rake format # Format code
|
|
366
98
|
```
|
|
367
99
|
|
|
368
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
|
369
|
-
|
|
370
100
|
## License
|
|
371
101
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
## Contribution
|
|
375
|
-
|
|
376
|
-
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you shall be dual licensed as above, without any additional terms or conditions.
|
|
102
|
+
Available as open source under the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
|
@@ -1,8 +1,51 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require 'bundler/gem_tasks'
|
|
4
|
-
require 'minitest/test_task'
|
|
5
4
|
|
|
6
|
-
|
|
5
|
+
STREE_FILES = '**/*.rb **/Rakefile'
|
|
6
|
+
EXAMPLES_DIR = File.join(__dir__, 'examples')
|
|
7
|
+
EXAMPLE_DIRS =
|
|
8
|
+
Dir.glob(File.join(EXAMPLES_DIR, '*')).select { |d| File.directory?(d) }.sort
|
|
7
9
|
|
|
8
|
-
task
|
|
10
|
+
task :test do
|
|
11
|
+
$LOAD_PATH.unshift(File.join(__dir__, 'test'))
|
|
12
|
+
Dir.glob('test/boxwerk/**/*_test.rb').sort.each { |f| require_relative f }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
task :e2e do
|
|
16
|
+
sh('ruby', 'test/e2e_test.rb')
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
namespace :example do
|
|
20
|
+
EXAMPLE_DIRS.each do |dir|
|
|
21
|
+
name = File.basename(dir)
|
|
22
|
+
desc "Run the #{name} example"
|
|
23
|
+
task name.to_sym do
|
|
24
|
+
dir = File.join(EXAMPLES_DIR, name)
|
|
25
|
+
abort("Example not found: #{name}") unless File.directory?(dir)
|
|
26
|
+
|
|
27
|
+
puts "==> example:#{name}"
|
|
28
|
+
run_script = File.join(dir, 'run.sh')
|
|
29
|
+
if File.exist?(run_script)
|
|
30
|
+
sh({ 'RUBY_BOX' => '1' }, 'sh', run_script, chdir: dir)
|
|
31
|
+
else
|
|
32
|
+
sh(
|
|
33
|
+
{ 'RUBY_BOX' => '1' },
|
|
34
|
+
File.join(dir, 'bin', 'boxwerk'),
|
|
35
|
+
'exec',
|
|
36
|
+
'--all',
|
|
37
|
+
'rake',
|
|
38
|
+
chdir: dir,
|
|
39
|
+
)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
task examples: EXAMPLE_DIRS.map { |d| "example:#{File.basename(d)}" }
|
|
46
|
+
|
|
47
|
+
task :format do
|
|
48
|
+
sh "bundle exec stree write #{STREE_FILES}"
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
task default: %i[test e2e examples]
|