lutaml-model 0.5.3 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
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