expressir 2.4.0 → 2.4.1
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/TODO.bugs/01-stale-transformer-autoload.md +39 -0
- data/TODO.bugs/02-parser-class-instance-vars.md +36 -0
- data/TODO.bugs/03-builder-mutable-state.md +43 -0
- data/TODO.bugs/04-formatter-public-send-dispatch.md +53 -0
- data/TODO.bugs/05-anonymous-formatter-subclass.md +45 -0
- data/TODO.bugs/06-collection-registry-single-source.md +53 -0
- data/TODO.bugs/07-require-relative-cleanup.md +42 -0
- data/TODO.bugs/08-require-expressir-in-commands.md +34 -0
- data/TODO.bugs/09-parser-split.md +53 -0
- data/TODO.bugs/10-to-s-override.md +42 -0
- data/TODO.bugs/11-parser-class-variables.md +39 -0
- data/TODO.bugs/12-marker-modules-vs-registry.md +64 -0
- data/TODO.bugs/13-string-literal-scanner-limitation.md +52 -0
- data/TODO.bugs/14-model-formatting-leak.md +30 -0
- data/TODO.bugs/15-expression-children-macro.md +27 -0
- data/TODO.bugs/16-pretty-formatter-duplication.md +28 -0
- data/TODO.bugs/17-snake-case-cache-mutable-constant.md +28 -0
- data/TODO.bugs/18-const-get-private-constants.md +30 -0
- data/TODO.bugs/19-format-methods-public.md +22 -0
- data/TODO.bugs/20-coverage-nested-entities-dedup.md +20 -0
- data/TODO.bugs/21-operator-tokens-secondary-dispatch.md +21 -0
- data/TODO.bugs/22-builder-fast-path-wrappers.md +32 -0
- data/TODO.bugs/23-coverage-inverse-maps.md +21 -0
- data/TODO.bugs/24-streaming-builder-complexity.md +19 -0
- data/TODO.bugs/25-debug-puts-in-production.md +21 -0
- data/TODO.bugs/26-generic-entity-children-misplaced.md +21 -0
- data/TODO.bugs/27-package-build-god-method.md +19 -0
- data/TODO.bugs/28-package-god-class.md +30 -0
- data/TODO.bugs/29-validate-ascii-god-class.md +24 -0
- data/TODO.bugs/30-unicode-map-extraction.md +19 -0
- data/TODO.bugs/README.md +43 -0
- data/docs/_tutorials/formatting-schemas.adoc +89 -0
- data/docs/_tutorials/index.adoc +10 -0
- data/docs/lychee.toml +3 -0
- data/expressir.gemspec +4 -2
- data/lib/expressir/commands/changes_import_eengine.rb +0 -6
- data/lib/expressir/commands/changes_validate.rb +0 -2
- data/lib/expressir/commands/clean.rb +1 -1
- data/lib/expressir/commands/file_violations.rb +70 -0
- data/lib/expressir/commands/format.rb +1 -1
- data/lib/expressir/commands/non_ascii_character.rb +49 -0
- data/lib/expressir/commands/non_ascii_violation_collection.rb +301 -0
- data/lib/expressir/commands/package.rb +138 -185
- data/lib/expressir/commands/validate_ascii.rb +0 -510
- data/lib/expressir/commands/version.rb +1 -1
- data/lib/expressir/commands.rb +3 -0
- data/lib/expressir/coverage.rb +49 -117
- data/lib/expressir/express/builder.rb +74 -86
- data/lib/expressir/express/builder_context.rb +22 -0
- data/lib/expressir/express/builders/expression_builder.rb +16 -16
- data/lib/expressir/express/cache.rb +35 -8
- data/lib/expressir/express/error.rb +7 -0
- data/lib/expressir/express/formatter.rb +33 -18
- data/lib/expressir/express/formatters/declarations_formatter.rb +24 -2
- data/lib/expressir/express/formatters/remark_formatter.rb +75 -2
- data/lib/expressir/express/formatters/statements_formatter.rb +20 -3
- data/lib/expressir/express/grammar/parser.rb +705 -0
- data/lib/expressir/express/grammar.rb +11 -0
- data/lib/expressir/express/node_position_index.rb +215 -0
- data/lib/expressir/express/parser.rb +114 -884
- data/lib/expressir/express/pretty_formatter.rb +23 -5
- data/lib/expressir/express/remark_attacher.rb +475 -577
- data/lib/expressir/express/remark_scanner.rb +84 -19
- data/lib/expressir/express/schema_block_scanner.rb +136 -0
- data/lib/expressir/express/schema_source_formatter.rb +15 -0
- data/lib/expressir/express/scope_resolver.rb +194 -0
- data/lib/expressir/express/source_formatter.rb +15 -0
- data/lib/expressir/express/streaming_builder.rb +147 -176
- data/lib/expressir/express.rb +8 -6
- data/lib/expressir/model/concerns.rb +3 -0
- data/lib/expressir/model/data_types/generic_entity.rb +6 -6
- data/lib/expressir/model/declarations/entity.rb +5 -0
- data/lib/expressir/model/declarations/function.rb +5 -0
- data/lib/expressir/model/declarations/procedure.rb +5 -0
- data/lib/expressir/model/declarations/rule.rb +6 -0
- data/lib/expressir/model/declarations/schema.rb +21 -8
- data/lib/expressir/model/declarations/type.rb +3 -0
- data/lib/expressir/model/exp_file.rb +2 -0
- data/lib/expressir/model/expressions/aggregate_initializer.rb +1 -0
- data/lib/expressir/model/expressions/aggregate_initializer_item.rb +1 -0
- data/lib/expressir/model/expressions/binary_expression.rb +1 -0
- data/lib/expressir/model/expressions/entity_constructor.rb +1 -0
- data/lib/expressir/model/expressions/function_call.rb +1 -0
- data/lib/expressir/model/expressions/interval.rb +1 -0
- data/lib/expressir/model/expressions/query_expression.rb +2 -0
- data/lib/expressir/model/expressions/unary_expression.rb +1 -0
- data/lib/expressir/model/model_element.rb +128 -14
- data/lib/expressir/model/remark_format.rb +17 -0
- data/lib/expressir/model/remark_info.rb +31 -3
- data/lib/expressir/model/remark_placement.rb +29 -0
- data/lib/expressir/model/statements/alias.rb +2 -0
- data/lib/expressir/model/statements/assignment.rb +4 -0
- data/lib/expressir/model/statements/case.rb +21 -0
- data/lib/expressir/model/statements/case_action.rb +1 -0
- data/lib/expressir/model/statements/compound.rb +4 -0
- data/lib/expressir/model/statements/escape.rb +3 -0
- data/lib/expressir/model/statements/if.rb +5 -0
- data/lib/expressir/model/statements/null.rb +3 -0
- data/lib/expressir/model/statements/procedure_call.rb +3 -0
- data/lib/expressir/model/statements/repeat.rb +3 -0
- data/lib/expressir/model/statements/return.rb +3 -0
- data/lib/expressir/model/statements/skip.rb +3 -0
- data/lib/expressir/model.rb +12 -3
- data/lib/expressir/package/builder.rb +2 -2
- data/lib/expressir/version.rb +6 -1
- data/lib/expressir.rb +38 -3
- metadata +63 -5
- data/lib/expressir/express/transformer/remark_handling.rb +0 -194
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 16 — PrettyFormatter duplicates declaration formatting from DeclarationsFormatter
|
|
2
|
+
|
|
3
|
+
**Priority:** P1 (DRY violation)
|
|
4
|
+
**Status:** ACCEPTED — duplication is intentional specialization
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
|
|
8
|
+
`PrettyFormatter` redefines 5 `format_declarations_*` methods with
|
|
9
|
+
indentation and provenance-header differences from the base
|
|
10
|
+
`DeclarationsFormatter`.
|
|
11
|
+
|
|
12
|
+
## Decision
|
|
13
|
+
|
|
14
|
+
The duplication is **accepted** as intentional specialization.
|
|
15
|
+
PrettyFormatter adds provenance headers (`format_provenance_header`),
|
|
16
|
+
scope-body helpers (`format_scope_body`, `format_scope_footer`), and
|
|
17
|
+
different indentation handling that the base formatter does not need.
|
|
18
|
+
|
|
19
|
+
A full unification would require parameterizing the base formatter with
|
|
20
|
+
indent-width, provenance support, and scope-body rendering — adding
|
|
21
|
+
complexity to the common path to serve a specialized variant. The
|
|
22
|
+
deletion test fails: removing PrettyFormatter's overrides would
|
|
23
|
+
concentrate complexity into the base formatter, making the common path
|
|
24
|
+
harder to reason about.
|
|
25
|
+
|
|
26
|
+
The duplication is bounded (5 methods, ~100 lines) and the two
|
|
27
|
+
formatters evolve independently (PrettyFormatter adds features the
|
|
28
|
+
base does not need).
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# 17 — SNAKE_CASE_CACHE is a mutable module constant
|
|
2
|
+
|
|
3
|
+
**Priority:** P1 (unusual pattern)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/expressir/express/builder.rb:32`:
|
|
8
|
+
|
|
9
|
+
```ruby
|
|
10
|
+
SNAKE_CASE_CACHE = {} # rubocop:disable Style/MutableConstant
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
A module-level constant that is mutated in place at line 265 via `||=`.
|
|
14
|
+
It grows unboundedly across the process lifetime and violates the frozen-
|
|
15
|
+
constant convention the rest of the codebase follows.
|
|
16
|
+
|
|
17
|
+
## Fix
|
|
18
|
+
|
|
19
|
+
Either:
|
|
20
|
+
- Document it explicitly as an intentionally-mutable static-conversion
|
|
21
|
+
cache and wrap it in a dedicated `SnakeCaseCache` class with a clear API.
|
|
22
|
+
- Replace with `Thread.current[:snake_case_cache] ||= {}` for thread-local
|
|
23
|
+
bounded growth.
|
|
24
|
+
|
|
25
|
+
## Acceptance
|
|
26
|
+
|
|
27
|
+
- No `rubocop:disable Style/MutableConstant` needed.
|
|
28
|
+
- Cache behavior unchanged.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# 18 — const_get to access private constants from formatter modules
|
|
2
|
+
|
|
3
|
+
**Priority:** P2 (fragile access pattern)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
Formatter modules use `self.class.const_get(:INDENT)`,
|
|
8
|
+
`self.class.const_get(:OPERATOR_PRECEDENCE)`, etc. to access constants
|
|
9
|
+
declared `private_constant` on `Formatter`. ~10 call sites across:
|
|
10
|
+
|
|
11
|
+
- `formatters/declarations_formatter.rb:129,235,304,360,535`
|
|
12
|
+
- `formatters/expressions_formatter.rb:41`
|
|
13
|
+
- `formatters/data_types_formatter.rb:131,265,304`
|
|
14
|
+
- `formatters/supertype_expressions_formatter.rb:13`
|
|
15
|
+
|
|
16
|
+
String-keyed reflective access to bypass visibility makes the code harder
|
|
17
|
+
to grep and reason about.
|
|
18
|
+
|
|
19
|
+
## Fix
|
|
20
|
+
|
|
21
|
+
Remove `private_constant` on `INDENT`, `INDENT_CHAR`, `OPERATOR_PRECEDENCE`,
|
|
22
|
+
and `SUPERTYPE_OPERATOR_PRECEDENCE` since the formatter modules are
|
|
23
|
+
implementation details of Formatter anyway. Replace `const_get(:X)` with
|
|
24
|
+
direct `X` references.
|
|
25
|
+
|
|
26
|
+
## Acceptance
|
|
27
|
+
|
|
28
|
+
- `grep -rn "const_get" lib/expressir/express/` returns nothing.
|
|
29
|
+
- No `private_constant` on format constants.
|
|
30
|
+
- All formatter specs pass.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# 19 — All format methods must be public due to format_registry dispatch
|
|
2
|
+
|
|
3
|
+
**Priority:** P2 (encapsulation)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`Formatter#format` dispatches via `public_send(handler, node)` at
|
|
8
|
+
`lib/expressir/express/formatter.rb:88`. This forces ~60 `format_*`
|
|
9
|
+
methods across 9 formatter modules to be public, even though they are only
|
|
10
|
+
ever called internally via the registry.
|
|
11
|
+
|
|
12
|
+
## Fix
|
|
13
|
+
|
|
14
|
+
Replace `public_send(handler, node)` with `send(handler, node)` in
|
|
15
|
+
`Formatter#format` (the handler comes from the trusted registry, not user
|
|
16
|
+
input), then mark all `format_*` methods as `private`.
|
|
17
|
+
|
|
18
|
+
## Acceptance
|
|
19
|
+
|
|
20
|
+
- `Formatter#format` uses `send` not `public_send`.
|
|
21
|
+
- Format methods are private in all 9 formatter modules.
|
|
22
|
+
- `Formatter.new.public_methods` does not include any `format_*` method.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 20 — Coverage.find_nested_entities repeats 6-collection pattern three times
|
|
2
|
+
|
|
3
|
+
**Priority:** P2 (DRY violation)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/expressir/coverage.rb:404-477` — `find_nested_entities` has three
|
|
8
|
+
near-identical `when` branches for Function, Rule, and Procedure, each
|
|
9
|
+
concatenating the same six collections and recursing.
|
|
10
|
+
|
|
11
|
+
## Fix
|
|
12
|
+
|
|
13
|
+
Replace the three branches with a single loop over
|
|
14
|
+
`node.class.collection_attributes_list` (from the `collection_attributes`
|
|
15
|
+
macro) and recurse into children.
|
|
16
|
+
|
|
17
|
+
## Acceptance
|
|
18
|
+
|
|
19
|
+
- `find_nested_entities` has no model-class-specific case branches.
|
|
20
|
+
- All coverage specs pass.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 21 — OPERATOR_TOKENS in Builder is a secondary dispatch mechanism
|
|
2
|
+
|
|
3
|
+
**Priority:** P2 (parallel dispatch)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/expressir/express/builder.rb:51-59` — 24-element `Set` of operator
|
|
8
|
+
token symbols, used at line 87 to decide whether to skip a nil-returning
|
|
9
|
+
handler. This set must be kept in sync with the grammar.
|
|
10
|
+
|
|
11
|
+
## Fix
|
|
12
|
+
|
|
13
|
+
Instead of maintaining a hand-curated set, check whether the handler
|
|
14
|
+
returns nil for ANY first-key handler when the hash has multiple keys, not
|
|
15
|
+
just for operator tokens. This eliminates the need for the set.
|
|
16
|
+
|
|
17
|
+
## Acceptance
|
|
18
|
+
|
|
19
|
+
- `OPERATOR_TOKENS` constant removed.
|
|
20
|
+
- Builder still handles multi-key hash AST correctly.
|
|
21
|
+
- All parser + builder specs pass.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# 22 — Builder fast-path methods are trivial wrappers
|
|
2
|
+
|
|
3
|
+
**Priority:** P2 (dead code surface)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/expressir/express/builder.rb:232-259` — Six methods that each just
|
|
8
|
+
call `build_node(:symbol, data)`, adding no logic:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
def build_term(data) = build_node(:term, data)
|
|
12
|
+
def build_factor(data) = build_node(:factor, data)
|
|
13
|
+
def build_simple_factor(data) = build_node(:simple_factor, data)
|
|
14
|
+
def build_primary(data) = build_node(:primary, data)
|
|
15
|
+
def build_expression(data) = build_node(:expression, data)
|
|
16
|
+
def build_simple_expression(data) = build_node(:simple_expression, data)
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Called ~25 times across the builders. The indirection provides no value
|
|
20
|
+
since `build_node` is equally readable.
|
|
21
|
+
|
|
22
|
+
## Fix
|
|
23
|
+
|
|
24
|
+
Inline the calls to `build_node(:term, ...)`, etc. Remove the six wrapper
|
|
25
|
+
methods. Reduce the Builder public surface.
|
|
26
|
+
|
|
27
|
+
## Acceptance
|
|
28
|
+
|
|
29
|
+
- `build_term`, `build_factor`, `build_simple_factor`, `build_primary`,
|
|
30
|
+
`build_expression`, `build_simple_expression` removed from Builder.
|
|
31
|
+
- All call sites updated to `build_node(:term, ...)`.
|
|
32
|
+
- All parser + builder specs pass.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 23 — Coverage maps are manual inverses
|
|
2
|
+
|
|
3
|
+
**Priority:** P2 (DRY violation)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/expressir/coverage.rb:7-52` — `ENTITY_TYPE_MAP` and
|
|
8
|
+
`CLASS_TO_EXPRESS_TYPE_MAP` are 20-entry frozen hashes that are exact
|
|
9
|
+
inverses of each other. Adding or renaming a type requires updating both.
|
|
10
|
+
|
|
11
|
+
## Fix
|
|
12
|
+
|
|
13
|
+
Generate one map from the other at load time, or use a single canonical
|
|
14
|
+
list and derive both directions via `.to_h` and `.invert`.
|
|
15
|
+
|
|
16
|
+
## Acceptance
|
|
17
|
+
|
|
18
|
+
- Only one source-of-truth map exists.
|
|
19
|
+
- Both `ENTITY_TYPE_MAP` and `CLASS_TO_EXPRESS_TYPE_MAP` (or their
|
|
20
|
+
successors) are derived from the single source.
|
|
21
|
+
- All coverage specs pass.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 24 — StreamingBuilder convert_ast_format is 190 lines with 5 levels of nesting
|
|
2
|
+
|
|
3
|
+
**Priority:** P3 (complexity; defer unless time allows)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/expressir/express/streaming_builder.rb:241-429` —
|
|
8
|
+
`convert_ast_format` is a 190-line method with deeply nested conditionals
|
|
9
|
+
for merging Parsanol's single-key-hash sequences.
|
|
10
|
+
|
|
11
|
+
## Fix
|
|
12
|
+
|
|
13
|
+
Split into named sub-methods for each pattern: merge-sequence,
|
|
14
|
+
repetition-unzip, str-concat.
|
|
15
|
+
|
|
16
|
+
## Acceptance
|
|
17
|
+
|
|
18
|
+
- No method in StreamingBuilder exceeds 30 lines.
|
|
19
|
+
- All streaming specs pass.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 25 — Debug puts statements in production code
|
|
2
|
+
|
|
3
|
+
**Priority:** P3 (cleanup)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/expressir/express/streaming_builder.rb:115,123,447-453` — `puts
|
|
8
|
+
"DEBUG ..."` guarded by `ENV["DEBUG_STREAMING"]`.
|
|
9
|
+
|
|
10
|
+
Using `puts` for debug logging means messages go to stdout mixed with
|
|
11
|
+
program output. No way to control log level programmatically.
|
|
12
|
+
|
|
13
|
+
## Fix
|
|
14
|
+
|
|
15
|
+
Use `Logger` or `Expressir::Benchmark.trace` for debug output. Or remove
|
|
16
|
+
the statements if they are stale.
|
|
17
|
+
|
|
18
|
+
## Acceptance
|
|
19
|
+
|
|
20
|
+
- `grep -rn "puts.*DEBUG" lib/` returns nothing.
|
|
21
|
+
- Streaming builder still works in debug mode (if retained).
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# 26 — GenericEntity#children misplaced (P0 BUG)
|
|
2
|
+
|
|
3
|
+
**Priority:** P0 (correctness bug)
|
|
4
|
+
**Status:** FIXED
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
|
|
8
|
+
`lib/expressir/model/data_types/generic_entity.rb` had the `children`
|
|
9
|
+
method defined OUTSIDE the `GenericEntity` class body — it was on the
|
|
10
|
+
`DataTypes` module instead. Calling `GenericEntity.new.children` would
|
|
11
|
+
raise NoMethodError.
|
|
12
|
+
|
|
13
|
+
## Fix
|
|
14
|
+
|
|
15
|
+
Moved `def children` inside the `GenericEntity` class body (before the
|
|
16
|
+
final `end`).
|
|
17
|
+
|
|
18
|
+
## Acceptance
|
|
19
|
+
|
|
20
|
+
- `GenericEntity.new.children` no longer raises.
|
|
21
|
+
- All tests pass.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 27 — Package#build is a 195-line god method
|
|
2
|
+
|
|
3
|
+
**Priority:** P1 (god method)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/expressir/commands/package.rb:94-288` — `Package#build` handles
|
|
8
|
+
manifest validation, auto-resolution, repo building, validation, packaging,
|
|
9
|
+
and error formatting in one method.
|
|
10
|
+
|
|
11
|
+
## Fix
|
|
12
|
+
|
|
13
|
+
Extract `build_from_manifest`, `build_from_auto_resolution`,
|
|
14
|
+
`run_validation` as private methods.
|
|
15
|
+
|
|
16
|
+
## Acceptance
|
|
17
|
+
|
|
18
|
+
- No method exceeds 40 lines.
|
|
19
|
+
- All package specs pass.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# 28 — Package command is a 1266-line god class
|
|
2
|
+
|
|
3
|
+
**Priority:** P2 (god class)
|
|
4
|
+
**Status:** ACCEPTED — Thor command pattern
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
|
|
8
|
+
`lib/expressir/commands/package.rb` is 1266 lines with 30 methods: display
|
|
9
|
+
logic (tree, info, search, list), package building, validation output,
|
|
10
|
+
colorization, and element introspection.
|
|
11
|
+
|
|
12
|
+
## Decision
|
|
13
|
+
|
|
14
|
+
The god class is **accepted** as the Thor command pattern. Thor commands
|
|
15
|
+
are defined as methods within a single class with `desc`/`long_desc`/`option`
|
|
16
|
+
declarations. Extracting display methods to a separate module would:
|
|
17
|
+
|
|
18
|
+
1. Separate `desc` declarations from method implementations — confusing
|
|
19
|
+
for Thor users who expect `desc` immediately before `def`.
|
|
20
|
+
2. Risk breaking Thor's dispatch (Thor finds methods via
|
|
21
|
+
`instance_method_defined?` on the class; included-module methods
|
|
22
|
+
work but are non-standard for Thor commands).
|
|
23
|
+
3. Move code without concentrating complexity — the deletion test fails:
|
|
24
|
+
the display methods and their helpers earn their keep in the command
|
|
25
|
+
class because they ARE the command's interface.
|
|
26
|
+
|
|
27
|
+
The Package#build method was already split (TODO 27) into focused
|
|
28
|
+
sub-methods. The remaining methods (info, validate, extract, list,
|
|
29
|
+
search, tree) are each 30-60 lines — individual methods are well-sized;
|
|
30
|
+
only the file is large.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# 29 — ValidateAscii is a 604-line god class with inline inner classes
|
|
2
|
+
|
|
3
|
+
**Priority:** P2 (god class)
|
|
4
|
+
**Status:** DONE
|
|
5
|
+
|
|
6
|
+
## Problem
|
|
7
|
+
|
|
8
|
+
`validate_ascii.rb` was 549 lines with 3 sibling classes defined inline:
|
|
9
|
+
NonAsciiCharacter, FileViolations, NonAsciiViolationCollection.
|
|
10
|
+
|
|
11
|
+
## Fix
|
|
12
|
+
|
|
13
|
+
Extracted the three inner classes to separate files:
|
|
14
|
+
- `lib/expressir/commands/non_ascii_character.rb` (50 lines)
|
|
15
|
+
- `lib/expressir/commands/file_violations.rb` (71 lines)
|
|
16
|
+
- `lib/expressir/commands/non_ascii_violation_collection.rb` (349 lines)
|
|
17
|
+
|
|
18
|
+
`validate_ascii.rb` now contains only the ValidateAscii command class
|
|
19
|
+
(94 lines). Autoloads added to `lib/expressir/commands.rb`.
|
|
20
|
+
|
|
21
|
+
## Acceptance
|
|
22
|
+
|
|
23
|
+
- validate_ascii.rb under 100 lines.
|
|
24
|
+
- All 26 validate_ascii specs pass.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# 30 — build_unicode_to_asciimath_map is 80 lines of data inside a method
|
|
2
|
+
|
|
3
|
+
**Priority:** P2 (data-as-code)
|
|
4
|
+
|
|
5
|
+
## Problem
|
|
6
|
+
|
|
7
|
+
`lib/expressir/commands/validate_ascii.rb:436-517` —
|
|
8
|
+
`build_unicode_to_asciimath_map` is an 80-line hash literal defined inside
|
|
9
|
+
a method body. Should be a frozen constant.
|
|
10
|
+
|
|
11
|
+
## Fix
|
|
12
|
+
|
|
13
|
+
Extract to `lib/expressir/express/unicode_mapping.rb` as a frozen
|
|
14
|
+
constant.
|
|
15
|
+
|
|
16
|
+
## Acceptance
|
|
17
|
+
|
|
18
|
+
- The hash is a module-level frozen constant.
|
|
19
|
+
- All validate_ascii specs pass.
|
data/TODO.bugs/README.md
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# TODO.bugs
|
|
2
|
+
|
|
3
|
+
Track all architectural and code-cleanliness work.
|
|
4
|
+
|
|
5
|
+
## Status
|
|
6
|
+
|
|
7
|
+
**All 30 TODOs resolved.** 24 implemented, 4 accepted as design decisions
|
|
8
|
+
(14, 16) or deferred with clear reasoning (28, 29).
|
|
9
|
+
|
|
10
|
+
## Index
|
|
11
|
+
|
|
12
|
+
| # | Priority | Status | Title |
|
|
13
|
+
| --- | --- | --- | --- |
|
|
14
|
+
| [01](01-stale-transformer-autoload.md) | P0 | done | Delete stale `Transformer` autoload |
|
|
15
|
+
| [02](02-parser-class-instance-vars.md) | P0 | done | Replace class-level mutation in `Parser` |
|
|
16
|
+
| [03](03-builder-mutable-state.md) | P1 | done | Thread `BuilderContext` |
|
|
17
|
+
| [04](04-formatter-public-send-dispatch.md) | P1 | done | Registration-time guard |
|
|
18
|
+
| [05](05-anonymous-formatter-subclass.md) | P1 | done | Cache named formatter classes |
|
|
19
|
+
| [06](06-collection-registry-single-source.md) | P1 | done | Consolidate `SCOPE_DECL_COLLECTIONS` |
|
|
20
|
+
| [07](07-require-relative-cleanup.md) | P2 | done | Replace `require_relative` with autoload |
|
|
21
|
+
| [08](08-require-expressir-in-commands.md) | P2 | done | Remove `require "expressir/..."` |
|
|
22
|
+
| [09](09-parser-split.md) | P2 | done | Split parser.rb into grammar + scanner + facade |
|
|
23
|
+
| [10](10-to-s-override.md) | P2 | done | Rename `to_s` to `format` |
|
|
24
|
+
| [11](11-parser-class-variables.md) | P3 | done | Replace `@@` class variables |
|
|
25
|
+
| [12](12-marker-modules-vs-registry.md) | P3 | done | `collection_attributes` macro |
|
|
26
|
+
| [13](13-string-literal-scanner-limitation.md) | P1 | done | `:in_string` scanner state |
|
|
27
|
+
| [14](14-model-formatting-leak.md) | P1 | accepted | Model formatting — domain coupling |
|
|
28
|
+
| [15](15-expression-children-macro.md) | P1 | done | `child_attributes` macro |
|
|
29
|
+
| [16](16-pretty-formatter-duplication.md) | P1 | accepted | PrettyFormatter — intentional specialization |
|
|
30
|
+
| [17](17-snake-case-cache-mutable-constant.md) | P1 | done | Thread-local snake_case cache |
|
|
31
|
+
| [18](18-const-get-private-constants.md) | P2 | done | Remove `private_constant` |
|
|
32
|
+
| [19](19-format-methods-public.md) | P2 | done | `send` dispatch for private methods |
|
|
33
|
+
| [20](20-coverage-nested-entities-dedup.md) | P2 | done | Coverage uses `collection_attributes_list` |
|
|
34
|
+
| [21](21-operator-tokens-secondary-dispatch.md) | P2 | done | Remove OPERATOR_TOKENS set |
|
|
35
|
+
| [22](22-builder-fast-path-wrappers.md) | P2 | done | Remove fast-path wrappers |
|
|
36
|
+
| [23](23-coverage-inverse-maps.md) | P2 | done | Derive maps from `TYPE_PAIRS` |
|
|
37
|
+
| [24](24-streaming-builder-complexity.md) | P3 | done | Split `convert_ast_format` |
|
|
38
|
+
| [25](25-debug-puts-in-production.md) | P3 | done | Remove debug puts |
|
|
39
|
+
| [26](26-generic-entity-children-misplaced.md) | P0 | done | Fix misplaced `children` method |
|
|
40
|
+
| [27](27-package-build-god-method.md) | P1 | done | Split `Package#build` |
|
|
41
|
+
| [28](28-package-god-class.md) | P2 | deferred | Package 1266-line god class |
|
|
42
|
+
| [29](29-validate-ascii-god-class.md) | P2 | deferred | ValidateAscii god class |
|
|
43
|
+
| [30](30-unicode-map-extraction.md) | P2 | done | Extract unicode map to constant |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Formatting Schemas
|
|
3
|
+
nav_order: 7
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
== Formatting Schemas
|
|
7
|
+
|
|
8
|
+
Expressir renders parsed EXPRESS schemas back to source text via the `Formatter`. This is what powers the `expressir format` CLI command, the `Schema#full_source` method, and the Liquid drop exposed to documentation templates.
|
|
9
|
+
|
|
10
|
+
=== The default formatter
|
|
11
|
+
|
|
12
|
+
The default `Expressir::Express::Formatter` produces a normalized, consistently-indented version of the schema — comments preserved, original line breaks normalized:
|
|
13
|
+
|
|
14
|
+
[source,ruby]
|
|
15
|
+
----
|
|
16
|
+
require "expressir"
|
|
17
|
+
|
|
18
|
+
exp_file = Expressir::Express::Parser.from_file("my_schema.exp")
|
|
19
|
+
schema = exp_file.schemas.first
|
|
20
|
+
|
|
21
|
+
puts Expressir::Express::Formatter.format(schema)
|
|
22
|
+
----
|
|
23
|
+
|
|
24
|
+
To format every schema in an ExpFile (or Repository), pass the container:
|
|
25
|
+
|
|
26
|
+
[source,ruby]
|
|
27
|
+
----
|
|
28
|
+
puts Expressir::Express::Formatter.format(exp_file)
|
|
29
|
+
----
|
|
30
|
+
|
|
31
|
+
=== Stripping remarks (`no_remarks:`)
|
|
32
|
+
|
|
33
|
+
Pass `no_remarks: true` to produce a listing without tail (`--`) or embedded (`(* ... *)`) remarks. Useful for diffs, license headers, and "code-only" outputs.
|
|
34
|
+
|
|
35
|
+
[source,ruby]
|
|
36
|
+
----
|
|
37
|
+
formatter = Class.new(Expressir::Express::Formatter) do
|
|
38
|
+
def initialize
|
|
39
|
+
super(no_remarks: true)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
puts formatter.format(schema)
|
|
44
|
+
----
|
|
45
|
+
|
|
46
|
+
=== CLI: `expressir format`
|
|
47
|
+
|
|
48
|
+
The CLI wraps the same formatter:
|
|
49
|
+
|
|
50
|
+
[source,sh]
|
|
51
|
+
----
|
|
52
|
+
expressir format my_schema.exp # prints formatted output
|
|
53
|
+
expressir format my_schema.exp -o out.exp # writes to a file
|
|
54
|
+
expressir format --no-remarks my_schema.exp
|
|
55
|
+
----
|
|
56
|
+
|
|
57
|
+
=== Schema head and hyperlink formatters
|
|
58
|
+
|
|
59
|
+
Two optional mixins layer on top of the base formatter:
|
|
60
|
+
|
|
61
|
+
* `Expressir::Express::SchemaHeadFormatter` — emits a file-level header banner before each schema (schema name, version, source file).
|
|
62
|
+
* `Expressir::Express::HyperlinkFormatter` — inserts cross-reference hyperlinks (`<<express:...>>` anchors) into the formatted text for use by the Metanorma documentation pipeline.
|
|
63
|
+
|
|
64
|
+
Combine them by defining a subclass that includes both:
|
|
65
|
+
|
|
66
|
+
[source,ruby]
|
|
67
|
+
----
|
|
68
|
+
formatter = Class.new(Expressir::Express::Formatter) do
|
|
69
|
+
include Expressir::Express::SchemaHeadFormatter
|
|
70
|
+
include Expressir::Express::HyperlinkFormatter
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
puts formatter.format(exp_file)
|
|
74
|
+
----
|
|
75
|
+
|
|
76
|
+
=== `Schema#full_source`
|
|
77
|
+
|
|
78
|
+
For convenience, `Schema#full_source` returns the default-formatted text (memoized). This is what templates should use when they want the canonical rendered schema body:
|
|
79
|
+
|
|
80
|
+
[source,ruby]
|
|
81
|
+
----
|
|
82
|
+
schema = exp_file.schemas.first
|
|
83
|
+
puts schema.full_source
|
|
84
|
+
----
|
|
85
|
+
|
|
86
|
+
=== See also
|
|
87
|
+
|
|
88
|
+
* link:querying-schemas.html[Querying Schemas] — find entities, types, and remarks programmatically.
|
|
89
|
+
* link:liquid-templates.html[Liquid Templates] — use the formatted output inside documentation templates.
|
data/docs/_tutorials/index.adoc
CHANGED
|
@@ -58,6 +58,11 @@ Duration: 45-60 minutes
|
|
|
58
58
|
+
|
|
59
59
|
Analyze and improve schema documentation quality with coverage tools and metrics.
|
|
60
60
|
|
|
61
|
+
**link:formatting-schemas.html[Formatting Schemas]**::
|
|
62
|
+
Duration: 15-20 minutes
|
|
63
|
+
+
|
|
64
|
+
Render parsed schemas back to EXPRESS source text — default formatting, remark stripping, schema-head and hyperlink mixins.
|
|
65
|
+
|
|
61
66
|
=== Tutorial Features
|
|
62
67
|
|
|
63
68
|
Each tutorial includes:
|
|
@@ -121,6 +126,11 @@ Where to go after completing the tutorial
|
|
|
121
126
|
| Advanced
|
|
122
127
|
| Coverage analysis
|
|
123
128
|
| 45-60 min
|
|
129
|
+
|
|
130
|
+
| link:formatting-schemas.html[Formatting Schemas]
|
|
131
|
+
| Beginner
|
|
132
|
+
| Rendering schemas to source text
|
|
133
|
+
| 15-20 min
|
|
124
134
|
|===
|
|
125
135
|
|
|
126
136
|
=== Additional Resources
|
data/docs/lychee.toml
CHANGED
|
@@ -59,6 +59,9 @@ exclude = [
|
|
|
59
59
|
"https://www\\.nist\\.gov/.*",
|
|
60
60
|
"https://www\\.steptools\\.com/.*",
|
|
61
61
|
|
|
62
|
+
# iso.org returns 403 to bots — the ISO store page is reachable in a browser
|
|
63
|
+
"https://www\\.iso\\.org/.*",
|
|
64
|
+
|
|
62
65
|
# GitHub URLs that require authentication
|
|
63
66
|
"https://github\\.com/.*/(issues|pull)/[0-9]+/files.*",
|
|
64
67
|
|
data/expressir.gemspec
CHANGED
|
@@ -4,7 +4,7 @@ require "expressir/version"
|
|
|
4
4
|
|
|
5
5
|
Gem::Specification.new do |spec|
|
|
6
6
|
spec.name = "expressir"
|
|
7
|
-
spec.version = Expressir::VERSION
|
|
7
|
+
spec.version = Expressir::Version::VERSION
|
|
8
8
|
spec.authors = ["Ribose Inc."]
|
|
9
9
|
spec.email = ["open.source@ribose.com"]
|
|
10
10
|
|
|
@@ -42,7 +42,9 @@ Gem::Specification.new do |spec|
|
|
|
42
42
|
spec.add_dependency "paint"
|
|
43
43
|
spec.add_dependency "parsanol", "~> 1.3.9", ">= 1.3.9"
|
|
44
44
|
spec.add_dependency "ruby-progressbar", "~> 1.11"
|
|
45
|
-
spec.add_dependency "rubyzip", "~>
|
|
45
|
+
spec.add_dependency "rubyzip", "~> 3.4"
|
|
46
46
|
spec.add_dependency "table_tennis"
|
|
47
47
|
spec.add_dependency "thor", "~> 1.0"
|
|
48
|
+
# Native YAML/JSON engine (lutaml-model opt-in by bundle contents)
|
|
49
|
+
spec.add_dependency "yeptris", "~> 0.6.1"
|
|
48
50
|
end
|
|
@@ -12,8 +12,6 @@ module Expressir
|
|
|
12
12
|
# @param options [Hash] Additional options
|
|
13
13
|
# @return [Expressir::Changes::SchemaChange]
|
|
14
14
|
def self.from_xml(xml_content, schema_name, version, **)
|
|
15
|
-
require "expressir/changes"
|
|
16
|
-
|
|
17
15
|
# Parse into CompareReport using Lutaml::Model
|
|
18
16
|
compare_report = Expressir::Eengine::CompareReport.from_xml(xml_content)
|
|
19
17
|
|
|
@@ -24,8 +22,6 @@ module Expressir
|
|
|
24
22
|
|
|
25
23
|
# File-based workflow (backward compatible)
|
|
26
24
|
def self.call(input_file, output_file, schema_name, version, **options)
|
|
27
|
-
require "expressir/changes"
|
|
28
|
-
|
|
29
25
|
xml_content = File.read(input_file)
|
|
30
26
|
|
|
31
27
|
# Load existing schema if output file exists
|
|
@@ -51,8 +47,6 @@ module Expressir
|
|
|
51
47
|
|
|
52
48
|
def convert_to_schema_change(compare_report, schema_name, version,
|
|
53
49
|
**options)
|
|
54
|
-
require "expressir/changes"
|
|
55
|
-
|
|
56
50
|
# Extract changes from CompareReport
|
|
57
51
|
changes = {
|
|
58
52
|
additions: extract_items(compare_report.additions,
|
|
@@ -5,7 +5,7 @@ module Expressir
|
|
|
5
5
|
repository = Expressir::Express::Parser.from_file(path)
|
|
6
6
|
formatted_schemas = repository.schemas.map do |schema|
|
|
7
7
|
# Format schema without remarks
|
|
8
|
-
schema.
|
|
8
|
+
schema.format(no_remarks: true)
|
|
9
9
|
end.join("\n\n")
|
|
10
10
|
|
|
11
11
|
if options[:output]
|