lutaml-model 0.8.17 → 0.8.18

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.
Files changed (192) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/downstream-performance.yml +27 -1
  3. data/.github/workflows/js-pr-check.yml +102 -0
  4. data/.github/workflows/js-sync.yml +51 -0
  5. data/.github/workflows/opal.yml +26 -6
  6. data/.github/workflows/performance.yml +32 -7
  7. data/.github/workflows/rake.yml +78 -32
  8. data/.gitmodules +6 -0
  9. data/.rubocop_todo.yml +8 -0
  10. data/Gemfile +16 -1
  11. data/README.adoc +267 -0
  12. data/Rakefile +166 -4
  13. data/docs/_guides/schema-import.adoc +42 -0
  14. data/docs/_guides/xml-mapping.adoc +21 -0
  15. data/docs/_pages/validation.adoc +65 -0
  16. data/lib/compat/opal/generate_boot.rb +123 -0
  17. data/lib/compat/opal/js_bundle_entry.rb +42 -0
  18. data/lib/compat/opal/lutaml_model_boot.rb +497 -0
  19. data/lib/compat/opal/moxml_boot.rb +68 -0
  20. data/lib/compat/opal/yaml_compat.rb +32 -0
  21. data/lib/lutaml/json.rb +1 -1
  22. data/lib/lutaml/key_value/transform.rb +3 -8
  23. data/lib/lutaml/key_value/transformation/value_serializer.rb +14 -1
  24. data/lib/lutaml/key_value/transformation.rb +17 -5
  25. data/lib/lutaml/model/adapter_resolver.rb +2 -2
  26. data/lib/lutaml/model/attribute.rb +88 -11
  27. data/lib/lutaml/model/cached_type_resolver.rb +1 -1
  28. data/lib/lutaml/model/error/union_schema_unsupported_error.rb +11 -0
  29. data/lib/lutaml/model/global_context.rb +11 -8
  30. data/lib/lutaml/model/instrumentation.rb +2 -2
  31. data/lib/lutaml/model/mapping/mapping_rule.rb +0 -6
  32. data/lib/lutaml/model/register.rb +2 -2
  33. data/lib/lutaml/model/runtime_compatibility.rb +50 -0
  34. data/lib/lutaml/model/schema/class_loader.rb +58 -0
  35. data/lib/lutaml/model/schema/compiled_output.rb +83 -0
  36. data/lib/lutaml/model/schema/definitions/attribute.rb +32 -0
  37. data/lib/lutaml/model/schema/definitions/choice.rb +20 -0
  38. data/lib/lutaml/model/schema/definitions/facet.rb +40 -0
  39. data/lib/lutaml/model/schema/definitions/group_import.rb +22 -0
  40. data/lib/lutaml/model/schema/definitions/member_walk.rb +33 -0
  41. data/lib/lutaml/model/schema/definitions/model.rb +40 -0
  42. data/lib/lutaml/model/schema/definitions/namespace.rb +23 -0
  43. data/lib/lutaml/model/schema/definitions/restricted_type.rb +30 -0
  44. data/lib/lutaml/model/schema/definitions/sequence.rb +19 -0
  45. data/lib/lutaml/model/schema/definitions/simple_content.rb +22 -0
  46. data/lib/lutaml/model/schema/definitions/transform_facet.rb +21 -0
  47. data/lib/lutaml/model/schema/definitions/type_ref.rb +20 -0
  48. data/lib/lutaml/model/schema/definitions/union_type.rb +28 -0
  49. data/lib/lutaml/model/schema/definitions/xml_root.rb +20 -0
  50. data/lib/lutaml/model/schema/definitions.rb +24 -0
  51. data/lib/lutaml/model/schema/file_writer.rb +70 -0
  52. data/lib/lutaml/model/schema/generator/definitions_collection.rb +16 -2
  53. data/lib/lutaml/model/schema/generator/property.rb +21 -4
  54. data/lib/lutaml/model/schema/module_nesting.rb +31 -0
  55. data/lib/lutaml/model/schema/namespace_naming.rb +46 -0
  56. data/lib/lutaml/model/schema/registry_generator.rb +115 -0
  57. data/lib/lutaml/model/schema/renderers/base.rb +40 -0
  58. data/lib/lutaml/model/schema/renderers/mappings.rb +72 -0
  59. data/lib/lutaml/model/schema/renderers/member_decls.rb +115 -0
  60. data/lib/lutaml/model/schema/renderers/model.rb +121 -0
  61. data/lib/lutaml/model/schema/renderers/namespace.rb +30 -0
  62. data/lib/lutaml/model/schema/renderers/registration.rb +64 -0
  63. data/lib/lutaml/model/schema/renderers/required_files_calculator.rb +93 -0
  64. data/lib/lutaml/model/schema/renderers/restricted_type.rb +88 -0
  65. data/lib/lutaml/model/schema/renderers/union.rb +83 -0
  66. data/lib/lutaml/model/schema/renderers.rb +19 -0
  67. data/lib/lutaml/model/schema/rnc_compiler/source_resolver.rb +47 -0
  68. data/lib/lutaml/model/schema/rnc_compiler.rb +62 -0
  69. data/lib/lutaml/model/schema/rng_compiler/define_classifier.rb +91 -0
  70. data/lib/lutaml/model/schema/rng_compiler/element_visitor.rb +392 -0
  71. data/lib/lutaml/model/schema/rng_compiler/member_collector.rb +30 -0
  72. data/lib/lutaml/model/schema/rng_compiler/rng_helpers.rb +152 -0
  73. data/lib/lutaml/model/schema/rng_compiler/value_type_resolver.rb +125 -0
  74. data/lib/lutaml/model/schema/rng_compiler.rb +220 -0
  75. data/lib/lutaml/model/schema/templates.rb +162 -0
  76. data/lib/lutaml/model/schema/xml_compiler/element_order.rb +41 -0
  77. data/lib/lutaml/model/schema/xml_compiler/registry_generator.rb +31 -107
  78. data/lib/lutaml/model/schema/xml_compiler/spec_builder/complex_types.rb +191 -0
  79. data/lib/lutaml/model/schema/xml_compiler/spec_builder/members.rb +216 -0
  80. data/lib/lutaml/model/schema/xml_compiler/spec_builder/simple_types.rb +153 -0
  81. data/lib/lutaml/model/schema/xml_compiler/spec_builder.rb +167 -0
  82. data/lib/lutaml/model/schema/xml_compiler/supported_data_types.rb +87 -0
  83. data/lib/lutaml/model/schema/xml_compiler.rb +38 -517
  84. data/lib/lutaml/model/schema.rb +19 -0
  85. data/lib/lutaml/model/serialize/attribute_definition.rb +14 -2
  86. data/lib/lutaml/model/serialize/builder.rb +15 -2
  87. data/lib/lutaml/model/serialize/value_mapping.rb +9 -57
  88. data/lib/lutaml/model/serialize.rb +6 -9
  89. data/lib/lutaml/model/services/rule_value_extractor.rb +2 -3
  90. data/lib/lutaml/model/services.rb +0 -2
  91. data/lib/lutaml/model/store.rb +2 -1
  92. data/lib/lutaml/model/toml.rb +1 -1
  93. data/lib/lutaml/model/transform.rb +17 -39
  94. data/lib/lutaml/model/transformation_registry.rb +1 -1
  95. data/lib/lutaml/model/type.rb +1 -0
  96. data/lib/lutaml/model/union.rb +320 -0
  97. data/lib/lutaml/model/utils.rb +6 -0
  98. data/lib/lutaml/model/validation.rb +1 -4
  99. data/lib/lutaml/model/version.rb +1 -1
  100. data/lib/lutaml/model.rb +18 -0
  101. data/lib/lutaml/toml.rb +1 -1
  102. data/lib/lutaml/xml/adapter.rb +1 -1
  103. data/lib/lutaml/xml/adapter_loader.rb +1 -1
  104. data/lib/lutaml/xml/builder/base.rb +1 -1
  105. data/lib/lutaml/xml/builder.rb +1 -1
  106. data/lib/lutaml/xml/error/schema_validation_error.rb +25 -0
  107. data/lib/lutaml/xml/model_transform.rb +5 -1
  108. data/lib/lutaml/xml/schema/relaxng_schema.rb +5 -0
  109. data/lib/lutaml/xml/schema/xsd_schema.rb +4 -0
  110. data/lib/lutaml/xml/serialization/format_conversion.rb +48 -0
  111. data/lib/lutaml/xml/serialization/instance_methods.rb +13 -0
  112. data/lib/lutaml/xml/transformation/element_builder.rb +21 -8
  113. data/lib/lutaml/xml/transformation/ordered_applier.rb +6 -0
  114. data/lib/lutaml/xml/xsd_validator.rb +66 -0
  115. data/lib/lutaml/xml.rb +53 -12
  116. data/lutaml-model.gemspec +4 -2
  117. data/spec/fixtures/xml/schema/rnc/address_book.rnc +15 -0
  118. data/spec/fixtures/xml/schema/rnc/book_features.rnc +18 -0
  119. data/spec/fixtures/xml/schema/rnc/includes/book.rnc +6 -0
  120. data/spec/fixtures/xml/schema/rnc/includes/circular_a.rnc +8 -0
  121. data/spec/fixtures/xml/schema/rnc/includes/circular_b.rnc +8 -0
  122. data/spec/fixtures/xml/schema/rnc/includes/library.rnc +8 -0
  123. data/spec/fixtures/xml/schema/rng/address_book.rng +16 -0
  124. data/spec/fixtures/xml/schema/rng/book.rng +27 -0
  125. data/spec/fixtures/xml/schema/rng/fixed_value.rng +11 -0
  126. data/spec/fixtures/xml/schema/rng/fragment_in_choice.rng +30 -0
  127. data/spec/fixtures/xml/schema/rng/full_name.rng +39 -0
  128. data/spec/fixtures/xml/schema/rng/integer_range.rng +32 -0
  129. data/spec/fixtures/xml/schema/rng/list_type.rng +12 -0
  130. data/spec/fixtures/xml/schema/rng/namespaced.rng +9 -0
  131. data/spec/fixtures/xml/schema/rng/paragraph.rng +13 -0
  132. data/spec/fixtures/xml/schema/rng/person.rng +23 -0
  133. data/spec/fixtures/xml/schema/rng/union.rng +14 -0
  134. data/spec/fixtures/xml/validate_xml_with_address.xsd +13 -0
  135. data/spec/fixtures/xml/validate_xml_with_contact.xsd +11 -0
  136. data/spec/fixtures/xml/validate_xml_with_person.xsd +11 -0
  137. data/spec/fixtures/xml/validate_xml_with_person_strict.xsd +17 -0
  138. data/spec/fixtures/xml/validate_xml_with_product.xsd +37 -0
  139. data/spec/lutaml/model/attribute_apply_value_map_spec.rb +134 -0
  140. data/spec/lutaml/model/attribute_spec.rb +169 -12
  141. data/spec/lutaml/model/boot_manifest_spec.rb +30 -0
  142. data/spec/lutaml/model/collection_spec.rb +18 -0
  143. data/spec/lutaml/model/mixed_content_spec.rb +171 -0
  144. data/spec/lutaml/model/opal_smoke_spec.rb +67 -2
  145. data/spec/lutaml/model/ordered_content_spec.rb +30 -0
  146. data/spec/lutaml/model/raw_element_spec.rb +125 -0
  147. data/spec/lutaml/model/rule_value_extractor_spec.rb +7 -14
  148. data/spec/lutaml/model/runtime_compatibility_spec.rb +25 -0
  149. data/spec/lutaml/model/schema/renderers/model_spec.rb +149 -0
  150. data/spec/lutaml/model/schema/renderers/namespace_spec.rb +36 -0
  151. data/spec/lutaml/model/schema/renderers/restricted_type_spec.rb +75 -0
  152. data/spec/lutaml/model/schema/renderers/union_spec.rb +58 -0
  153. data/spec/lutaml/model/transform_apply_value_map_spec.rb +64 -0
  154. data/spec/lutaml/model/union_attribute_spec.rb +658 -0
  155. data/spec/lutaml/model/union_spec.rb +257 -0
  156. data/spec/lutaml/model/utils_spec.rb +14 -0
  157. data/spec/lutaml/xml/opal_xml_spec.rb +22 -20
  158. data/spec/lutaml/xml/schema/compiler_spec.rb +21 -4
  159. data/spec/lutaml/xml/schema/rnc_compiler_spec.rb +404 -0
  160. data/spec/lutaml/xml/schema/rng_compiler_spec.rb +893 -0
  161. data/spec/lutaml/xml/validate_xml_with_constructs_spec.rb +222 -0
  162. data/spec/lutaml/xml/validate_xml_with_spec.rb +186 -0
  163. data/spec/spec_helper.rb +28 -11
  164. data/spec/support/opal.rb +5 -2
  165. metadata +97 -31
  166. data/lib/lutaml/model/schema/xml_compiler/attribute.rb +0 -106
  167. data/lib/lutaml/model/schema/xml_compiler/attribute_group.rb +0 -47
  168. data/lib/lutaml/model/schema/xml_compiler/choice.rb +0 -67
  169. data/lib/lutaml/model/schema/xml_compiler/complex_content.rb +0 -27
  170. data/lib/lutaml/model/schema/xml_compiler/complex_content_restriction.rb +0 -34
  171. data/lib/lutaml/model/schema/xml_compiler/complex_type.rb +0 -173
  172. data/lib/lutaml/model/schema/xml_compiler/element.rb +0 -121
  173. data/lib/lutaml/model/schema/xml_compiler/group.rb +0 -113
  174. data/lib/lutaml/model/schema/xml_compiler/restriction.rb +0 -104
  175. data/lib/lutaml/model/schema/xml_compiler/sequence.rb +0 -52
  176. data/lib/lutaml/model/schema/xml_compiler/simple_content.rb +0 -40
  177. data/lib/lutaml/model/schema/xml_compiler/simple_type.rb +0 -265
  178. data/lib/lutaml/model/schema/xml_compiler/xml_namespace_class.rb +0 -110
  179. data/lib/lutaml/model/services/default_value_resolver.rb +0 -60
  180. data/spec/lutaml/model/services/default_value_resolver_spec.rb +0 -162
  181. data/spec/lutaml/xml/schema/compiler/attribute_group_spec.rb +0 -71
  182. data/spec/lutaml/xml/schema/compiler/attribute_spec.rb +0 -67
  183. data/spec/lutaml/xml/schema/compiler/choice_spec.rb +0 -74
  184. data/spec/lutaml/xml/schema/compiler/complex_content_restriction_spec.rb +0 -60
  185. data/spec/lutaml/xml/schema/compiler/complex_content_spec.rb +0 -41
  186. data/spec/lutaml/xml/schema/compiler/complex_type_spec.rb +0 -202
  187. data/spec/lutaml/xml/schema/compiler/element_spec.rb +0 -68
  188. data/spec/lutaml/xml/schema/compiler/group_spec.rb +0 -90
  189. data/spec/lutaml/xml/schema/compiler/restriction_spec.rb +0 -77
  190. data/spec/lutaml/xml/schema/compiler/sequence_spec.rb +0 -65
  191. data/spec/lutaml/xml/schema/compiler/simple_content_spec.rb +0 -61
  192. data/spec/lutaml/xml/schema/compiler/simple_type_spec.rb +0 -184
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module XmlCompiler
7
+ # Walks parsed XSD AST objects and emits Definitions::* specs.
8
+ # Holds the in-progress class hashes that ref resolution looks
9
+ # up by name. Stateful by design (refs and forward references
10
+ # demand it); kept out of XmlCompiler.rb so the orchestrator
11
+ # stays focused on parse/dispatch.
12
+ class SpecBuilder
13
+ autoload :SimpleTypes, "#{__dir__}/spec_builder/simple_types"
14
+ autoload :Members, "#{__dir__}/spec_builder/members"
15
+ autoload :ComplexTypes, "#{__dir__}/spec_builder/complex_types"
16
+
17
+ attr_reader :simple_types, :complex_types, :group_types,
18
+ :elements, :attributes, :attribute_groups,
19
+ :namespace_classes, :namespace_class_name,
20
+ :members_builder
21
+
22
+ def initialize
23
+ @simple_types = MappingHash.new
24
+ @complex_types = MappingHash.new
25
+ @group_types = MappingHash.new
26
+ @elements = MappingHash.new
27
+ @attributes = MappingHash.new
28
+ @attribute_groups = MappingHash.new
29
+ @namespace_classes = MappingHash.new
30
+ @simple_types_builder = SimpleTypes.new(self)
31
+ @members_builder = Members.new(self)
32
+ @complex_types_builder = ComplexTypes.new(self)
33
+ end
34
+
35
+ def populate_default_attributes
36
+ XmlCompiler::XML_DEFINED_ATTRIBUTES.each do |name, w3c_class|
37
+ @attributes[name] = Definitions::Attribute.new(
38
+ name: Utils.snake_case(name),
39
+ type: Definitions::TypeRef.new(kind: :w3c, value: w3c_class),
40
+ xml_name: name,
41
+ kind: :attribute,
42
+ )
43
+ end
44
+ end
45
+
46
+ def collect_namespaces(schemas, options)
47
+ requested_uri = options[:namespace]
48
+ uris = []
49
+ uris << requested_uri if requested_uri
50
+ schemas.each do |schema|
51
+ uris << schema.target_namespace if schema.target_namespace
52
+ end
53
+ uris.uniq.each do |uri|
54
+ next if uri.nil? || uri.empty?
55
+
56
+ prefix = options[:prefix] if requested_uri == uri
57
+ ns = Definitions::Namespace.new(
58
+ class_name: NamespaceNaming.class_name_for(uri),
59
+ uri: uri,
60
+ prefix_default: prefix || NamespaceNaming.prefix_for(uri),
61
+ )
62
+ @namespace_classes[ns.class_name] = ns
63
+ end
64
+ # Resolve the namespace_class_name once. When the caller
65
+ # passed :namespace, every generated complex type gets this
66
+ # name. Mirrors XmlCompiler::ComplexType#setup_options on main.
67
+ @namespace_class_name =
68
+ requested_uri && @namespace_classes.values.find { |ns| ns.uri == requested_uri }&.class_name
69
+ end
70
+
71
+ def walk_schemas(schemas)
72
+ return if schemas.empty?
73
+
74
+ # Two-pass walk: register top-level Elements / Attributes /
75
+ # AttributeGroups / SimpleTypes first so forward references
76
+ # from ComplexTypes / Groups resolve in pass 2.
77
+ collect_lookups(schemas)
78
+ build_complex_types(schemas)
79
+ end
80
+
81
+ def collect_lookups(schemas)
82
+ each_schema_item(schemas) { |item, schema| dispatch_lookup(item, schema) }
83
+ end
84
+
85
+ def build_complex_types(schemas)
86
+ each_schema_item(schemas) { |item, schema| dispatch_complex(item, schema) }
87
+ end
88
+
89
+ def dispatch_lookup(item, schema)
90
+ case item
91
+ when Lutaml::Xml::Schema::Xsd::SimpleType
92
+ @simple_types[item.name] = build_simple_type(item)
93
+ when Lutaml::Xml::Schema::Xsd::Element
94
+ @elements[item.name] = @members_builder.build_top_level_attribute(item, kind: :element)
95
+ when Lutaml::Xml::Schema::Xsd::Attribute
96
+ return if xml_defined_attribute?(schema, item.name)
97
+
98
+ @attributes[item.name] = @members_builder.build_top_level_attribute(item, kind: :attribute)
99
+ when Lutaml::Xml::Schema::Xsd::AttributeGroup
100
+ @attribute_groups[item.name] = @complex_types_builder.build_attribute_group(item)
101
+ end
102
+ end
103
+
104
+ def dispatch_complex(item, _schema)
105
+ case item
106
+ when Lutaml::Xml::Schema::Xsd::ComplexType
107
+ @complex_types[item.name] = @complex_types_builder.build(item)
108
+ when Lutaml::Xml::Schema::Xsd::Group
109
+ @group_types[item.name] = @complex_types_builder.build_group(item)
110
+ end
111
+ end
112
+
113
+ # Add the built-in XSD types (NonNegativeInteger, NormalizedString,
114
+ # etc.) as Definitions::RestrictedType entries.
115
+ def add_supported_types
116
+ SupportedDataTypes.each do |name, info|
117
+ next if info[:skippable]
118
+
119
+ str_name = name.to_s
120
+ @simple_types[str_name] = @simple_types_builder.build_supported(str_name, info)
121
+ end
122
+ end
123
+
124
+ # Every generated class spec keyed by name. Used by the orchestrator
125
+ # to build CompiledOutput entries without reaching into the builder's
126
+ # internal hashes.
127
+ def all_models
128
+ @simple_types.merge(@complex_types).merge(@group_types)
129
+ end
130
+
131
+ # Stamp `required_files` on every walked complex / group model.
132
+ # The skippable predicate is supplied by SupportedDataTypes —
133
+ # the orchestrator never has to thread the type table through.
134
+ def finalize_required_files!
135
+ (@complex_types.each_value.to_a + @group_types.each_value.to_a).each do |model|
136
+ model.required_files = Renderers::RequiredFilesCalculator
137
+ .for_xml(model, skippable_type: SupportedDataTypes.method(:skippable?))
138
+ end
139
+ end
140
+
141
+ # Public surface used by the Members sub-builder to register
142
+ # nested anonymous types it encounters during attribute /
143
+ # element resolution.
144
+ def build_simple_type(simple_type) = @simple_types_builder.build(simple_type)
145
+ def build_complex_type(complex_type) = @complex_types_builder.build(complex_type)
146
+
147
+ private
148
+
149
+ # Recursively walks `schemas` (following include / import) and
150
+ # yields each resolved element-order item with its owning schema.
151
+ def each_schema_item(schemas, &dispatch)
152
+ schemas.each do |schema|
153
+ each_schema_item(schema.include, &dispatch) if schema.include&.any?
154
+ each_schema_item(schema.import, &dispatch) if schema.import&.any?
155
+ schema.resolved_element_order.each { |item| yield(item, schema) }
156
+ end
157
+ end
158
+
159
+ def xml_defined_attribute?(schema, name)
160
+ schema.target_namespace == XmlCompiler::XML_NAMESPACE_URI &&
161
+ XmlCompiler::XML_DEFINED_ATTRIBUTES.key?(name)
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Schema
6
+ module XmlCompiler
7
+ # XSD built-in type table. Keys are the XSD type names; values
8
+ # describe how to render the generated subclass.
9
+ # `skippable: true` means the XSD type maps directly to a
10
+ # Lutaml primitive symbol and needs no generated class.
11
+ module SupportedDataTypes
12
+ TC = Lutaml::Model::Type::TYPE_CODES
13
+
14
+ TABLE = {
15
+ nonNegativeInteger: { skippable: false, class_name: TC[:string],
16
+ validations: { pattern: /\+?[0-9]+/ } },
17
+ normalizedString: { skippable: false, class_name: TC[:string],
18
+ validations: { transform: "value.gsub(/[\\r\\n\\t]/, ' ')" } },
19
+ positiveInteger: { skippable: false, class_name: TC[:integer],
20
+ validations: { min_inclusive: 0 } },
21
+ unsignedShort: { skippable: false, class_name: TC[:integer],
22
+ validations: { min_inclusive: 0, max_inclusive: 65535 } },
23
+ base64Binary: { skippable: false, class_name: TC[:string],
24
+ validations: { pattern: /\A([A-Za-z0-9+\/]+={0,2}|\s)*\z/ } },
25
+ unsignedLong: { skippable: false, class_name: TC[:integer],
26
+ validations: { min_inclusive: 0, max_inclusive: 18446744073709551615 } },
27
+ unsignedByte: { skippable: false, class_name: TC[:integer],
28
+ validations: { min_inclusive: 0, max_inclusive: 255 } },
29
+ unsignedInt: { skippable: false, class_name: TC[:integer],
30
+ validations: { min_inclusive: 0, max_inclusive: 4294967295 } },
31
+ hexBinary: { skippable: false, class_name: TC[:string],
32
+ validations: { pattern: /([0-9a-fA-F]{2})*/ } },
33
+ language: { skippable: false, class_name: TC[:string],
34
+ validations: { pattern: /\A[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*\z/ } },
35
+ dateTime: { skippable: true, class_name: TC[:date_time] },
36
+ boolean: { skippable: true, class_name: TC[:boolean] },
37
+ integer: { skippable: true, class_name: TC[:integer] },
38
+ decimal: { skippable: true, class_name: TC[:decimal] },
39
+ string: { skippable: true, class_name: TC[:string] },
40
+ double: { skippable: true, class_name: TC[:float] },
41
+ NCName: { skippable: false, class_name: TC[:string],
42
+ validations: { pattern: /\A[a-zA-Z_][\w.-]*\z/ } },
43
+ anyURI: { skippable: false, class_name: TC[:string],
44
+ validations: { pattern: "\\A\#{URI::DEFAULT_PARSER.make_regexp(%w[http https ftp])}\\z" } },
45
+ token: { skippable: false, class_name: TC[:string],
46
+ validations: { pattern: /\A[^\t\n\f\r ]+(?: [^\t\n\f\r ]+)*\z/ } },
47
+ byte: { skippable: false, class_name: TC[:integer],
48
+ validations: { min_inclusive: -128, max_inclusive: 127 } },
49
+ long: { skippable: false, class_name: TC[:decimal] },
50
+ int: { skippable: true, class_name: TC[:integer] },
51
+ id: { skippable: false, class_name: TC[:string],
52
+ validations: { pattern: /\A[a-zA-Z_][\w.-]*\z/ } },
53
+ }.freeze
54
+
55
+ module_function
56
+
57
+ # XSD-name lookup (`SupportedDataTypes[:dateTime]`).
58
+ def [](name)
59
+ TABLE[name]
60
+ end
61
+
62
+ def each(&)
63
+ TABLE.each(&)
64
+ end
65
+
66
+ # `TypeRef.value` and `simple_content.base_class` ask the same
67
+ # "is this a built-in primitive?" question but pass two different
68
+ # spellings: `simple_content` keeps the XSD name (`"dateTime"`),
69
+ # while attribute TypeRefs store the snake_case rendering
70
+ # (`"date_time"`). Precompute both spellings so neither caller
71
+ # has to know which form the other uses.
72
+ SKIPPABLE_NAMES = TABLE
73
+ .select { |_, info| info[:skippable] }
74
+ .flat_map { |name, _| [name.to_s, Utils.snake_case(name.to_s)] }
75
+ .uniq.freeze
76
+
77
+ # True when the XSD type maps to a Lutaml primitive symbol that
78
+ # doesn't need a generated subclass. Accepts XSD-spelled
79
+ # (`"dateTime"`) or snake_case (`"date_time"`) input.
80
+ def skippable?(value)
81
+ SKIPPABLE_NAMES.include?(value.to_s)
82
+ end
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end