suma 0.1.23 → 0.1.24

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: 2c9cab2e048b99f3208e2956c0611dd64774a280afbe06cd60fb72474e4a8b4b
4
- data.tar.gz: 9b12e455229ec616e08c3cba4d6e7e23adc14193241bfaeb33f605ba29a9ee2e
3
+ metadata.gz: b2e8f69b5a73e7e808b0dceb8443829f1ee07370f391e29144187eac0870b3ba
4
+ data.tar.gz: 912fa2f86f74512f95fb25dbf082de1d65bb65848e832cab66444e1e4e698956
5
5
  SHA512:
6
- metadata.gz: e31649ea7c3e8bd144f212f4f3568a017bcf4a85550d9ad17d8210bb8df674163bdee055484f94bab2d41b42281444bfaff9e8d234cf970e71da5e8dd27f5308
7
- data.tar.gz: '0281f7449d90c3979507e7a834091970cfb2f380bcc2a9fe8186f0e4cad2315f3a7e82d8279093d4ca721190c63a24a227d70ee87b05931354c37a6a3ffc7972'
6
+ metadata.gz: f9d9d3a67a2425b3b96d0a8ceddfa964e1d2a7a391fb626201f4ca1be98c23a96eb622d10ee5b7073c2d80be65f9d9a88b9d75996231c02da5035593aa4e7860
7
+ data.tar.gz: 79435485f1e2c1389a8054153e1e6abfb145f098038937c3f79d3d7a55e9b6b9ead89b78f6a2f74f462d8d71e7f726ed455e20ffd712357b58455818691eacc1
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-10-05 11:55:30 UTC using RuboCop version 1.81.1.
3
+ # on 2025-10-12 09:15:38 UTC using RuboCop version 1.81.1.
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
@@ -18,23 +18,51 @@ Gemspec/RequiredRubyVersion:
18
18
  Exclude:
19
19
  - 'suma.gemspec'
20
20
 
21
- # Offense count: 70
21
+ # Offense count: 2
22
+ # This cop supports safe autocorrection (--autocorrect).
23
+ # Configuration parameters: EnforcedStyle, IndentationWidth.
24
+ # SupportedStyles: with_first_argument, with_fixed_indentation
25
+ Layout/ArgumentAlignment:
26
+ Exclude:
27
+ - 'spec/suma/cli/export_spec.rb'
28
+
29
+ # Offense count: 1
30
+ # This cop supports safe autocorrection (--autocorrect).
31
+ # Configuration parameters: EnforcedStyleAlignWith.
32
+ # SupportedStylesAlignWith: either, start_of_block, start_of_line
33
+ Layout/BlockAlignment:
34
+ Exclude:
35
+ - 'spec/suma/cli/export_spec.rb'
36
+
37
+ # Offense count: 1
38
+ # This cop supports safe autocorrection (--autocorrect).
39
+ Layout/BlockEndNewline:
40
+ Exclude:
41
+ - 'spec/suma/cli/export_spec.rb'
42
+
43
+ # Offense count: 2
44
+ # This cop supports safe autocorrection (--autocorrect).
45
+ # Configuration parameters: Width, AllowedPatterns.
46
+ Layout/IndentationWidth:
47
+ Exclude:
48
+ - 'spec/suma/cli/export_spec.rb'
49
+
50
+ # Offense count: 74
22
51
  # This cop supports safe autocorrection (--autocorrect).
23
52
  # Configuration parameters: Max, AllowHeredoc, AllowURI, AllowQualifiedName, URISchemes, IgnoreCopDirectives, AllowedPatterns, SplitStrings.
24
53
  # URISchemes: http, https
25
54
  Layout/LineLength:
26
55
  Exclude:
27
- - 'lib/suma/cli.rb'
28
56
  - 'lib/suma/cli/build.rb'
29
- - 'lib/suma/cli/export.rb'
30
57
  - 'lib/suma/cli/validate.rb'
58
+ - 'lib/suma/collection_manifest.rb'
31
59
  - 'lib/suma/jsdai/figure.rb'
32
60
  - 'lib/suma/jsdai/figure_image.rb'
33
61
  - 'lib/suma/processor.rb'
34
62
  - 'lib/suma/schema_attachment.rb'
35
- - 'lib/suma/schema_collection.rb'
36
63
  - 'lib/suma/schema_document.rb'
37
64
  - 'lib/suma/thor_ext.rb'
65
+ - 'spec/suma/cli/export_spec.rb'
38
66
  - 'spec/suma/cli/extract_terms_spec.rb'
39
67
  - 'spec/suma/cli/validate_ascii_spec.rb'
40
68
  - 'spec/suma/jsdai/figure_spec.rb'
@@ -74,11 +102,6 @@ Metrics/CyclomaticComplexity:
74
102
  - 'lib/suma/jsdai/figure_image.rb'
75
103
  - 'lib/suma/thor_ext.rb'
76
104
 
77
- # Offense count: 4
78
- # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
79
- Metrics/MethodLength:
80
- Max: 13
81
-
82
105
  # Offense count: 1
83
106
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
84
107
  Metrics/PerceivedComplexity:
@@ -94,20 +117,21 @@ Naming/VariableNumber:
94
117
  - 'spec/suma/cli/export_spec.rb'
95
118
  - 'spec/suma/cli/validate_ascii_spec.rb'
96
119
 
97
- # Offense count: 1
120
+ # Offense count: 2
98
121
  # Configuration parameters: MinSize.
99
122
  Performance/CollectionLiteralInLoop:
100
123
  Exclude:
124
+ - 'spec/suma/cli/export_spec.rb'
101
125
  - 'spec/suma/cli_spec.rb'
102
126
 
103
- # Offense count: 1
127
+ # Offense count: 2
104
128
  # Configuration parameters: Prefixes, AllowedPatterns.
105
129
  # Prefixes: when, with, without
106
130
  RSpec/ContextWording:
107
131
  Exclude:
108
132
  - 'spec/suma/cli/export_spec.rb'
109
133
 
110
- # Offense count: 32
134
+ # Offense count: 37
111
135
  # Configuration parameters: CountAsOne.
112
136
  RSpec/ExampleLength:
113
137
  Max: 44
@@ -118,6 +142,24 @@ RSpec/IndexedLet:
118
142
  Exclude:
119
143
  - 'spec/suma/cli/export_spec.rb'
120
144
 
121
- # Offense count: 28
145
+ # Offense count: 33
122
146
  RSpec/MultipleExpectations:
123
147
  Max: 12
148
+
149
+ # Offense count: 1
150
+ # Configuration parameters: CustomTransform, IgnoreMethods, IgnoreMetadata.
151
+ RSpec/SpecFilePathFormat:
152
+ Exclude:
153
+ - '**/spec/routing/**/*'
154
+ - 'spec/suma/cli/expressir_spec.rb'
155
+
156
+ # Offense count: 1
157
+ # This cop supports safe autocorrection (--autocorrect).
158
+ # Configuration parameters: EnforcedStyle, ProceduralMethods, FunctionalMethods, AllowedMethods, AllowedPatterns, AllowBracesOnProceduralOneLiners, BracesRequiredMethods.
159
+ # SupportedStyles: line_count_based, semantic, braces_for_chaining, always_braces
160
+ # ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
161
+ # FunctionalMethods: let, let!, subject, watch
162
+ # AllowedMethods: lambda, proc, it
163
+ Style/BlockDelimiters:
164
+ Exclude:
165
+ - 'spec/suma/cli/export_spec.rb'
data/README.adoc CHANGED
@@ -138,8 +138,9 @@ $ suma validate SUBCOMMAND [options]
138
138
  ----
139
139
 
140
140
  Subcommands:
141
- - `links` - Validate EXPRESS links
142
- - `ascii` - Check for non-ASCII characters in EXPRESS files
141
+
142
+ `links`:: Validate EXPRESS links
143
+ `ascii`:: Check for non-ASCII characters in EXPRESS files
143
144
 
144
145
  ==== Links subcommand
145
146
 
@@ -552,38 +553,50 @@ list
552
553
 
553
554
  ==== General
554
555
 
555
- The `suma export` command exports EXPRESS schemas from a manifest file to a
556
- specified output directory. This command is useful for extracting plain or
557
- annotated EXPRESS schemas for distribution or further processing.
556
+ The `suma export` command exports EXPRESS schemas from manifest files and/or
557
+ standalone EXPRESS files to a specified output directory.
558
+
559
+ This command is useful for extracting plain or annotated EXPRESS schemas for
560
+ distribution or further processing.
558
561
 
559
562
  [source,sh]
560
563
  ----
561
- $ suma export MANIFEST_FILE [options]
564
+ $ suma export -o OUTPUT_DIR FILE1 [FILE2 FILE3 ...]
562
565
  ----
563
566
 
564
567
  Parameters:
565
568
 
566
- `MANIFEST_FILE`:: Path to the EXPRESS schema manifest file (e.g., "schemas-srl.yml")
569
+ `FILE1 [FILE2 FILE3 ...]`:: One or more files to export. Each file can be:
570
+
571
+ An EXPRESS schema manifest file (`.yml`, `.yaml`)::: schemas listed in the
572
+ manifest will be exported
573
+
574
+ An standalone EXPRESS file (`.exp`)::: the schema will be exported directly
567
575
 
568
576
  Options:
569
577
 
570
578
  `--output=PATH`, `-o PATH`:: (required) Output directory path
571
579
 
572
- `--additional=PATH`, `-a PATH`:: Additional schemas manifest file to merge
573
-
574
580
  `--[no-]annotations`:: Include annotations (remarks/comments) in exported schemas (default: false)
575
581
 
576
582
  `--[no-]zip`:: Create ZIP archive of exported schemas (default: false)
577
583
 
578
584
  ==== Behavior
579
585
 
580
- The command exports schemas while preserving the directory structure from the
581
- manifest file paths:
586
+ The command exports schemas based on the input file types:
587
+
588
+ **For EXPRESS schema manifest files:**
582
589
 
590
+ * Schemas are exported while preserving the directory structure from the manifest file paths
583
591
  * Schemas under `resources/` are exported to `OUTPUT/resources/`
584
592
  * Schemas under `modules/` are exported to `OUTPUT/modules/`
585
593
  * Other categories (`business_object_models/`, `core_model/`) follow the same pattern
586
594
 
595
+ **For standalone EXPRESS files:**
596
+
597
+ * Schemas are exported directly to the output directory root
598
+ * Output filename format: `{schema_id}.exp`
599
+
587
600
  By default, schemas are exported without annotations (plain EXPRESS). Use the
588
601
  `--annotations` flag to include remarks and comments.
589
602
 
@@ -591,7 +604,7 @@ The `--zip` flag creates a ZIP archive in addition to the directory output,
591
604
  named `OUTPUT.zip`.
592
605
 
593
606
  [example]
594
- .To export schemas without annotations
607
+ .To export schemas from a manifest file
595
608
  [source,sh]
596
609
  ----
597
610
  $ bundle exec suma export -o express-files schemas-srl.yml
@@ -599,52 +612,54 @@ $ bundle exec suma export -o express-files schemas-srl.yml
599
612
  ----
600
613
 
601
614
  [example]
602
- .To export schemas with annotations
615
+ .To export a single plain schema
603
616
  [source,sh]
604
617
  ----
605
- $ bundle exec suma export -o express-files --annotations schemas-srl.yml
606
- # => generates express-files/ directory with annotated EXPRESS schemas
618
+ $ bundle exec suma export -o express-files geometry_schema.exp
619
+ # => generates express-files/geometry_schema.exp
607
620
  ----
608
621
 
609
622
  [example]
610
- .To export and create ZIP archive
623
+ .To export multiple plain schemas
611
624
  [source,sh]
612
625
  ----
613
- $ bundle exec suma export -o express-files --zip schemas-srl.yml
614
- # => generates both:
615
- # - express-files/ directory
616
- # - express-files.zip archive
626
+ $ bundle exec suma export -o express-files schema1.exp schema2.exp schema3.exp
627
+ # => generates express-files/{schema1,schema2,schema3}.exp
617
628
  ----
618
629
 
619
630
  [example]
620
- .To merge additional schemas and export
631
+ .To export from multiple manifest files
621
632
  [source,sh]
622
633
  ----
623
- $ bundle exec suma export -o express-files \
624
- -a additional-schemas.yml \
625
- schemas-srl.yml
634
+ $ bundle exec suma export -o express-files schemas-srl.yml additional-schemas.yml
626
635
  # => exports schemas from both manifest files
627
636
  ----
628
637
 
629
638
  [example]
630
- .To merge multiple additional schemas
639
+ .To export mix of manifest and plain schemas
631
640
  [source,sh]
632
641
  ----
633
- $ bundle exec suma export -o express-files \
634
- -a additional-schemas-1.yml \
635
- -a additional-schemas-2.yml \
636
- schemas-srl.yml
637
- # => exports schemas from primary manifest and all additional manifests
642
+ $ bundle exec suma export -o express-files schemas-srl.yml geometry_schema.exp
643
+ # => exports schemas from manifest with directory structure
644
+ # plus geometry_schema.exp at the root
638
645
  ----
639
646
 
640
647
  [example]
641
- .To export with all options
648
+ .To export schemas with annotations
642
649
  [source,sh]
643
650
  ----
644
- $ bundle exec suma export -o express-files \
645
- -a additional-schemas.yml \
646
- schemas-srl.yml
647
- # => exports schemas from both manifest files
651
+ $ bundle exec suma export -o express-files --annotations schemas-srl.yml
652
+ # => generates express-files/ directory with annotated EXPRESS schemas
653
+ ----
654
+
655
+ [example]
656
+ .To export and create ZIP archive
657
+ [source,sh]
658
+ ----
659
+ $ bundle exec suma export -o express-files --zip schemas-srl.yml
660
+ # => generates both:
661
+ # - express-files/ directory
662
+ # - express-files.zip archive
648
663
  ----
649
664
 
650
665
  [example]
@@ -654,35 +669,197 @@ $ bundle exec suma export -o express-files \
654
669
  $ bundle exec suma export -o express-files \
655
670
  --annotations \
656
671
  --zip \
657
- -a additional-schemas.yml \
658
- schemas-srl.yml
659
- # => generates annotated schemas in both directory and ZIP format,
660
- # including schemas from both manifest files
672
+ schemas-srl.yml geometry_schema.exp
673
+ # => generates annotated schemas in both directory and ZIP format
661
674
  ----
662
675
 
663
676
  ==== Output structure
664
677
 
665
- The exported directory structure mirrors the schema paths in the manifest:
678
+ The exported directory structure depends on the input file types:
666
679
 
667
680
  [source]
668
681
  ----
669
682
  express-files/
670
- ├── resources/
683
+ ├── geometry_schema.exp # standalone EXPRESS file (at root)
684
+ ├── topology_schema.exp # standalone EXPRESS file (at root)
685
+ ├── resources/ # from manifest files
671
686
  │ ├── action_schema/
672
687
  │ │ └── action_schema.exp
673
688
  │ └── ...
674
- ├── modules/
689
+ ├── modules/ # from manifest files
675
690
  │ ├── activity/
676
691
  │ │ ├── arm.exp
677
692
  │ │ └── mim.exp
678
693
  │ └── ...
679
- ├── business_object_models/
694
+ ├── business_object_models/ # from manifest files
680
695
  │ └── ...
681
- └── core_model/
696
+ └── core_model/ # from manifest files
682
697
  └── ...
683
698
  ----
684
699
 
685
700
 
701
+ === Expressir command
702
+
703
+ ==== General
704
+
705
+ The `suma expressir` command provides access to all Expressir commands for
706
+ working with EXPRESS schemas. This includes documentation coverage analysis,
707
+ schema formatting, validation, and other EXPRESS-related operations.
708
+
709
+ Expressir is a comprehensive toolkit for EXPRESS schema processing that Suma
710
+ integrates to provide additional functionality beyond Suma's core features.
711
+
712
+ [source,sh]
713
+ ----
714
+ $ suma expressir SUBCOMMAND ...ARGS [options]
715
+ ----
716
+
717
+ Available subcommands:
718
+
719
+ `coverage`:: Check documentation coverage of EXPRESS schemas
720
+ `format`:: Pretty print EXPRESS schemas
721
+ `clean`:: Strip remarks and prettify EXPRESS schemas
722
+ `validate`:: Validate EXPRESS schemas
723
+ `benchmark`:: Benchmark schema loading performance
724
+ `version`:: Display expressir version
725
+
726
+ ==== Coverage subcommand
727
+
728
+ The `coverage` subcommand analyzes EXPRESS schemas to determine documentation
729
+ coverage, reporting how many EXPRESS objects (entities, constants, functions,
730
+ types, etc.) have been documented with Annotated EXPRESS.
731
+
732
+ This addresses the need to measure and improve documentation quality in EXPRESS
733
+ schema collections.
734
+
735
+ [source,sh]
736
+ ----
737
+ $ suma expressir coverage PATH [PATH...] [options]
738
+ ----
739
+
740
+ Parameters:
741
+
742
+ `PATH`:: One or more paths to analyze. Each path can be:
743
+ +
744
+ --
745
+ * An EXPRESS schema manifest file (`.yml`, `.yaml`)
746
+ * A single EXPRESS file (`.exp`)
747
+ * A directory containing EXPRESS files
748
+ --
749
+
750
+ Options:
751
+
752
+ `--format=FORMAT`:: Output format: `text` (default), `json`, or `yaml`
753
+ `--output=FILE`:: Output file path for JSON/YAML formats (defaults to
754
+ `coverage_report.json` or `coverage_report.yaml`)
755
+ `--exclude=TYPES`:: Comma-separated list of EXPRESS entity types to exclude
756
+ from coverage analysis (e.g., `TYPE,CONSTANT,TYPE:SELECT`)
757
+ `--ignore-files=FILE`:: Path to YAML file containing array of file patterns
758
+ to ignore from overall coverage calculation
759
+
760
+ [example]
761
+ .To analyze documentation coverage of a schema collection
762
+ [source,sh]
763
+ ----
764
+ $ suma expressir coverage schemas-srl.yml
765
+ ----
766
+
767
+ .To analyze coverage and export as JSON
768
+ [source,sh]
769
+ ----
770
+ $ suma expressir coverage schemas-srl.yml --format=json --output=coverage.json
771
+ ----
772
+
773
+ .To analyze with exclusions
774
+ [source,sh]
775
+ ----
776
+ $ suma expressir coverage schemas/ --exclude=TYPE:ENUMERATION,PARAMETER
777
+ ----
778
+
779
+ .To analyze multiple sources
780
+ [source,sh]
781
+ ----
782
+ $ suma expressir coverage schemas-srl.yml additional-schemas.yml
783
+ ----
784
+
785
+ The coverage report includes:
786
+
787
+ * Overall coverage statistics (total entities, documented entities, percentage)
788
+ * Per-file coverage details
789
+ * Per-directory coverage summaries
790
+ * List of undocumented entities by type and name
791
+
792
+ ==== Format subcommand
793
+
794
+ The `format` subcommand pretty-prints EXPRESS schemas.
795
+
796
+ [source,sh]
797
+ ----
798
+ $ suma expressir format PATH
799
+ ----
800
+
801
+ Parameters:
802
+
803
+ `PATH`:: Path to the EXPRESS schema file to format
804
+
805
+ [example]
806
+ .To format an EXPRESS schema
807
+ [source,sh]
808
+ ----
809
+ $ suma expressir format schema.exp
810
+ ----
811
+
812
+ ==== Clean subcommand
813
+
814
+ The `clean` subcommand strips remarks and prettifies EXPRESS schemas.
815
+
816
+ [source,sh]
817
+ ----
818
+ $ suma expressir clean PATH [options]
819
+ ----
820
+
821
+ Parameters:
822
+
823
+ `PATH`:: Path to the EXPRESS schema file to clean
824
+
825
+ Options:
826
+
827
+ `--output=FILE`:: Output file path (defaults to stdout)
828
+
829
+ [example]
830
+ .To clean an EXPRESS schema and save to file
831
+ [source,sh]
832
+ ----
833
+ $ suma expressir clean schema.exp --output=cleaned_schema.exp
834
+ ----
835
+
836
+ ==== Validate subcommand
837
+
838
+ The `validate` subcommand validates EXPRESS schemas for syntactic correctness.
839
+
840
+ [source,sh]
841
+ ----
842
+ $ suma expressir validate PATH [PATH...]
843
+ ----
844
+
845
+ Parameters:
846
+
847
+ `PATH`:: One or more paths to EXPRESS schema files or directories to validate
848
+
849
+ [example]
850
+ .To validate EXPRESS schemas
851
+ [source,sh]
852
+ ----
853
+ $ suma expressir validate schema.exp
854
+ $ suma expressir validate schemas/
855
+ ----
856
+
857
+ ==== Additional information
858
+
859
+ For complete documentation of all Expressir commands and options, see the
860
+ https://github.com/lutaml/expressir[Expressir documentation].
861
+
862
+
686
863
  == Usage: Ruby
687
864
 
688
865
  === General
@@ -7,37 +7,44 @@ module Suma
7
7
  module Cli
8
8
  # Export command for exporting EXPRESS schemas from a manifest
9
9
  class Export < Thor
10
- desc "export MANIFEST_FILE",
11
- "Export EXPRESS schemas from manifest"
10
+ desc "export *FILES",
11
+ "Export EXPRESS schemas from manifest files or " \
12
+ "standalone EXPRESS files"
12
13
  option :output, type: :string, aliases: "-o", required: true,
13
14
  desc: "Output directory path"
14
- option :additional, type: :array, aliases: "-a",
15
- desc: "Additional schemas manifest files to merge (can be specified multiple times)"
16
15
  option :annotations, type: :boolean, default: false,
17
16
  desc: "Include annotations (remarks/comments)"
18
17
  option :zip, type: :boolean, default: false,
19
18
  desc: "Create ZIP archive of exported schemas"
20
19
 
21
- def export(manifest_file)
20
+ def export(*files)
22
21
  require_relative "../schema_exporter"
23
22
  require_relative "../utils"
24
23
  require "expressir"
25
24
 
26
- unless File.exist?(manifest_file)
27
- raise Errno::ENOENT, "Specified manifest file " \
28
- "`#{manifest_file}` not found."
29
- end
30
-
31
- run(manifest_file, options)
25
+ validate_files(files)
26
+ run(files, options)
32
27
  end
33
28
 
34
29
  private
35
30
 
36
- def run(manifest_file, options)
37
- config = load_and_merge_configs(manifest_file, options[:additional])
31
+ def validate_files(files)
32
+ if files.empty?
33
+ raise ArgumentError, "At least one file must be specified"
34
+ end
35
+
36
+ files.each do |file|
37
+ unless File.exist?(file)
38
+ raise Errno::ENOENT, "Specified file `#{file}` not found."
39
+ end
40
+ end
41
+ end
42
+
43
+ def run(files, options)
44
+ schemas = load_schemas_from_files(files)
38
45
 
39
46
  exporter = SchemaExporter.new(
40
- config: config,
47
+ schemas: schemas,
41
48
  output_path: options[:output],
42
49
  options: {
43
50
  annotations: options[:annotations],
@@ -48,39 +55,53 @@ module Suma
48
55
  exporter.export
49
56
  end
50
57
 
51
- # rubocop:disable Metrics/MethodLength
52
- def load_and_merge_configs(primary_path, additional_paths)
53
- primary_config = Expressir::SchemaManifest.from_file(primary_path)
54
- return primary_config unless additional_paths && !additional_paths.empty?
58
+ def load_schemas_from_files(files)
59
+ all_schemas = []
55
60
 
56
- # Load all additional manifests
57
- additional_configs = additional_paths.map do |path|
58
- unless File.exist?(path)
59
- raise Errno::ENOENT, "Specified additional manifest file " \
60
- "`#{path}` not found."
61
- end
62
- Expressir::SchemaManifest.from_file(path)
61
+ files.each do |file|
62
+ all_schemas += process_file(file)
63
63
  end
64
64
 
65
- # Merge all configs into the primary
66
- merge_all_configs(primary_config, additional_configs)
65
+ all_schemas
67
66
  end
68
- # rubocop:enable Metrics/MethodLength
69
67
 
70
- def merge_all_configs(primary, additional_configs)
71
- # Collect all schemas from primary and all additional manifests
72
- all_schemas = primary.schemas.dup
73
-
74
- additional_configs.each do |config|
75
- all_schemas += config.schemas
68
+ def process_file(file)
69
+ case File.extname(file).downcase
70
+ when ".yml", ".yaml"
71
+ load_manifest_schemas(file)
72
+ when ".exp"
73
+ [create_schema_from_exp_file(file)]
74
+ else
75
+ raise ArgumentError, "Unsupported file type: #{file}. " \
76
+ "Only .yml, .yaml, and .exp files are " \
77
+ "supported."
76
78
  end
79
+ end
80
+
81
+ def load_manifest_schemas(file)
82
+ manifest = Expressir::SchemaManifest.from_file(file)
83
+ manifest.schemas
84
+ end
77
85
 
78
- Expressir::SchemaManifest.new(
79
- path: primary.path,
80
- schemas: all_schemas,
86
+ def create_schema_from_exp_file(exp_file)
87
+ # Create a schema object from a standalone EXPRESS file
88
+ # The id will be determined during parsing
89
+ StandaloneSchema.new(
90
+ id: nil,
91
+ path: File.expand_path(exp_file),
81
92
  )
82
93
  end
83
94
 
95
+ # Simple schema class for standalone EXPRESS files
96
+ class StandaloneSchema
97
+ attr_accessor :id, :path
98
+
99
+ def initialize(id:, path:)
100
+ @id = id
101
+ @path = path
102
+ end
103
+ end
104
+
84
105
  def self.exit_on_failure?
85
106
  true
86
107
  end
data/lib/suma/cli.rb CHANGED
@@ -3,6 +3,7 @@
3
3
  require "thor"
4
4
  require_relative "thor_ext"
5
5
  require_relative "cli/validate"
6
+ require "expressir/cli"
6
7
 
7
8
  module Suma
8
9
  module Cli
@@ -15,10 +16,9 @@ module Suma
15
16
  option :compile, type: :boolean, default: true,
16
17
  desc: "Compile or skip compile of collection"
17
18
  option :schemas_all_path, type: :string, aliases: "-s",
18
- desc: "Generate file that contains all schemas in the collection."
19
+ desc: "Generate file that contains all " \
20
+ "schemas in the collection."
19
21
  def build(_site_manifest)
20
- # # If no arguments, add an empty array to ensure the default command is triggered
21
- # args = [] if args.empty?
22
22
  require_relative "cli/build"
23
23
  Cli::Build.start
24
24
  end
@@ -60,17 +60,16 @@ module Suma
60
60
  Cli::ConvertJsdai.start
61
61
  end
62
62
 
63
- desc "export MANIFEST_FILE",
64
- "Export EXPRESS schemas from manifest"
63
+ desc "export *FILES",
64
+ "Export EXPRESS schemas from manifest files or " \
65
+ "standalone EXPRESS files"
65
66
  option :output, type: :string, aliases: "-o", required: true,
66
67
  desc: "Output directory path"
67
- option :additional, type: :array, aliases: "-a",
68
- desc: "Additional schemas manifest files to merge (can be specified multiple times)"
69
68
  option :annotations, type: :boolean, default: false,
70
69
  desc: "Include annotations (remarks/comments)"
71
70
  option :zip, type: :boolean, default: false,
72
71
  desc: "Create ZIP archive of exported schemas"
73
- def export(_manifest_file)
72
+ def export(*_files)
74
73
  require_relative "cli/export"
75
74
  Cli::Export.start
76
75
  end
@@ -78,6 +77,9 @@ module Suma
78
77
  desc "validate SUBCOMMAND ...ARGS", "Validate express documents"
79
78
  subcommand "validate", Cli::Validate
80
79
 
80
+ desc "expressir SUBCOMMAND ...ARGS", "Expressir commands"
81
+ subcommand "expressir", Expressir::Cli
82
+
81
83
  def self.exit_on_failure?
82
84
  true
83
85
  end
@@ -36,12 +36,25 @@ module Suma
36
36
  model.entry = CollectionManifest.from_yaml(value.to_yaml)
37
37
  end
38
38
 
39
+ # Recursively exports schema configuration by traversing collection manifests.
40
+ #
41
+ # This method builds an EXPRESS Schema Manifest (Expressir::SchemaManifest) by:
42
+ # 1. Starting with an empty or existing Expressir::SchemaManifest
43
+ # 2. Recursively traversing child entries to collect schemas
44
+ # 3. Using Expressir::SchemaManifest#concat to combine manifests
45
+ #
46
+ # The actual schema manifest operations (creation, concatenation, serialization)
47
+ # are handled by Expressir's SchemaManifest class, keeping the logic DRY.
48
+ #
49
+ # @param path [String] Base path for resolving relative schema paths
50
+ # @return [Expressir::SchemaManifest] Combined schema manifest
39
51
  def export_schema_config(path)
40
52
  export_config = @schema_config || Expressir::SchemaManifest.new
41
53
  return export_config unless entry
42
54
 
43
55
  entry.each do |x|
44
56
  child_config = x.export_schema_config(path)
57
+ # Use Expressir's concat method to combine schema manifests
45
58
  export_config.concat(child_config) if child_config
46
59
  end
47
60
 
@@ -6,19 +6,20 @@ require "expressir"
6
6
 
7
7
  module Suma
8
8
  class ExpressSchema
9
- attr_accessor :path, :id, :parsed, :output_path
9
+ attr_accessor :path, :id, :parsed, :output_path, :is_standalone_file
10
10
 
11
- def initialize(id:, path:, output_path:)
11
+ def initialize(id:, path:, output_path:, is_standalone_file: false)
12
12
  @path = Pathname.new(path).expand_path
13
13
  @id = id
14
14
  @output_path = output_path
15
+ @is_standalone_file = is_standalone_file
15
16
  end
16
17
 
17
18
  def type
18
- case @path.to_s
19
- when %r{.*/resources/.*}
19
+ path_str = @path.to_s
20
+ if path_str.include?("/resources/")
20
21
  "resources"
21
- when %r{.*/modules/.*}
22
+ elsif path_str.include?("/modules/")
22
23
  "modules"
23
24
  else
24
25
  "unknown_type"
@@ -39,7 +40,23 @@ module Suma
39
40
  end
40
41
 
41
42
  def filename_plain
42
- File.join(@output_path, type, id, File.basename(@path))
43
+ ensure_id_loaded
44
+ build_output_filename
45
+ end
46
+
47
+ def ensure_id_loaded
48
+ parsed unless @id
49
+ end
50
+
51
+ def build_output_filename
52
+ if @is_standalone_file
53
+ # For standalone files, output directly to output_path
54
+ File.join(@output_path, "#{@id}.exp")
55
+ else
56
+ # For manifest schemas, preserve directory structure
57
+ # Note: @output_path already contains the category (resources/modules)
58
+ File.join(@output_path, @id, File.basename(@path))
59
+ end
43
60
  end
44
61
 
45
62
  def save_exp(with_annotations: false)
@@ -15,12 +15,15 @@ module Suma
15
15
  def run(metanorma_yaml_path:, schemas_all_path:, compile:,
16
16
  output_directory: "_site")
17
17
  Utils.log "Current directory: #{Dir.getwd}, writing #{schemas_all_path}..."
18
+
19
+ # Generate EXPRESS Schema Manifest by traversing Metanorma Site Manifest
20
+ # This uses Expressir::SchemaManifest for all manifest operations
18
21
  collection_config = export_schema_config(metanorma_yaml_path,
19
22
  schemas_all_path)
20
23
 
21
24
  unless compile
22
25
  Utils.log "No compile option set. Skipping schema compilation."
23
- nil
26
+ return nil
24
27
  end
25
28
 
26
29
  Utils.log "Compiling schema collection..."
@@ -33,6 +36,18 @@ output_directory: "_site")
33
36
 
34
37
  private
35
38
 
39
+ # Generates EXPRESS Schema Manifest from Metanorma Site Manifest structure.
40
+ #
41
+ # This method:
42
+ # 1. Reads the Metanorma site manifest to discover collection files
43
+ # 2. Traverses collection manifests to find individual schemas.yaml files
44
+ # 3. Uses Expressir::SchemaManifest to aggregate and manage schema entries
45
+ # 4. Saves the unified schema manifest using Expressir's to_file method
46
+ #
47
+ # @param metanorma_yaml_path [String] Path to Metanorma site manifest
48
+ # @param schemas_all_path [String] Output path for unified schema manifest
49
+ # @return [CollectionConfig] The loaded collection configuration
50
+ # rubocop:disable Metrics/MethodLength
36
51
  def export_schema_config(metanorma_yaml_path, schemas_all_path)
37
52
  # This reads the metanorma.yml file
38
53
  site_config = Suma::SiteConfig::Config.from_file(metanorma_yaml_path)
@@ -43,13 +58,17 @@ output_directory: "_site")
43
58
  collection_config.path = collection_config_path
44
59
  collection_config.manifest.expand_schemas_only("schema_docs")
45
60
 
61
+ # Recursively traverse collection manifests to build unified schema manifest
62
+ # Uses Expressir::SchemaManifest methods (concat, to_file) for operations
46
63
  exported_schema_config = collection_config.manifest.export_schema_config(schemas_all_path)
47
64
  exported_schema_config.path = schemas_all_path
48
65
 
66
+ # Save using Expressir's SchemaManifest#to_file method
49
67
  exported_schema_config.to_file
50
68
 
51
69
  collection_config
52
70
  end
71
+ # rubocop:enable Metrics/MethodLength
53
72
 
54
73
  def compile_schema(schemas_all_path, collection_config)
55
74
  # now get rid of the source documents for schema sources
@@ -9,8 +9,8 @@ require_relative "utils"
9
9
 
10
10
  module Suma
11
11
  class SchemaCollection
12
- attr_accessor :config, :schemas, :docs, :output_path_docs, :output_path_schemas,
13
- :manifest
12
+ attr_accessor :config, :schemas, :docs, :output_path_docs,
13
+ :output_path_schemas, :manifest
14
14
 
15
15
  def initialize(config: nil, config_yaml: nil, output_path_docs: nil,
16
16
  output_path_schemas: nil, manifest: nil)
@@ -18,7 +18,9 @@ module Suma
18
18
  @docs = {}
19
19
  @schema_name_to_docs = {}
20
20
  @output_path_docs = Pathname.new(output_path_docs || Dir.pwd).expand_path
21
- @output_path_schemas = Pathname.new(output_path_schemas || Dir.pwd).expand_path
21
+ @output_path_schemas = Pathname.new(
22
+ output_path_schemas || Dir.pwd,
23
+ ).expand_path
22
24
  @config = config
23
25
  @config ||= config_yaml && Expressir::SchemaManifest.from_file(config_yaml)
24
26
  @manifest = manifest
@@ -69,7 +71,7 @@ module Suma
69
71
 
70
72
  # Use SchemaExporter for schema export
71
73
  exporter = SchemaExporter.new(
72
- config: @config,
74
+ schemas: @config.schemas,
73
75
  output_path: @output_path_schemas,
74
76
  options: { annotations: false },
75
77
  )
@@ -8,10 +8,10 @@ module Suma
8
8
  # SchemaExporter exports EXPRESS schemas from a manifest
9
9
  # with configurable options for annotations and ZIP packaging
10
10
  class SchemaExporter
11
- attr_reader :config, :output_path, :options
11
+ attr_reader :schemas, :output_path, :options
12
12
 
13
- def initialize(config:, output_path:, options: {})
14
- @config = config
13
+ def initialize(schemas:, output_path:, options: {})
14
+ @schemas = schemas
15
15
  @output_path = Pathname.new(output_path).expand_path
16
16
  @options = default_options.merge(options)
17
17
  end
@@ -19,7 +19,6 @@ module Suma
19
19
  def export
20
20
  Utils.log "Exporting schemas to: #{output_path}"
21
21
 
22
- schemas = config.schemas
23
22
  export_to_directory(schemas)
24
23
  create_zip_archive if options[:create_zip]
25
24
 
@@ -43,22 +42,37 @@ module Suma
43
42
  end
44
43
 
45
44
  def export_single_schema(schema)
46
- category = categorize_schema(schema)
47
- schema_output_path = output_path.join(category).to_s
45
+ # Check if this is a standalone EXPRESS file
46
+ # (not from a manifest structure)
47
+ is_standalone_file = schema.is_a?(Cli::Export::StandaloneSchema)
48
+ schema_output_path = determine_output_path(schema, is_standalone_file)
48
49
 
49
50
  express_schema = ExpressSchema.new(
50
51
  id: schema.id,
51
52
  path: schema.path.to_s,
52
53
  output_path: schema_output_path,
54
+ is_standalone_file: is_standalone_file,
53
55
  )
54
56
 
55
57
  express_schema.save_exp(with_annotations: options[:annotations])
56
58
  end
57
59
 
60
+ def determine_output_path(schema, is_standalone_file)
61
+ if is_standalone_file
62
+ # For standalone files, output directly to the root
63
+ output_path.to_s
64
+ else
65
+ # For manifest schemas, preserve directory structure
66
+ category = categorize_schema(schema)
67
+ output_path.join(category).to_s
68
+ end
69
+ end
70
+
58
71
  # rubocop:disable Metrics/MethodLength
59
72
  def categorize_schema(schema)
60
73
  path = schema.path.to_s
61
74
 
75
+ # Check if this is from a manifest structure or a standalone EXPRESS file
62
76
  case path
63
77
  when %r{/resources/}
64
78
  "resources"
@@ -69,7 +83,8 @@ module Suma
69
83
  when %r{/core_model/}
70
84
  "core_model"
71
85
  else
72
- "other"
86
+ # standalone EXPRESS file not from a manifest structure
87
+ "standalone"
73
88
  end
74
89
  end
75
90
  # rubocop:enable Metrics/MethodLength
data/lib/suma/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Suma
4
- VERSION = "0.1.23"
4
+ VERSION = "0.1.24"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: suma
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.23
4
+ version: 0.1.24
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-10-06 00:00:00.000000000 Z
11
+ date: 2025-10-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: expressir