expressir 2.1.18 → 2.1.19

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d20d882f0b9e6b30860d41294671b6c235cff0cc024521276ca9ca3283e1f79a
4
- data.tar.gz: 8625cc4fa425c9d02c73e056b8ee9ee8aa69eaab566452b44e1a1ae137faacf6
3
+ metadata.gz: 4b68644d000b4b7d47a7e132922afa9b4e9f1d3fb19ba6c3b9e2a8db28c5fd33
4
+ data.tar.gz: 132ecfd9ca85168335536626fea46c3648757ccd973823242b04b0744869376c
5
5
  SHA512:
6
- metadata.gz: 88dc6630b82e7b7c01f49df868664caa34d54e5d21d2af4d786094f7c880b7f39bbd7295f084e1e3558f9705a7798364740dfdc40086241b7d20e36419ae75b9
7
- data.tar.gz: 0f87bcc8eda3e6b321202f64b9bcfc5a357c8b3ee4c1f0dc481a7610e9680f2af50a0b257838809d05888fda647f28be9583e1debfac557fa31c78b1b2c66fb2
6
+ metadata.gz: 895f018faf2bbdd075991d45fa22061459b918f907e568e4481a98dd2e0eb96a312cb98d96e2e7de381c7c85841d05631119f7c26e1773ac634377cdac8e6fe2
7
+ data.tar.gz: 025421a41b6463548b8ac099fe8b5bc94eb21ee58339f77a6ff028c59ebdb1ac27c5f24ea46050d1fa5b84ee5e80802efcb45b0a549bc733ea833f43c48e0e14
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-03-21 10:31:17 UTC using RuboCop version 1.74.0.
3
+ # on 2025-06-04 17:31:42 UTC using RuboCop version 1.75.2.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -35,13 +35,17 @@ Lint/UnusedMethodArgument:
35
35
  - 'lib/expressir/express/cache.rb'
36
36
  - 'lib/expressir/express/parser.rb'
37
37
 
38
- # Offense count: 61
38
+ # Offense count: 71
39
39
  # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
40
40
  Metrics/AbcSize:
41
41
  Exclude:
42
42
  - 'lib/expressir/benchmark.rb'
43
- - 'lib/expressir/cli.rb'
43
+ - 'lib/expressir/commands/benchmark.rb'
44
+ - 'lib/expressir/commands/benchmark_cache.rb'
45
+ - 'lib/expressir/commands/coverage.rb'
46
+ - 'lib/expressir/commands/validate.rb'
44
47
  - 'lib/expressir/config.rb'
48
+ - 'lib/expressir/coverage.rb'
45
49
  - 'lib/expressir/express/formatter.rb'
46
50
  - 'lib/expressir/express/hyperlink_formatter.rb'
47
51
  - 'lib/expressir/express/parser.rb'
@@ -50,17 +54,19 @@ Metrics/AbcSize:
50
54
  - 'lib/expressir/model/declarations/schema.rb'
51
55
  - 'lib/expressir/model/model_element.rb'
52
56
 
53
- # Offense count: 2
57
+ # Offense count: 1
54
58
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
55
59
  # AllowedMethods: refine
56
60
  Metrics/BlockLength:
57
- Max: 141
61
+ Max: 143
58
62
 
59
- # Offense count: 45
63
+ # Offense count: 51
60
64
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
61
65
  Metrics/CyclomaticComplexity:
62
66
  Exclude:
63
67
  - 'lib/expressir/benchmark.rb'
68
+ - 'lib/expressir/commands/coverage.rb'
69
+ - 'lib/expressir/coverage.rb'
64
70
  - 'lib/expressir/express/formatter.rb'
65
71
  - 'lib/expressir/express/model_visitor.rb'
66
72
  - 'lib/expressir/express/parser.rb'
@@ -69,16 +75,18 @@ Metrics/CyclomaticComplexity:
69
75
  - 'lib/expressir/model/model_element.rb'
70
76
  - 'spec/support/model_element_helper.rb'
71
77
 
72
- # Offense count: 80
78
+ # Offense count: 94
73
79
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
74
80
  Metrics/MethodLength:
75
- Max: 44
81
+ Max: 50
76
82
 
77
- # Offense count: 35
83
+ # Offense count: 39
78
84
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
79
85
  Metrics/PerceivedComplexity:
80
86
  Exclude:
81
87
  - 'lib/expressir/benchmark.rb'
88
+ - 'lib/expressir/commands/coverage.rb'
89
+ - 'lib/expressir/coverage.rb'
82
90
  - 'lib/expressir/express/formatter.rb'
83
91
  - 'lib/expressir/express/visitor.rb'
84
92
  - 'lib/expressir/model/declarations/schema.rb'
data/README.adoc CHANGED
@@ -80,20 +80,31 @@ Commands:
80
80
  expressir format PATH # pretty print EXPRESS schema located at PATH
81
81
  expressir help [COMMAND] # Describe available commands or one specific command
82
82
  expressir validate *PATH # validate EXPRESS schema located at PATH
83
+ expressir coverage *PATH # List EXPRESS entities and check documentation coverage
83
84
  expressir version # Expressir Version
84
85
  ----
85
86
 
86
- === Pretty print
87
+ === Format schema
88
+
89
+ The `format` command pretty prints an EXPRESS schema, making it more readable
90
+ while preserving its structure.
87
91
 
88
92
  [source, sh]
89
93
  ----
94
+ # Pretty print a schema to stdout
90
95
  expressir format schemas/resources/action_schema/action_schema.exp
91
96
  ----
92
97
 
98
+ This command:
99
+ 1. Parses the EXPRESS schema
100
+ 2. Formats it with consistent indentation and spacing
101
+ 3. Outputs the formatted schema to stdout
102
+
93
103
  === Clean schema
94
104
 
95
- The `clean` command strips remarks and prettifies EXPRESS schemas. You can
96
- optionally save the result to a file.
105
+ The `clean` command strips remarks and prettifies EXPRESS schemas. This is
106
+ useful for removing all documentation comments while maintaining the schema's
107
+ functional definition. You can optionally save the result to a file.
97
108
 
98
109
  [source, sh]
99
110
  ----
@@ -104,6 +115,46 @@ expressir clean schemas/resources/action_schema/action_schema.exp
104
115
  expressir clean schemas/resources/action_schema/action_schema.exp --output clean_schema.exp
105
116
  ----
106
117
 
118
+ [options="header"]
119
+ |===
120
+ | Option | Description
121
+ | `--output PATH` | Path to save the cleaned schema (optional, defaults to stdout)
122
+ |===
123
+
124
+ === Validate schema
125
+
126
+ The `validate` command performs validation checks on EXPRESS schema files.
127
+
128
+ It verifies:
129
+
130
+ . That the schema can be parsed correctly
131
+ . That the schema includes a version string
132
+
133
+ [source, sh]
134
+ ----
135
+ # Validate a single schema
136
+ expressir validate schemas/resources/action_schema/action_schema.exp
137
+
138
+ # Validate multiple schemas
139
+ expressir validate schemas/resources/action_schema/action_schema.exp schemas/resources/approval_schema/approval_schema.exp
140
+ ----
141
+
142
+ The command reports any schemas that:
143
+
144
+ * Failed to parse
145
+ * Are missing a version string
146
+
147
+ If all validations pass, it will display "Validation passed for all EXPRESS schemas."
148
+
149
+ === Version
150
+
151
+ The `version` command displays the current version of the Expressir gem.
152
+
153
+ [source, sh]
154
+ ----
155
+ expressir version
156
+ ----
157
+
107
158
  === Benchmarking
108
159
 
109
160
  Expressir includes powerful benchmarking capabilities for measuring schema
@@ -181,6 +232,115 @@ The benchmark commands support several options:
181
232
  When using the `--format json` option, results will be output in JSON format,
182
233
  making it easy to parse for further analysis or visualization.
183
234
 
235
+ === Documentation coverage
236
+
237
+ Expressir can analyze EXPRESS schemas to check for documentation coverage. This helps
238
+ identify which entities are properly documented with remarks and which ones require
239
+ documentation.
240
+
241
+ ==== Analyzing documentation coverage
242
+
243
+ Use the `coverage` command to check documentation coverage of EXPRESS schemas:
244
+
245
+ [source, sh]
246
+ ----
247
+ # Analyze a single EXPRESS file
248
+ expressir coverage schemas/resources/action_schema/action_schema.exp
249
+
250
+ # Analyze multiple EXPRESS files
251
+ expressir coverage schemas/resources/action_schema/action_schema.exp schemas/resources/approval_schema/approval_schema.exp
252
+
253
+ # Analyze all EXPRESS files in a directory (recursively)
254
+ expressir coverage schemas/resources/
255
+
256
+ # Analyze files specified in a YAML file
257
+ expressir coverage schemas.yml
258
+ ----
259
+
260
+ The output shows which entities are missing documentation, calculates coverage percentages,
261
+ and provides an overall documentation coverage summary.
262
+
263
+ ==== Coverage options
264
+
265
+ The coverage command supports different output formats and exclusion options:
266
+
267
+ [options="header"]
268
+ |===
269
+ | Option | Description
270
+ | `--format text` | (Default) Display a human-readable table with coverage information
271
+ | `--format json` | Output in JSON format for programmatic processing
272
+ | `--format yaml` | Output in YAML format for programmatic processing
273
+ | `--exclude TYPES` | Comma-separated list of EXPRESS entity types to exclude from coverage analysis
274
+ |===
275
+
276
+ ==== Excluding entity types from coverage
277
+
278
+ You can exclude specific EXPRESS entity types from coverage analysis using the
279
+ `--exclude` option.
280
+
281
+ This is useful when certain entity types don't require documentation coverage,
282
+ such as TYPE entities whose descriptions are generated by template strings.
283
+
284
+ [source, sh]
285
+ ----
286
+ # Exclude TYPE entities from coverage analysis
287
+ expressir coverage --exclude=TYPE schemas/resources/action_schema/action_schema.exp
288
+
289
+ # Exclude multiple entity types
290
+ expressir coverage --exclude=TYPE,CONSTANT,FUNCTION schemas/resources/action_schema/action_schema.exp
291
+
292
+ # Combine with output format options
293
+ expressir coverage --exclude=TYPE --format=json schemas/resources/action_schema/action_schema.exp
294
+ ----
295
+
296
+ Valid entity types that can be excluded:
297
+
298
+ * `TYPE` - Type definitions
299
+ * `ENTITY` - Entity definitions
300
+ * `CONSTANT` - Constant definitions
301
+ * `FUNCTION` - Function definitions
302
+ * `RULE` - Rule definitions
303
+ * `PROCEDURE` - Procedure definitions
304
+ * `SUBTYPE_CONSTRAINT` - Subtype constraint definitions
305
+
306
+ If you specify an invalid entity type, the command will display an error message
307
+ with the list of valid types.
308
+
309
+ .Example with JSON output:
310
+ [example]
311
+ ====
312
+ [source, sh]
313
+ ----
314
+ expressir coverage schemas/resources/ --format json
315
+ ----
316
+ ====
317
+
318
+ When using JSON or YAML output formats, all file and directory paths are
319
+ displayed relative to the current working directory:
320
+
321
+ [source, yaml]
322
+ ----
323
+ - file: "schemas/resources/action_schema/action_schema.exp"
324
+ file_basename: action_schema.exp
325
+ directory: "schemas/resources/action_schema"
326
+ # ... other fields
327
+ ----
328
+
329
+ ==== Coverage output
330
+
331
+ The default text output displays:
332
+
333
+ . Directory coverage (when analyzing multiple directories)
334
+
335
+ . File coverage, showing:
336
+ ** File path
337
+ ** List of undocumented entities
338
+ ** Coverage percentage
339
+
340
+ . Overall documentation coverage statistics
341
+
342
+ This helps identify areas of your EXPRESS schemas that need documentation
343
+ improvement.
184
344
 
185
345
 
186
346
  == Usage: Ruby
@@ -318,6 +478,68 @@ schema = repo_drop.schemas.first
318
478
  schema.file = "path/to/file.exp"
319
479
  ----
320
480
 
481
+ === Documentation coverage analysis
482
+
483
+ Expressir's documentation coverage feature can be used programmatically to
484
+ analyze and report on documentation coverage of EXPRESS schemas.
485
+
486
+ [source,ruby]
487
+ ----
488
+ # Create a coverage report from a file
489
+ report = Expressir::Coverage::Report.from_file("path/to/schema.exp")
490
+
491
+ # Or create a report from a repository
492
+ repository = Expressir::Express::Parser.from_file("path/to/schema.exp")
493
+ report = Expressir::Coverage::Report.from_repository(repository)
494
+
495
+ # Access overall statistics
496
+ puts "Overall coverage: #{report.coverage_percentage}%"
497
+ puts "Total entities: #{report.total_entities.size}"
498
+ puts "Documented entities: #{report.documented_entities.size}"
499
+ puts "Undocumented entities: #{report.undocumented_entities.size}"
500
+
501
+ # Access file-level reports
502
+ report.file_reports.each do |file_report|
503
+ puts "File: #{file_report[:file]}"
504
+ puts " Coverage: #{file_report[:coverage]}%"
505
+ puts " Total entities: #{file_report[:total]}"
506
+ puts " Documented entities: #{file_report[:documented]}"
507
+ puts " Undocumented entities: #{file_report[:undocumented].join(', ')}"
508
+ end
509
+
510
+ # Access directory-level reports
511
+ report.directory_reports.each do |dir_report|
512
+ puts "Directory: #{dir_report[:directory]}"
513
+ puts " Coverage: #{dir_report[:coverage]}%"
514
+ puts " Total entities: #{dir_report[:total]}"
515
+ puts " Documented entities: #{dir_report[:documented]}"
516
+ puts " Undocumented entities: #{dir_report[:undocumented]}"
517
+ puts " Number of files: #{dir_report[:files]}"
518
+ end
519
+
520
+ # Generate a structured hash representation
521
+ report_hash = report.to_h # Contains overall, directories and files sections
522
+ ----
523
+
524
+ You can also use the core methods directly to check documentation status:
525
+
526
+ [source,ruby]
527
+ ----
528
+ # Check if an entity has documentation
529
+ schema = repository.schemas.first
530
+ entity = schema.entities.first
531
+
532
+ if Expressir::Coverage.entity_documented?(entity)
533
+ puts "Entity #{entity.id} is documented"
534
+ else
535
+ puts "Entity #{entity.id} is not documented"
536
+ end
537
+
538
+ # Find all entities in a schema
539
+ all_entities = Expressir::Coverage.find_entities(schema)
540
+ puts "Found #{all_entities.size} entities in schema #{schema.id}"
541
+ ----
542
+
321
543
 
322
544
  == Contributing
323
545
 
data/expressir.gemspec CHANGED
@@ -39,6 +39,8 @@ Gem::Specification.new do |spec|
39
39
  spec.add_dependency "liquid"
40
40
  spec.add_dependency "lutaml-model", "~>0.7"
41
41
  spec.add_dependency "parslet", "~> 2.0"
42
+ spec.add_dependency "ruby-progressbar", "~> 1.11"
43
+ spec.add_dependency "terminal-table", "~> 3.0"
42
44
  spec.add_dependency "thor", "~> 1.0"
43
45
  spec.add_dependency "zeitwerk", "~> 2.6"
44
46
  end
data/lib/expressir/cli.rb CHANGED
@@ -1,32 +1,32 @@
1
1
  require "thor"
2
2
  require "yaml"
3
+ require "terminal-table"
4
+
5
+ require_relative "commands/base"
6
+ require_relative "commands/format"
7
+ require_relative "commands/clean"
8
+ require_relative "commands/benchmark"
9
+ require_relative "commands/benchmark_cache"
10
+ require_relative "commands/validate"
11
+ require_relative "commands/coverage"
12
+ require_relative "commands/version"
3
13
 
4
14
  module Expressir
5
15
  class Cli < Thor
16
+ # Exit with error code on command failures
17
+ def self.exit_on_failure?
18
+ true
19
+ end
20
+
6
21
  desc "format PATH", "pretty print EXPRESS schema located at PATH"
7
22
  def format(path)
8
- repository = Expressir::Express::Parser.from_file(path)
9
- repository.schemas.each do |schema|
10
- puts "\n(* Expressir formatted schema: #{schema.id} *)\n"
11
- puts schema.to_s(no_remarks: true)
12
- end
23
+ Commands::Format.new(options).run(path)
13
24
  end
14
25
 
15
26
  desc "clean PATH", "Strip remarks and prettify EXPRESS schema at PATH"
16
27
  method_option :output, type: :string, desc: "Output file path (defaults to stdout)"
17
28
  def clean(path)
18
- repository = Expressir::Express::Parser.from_file(path)
19
- formatted_schemas = repository.schemas.map do |schema|
20
- # Format schema without remarks
21
- schema.to_s(no_remarks: true)
22
- end.join("\n\n")
23
-
24
- if options[:output]
25
- File.write(options[:output], formatted_schemas)
26
- puts "Cleaned schema written to #{options[:output]}"
27
- else
28
- puts formatted_schemas
29
- end
29
+ Commands::Clean.new(options).run(path)
30
30
  end
31
31
 
32
32
  desc "benchmark FILE_OR_YAML", "Benchmark schema loading performance for a file or list of files from YAML"
@@ -35,18 +35,7 @@ module Expressir
35
35
  method_option :save, type: :boolean, desc: "Save benchmark results to file"
36
36
  method_option :format, type: :string, desc: "Output format (json, csv, default)"
37
37
  def benchmark(path)
38
- # Enable benchmarking via configuration
39
- Expressir.configuration.benchmark_enabled = true
40
- Expressir.configuration.benchmark_ips = options[:ips]
41
- Expressir.configuration.benchmark_verbose = options[:verbose]
42
- Expressir.configuration.benchmark_save = options[:save]
43
- Expressir.configuration.benchmark_format = options[:format] if options[:format]
44
-
45
- if [".yml", ".yaml"].include?(File.extname(path).downcase)
46
- benchmark_from_yaml(path)
47
- else
48
- benchmark_file(path)
49
- end
38
+ Commands::Benchmark.new(options).run(path)
50
39
  end
51
40
 
52
41
  desc "benchmark-cache FILE_OR_YAML", "Benchmark schema loading with caching"
@@ -56,178 +45,24 @@ module Expressir
56
45
  method_option :format, type: :string, desc: "Output format (json, csv, default)"
57
46
  method_option :cache_path, type: :string, desc: "Path to store the cache file"
58
47
  def benchmark_cache(path)
59
- # Same options as benchmark + cache_path
60
- Expressir.configuration.benchmark_enabled = true
61
- Expressir.configuration.benchmark_ips = options[:ips]
62
- Expressir.configuration.benchmark_verbose = options[:verbose]
63
- Expressir.configuration.benchmark_save = options[:save]
64
- Expressir.configuration.benchmark_format = options[:format] if options[:format]
65
-
66
- # Run benchmarks with cache
67
- cache_path = options[:cache_path] || generate_temp_cache_path
68
-
69
- if [".yml", ".yaml"].include?(File.extname(path).downcase)
70
- benchmark_cache_from_yaml(path, cache_path)
71
- else
72
- benchmark_cache_file(path, cache_path)
73
- end
74
- end
75
-
76
- no_commands do
77
- def _validate_schema(path)
78
- repository = Expressir::Express::Parser.from_file(path)
79
- repository.schemas.inject([]) do |acc, schema|
80
- acc << schema.id unless schema.version&.value
81
- acc
82
- end
83
- rescue StandardError
84
- nil
85
- end
86
-
87
- def _print_validation_errors(type, array)
88
- return if array.empty?
89
-
90
- puts "#{'*' * 20} RESULTS: #{type.to_s.upcase.tr('_', ' ')} #{'*' * 20}"
91
- array.each do |msg|
92
- puts msg
93
- end
94
- end
95
-
96
- def benchmark_file(path)
97
- puts "Express Schema Loading Benchmark"
98
- puts "--------------------------------"
99
-
100
- repository = Expressir::Benchmark.measure_file(path) do
101
- Expressir::Express::Parser.from_file(path)
102
- end
103
-
104
- if repository
105
- puts "Loaded #{repository.schemas.size} schemas"
106
- else
107
- puts "Failed to load schema"
108
- end
109
- end
110
-
111
- def benchmark_from_yaml(yaml_path)
112
- puts "Express Schema Loading Benchmark from YAML"
113
- puts "--------------------------------"
114
-
115
- # Load schema list from YAML
116
- schema_list = YAML.load_file(yaml_path)
117
- if schema_list.is_a?(Hash) && schema_list["schemas"]
118
- # Handle format: { "schemas": ["path1", "path2", ...] }
119
- schema_files = schema_list["schemas"]
120
- elsif schema_list.is_a?(Array)
121
- # Handle format: ["path1", "path2", ...]
122
- schema_files = schema_list
123
- else
124
- puts "Invalid YAML format. Expected an array of schema paths or a hash with a 'schemas' key."
125
- return
126
- end
127
-
128
- puts "YAML File: #{yaml_path}"
129
- puts "Number of schemas in list: #{schema_files.size}"
130
- puts "--------------------------------"
131
-
132
- # Load the schemas
133
- Expressir::Benchmark.measure_collection(schema_files) do |file|
134
- repository = Expressir::Express::Parser.from_file(file)
135
- repository.schemas
136
- end
137
- end
138
-
139
- def benchmark_cache_file(path, cache_path)
140
- puts "Express Schema Loading Benchmark with Caching"
141
- puts "--------------------------------"
142
- puts "Schema: #{path}"
143
- puts "Cache: #{cache_path}"
144
- puts "--------------------------------"
145
-
146
- # Benchmark with caching
147
- results = Expressir::Benchmark.measure_with_cache(path, cache_path) do |file|
148
- Expressir::Express::Parser.from_file(file)
149
- end
150
-
151
- if results[:repository]
152
- schema_count = results[:repository].schemas.size
153
- puts "Loaded #{schema_count} schemas"
154
- else
155
- puts "Failed to load schema"
156
- end
157
- end
158
-
159
- def benchmark_cache_from_yaml(yaml_path, cache_path)
160
- puts "Express Schema Loading Benchmark with Caching from YAML"
161
- puts "--------------------------------"
162
-
163
- # Load schema list from YAML
164
- schema_list = YAML.load_file(yaml_path)
165
- if schema_list.is_a?(Hash) && schema_list["schemas"]
166
- # Handle format: { "schemas": ["path1", "path2", ...] }
167
- schema_files = schema_list["schemas"]
168
- elsif schema_list.is_a?(Array)
169
- # Handle format: ["path1", "path2", ...]
170
- schema_files = schema_list
171
- else
172
- puts "Invalid YAML format. Expected an array of schema paths or a hash with a 'schemas' key."
173
- return
174
- end
175
-
176
- puts "YAML File: #{yaml_path}"
177
- puts "Number of schemas in list: #{schema_files.size}"
178
- puts "Cache: #{cache_path}"
179
- puts "--------------------------------"
180
-
181
- # Process each file with caching
182
- schema_files.each_with_index do |file, index|
183
- puts "Processing file #{index + 1}/#{schema_files.size}: #{file}"
184
-
185
- # Benchmark with caching
186
- Expressir::Benchmark.measure_with_cache(file, cache_path) do |path|
187
- Expressir::Express::Parser.from_file(path)
188
- end
189
-
190
- puts "--------------------------------"
191
- end
192
- end
193
-
194
- def generate_temp_cache_path
195
- require "tempfile"
196
- Tempfile.new(["expressir_cache", ".bin"]).path
197
- end
48
+ Commands::BenchmarkCache.new(options).run(path)
198
49
  end
199
50
 
200
51
  desc "validate *PATH", "validate EXPRESS schema located at PATH"
201
- def validate(*paths) # rubocop:disable Metrics/AbcSize,Metrics/MethodLength
202
- no_version = []
203
- no_valid = []
204
-
205
- paths.each do |path|
206
- x = Pathname.new(path).realpath.relative_path_from(Dir.pwd)
207
- puts "Validating #{x}"
208
- ret = _validate_schema(path)
209
-
210
- if ret.nil?
211
- no_valid << "Failed to parse: #{x}"
212
- next
213
- end
214
-
215
- ret.each do |schema_id|
216
- no_version << "Missing version string: schema `#{schema_id}` | #{x}"
217
- end
218
- end
219
-
220
- _print_validation_errors(:failed_to_parse, no_valid)
221
- _print_validation_errors(:missing_version_string, no_version)
222
-
223
- exit 1 unless [no_valid, no_version].all?(&:empty?)
52
+ def validate(*paths)
53
+ Commands::Validate.new(options).run(paths)
54
+ end
224
55
 
225
- puts "Validation passed for all EXPRESS schemas."
56
+ desc "coverage *PATH", "List EXPRESS entities and check documentation coverage"
57
+ method_option :format, type: :string, desc: "Output format (text, json, yaml)", default: "text"
58
+ method_option :exclude, type: :string, desc: "Comma-separated list of EXPRESS entity types to skip from coverage (e.g., TYPE,CONSTANT)"
59
+ def coverage(*paths)
60
+ Commands::Coverage.new(options).run(paths)
226
61
  end
227
62
 
228
63
  desc "version", "Expressir Version"
229
64
  def version
230
- say(Expressir::VERSION)
65
+ Commands::Version.new(options).run
231
66
  end
232
67
  end
233
68
  end
@@ -0,0 +1,32 @@
1
+ module Expressir
2
+ module Commands
3
+ class Base
4
+ attr_reader :options
5
+
6
+ def initialize(options = {})
7
+ @options = options
8
+ end
9
+
10
+ # Common utilities that all commands might need
11
+ def say(message)
12
+ # Use instance variable for testability - if output is set, use it
13
+ if defined?(@output) && @output
14
+ @output.puts(message)
15
+ else
16
+ puts(message)
17
+ end
18
+ end
19
+
20
+ def exit_with_error(message, exit_code = 1)
21
+ say message
22
+ # In test mode, raise an exception instead of exiting
23
+ # This makes it easier to test error cases
24
+ if defined?(@test_mode) && @test_mode
25
+ raise message # Just raise the message as an exception
26
+ else
27
+ exit exit_code
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end