lutaml-model 0.5.3 → 0.5.4

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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +2 -0
  3. data/.rubocop_todo.yml +39 -13
  4. data/Gemfile +1 -0
  5. data/README.adoc +396 -23
  6. data/lib/lutaml/model/constants.rb +7 -0
  7. data/lib/lutaml/model/error/type/invalid_value_error.rb +19 -0
  8. data/lib/lutaml/model/error.rb +1 -0
  9. data/lib/lutaml/model/key_value_mapping.rb +31 -2
  10. data/lib/lutaml/model/mapping_hash.rb +8 -0
  11. data/lib/lutaml/model/mapping_rule.rb +4 -0
  12. data/lib/lutaml/model/schema/templates/simple_type.rb +247 -0
  13. data/lib/lutaml/model/schema/xml_compiler.rb +720 -0
  14. data/lib/lutaml/model/schema.rb +5 -0
  15. data/lib/lutaml/model/serialize.rb +24 -8
  16. data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +1 -2
  17. data/lib/lutaml/model/type/hash.rb +11 -11
  18. data/lib/lutaml/model/version.rb +1 -1
  19. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +5 -1
  20. data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -15
  21. data/lib/lutaml/model/xml_mapping.rb +4 -2
  22. data/lib/lutaml/model/xml_mapping_rule.rb +1 -4
  23. data/lib/lutaml/model.rb +1 -0
  24. data/spec/fixtures/xml/invalid_math_document.xml +4 -0
  25. data/spec/fixtures/xml/math_document_schema.xsd +56 -0
  26. data/spec/fixtures/xml/test_schema.xsd +53 -0
  27. data/spec/fixtures/xml/valid_math_document.xml +4 -0
  28. data/spec/lutaml/model/cdata_spec.rb +2 -2
  29. data/spec/lutaml/model/custom_model_spec.rb +7 -20
  30. data/spec/lutaml/model/key_value_mapping_spec.rb +27 -0
  31. data/spec/lutaml/model/map_all_spec.rb +188 -0
  32. data/spec/lutaml/model/mixed_content_spec.rb +15 -15
  33. data/spec/lutaml/model/schema/xml_compiler_spec.rb +1431 -0
  34. data/spec/lutaml/model/with_child_mapping_spec.rb +2 -2
  35. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +52 -0
  36. data/spec/lutaml/model/xml_mapping_spec.rb +108 -1
  37. metadata +12 -2
data/README.adoc CHANGED
@@ -49,12 +49,12 @@ The Lutaml::Model data modelling approach is as follows:
49
49
  .Modeling relationships of a LutaML Model
50
50
  [source]
51
51
  ----
52
- Lutaml Model
52
+ LutaML Model
53
53
 
54
54
  Has many attributes
55
55
 
56
56
 
57
- Attribute
57
+ Attribute
58
58
 
59
59
  Has type of
60
60
 
@@ -97,7 +97,7 @@ Studio (Model)
97
97
  [source]
98
98
  ----
99
99
  ╔═══════════════════════╗ ╔════════════════════════════╗
100
- Core Model ║ ║ Serialization Models ║
100
+ LutaML Core Model ║ ║ Serialization Models ║
101
101
  ╚═══════════════════════╝ ╚════════════════════════════╝
102
102
 
103
103
  ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
@@ -109,8 +109,8 @@ Studio (Model)
109
109
  ┆ │ │ ┆ │ & │ ┆ │ │ ┆
110
110
  ┆ │ │ ┆ │ Mapping Rules │ ┆ │ │ ┆
111
111
  ┆ │ ┌──────┴──┐ ┆ │ │ ┆ ┌────┴────┐ ┌─┴─┐ ┆
112
- ┆ │ │ │ ┆ │ │ ┆ │ │ │ │ ┆
113
- ┆ │ String Integer ┆ └────────────────┘ ┆ Element Value xs:string ┆
112
+ ┆ │ │ │ ┆ └────────────────┘ ┆ │ │ │ │ ┆
113
+ ┆ │ String Integer ┆┆ Element Value xs:string ┆
114
114
  ┆ │ Date Float ┆ │ ┆ Attribute Type xs:date ┆
115
115
  ┆ │ Time Boolean ┆ ├──────► ┆ xs:boolean ┆
116
116
  ┆ │ ┆ │ ┆ xs:anyURI ┆
@@ -131,22 +131,109 @@ Studio (Model)
131
131
  ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
132
132
  ----
133
133
 
134
+ .Model transformation of a LutaML Model to another LutaML Model
135
+ [source]
136
+ ----
137
+ ╔═══════════════════════╗ ╔══════════════════╗ ╔═══════════════════════╗
138
+ ║LutaML Model Class FOO ║ ║LutaML Transformer║ ║LutaML Model Class BAR ║
139
+ ╚═══════════════════════╝ ╚══════════════════╝ ╚═══════════════════════╝
140
+
141
+ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
142
+ ┆ Model ┆ ┆ Model ┆
143
+ ┆ │ ┆ ┌────────────────┐ ┆ │ ┆
144
+ ┆ ┌────────┴──┐ ┆ │ │ ┆ ┌────────┴──┐ ┆
145
+ ┆ │ │ ┆ │ Model │ ┆ │ │ ┆
146
+ ┆ Models Value Types ┆───►│ Transformation │───►┆ Models Value Types ┆
147
+ ┆ │ │ ┆◄───│ & │◄───┆ │ │ ┆
148
+ ┆ │ │ ┆ │ Mapping Rules │ ┆ │ │ ┆
149
+ ┆ │ ┌──────┴──┐ ┆ │ │ ┆ │ ┌──────┴──┐ ┆
150
+ ┆ │ │ │ ┆ └────────────────┘ ┆ │ │ │ ┆
151
+ ┆ │ String Integer ┆ ┆ │ String Integer ┆
152
+ ┆ │ Date Float ┆ ┆ │ Date Float ┆
153
+ ┆ │ Time Boolean ┆ ┆ │ Time Boolean ┆
154
+ ┆ │ ┆ ┆ │ ┆
155
+ ┆ └──────┐ ┆ ┆ └──────┐ ┆
156
+ ┆ │ ┆ ┆ │ ┆
157
+ ┆ Contains ┆ ┆ Contains ┆
158
+ ┆ more Models ┆ ┆ more Models ┆
159
+ ┆ (recursive) ┆ ┆ (recursive) ┆
160
+ ┆ ┆ ┆ ┆
161
+ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
162
+ ----
163
+
164
+ .The `Value` class, transformation, and serialization formats
165
+ [source]
166
+ ----
167
+ ╔═══════════════════════╗ ╔═══════════════════════╗
168
+ ║LutaML Value Class FOO ║ ║ Serialization Value ║
169
+ ╚═══════════════════════╝ ╚═══════════════════════╝
170
+ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
171
+ ┆ ┌───────────────┐ ┆ ┆ ┌───────────────┐ ┆
172
+ ┆ │ Value │ ┆ ┌──────────────────┐ ┆ │ XML Value │ ┆
173
+ ┆ └───────────────┘ ┆──►│ Value Serializer │──►┆ └───────────────┘ ┆
174
+ ┆ ┌───────────────┐ ┆ └──────────────────┘ ┆ ┌───────────────┐ ┆
175
+ ┆ │Primitive Types│ ┆ ┆ │XML Value Types│ ┆
176
+ ┆ └───────────────┘ ┆ ┆ └───────────────┘ ┆
177
+ ┆ ┌───┘ ┆ ┆ ┌───┘ ┆
178
+ ┆ ├─ string ┆ ┆ ├─ xs:string ┆
179
+ ┆ ├─ integer ┆ ┆ ├─ xs:integer ┆
180
+ ┆ ├─ float ┆ ┆ ├─ xs:decimal ┆
181
+ ┆ ├─ boolean ┆ ┆ ├─ xs:boolean ┆
182
+ ┆ ├─ date ┆ ┆ ├─ xs:date ┆
183
+ ┆ ├─ time_without_date ┆ ┆ ├─ xs:time ┆
184
+ ┆ ├─ date_time ┆ ┆ ├─ xs:dateTime ┆
185
+ ┆ ├─ time ┆ ┆ ├─ xs:decimal ┆
186
+ ┆ ├─ decimal ┆ ┆ ├─ xs:anyType ┆
187
+ ┆ └─ hash ┆ ┆ └─ (complex element) ┆
188
+ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
189
+
190
+
191
+ ┌───────────────────┐
192
+ │ Value Transformer │
193
+ └───────────────────┘
194
+
195
+
196
+ ╔═══════════════════════╗
197
+ ║LutaML Value Class BAR ║
198
+ ╚═══════════════════════╝
199
+ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
200
+ ┆ ┌───────────────┐ ┆
201
+ ┆ │ Value │ ┆
202
+ ┆ └───────────────┘ ┆
203
+ ┆ ┌───────────────┐ ┆
204
+ ┆ │Primitive Types│ ┆
205
+ ┆ └───────────────┘ ┆
206
+ ┆ ┌───┘ ┆
207
+ ┆ ├─ string ┆
208
+ ┆ ├─ integer ┆
209
+ ┆ ├─ float ┆
210
+ ┆ ├─ boolean ┆
211
+ ┆ ├─ date ┆
212
+ ┆ ├─ time_without_date ┆
213
+ ┆ ├─ date_time ┆
214
+ ┆ ├─ time ┆
215
+ ┆ ├─ decimal ┆
216
+ ┆ └─ hash ┆
217
+ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
218
+ ----
219
+
134
220
  .Example of LutaML Model instance transformed into a serialization model and serialized to JSON
135
221
  ====
136
222
  [source]
137
223
  ----
138
- Studio (Core Model) JSON Model Serialized JSON
139
- │ │ │
140
- ▼ ▼ ▼
141
- name: "Studio 1" ┌─► { "name": "...", {
142
- address: │ "address": { "name": "Studio 1",
143
- ├── street: "..." "street": "...", "address": {
144
- └── city: "..." │ "city": "..." ──► "street": "...",
145
- kilns: }, "city": "..."
146
- ├── count: 3 "kilnsCount": ..., },
147
- └── temp: 1200 │ "kilnsTemp": ... "kilnsCount": 3,
148
- └─► } "kilnsTemp": 1200
149
- }
224
+ ╔═════════════════════╗ ╔═════════════════════╗ ╔═════════════════════╗
225
+ ║ Studio (Core Model) ║ ║ JSON Model ║ ║ Serialized JSON ║
226
+ ╚═════════════════════╝ ╚═════════════════════╝ ╚═════════════════════╝
227
+
228
+ name: "Studio 1" ┌─► { ┌─► {
229
+ address: "name": "...", "name": "Studio 1",
230
+ ├── street: "..." │ "address": { │ "address": {
231
+ └── city: "..." "street": "...","street": "...",
232
+ kilns: ──┤ "city": "..." ──┤ "city": "..."
233
+ ├── count: 3 │ }, │ },
234
+ └── temp: 1200 │ "kilnsCount": ..., │ "kilnsCount": 3,
235
+ │ "kilnsTemp": ... │ "kilnsTemp": 1200
236
+ └─► } └─► }
150
237
  ----
151
238
  ====
152
239
 
@@ -926,10 +1013,8 @@ end
926
1013
  ----
927
1014
  ====
928
1015
 
929
-
930
- ==== Mapping all content (XML only)
931
-
932
- WARNING: This feature is only applicable to XML (for now).
1016
+ [[xml-map-all]]
1017
+ ==== Mapping all XML content
933
1018
 
934
1019
  The `map_all` tag in XML mapping captures and maps all content within an XML
935
1020
  element into a single attribute in the target Ruby object.
@@ -937,6 +1022,12 @@ element into a single attribute in the target Ruby object.
937
1022
  The use case for `map_all` is to tell Lutaml::Model to not parse the content of
938
1023
  the XML element at all, and instead handle it as an XML string.
939
1024
 
1025
+ NOTE: The corresponding method for key-value formats is at <<key-value-map-all>>.
1026
+
1027
+ WARNING: Notice that usage of mapping all will lead to incompatibility between
1028
+ serialization formats, i.e. the raw string content will not be portable as
1029
+ objects are across different formats.
1030
+
940
1031
  This is useful in the case where the content of an XML element is not to be
941
1032
  handled by a Lutaml::Model::Serializable object.
942
1033
 
@@ -949,8 +1040,9 @@ This includes:
949
1040
  * attributes
950
1041
  * text nodes
951
1042
 
952
- The `map_all` tag is **exclusive** and cannot be combined with other mappings (`map_element`, `map_content`) except for `map_attribute` for the same element, ensuring
953
- it captures the entire inner XML content.
1043
+ The `map_all` tag is **exclusive** and cannot be combined with other mappings
1044
+ (`map_element`, `map_content`) except for `map_attribute` for the same element,
1045
+ ensuring it captures the entire inner XML content.
954
1046
 
955
1047
  NOTE: An error is raised if `map_all` is defined alongside any other mapping in
956
1048
  the same XML mapping context.
@@ -1672,6 +1764,7 @@ end
1672
1764
 
1673
1765
  // TODO: How to create mixed content from `#new`?
1674
1766
 
1767
+
1675
1768
  [[xml-schema-location]]
1676
1769
  ==== Automatic support of `xsi:schemaLocation`
1677
1770
 
@@ -1931,6 +2024,102 @@ end
1931
2024
  ----
1932
2025
  ====
1933
2026
 
2027
+ [[key-value-map-all]]
2028
+ ==== Mapping all key-value content
2029
+
2030
+ The `map_all` tag captures and maps all content within a serialization format
2031
+ into a single attribute in the target Ruby object.
2032
+
2033
+ The use case for `map_all` is to tell Lutaml::Model to not parse the content at
2034
+ all, and instead handle it as a raw string.
2035
+
2036
+ NOTE: The corresponding method for XML is at <<xml-map-all>>.
2037
+
2038
+ WARNING: Notice that usage of mapping all will lead to incompatibility between
2039
+ serialization formats, i.e. the raw string content will not be portable as
2040
+ objects are across different formats.
2041
+
2042
+ This is useful when the content needs to be handled as-is without parsing into
2043
+ individual attributes.
2044
+
2045
+ The `map_all` tag is **exclusive** and cannot be combined with other mappings,
2046
+ ensuring it captures the entire content.
2047
+
2048
+ NOTE: An error is raised if `map_all` is defined alongside any other mapping in
2049
+ the same mapping context.
2050
+
2051
+ Syntax:
2052
+
2053
+ [source,ruby]
2054
+ ----
2055
+ json | yaml | toml | key_value do
2056
+ map_all to: :name_of_attribute
2057
+ end
2058
+ ----
2059
+
2060
+ .Using `map_all` to capture all content across different formats
2061
+ [example]
2062
+ ====
2063
+ [source,ruby]
2064
+ ----
2065
+ class Document < Lutaml::Model::Serializable
2066
+ attribute :content, :string
2067
+
2068
+ json do
2069
+ map_all to: :content
2070
+ end
2071
+
2072
+ yaml do
2073
+ map_all to: :content
2074
+ end
2075
+
2076
+ toml do
2077
+ map_all to: :content
2078
+ end
2079
+ end
2080
+ ----
2081
+
2082
+ For JSON:
2083
+ [source,json]
2084
+ ----
2085
+ {
2086
+ "sections": [
2087
+ { "title": "Introduction", "text": "Chapter 1" },
2088
+ { "title": "Conclusion", "text": "Final chapter" }
2089
+ ],
2090
+ "metadata": {
2091
+ "author": "John Doe",
2092
+ "date": "2024-01-15"
2093
+ }
2094
+ }
2095
+ ----
2096
+
2097
+ For YAML:
2098
+ [source,yaml]
2099
+ ----
2100
+ sections:
2101
+ - title: Introduction
2102
+ text: Chapter 1
2103
+ - title: Conclusion
2104
+ text: Final chapter
2105
+ metadata:
2106
+ author: John Doe
2107
+ date: 2024-01-15
2108
+ ----
2109
+
2110
+ The content is preserved exactly as provided:
2111
+
2112
+ [source,ruby]
2113
+ ----
2114
+ > doc = Document.from_json(json_content)
2115
+ > puts doc.content
2116
+ > # "{\"sections\":[{\"title\":\"Introduction\",\"text\":\"Chapter 1\"},{\"title\":\"Conclusion\",\"text\":\"Final chapter\"}],\"metadata\":{\"author\":\"John Doe\",\"date\":\"2024-01-15\"}}"
2117
+
2118
+ > doc = Document.from_yaml(yaml_content)
2119
+ > puts doc.content
2120
+ > # "sections:\n - title: Introduction\n text: Chapter 1\n - title: Conclusion\n text: Final chapter\nmetadata:\n author: John Doe\n date: 2024-01-15\n"
2121
+ ----
2122
+ ====
1934
2123
 
1935
2124
  ==== Nested attribute mappings
1936
2125
 
@@ -3310,6 +3499,181 @@ end
3310
3499
  ====
3311
3500
 
3312
3501
 
3502
+ == Importing data models
3503
+
3504
+ === General
3505
+
3506
+ Lutaml::Model provides a way to import data models defined from various formats
3507
+ into the LutaML data modeling system.
3508
+
3509
+ Data model languages supported are:
3510
+
3511
+ * XSD (https://w3.org/TR/xmlschema-1/[XML Schema Definition Language])
3512
+ // * RNC (https://relaxng.org/compact-tutorial-20030326.html[RELAX NG Compact Syntax])
3513
+ // * RNG (https://relaxng.org/relaxng-compact.html[RELAX NG XML Syntax])
3514
+ // * JSON Schema (https://json-schema.org/understanding-json-schema/[JSON Schema])
3515
+ // * YAML Schema (https://yaml.org/spec/1.2/spec.html[YAML])
3516
+ // * LutaML
3517
+
3518
+
3519
+ The following figure illustrates the process of importing an XML Schema model to
3520
+ create LutaML core models. Once the LutaML core models are created, they can be
3521
+ used to parse and generate XML documents according to the imported XML Schema
3522
+ model.
3523
+
3524
+ Today, the LutaML core models are written into Ruby files, which can be used to
3525
+ parse and generate XML documents according to the imported XML Schema.
3526
+ This is to be changed so that the LutaML core models are directly loaded and
3527
+ interpreted.
3528
+
3529
+ .Importing an XML Schema model to create LutaML core models
3530
+ [source]
3531
+ ----
3532
+ ╔════════════════════════════╗ ╔═══════════════════════╗
3533
+ ║ Serialization Models ║ ║ Core Model ║
3534
+ ╚════════════════════════════╝ ╚═══════════════════════╝
3535
+
3536
+ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮ ╭┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╮
3537
+ ┆ XML Schema (XSD/RNG/RNC) ┆ ┆ Model ┆
3538
+ ┆ │ ┆ ┌────────────────┐ ┆ │ ┆
3539
+ ┆ ┌──────┴──────┐ ┆ │ │ ┆ ┌────────┴──┐ ┆
3540
+ ┆ │ │ ┆ │ Model │ ┆ │ │ ┆
3541
+ ┆ Models Value Types ┆──►│ Importing │──►┆ Models Value Types ┆
3542
+ ┆ │ │ ┆ │ │ ┆ │ │ ┆
3543
+ ┆ │ │ ┆ └────────────────┘ ┆ │ │ ┆
3544
+ ┆ ┌────┴────┐ ┌─┴─┐ ┆ │ ┆ │ ┌──────┴──┐ ┆
3545
+ ┆ │ │ │ │ ┆ │ ┆ │ │ │ ┆
3546
+ ┆ Element Value xs:string ┆ │ ┆ │ String Integer ┆
3547
+ ┆ Attribute Type xs:date ┆ │ ┆ │ Date Float ┆
3548
+ ┆ Union Complex xs:boolean ┆ │ ┆ │ Time Boolean ┆
3549
+ ┆ Sequence Choice xs:anyURI ┆ │ ┆ │ ┆
3550
+ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯ │ ┆ └──────┐ ┆
3551
+ │ ┆ │ ┆
3552
+ │ ┆ Contains ┆
3553
+ │ ┆ more Models ┆
3554
+ │ ┆ (recursive) ┆
3555
+ │ ┆ ┆
3556
+ │ ╰┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄╯
3557
+ │ ┌────────────────┐
3558
+ │ │ │
3559
+ │ │ Model │
3560
+ └──────────► │ Transformation │
3561
+ │ & │
3562
+ │ Mapping Rules │
3563
+ │ │
3564
+ └────────────────┘
3565
+ ----
3566
+
3567
+
3568
+ [[xml-schema-to-model-files]]
3569
+ === XML Schema (XSD)
3570
+
3571
+ W3C XSD is a schema language designed to define the structure of XML documents,
3572
+ alongside other XML schema languages like DTD, RELAX NG, and Schematron.
3573
+
3574
+ Lutaml::Model supports the import of XSD schema files to define information
3575
+ models that can be used to parse and generate XML documents.
3576
+
3577
+ Specifically, the `Lutaml::Model::Schema#from_xml` method loads XML Schema files
3578
+ (XSD, `*.xsd`) and generates Ruby files (`*.rb`) that inherit from
3579
+ `Lutaml::Model::Serializable` that are saved to disk.
3580
+
3581
+ Syntax:
3582
+
3583
+ [source,ruby]
3584
+ ----
3585
+ Lutaml::Model::Schema.from_xml(
3586
+ xsd_schema, <1>
3587
+ options: options <2>
3588
+ )
3589
+ ----
3590
+ <1> The `xsd_schema` is the XML Schema string to be converted to model files.
3591
+ <2> The `options` hash is an optional argument.
3592
+
3593
+ `options`:: Optional hash containing potentially the following key-values.
3594
+
3595
+ `output_dir`::: The directory where the model files will be saved. If not
3596
+ provided, a default directory named `lutaml_models_<timestamp>` is created.
3597
+ +
3598
+ [example]
3599
+ `"path/to/directory"`
3600
+
3601
+ `namespace`::: The namespace of the schema. This will be added in the
3602
+ `Lutaml::Model::Serializable` file's `xml do` block.
3603
+ +
3604
+ [example]
3605
+ `http://example.com/namespace`
3606
+
3607
+ `prefix`::: The prefix of the namespace provided in the `namespace` option.
3608
+ +
3609
+ [example]
3610
+ `example-prefix`
3611
+
3612
+ `location`::: The URL or path of the directory containing all the files of the
3613
+ schema. For more information, refer to the
3614
+ link:https://www.w3.org/TR/xmlschema-1/#include[XML Schema specification].
3615
+ +
3616
+ [example]
3617
+ `"http://example.com/example.xsd"`
3618
+ +
3619
+ [example]
3620
+ `"path/to/schema/directory"`
3621
+
3622
+
3623
+ The generated LutaML models consists of two different kind of Ruby classes
3624
+ depending on the XSD schema:
3625
+
3626
+ XSD "SimpleTypes":: converted into classes that inherit from
3627
+ `Lutaml::Model::Type::Value`, which define the data types with restrictions and
3628
+ other validations of these values.
3629
+
3630
+ XSD "ComplexTypes":: converted into classes that inherit from
3631
+ `Lutaml::Model::Serializable` that model according to the defined structure.
3632
+
3633
+ Lutaml::Model uses the https://github.com/lutaml/lutaml-xsd[`lutaml-xsd` gem] to
3634
+ automatically resolve the `include` and `import` elements, enabling
3635
+ *Lutaml-Model* to generate the corresponding model files.
3636
+
3637
+ This auto-resolving feature allows seamless integration of these files into your
3638
+ models without the need for manual resolution of includes and imports.
3639
+
3640
+ [example]
3641
+ .Using `Lutaml::Model::Schema#from_xml` to convert an XML Schema to model files
3642
+ ====
3643
+ [source,ruby]
3644
+ ----
3645
+ xsd_schema = <<~XSD
3646
+ <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3647
+ /* your schema here */
3648
+ </xs:schema>
3649
+ XSD
3650
+ options = {
3651
+ # These are all optional:
3652
+ output_dir: 'path/to/directory',
3653
+ namespace: 'http://example.com/namespace',
3654
+ prefix: "example-prefix",
3655
+ location: "http://example.com/example.xsd"
3656
+ # or
3657
+ # location: "path/to/schema/directory"
3658
+ }
3659
+
3660
+ # generates the files in the output_dir | default_dir
3661
+ Lutaml::Model::Schema.from_xml(xsd_schema, options: options)
3662
+ ----
3663
+ ====
3664
+
3665
+ You could also directly load the generated Ruby files into your application by
3666
+ requiring them.
3667
+
3668
+ [example]
3669
+ .Using the generated Ruby files in your application
3670
+ ====
3671
+ [source,ruby]
3672
+ ----
3673
+ Lutaml::Model::Schema.from_xml(xsd_schema, options: {output_dir: 'path/to/directory'})
3674
+ require_relative 'path/to/directory/*.rb'
3675
+ ----
3676
+ ====
3313
3677
 
3314
3678
 
3315
3679
  == Validation
@@ -3701,6 +4065,15 @@ attribute for every element.
3701
4065
  | Requires manual specification on every XML element that uses it.
3702
4066
  |
3703
4067
 
4068
+
4069
+ | Compiling XML Schema to *Lutaml::Model::Serializable* classes
4070
+ | Yes. Using <<xml-schema-to-model-files, `Lutaml::Model::Schema#from_xml`>>
4071
+
4072
+ 1. *ComplexTypes* are compiled to *Lutaml::Model::Serializable* classes containing the attributes.
4073
+ 2. *SimpleTypes* are compiled to *Lutaml::Model::Type::Value* classes to support XML Schema level validations.
4074
+ | Yes, Provides only an array of the classes and doesn't support `simple types` with restrictions and/or other validations.
4075
+ |
4076
+
3704
4077
  4+h| Attribute features
3705
4078
 
3706
4079
  | Attribute delegation
@@ -0,0 +1,7 @@
1
+ module Lutaml
2
+ module Model
3
+ module Constants
4
+ RAW_MAPPING_KEY = "__raw_mapping".freeze
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Type
6
+ class InvalidValueError < Error
7
+ def initialize(message)
8
+ @message = message
9
+
10
+ super()
11
+ end
12
+
13
+ def to_s
14
+ @message
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -16,3 +16,4 @@ require_relative "error/type_error"
16
16
  require_relative "error/unknown_type_error"
17
17
  require_relative "error/multiple_mappings_error"
18
18
  require_relative "error/collection_true_missing_error"
19
+ require_relative "error/type/invalid_value_error"
@@ -36,6 +36,27 @@ module Lutaml
36
36
 
37
37
  alias map_element map
38
38
 
39
+ def map_all(
40
+ to: nil,
41
+ render_nil: false,
42
+ render_default: false,
43
+ with: {},
44
+ delegate: nil
45
+ )
46
+ @raw_mapping = true
47
+ validate!(Constants::RAW_MAPPING_KEY, to, with)
48
+ @mappings << KeyValueMappingRule.new(
49
+ Constants::RAW_MAPPING_KEY,
50
+ to: to,
51
+ render_nil: render_nil,
52
+ render_default: render_default,
53
+ with: with,
54
+ delegate: delegate,
55
+ )
56
+ end
57
+
58
+ alias map_all_content map_all
59
+
39
60
  def name_for_mapping(root_mappings, name)
40
61
  return "root_mapping" if root_mappings
41
62
 
@@ -43,12 +64,14 @@ module Lutaml
43
64
  end
44
65
 
45
66
  def validate!(key, to, with)
46
- if to.nil? && with.empty?
67
+ validate_mappings!(key)
68
+
69
+ if to.nil? && with.empty? && !@raw_mapping
47
70
  msg = ":to or :with argument is required for mapping '#{key}'"
48
71
  raise IncorrectMappingArgumentsError.new(msg)
49
72
  end
50
73
 
51
- if !with.empty? && (with[:from].nil? || with[:to].nil?)
74
+ if !with.empty? && (with[:from].nil? || with[:to].nil?) && !@raw_mapping
52
75
  msg = ":with argument for mapping '#{key}' requires :to and :from keys"
53
76
  raise IncorrectMappingArgumentsError.new(msg)
54
77
  end
@@ -62,6 +85,12 @@ module Lutaml
62
85
  end
63
86
  end
64
87
 
88
+ def validate_mappings!(_type)
89
+ if (@raw_mapping && Utils.present?(@mappings)) || (!@raw_mapping && @mappings.any?(&:raw_mapping?))
90
+ raise StandardError, "map_all is not allowed with other mappings"
91
+ end
92
+ end
93
+
65
94
  def deep_dup
66
95
  self.class.new.tap do |new_mapping|
67
96
  new_mapping.instance_variable_set(:@mappings, duplicate_mappings)
@@ -36,6 +36,14 @@ module Lutaml
36
36
  key?("#cdata-section") || key?("text")
37
37
  end
38
38
 
39
+ def assign_or_append_value(key, value)
40
+ self[key] = if self[key]
41
+ [self[key], value].flatten
42
+ else
43
+ value
44
+ end
45
+ end
46
+
39
47
  def ordered?
40
48
  @ordered
41
49
  end
@@ -84,6 +84,10 @@ module Lutaml
84
84
  name.is_a?(Array)
85
85
  end
86
86
 
87
+ def raw_mapping?
88
+ name == Constants::RAW_MAPPING_KEY
89
+ end
90
+
87
91
  def deep_dup
88
92
  raise NotImplementedError, "Subclasses must implement `deep_dup`."
89
93
  end