lutaml-model 0.3.24 → 0.3.26
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop_todo.yml +36 -17
- data/README.adoc +396 -30
- data/lib/lutaml/model/attribute.rb +52 -27
- data/lib/lutaml/model/error/pattern_not_matched_error.rb +17 -0
- data/lib/lutaml/model/error/type_error.rb +9 -0
- data/lib/lutaml/model/error/unknown_type_error.rb +9 -0
- data/lib/lutaml/model/error/validation_error.rb +0 -1
- data/lib/lutaml/model/error.rb +3 -0
- data/lib/lutaml/model/mapping_hash.rb +8 -0
- data/lib/lutaml/model/serialize.rb +10 -5
- data/lib/lutaml/model/type/boolean.rb +38 -0
- data/lib/lutaml/model/type/date.rb +35 -0
- data/lib/lutaml/model/type/date_time.rb +32 -4
- data/lib/lutaml/model/type/decimal.rb +42 -0
- data/lib/lutaml/model/type/float.rb +37 -0
- data/lib/lutaml/model/type/hash.rb +62 -0
- data/lib/lutaml/model/type/integer.rb +41 -0
- data/lib/lutaml/model/type/string.rb +49 -0
- data/lib/lutaml/model/type/time.rb +49 -0
- data/lib/lutaml/model/type/time_without_date.rb +37 -5
- data/lib/lutaml/model/type/value.rb +52 -0
- data/lib/lutaml/model/type.rb +50 -114
- data/lib/lutaml/model/validation.rb +2 -1
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +12 -3
- data/lib/lutaml/model/xml_adapter/builder/ox.rb +7 -1
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +3 -1
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +12 -8
- data/lib/lutaml/model/xml_adapter/xml_document.rb +6 -8
- data/lib/lutaml/model/xml_mapping.rb +6 -2
- data/lib/lutaml/model/xml_mapping_rule.rb +7 -1
- data/lutaml-model.gemspec +1 -1
- data/spec/address_spec.rb +170 -0
- data/spec/fixtures/address.rb +33 -0
- data/spec/fixtures/person.rb +73 -0
- data/spec/fixtures/sample_model.rb +40 -0
- data/spec/fixtures/vase.rb +38 -0
- data/spec/fixtures/xml/special_char.xml +13 -0
- data/spec/lutaml/model/attribute_spec.rb +139 -0
- data/spec/lutaml/model/cdata_spec.rb +520 -0
- data/spec/lutaml/model/collection_spec.rb +299 -0
- data/spec/lutaml/model/comparable_model_spec.rb +106 -0
- data/spec/lutaml/model/custom_model_spec.rb +410 -0
- data/spec/lutaml/model/custom_serialization_spec.rb +170 -0
- data/spec/lutaml/model/defaults_spec.rb +221 -0
- data/spec/lutaml/model/delegation_spec.rb +340 -0
- data/spec/lutaml/model/inheritance_spec.rb +92 -0
- data/spec/lutaml/model/json_adapter_spec.rb +37 -0
- data/spec/lutaml/model/key_value_mapping_spec.rb +86 -0
- data/spec/lutaml/model/map_content_spec.rb +118 -0
- data/spec/lutaml/model/mixed_content_spec.rb +625 -0
- data/spec/lutaml/model/namespace_spec.rb +57 -0
- data/spec/lutaml/model/ordered_content_spec.rb +83 -0
- data/spec/lutaml/model/render_nil_spec.rb +138 -0
- data/spec/lutaml/model/schema/json_schema_spec.rb +79 -0
- data/spec/lutaml/model/schema/relaxng_schema_spec.rb +60 -0
- data/spec/lutaml/model/schema/xsd_schema_spec.rb +55 -0
- data/spec/lutaml/model/schema/yaml_schema_spec.rb +47 -0
- data/spec/lutaml/model/serializable_spec.rb +297 -0
- data/spec/lutaml/model/serializable_validation_spec.rb +90 -0
- data/spec/lutaml/model/simple_model_spec.rb +314 -0
- data/spec/lutaml/model/toml_adapter_spec.rb +39 -0
- data/spec/lutaml/model/type/boolean_spec.rb +54 -0
- data/spec/lutaml/model/type/date_spec.rb +118 -0
- data/spec/lutaml/model/type/date_time_spec.rb +127 -0
- data/spec/lutaml/model/type/decimal_spec.rb +125 -0
- data/spec/lutaml/model/type/float_spec.rb +191 -0
- data/spec/lutaml/model/type/hash_spec.rb +63 -0
- data/spec/lutaml/model/type/integer_spec.rb +145 -0
- data/spec/lutaml/model/type/string_spec.rb +150 -0
- data/spec/lutaml/model/type/time_spec.rb +142 -0
- data/spec/lutaml/model/type/time_without_date_spec.rb +125 -0
- data/spec/lutaml/model/type_spec.rb +276 -0
- data/spec/lutaml/model/utils_spec.rb +79 -0
- data/spec/lutaml/model/validation_spec.rb +83 -0
- data/spec/lutaml/model/with_child_mapping_spec.rb +174 -0
- data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +56 -0
- data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +56 -0
- data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +61 -0
- data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +251 -0
- data/spec/lutaml/model/xml_adapter_spec.rb +178 -0
- data/spec/lutaml/model/xml_mapping_spec.rb +863 -0
- data/spec/lutaml/model/yaml_adapter_spec.rb +30 -0
- data/spec/lutaml/model_spec.rb +1 -0
- data/spec/person_spec.rb +161 -0
- data/spec/spec_helper.rb +33 -0
- metadata +68 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1dc709174fab4f4df2214f08b536e0b1aded39f0c9f48a18eb5a4a53da3fc4b6
|
4
|
+
data.tar.gz: 9eea83160b8210f84d4b2f9baf2ab577ca91d2a737eaf98b175d52385e6fb5d3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1e75326adeb9b8804f1bcc99104707edc3656e870d16c8af22e07e32ead9f4d78cd87eb372ebb834f2a5d942d58819e0fd3800e23a8099cf951360765223cd25
|
7
|
+
data.tar.gz: ecc145fb2d9250cf855243c7b9cf468919f2530eed03039bd6c7a521fa534327a1a67c5348237b3d5959e20fa0fc2b2cadc87e57139f451610cd2d7d695f251b
|
data/.rubocop_todo.yml
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2024-11-
|
3
|
+
# on 2024-11-11 10:52:30 UTC using RuboCop version 1.68.0.
|
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
|
7
7
|
# versions of RuboCop, may require this file to be generated again.
|
8
8
|
|
9
|
-
# Offense count:
|
9
|
+
# Offense count: 132
|
10
10
|
# This cop supports safe autocorrection (--autocorrect).
|
11
11
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
12
12
|
# URISchemes: http, https
|
13
13
|
Layout/LineLength:
|
14
14
|
Enabled: false
|
15
15
|
|
16
|
-
# Offense count:
|
16
|
+
# Offense count: 12
|
17
17
|
# Configuration parameters: AllowedMethods.
|
18
18
|
# AllowedMethods: enums
|
19
19
|
Lint/ConstantDefinitionInBlock:
|
@@ -22,15 +22,17 @@ Lint/ConstantDefinitionInBlock:
|
|
22
22
|
- 'spec/lutaml/model/schema/relaxng_schema_spec.rb'
|
23
23
|
- 'spec/lutaml/model/schema/xsd_schema_spec.rb'
|
24
24
|
- 'spec/lutaml/model/schema/yaml_schema_spec.rb'
|
25
|
+
- 'spec/lutaml/model/type_spec.rb'
|
25
26
|
- 'spec/lutaml/model/validation_spec.rb'
|
26
27
|
- 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
|
27
28
|
|
28
|
-
# Offense count:
|
29
|
+
# Offense count: 2
|
29
30
|
Lint/DuplicateMethods:
|
30
31
|
Exclude:
|
31
32
|
- 'lib/lutaml/model/attribute.rb'
|
33
|
+
- 'lib/lutaml/model/type/float.rb'
|
32
34
|
|
33
|
-
# Offense count:
|
35
|
+
# Offense count: 36
|
34
36
|
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes, Max.
|
35
37
|
Metrics/AbcSize:
|
36
38
|
Exclude:
|
@@ -40,7 +42,6 @@ Metrics/AbcSize:
|
|
40
42
|
- 'lib/lutaml/model/schema/relaxng_schema.rb'
|
41
43
|
- 'lib/lutaml/model/schema/xsd_schema.rb'
|
42
44
|
- 'lib/lutaml/model/serialize.rb'
|
43
|
-
- 'lib/lutaml/model/type.rb'
|
44
45
|
- 'lib/lutaml/model/xml_adapter/nokogiri_adapter.rb'
|
45
46
|
- 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
|
46
47
|
- 'lib/lutaml/model/xml_adapter/xml_document.rb'
|
@@ -52,19 +53,19 @@ Metrics/AbcSize:
|
|
52
53
|
Metrics/BlockLength:
|
53
54
|
Max: 47
|
54
55
|
|
55
|
-
# Offense count:
|
56
|
+
# Offense count: 26
|
56
57
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
57
58
|
Metrics/CyclomaticComplexity:
|
58
59
|
Exclude:
|
59
60
|
- 'lib/lutaml/model/attribute.rb'
|
60
61
|
- 'lib/lutaml/model/comparable_model.rb'
|
61
62
|
- 'lib/lutaml/model/serialize.rb'
|
62
|
-
- 'lib/lutaml/model/type.rb'
|
63
|
+
- 'lib/lutaml/model/type/integer.rb'
|
63
64
|
- 'lib/lutaml/model/xml_adapter/nokogiri_adapter.rb'
|
64
65
|
- 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
|
65
66
|
- 'lib/lutaml/model/xml_adapter/xml_document.rb'
|
66
67
|
|
67
|
-
# Offense count:
|
68
|
+
# Offense count: 53
|
68
69
|
# Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
|
69
70
|
Metrics/MethodLength:
|
70
71
|
Max: 46
|
@@ -72,15 +73,16 @@ Metrics/MethodLength:
|
|
72
73
|
# Offense count: 7
|
73
74
|
# Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
|
74
75
|
Metrics/ParameterLists:
|
75
|
-
Max:
|
76
|
+
Max: 14
|
76
77
|
|
77
|
-
# Offense count:
|
78
|
+
# Offense count: 23
|
78
79
|
# Configuration parameters: AllowedMethods, AllowedPatterns, Max.
|
79
80
|
Metrics/PerceivedComplexity:
|
80
81
|
Exclude:
|
81
82
|
- 'lib/lutaml/model/attribute.rb'
|
82
83
|
- 'lib/lutaml/model/comparable_model.rb'
|
83
84
|
- 'lib/lutaml/model/serialize.rb'
|
85
|
+
- 'lib/lutaml/model/type/integer.rb'
|
84
86
|
- 'lib/lutaml/model/xml_adapter/nokogiri_adapter.rb'
|
85
87
|
- 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
|
86
88
|
- 'lib/lutaml/model/xml_adapter/xml_document.rb'
|
@@ -95,7 +97,7 @@ RSpec/ContextWording:
|
|
95
97
|
- 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
|
96
98
|
- 'spec/lutaml/model/xml_mapping_spec.rb'
|
97
99
|
|
98
|
-
# Offense count:
|
100
|
+
# Offense count: 117
|
99
101
|
# Configuration parameters: CountAsOne.
|
100
102
|
RSpec/ExampleLength:
|
101
103
|
Max: 54
|
@@ -106,13 +108,14 @@ RSpec/IndexedLet:
|
|
106
108
|
Exclude:
|
107
109
|
- 'spec/address_spec.rb'
|
108
110
|
|
109
|
-
# Offense count:
|
111
|
+
# Offense count: 20
|
110
112
|
RSpec/LeakyConstantDeclaration:
|
111
113
|
Exclude:
|
112
114
|
- 'spec/lutaml/model/schema/json_schema_spec.rb'
|
113
115
|
- 'spec/lutaml/model/schema/relaxng_schema_spec.rb'
|
114
116
|
- 'spec/lutaml/model/schema/xsd_schema_spec.rb'
|
115
117
|
- 'spec/lutaml/model/schema/yaml_schema_spec.rb'
|
118
|
+
- 'spec/lutaml/model/type_spec.rb'
|
116
119
|
- 'spec/lutaml/model/validation_spec.rb'
|
117
120
|
- 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
|
118
121
|
|
@@ -124,7 +127,7 @@ RSpec/MultipleDescribes:
|
|
124
127
|
- 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
|
125
128
|
- 'spec/lutaml/model/xml_adapter_spec.rb'
|
126
129
|
|
127
|
-
# Offense count:
|
130
|
+
# Offense count: 149
|
128
131
|
RSpec/MultipleExpectations:
|
129
132
|
Max: 14
|
130
133
|
|
@@ -133,24 +136,34 @@ RSpec/MultipleExpectations:
|
|
133
136
|
RSpec/MultipleMemoizedHelpers:
|
134
137
|
Max: 9
|
135
138
|
|
136
|
-
# Offense count:
|
139
|
+
# Offense count: 9
|
137
140
|
# Configuration parameters: AllowedGroups.
|
138
141
|
RSpec/NestedGroups:
|
139
142
|
Max: 4
|
140
143
|
|
141
|
-
# Offense count:
|
144
|
+
# Offense count: 13
|
142
145
|
RSpec/PendingWithoutReason:
|
143
146
|
Exclude:
|
144
147
|
- 'spec/lutaml/model/mixed_content_spec.rb'
|
148
|
+
- 'spec/lutaml/model/type/date_time_spec.rb'
|
149
|
+
- 'spec/lutaml/model/type/integer_spec.rb'
|
150
|
+
- 'spec/lutaml/model/type/time_spec.rb'
|
151
|
+
- 'spec/lutaml/model/type/time_without_date_spec.rb'
|
145
152
|
- 'spec/lutaml/model/validation_spec.rb'
|
146
153
|
- 'spec/lutaml/model/xml_adapter/oga_adapter_spec.rb'
|
147
154
|
- 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
|
148
155
|
- 'spec/lutaml/model/xml_adapter_spec.rb'
|
149
156
|
|
150
|
-
# Offense count:
|
157
|
+
# Offense count: 2
|
151
158
|
RSpec/RemoveConst:
|
152
159
|
Exclude:
|
153
160
|
- 'spec/lutaml/model/type/decimal_spec.rb'
|
161
|
+
- 'spec/lutaml/model/type_spec.rb'
|
162
|
+
|
163
|
+
# Offense count: 2
|
164
|
+
RSpec/RepeatedExampleGroupBody:
|
165
|
+
Exclude:
|
166
|
+
- 'spec/lutaml/model/type/time_without_date_spec.rb'
|
154
167
|
|
155
168
|
# Offense count: 2
|
156
169
|
RSpec/RepeatedExampleGroupDescription:
|
@@ -167,6 +180,12 @@ RSpec/SpecFilePathFormat:
|
|
167
180
|
- 'spec/lutaml/model/defaults_spec.rb'
|
168
181
|
- 'spec/lutaml/model/type/decimal_spec.rb'
|
169
182
|
|
183
|
+
# Offense count: 1
|
184
|
+
# Configuration parameters: IgnoreNameless, IgnoreSymbolicNames.
|
185
|
+
RSpec/VerifiedDoubles:
|
186
|
+
Exclude:
|
187
|
+
- 'spec/lutaml/model/type/hash_spec.rb'
|
188
|
+
|
170
189
|
# Offense count: 1
|
171
190
|
Security/CompoundHash:
|
172
191
|
Exclude:
|
data/README.adoc
CHANGED
@@ -22,6 +22,17 @@ NOTE: Instructions on how to migrate from Shale to Lutaml::Model are provided in
|
|
22
22
|
<<migrate-from-shale>>.
|
23
23
|
|
24
24
|
|
25
|
+
== Features
|
26
|
+
|
27
|
+
* Define models with attributes and types
|
28
|
+
* Serialize and deserialize models to/from JSON, XML, YAML, and TOML
|
29
|
+
* Support for multiple serialization libraries (e.g., `toml-rb`, `tomlib`)
|
30
|
+
* Configurable adapters for different serialization formats
|
31
|
+
* Support for collections and default values
|
32
|
+
* Custom serialization/deserialization methods
|
33
|
+
* XML namespaces and mappings
|
34
|
+
|
35
|
+
|
25
36
|
== Data modeling in a nutshell
|
26
37
|
|
27
38
|
Data modeling is the process of creating a data model for the data to be stored
|
@@ -32,16 +43,110 @@ Lutaml::Model simplifies data modeling in Ruby by allowing you to define models
|
|
32
43
|
with attributes and serialize/deserialize them to/from various serialization
|
33
44
|
formats seamlessly.
|
34
45
|
|
46
|
+
The Lutaml::Model data modelling approach is as follows:
|
47
|
+
|
48
|
+
.Modeling relationships of a LutaML Model
|
49
|
+
[source]
|
50
|
+
----
|
51
|
+
Lutaml Model
|
52
|
+
│
|
53
|
+
Has many attributes
|
54
|
+
│
|
55
|
+
▼
|
56
|
+
Attribute
|
57
|
+
│
|
58
|
+
Has type of
|
59
|
+
│
|
60
|
+
┌──────────┴──────────┐
|
61
|
+
│ │
|
62
|
+
Model Value (Leaf)
|
63
|
+
│ │
|
64
|
+
Has many attributes Contains one basic value
|
65
|
+
│ │
|
66
|
+
┌───────┴─────┐ ┌──────┴──────┐
|
67
|
+
│ │ │ │
|
68
|
+
Model Value (Leaf) String Integer
|
69
|
+
│ Date Boolean
|
70
|
+
│ Time Float
|
71
|
+
Has many attributes ... ...
|
72
|
+
│
|
73
|
+
▼
|
74
|
+
(Recursive pattern continues...)
|
75
|
+
----
|
76
|
+
|
77
|
+
.Example of LutaML Model instance with assigned values
|
78
|
+
====
|
79
|
+
[source]
|
80
|
+
----
|
81
|
+
Studio (Model)
|
82
|
+
├── name (Value: String) = "Pottery Studio"
|
83
|
+
├── address (Model)
|
84
|
+
│ ├── street (Value: String) = "123 Clay St"
|
85
|
+
│ ├── city (Value: String) = "Ceramics City"
|
86
|
+
│ └── postcode (Value: String) = "12345"
|
87
|
+
├── established (Value: Date) = 2020-01-01
|
88
|
+
└── kilns (Model)
|
89
|
+
├── count (Value: Integer) = 3
|
90
|
+
└── temperature (Value: Float) = 1200.0
|
91
|
+
----
|
92
|
+
====
|
93
|
+
|
94
|
+
|
95
|
+
.Modeling relationships of a LutaML Model to serialization models
|
96
|
+
[source]
|
97
|
+
----
|
98
|
+
Core Model Serialization Model
|
99
|
+
========== ===================
|
100
|
+
│ (mapping)
|
101
|
+
│ │
|
102
|
+
▼ ▼
|
103
|
+
Model XML Model
|
104
|
+
│ │
|
105
|
+
┌─────┴─────┐ ┌──────┴──────┐
|
106
|
+
│ │ │ │
|
107
|
+
Models Value Types ────┬──► Models Value Types
|
108
|
+
│ │ │ │ │
|
109
|
+
│ │ │ │ │
|
110
|
+
│ ┌──────┴──┐ │ ┌────┴────┐ ┌─┴─┐
|
111
|
+
│ │ │ │ │ │ │ │
|
112
|
+
│ String Integer │ Element Value xs:string
|
113
|
+
│ Date Float │ Attribute Type xs:date
|
114
|
+
│ Time Boolean │ xs:boolean
|
115
|
+
│ │ xs:anyURI
|
116
|
+
└──────┐ │
|
117
|
+
│ │ JSON Model
|
118
|
+
Contains │ │
|
119
|
+
more Models │ ┌──────┴──────┐
|
120
|
+
(recursive) │ │ │
|
121
|
+
└──► Models Value Types
|
122
|
+
│ │
|
123
|
+
│ │
|
124
|
+
┌────┴────┐ ┌─┴─┐
|
125
|
+
│ │ │ │
|
126
|
+
object array number string
|
127
|
+
value boolean null
|
128
|
+
----
|
129
|
+
|
130
|
+
.Example of LutaML Model instance transformed into a serialization model and serialized to JSON
|
131
|
+
====
|
132
|
+
[source]
|
133
|
+
----
|
134
|
+
Studio (Core Model) JSON Model Serialized JSON
|
135
|
+
│ │ │
|
136
|
+
▼ ▼ ▼
|
137
|
+
name: "Studio 1" ┌─► { "name": "...", {
|
138
|
+
address: │ "address": { "name": "Studio 1",
|
139
|
+
├── street: "..." │ "street": "...", "address": {
|
140
|
+
└── city: "..." │ "city": "..." ──► "street": "...",
|
141
|
+
kilns: │ }, "city": "..."
|
142
|
+
├── count: 3 │ "kilnsCount": ..., },
|
143
|
+
└── temp: 1200 │ "kilnsTemp": ... "kilnsCount": 3,
|
144
|
+
└─► } "kilnsTemp": 1200
|
145
|
+
}
|
146
|
+
----
|
147
|
+
====
|
35
148
|
|
36
|
-
== Features
|
37
149
|
|
38
|
-
* Define models with attributes and types
|
39
|
-
* Serialize and deserialize models to/from JSON, XML, YAML, and TOML
|
40
|
-
* Support for multiple serialization libraries (e.g., `toml-rb`, `tomlib`)
|
41
|
-
* Configurable adapters for different serialization formats
|
42
|
-
* Support for collections and default values
|
43
|
-
* Custom serialization/deserialization methods
|
44
|
-
* XML namespaces and mappings
|
45
150
|
|
46
151
|
== Installation
|
47
152
|
|
@@ -150,6 +255,8 @@ same class and all their attributes are equal.
|
|
150
255
|
Lutaml::Model supports the following attribute types, they can be
|
151
256
|
referred by a string, a symbol, or their class constant.
|
152
257
|
|
258
|
+
Every type has a corresponding Ruby class and a serialization format type.
|
259
|
+
|
153
260
|
Syntax:
|
154
261
|
|
155
262
|
[source,ruby]
|
@@ -159,20 +266,20 @@ attribute :name_of_attribute, {symbol | string | class}
|
|
159
266
|
|
160
267
|
.Mapping between Lutaml::Model::Type classes, Ruby equivalents and serialization format types
|
161
268
|
|===
|
162
|
-
| Lutaml::Model::Type | Ruby
|
163
|
-
|
164
|
-
| `:string` | `String` | `xs:string` | `string`
|
165
|
-
| `:integer` | `Integer` | `xs:integer` | `number`
|
166
|
-
| `:float` | `Float` | `xs:decimal` | `number`
|
167
|
-
| `:boolean` | `TrueClass`/`FalseClass` | `xs:boolean` | `boolean`
|
168
|
-
| `:date` | `Date` | `xs:date` | `string`
|
169
|
-
| `:time_without_date` | `Time` | `xs:time` | `string`
|
170
|
-
| `:date_time` | `DateTime` | `xs:dateTime` | `string`
|
171
|
-
| `:time` | `Time` | `xs:dateTime` | `string`
|
172
|
-
| `:decimal` (optional) | `BigDecimal` | `xs:decimal` | `number`
|
173
|
-
| `:hash` | `Hash` | complex element |
|
174
|
-
| `class` | Custom
|
175
|
-
| `collection: true` | `Array` of
|
269
|
+
| Lutaml::Model::Type | Ruby class | XML | JSON | YAML | Example value
|
270
|
+
|
271
|
+
| `:string` | `String` | `xs:string` | `string` | `string` | `"text"`
|
272
|
+
| `:integer` | `Integer` | `xs:integer` | `number` | `integer` | `42`
|
273
|
+
| `:float` | `Float` | `xs:decimal` | `number` | `float` | `3.14`
|
274
|
+
| `:boolean` | `TrueClass`/`FalseClass` | `xs:boolean` | `boolean` | `boolean` | `true`, `false`
|
275
|
+
| `:date` | `Date` | `xs:date` | `string` | `string` | `2024-01-01` (JSON/YAML `"2024-01-01"`)
|
276
|
+
| `:time_without_date` | `Time` | `xs:time` | `string` | `string` | `"12:34:56"`
|
277
|
+
| `:date_time` | `DateTime` | `xs:dateTime` | `string` | `string` | `"2024-01-01T12:00:00+00:00"`
|
278
|
+
| `:time` | `Time` | `xs:dateTime` | `string` | `string` | `"2024-01-01T12:00:00+00:00"`
|
279
|
+
| `:decimal` (optional) | `BigDecimal` | `xs:decimal` | `number` | `float` | `123.45`
|
280
|
+
| `:hash` | `Hash` | complex element | object | map | `{key: "value"}`
|
281
|
+
// | `class` | Custom class | complex element | object | map | `CustomObject`
|
282
|
+
// | `collection: true` | `Array` of type | repeated elements | array | sequence | `[obj1, obj2]`
|
176
283
|
// | `any`
|
177
284
|
|
178
285
|
|===
|
@@ -204,14 +311,17 @@ end
|
|
204
311
|
----
|
205
312
|
====
|
206
313
|
|
207
|
-
====
|
314
|
+
==== Decimal type
|
315
|
+
|
316
|
+
The Decimal type is an optional type that is disabled by default.
|
208
317
|
|
209
|
-
The
|
210
|
-
|
211
|
-
library is
|
318
|
+
NOTE: The reason why the Decimal type is disalbed by default is that the
|
319
|
+
`BigDecimal` class became optional to the standard Ruby library from Ruby 3.4
|
320
|
+
onwards. The `Decimal` type is only enabled when the `bigdecimal` library is
|
321
|
+
loaded.
|
212
322
|
|
213
|
-
|
214
|
-
|
323
|
+
The following code needs to be run before using (and parsing) the Decimal
|
324
|
+
type:
|
215
325
|
|
216
326
|
[source,ruby]
|
217
327
|
----
|
@@ -222,6 +332,142 @@ If the `bigdecimal` library is not loaded, usage of the `Decimal` type will
|
|
222
332
|
raise a `Lutaml::Model::TypeNotSupportedError`.
|
223
333
|
|
224
334
|
|
335
|
+
==== Custom type
|
336
|
+
|
337
|
+
A custom class can be used as an attribute type. The custom class must inherit
|
338
|
+
from `Lutaml::Model::Type::Value` or a class that inherits from it.
|
339
|
+
|
340
|
+
A class inheriting from the `Value` class carries the attribute `value` which
|
341
|
+
stores the one-and-only "true" value that is independent of serialization
|
342
|
+
formats.
|
343
|
+
|
344
|
+
The minimum requirement for a custom class is to implement the following
|
345
|
+
methods:
|
346
|
+
|
347
|
+
`self.cast(value)`:: Assignment of an external value to the `Value` class to be
|
348
|
+
set as `value`. Casts the value to the custom type.
|
349
|
+
|
350
|
+
`self.serialize(value)`:: Serializes the custom type to an object (e.g. a
|
351
|
+
string). Takes the internal `value` and converts it into an output suitable for
|
352
|
+
serialization.
|
353
|
+
|
354
|
+
|
355
|
+
.Using a custom value type to normalize a postcode with minimal methods
|
356
|
+
[example]
|
357
|
+
====
|
358
|
+
[source,ruby]
|
359
|
+
----
|
360
|
+
class FiveDigitPostCode < Lutaml::Model::Type::String
|
361
|
+
def self.cast(value)
|
362
|
+
value = value.to_s if value.is_a?(Integer)
|
363
|
+
|
364
|
+
unless value.is_a?(::String)
|
365
|
+
raise Lutaml::Model::InvalidValueError, "Invalid value for type 'FiveDigitPostCode'"
|
366
|
+
end
|
367
|
+
|
368
|
+
# Pad zeros to the left
|
369
|
+
value.rjust(5, '0')
|
370
|
+
end
|
371
|
+
|
372
|
+
def self.serialize(value)
|
373
|
+
value
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
377
|
+
class Studio < Lutaml::Model::Serializable
|
378
|
+
attribute :postcode, FiveDigitPostCode
|
379
|
+
end
|
380
|
+
----
|
381
|
+
====
|
382
|
+
|
383
|
+
|
384
|
+
==== Serialization of custom types
|
385
|
+
|
386
|
+
The serialization of custom types can be made to differ per serialization format
|
387
|
+
by defining methods in the class definitions. This requires additional methods
|
388
|
+
than the minimum required for a custom class (i.e. `self.cast(value)` and
|
389
|
+
`self.serialize(value)`).
|
390
|
+
|
391
|
+
This is useful in the case when different serialization formats of the same
|
392
|
+
model expect differentiated value representations.
|
393
|
+
|
394
|
+
The methods that can be overridden are named:
|
395
|
+
|
396
|
+
`self.from_{format}(serialized_string)`:: Deserializes a string of the
|
397
|
+
serialization format and returns the object to be assigned to the `Value` class'
|
398
|
+
`value`.
|
399
|
+
|
400
|
+
`to_{format}`:: Serializes the object to a string of the serialization format.
|
401
|
+
|
402
|
+
The `{format}` part of the method name is the serialization format in lowercase
|
403
|
+
(e.g. `json`, `xml`, `yaml`, `toml`).
|
404
|
+
|
405
|
+
.Using custom serialization methods to handle a high-precision date-time type
|
406
|
+
[example]
|
407
|
+
====
|
408
|
+
Suppose in XML we handle a high-precision date-time type that requires custom
|
409
|
+
serialization methods, but other formats such as JSON do not support this type.
|
410
|
+
|
411
|
+
For instance, in the normal DateTime class, the serialized string is
|
412
|
+
`2012-04-07T01:51:37+02:00`, and the high-precision format is
|
413
|
+
`2012-04-07T01:51:37.112+02:00`.
|
414
|
+
|
415
|
+
We create `HighPrecisionDateTime` class is a custom class that inherits
|
416
|
+
from `Lutaml::Model::Type::DateTime`.
|
417
|
+
|
418
|
+
[source,ruby]
|
419
|
+
----
|
420
|
+
class HighPrecisionDateTime < Lutaml::Model::Type::DateTime
|
421
|
+
# Inherit the `self.cast(value)` and `self.serialize(value)` methods
|
422
|
+
# from Lutaml::Model::Type::DateTime
|
423
|
+
|
424
|
+
# The format looks like this `2012-04-07T01:51:37.112+02:00`
|
425
|
+
def self.from_xml(xml_string)
|
426
|
+
::DateTime.parse(xml_string)
|
427
|
+
end
|
428
|
+
|
429
|
+
# The %L adds milliseconds to the time
|
430
|
+
def to_xml
|
431
|
+
value.strftime('%Y-%m-%dT%H:%M:%S.%L%:z')
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
class Ceramic < Lutaml::Model::Serializable
|
436
|
+
attribute :kiln_firing_time, HighPrecisionDateTime
|
437
|
+
xml do
|
438
|
+
root 'ceramic'
|
439
|
+
map_element 'kilnFiringTime', to: :kiln_firing_time
|
440
|
+
# ...
|
441
|
+
end
|
442
|
+
end
|
443
|
+
----
|
444
|
+
|
445
|
+
An XML snippet with the high-precision date-time type:
|
446
|
+
|
447
|
+
[source,xml]
|
448
|
+
----
|
449
|
+
<ceramic>
|
450
|
+
<kilnFiringTime>2012-04-07T01:51:37.112+02:00</kilnFiringTime>
|
451
|
+
<!-- ... -->
|
452
|
+
</ceramic>
|
453
|
+
----
|
454
|
+
|
455
|
+
When loading the XML snippet, the `HighPrecisionDateTime` class will be used to
|
456
|
+
parse the high-precision date-time string.
|
457
|
+
|
458
|
+
However, when serializing to JSON, the value will have the high-precision
|
459
|
+
part lost due to the inability of JSON to handle high-precision date-time.
|
460
|
+
|
461
|
+
[source,ruby]
|
462
|
+
----
|
463
|
+
> c = Ceramic.from_xml(xml)
|
464
|
+
> #<Ceramic:0x0000000104ac7240 @kiln_firing_time=#<HighPrecisionDateTime:0x0000000104ac7240 @value=2012-04-07 01:51:37.112000000 +0200>>
|
465
|
+
> c.to_json
|
466
|
+
> # {"kilnFiringTime":"2012-04-07T01:51:37+02:00"}
|
467
|
+
----
|
468
|
+
====
|
469
|
+
|
470
|
+
|
225
471
|
=== Attribute as a collection
|
226
472
|
|
227
473
|
Define attributes as collections (arrays or hashes) to store multiple values
|
@@ -297,8 +543,15 @@ end
|
|
297
543
|
====
|
298
544
|
|
299
545
|
|
546
|
+
=== Attribute value validation
|
547
|
+
|
548
|
+
==== General
|
549
|
+
|
550
|
+
There are several mechanisms to validate attribute values in Lutaml::Model.
|
551
|
+
|
552
|
+
|
300
553
|
[[attribute-enumeration]]
|
301
|
-
|
554
|
+
==== Values of an enumeration
|
302
555
|
|
303
556
|
An attribute can be defined as an enumeration by using the `values` directive.
|
304
557
|
|
@@ -395,6 +648,44 @@ acceptance of the newly updated component.
|
|
395
648
|
====
|
396
649
|
|
397
650
|
|
651
|
+
==== String values restricted to patterns
|
652
|
+
|
653
|
+
An attribute that accepts a string value accepts value validation using regular
|
654
|
+
expressions.
|
655
|
+
|
656
|
+
Syntax:
|
657
|
+
|
658
|
+
[source,ruby]
|
659
|
+
----
|
660
|
+
attribute :name_of_attribute, :string, pattern: /regex/
|
661
|
+
----
|
662
|
+
|
663
|
+
.Using the `pattern` option to restrict the value of an attribute
|
664
|
+
[example]
|
665
|
+
====
|
666
|
+
In this example, the `color` attribute takes hex color values such as `#ccddee`.
|
667
|
+
|
668
|
+
A regular expression can be used to validate values assigned to the attribute.
|
669
|
+
In this case, it is `/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/`.
|
670
|
+
|
671
|
+
[source,ruby]
|
672
|
+
----
|
673
|
+
class Glaze < Lutaml::Model::Serializable
|
674
|
+
attribute :color, :string, pattern: /\A#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\z/
|
675
|
+
end
|
676
|
+
----
|
677
|
+
|
678
|
+
[source,ruby]
|
679
|
+
----
|
680
|
+
> Glaze.new(color: '#ff0000').color
|
681
|
+
> # "#ff0000"
|
682
|
+
> Glaze.new(color: '#ff000').color
|
683
|
+
> # Lutaml::Model::InvalidValueError: Invalid value for attribute 'color'
|
684
|
+
----
|
685
|
+
====
|
686
|
+
|
687
|
+
|
688
|
+
|
398
689
|
=== Attribute value default and rendering defaults
|
399
690
|
|
400
691
|
Specify default values for attributes using the `default` option.
|
@@ -525,7 +816,8 @@ class Person < Lutaml::Model::Serializable
|
|
525
816
|
end
|
526
817
|
----
|
527
818
|
|
528
|
-
For the following
|
819
|
+
For the following XML snippet:
|
820
|
+
|
529
821
|
[source,xml]
|
530
822
|
----
|
531
823
|
<Person>
|
@@ -898,6 +1190,80 @@ end
|
|
898
1190
|
====
|
899
1191
|
|
900
1192
|
|
1193
|
+
==== CDATA nodes
|
1194
|
+
|
1195
|
+
CDATA is an XML feature that allows the inclusion of text that may contain
|
1196
|
+
characters that are unescaped in XML.
|
1197
|
+
|
1198
|
+
While CDATA is not preferred in XML, it is sometimes necessary to handle CDATA
|
1199
|
+
nodes for both input and output.
|
1200
|
+
|
1201
|
+
NOTE: The W3C XML Recommendation explicitly encourages escaping characters over
|
1202
|
+
usage of CDATA.
|
1203
|
+
|
1204
|
+
Lutaml::Model supports the handling of CDATA nodes in XML in the following
|
1205
|
+
behavior:
|
1206
|
+
|
1207
|
+
. When an attribute contains a CDATA node with no text:
|
1208
|
+
** On reading: The node (CDATA or text) is read as its value.
|
1209
|
+
** On writing: The value is written as its native type.
|
1210
|
+
|
1211
|
+
. When an XML mapping sets `cdata: true` on `map_element` or `map_content`:
|
1212
|
+
** On reading: The node (CDATA or text) is read as its value.
|
1213
|
+
** On writing: The value is written as a CDATA node.
|
1214
|
+
|
1215
|
+
. When an XML mapping sets `cdata: false` on `map_element` or `map_content`:
|
1216
|
+
** On reading: The node (CDATA or text) is read as its value.
|
1217
|
+
** On writing: The value is written as a text node (string).
|
1218
|
+
|
1219
|
+
|
1220
|
+
Syntax:
|
1221
|
+
|
1222
|
+
[source,ruby]
|
1223
|
+
----
|
1224
|
+
xml do
|
1225
|
+
map_content to: :name_of_attribute, cdata: (true | false)
|
1226
|
+
map_element :name, to: :name, cdata: (true | false)
|
1227
|
+
end
|
1228
|
+
----
|
1229
|
+
|
1230
|
+
.Using `cdata` to map CDATA content
|
1231
|
+
[example]
|
1232
|
+
====
|
1233
|
+
The following class will parse the XML snippet below:
|
1234
|
+
|
1235
|
+
[source,ruby]
|
1236
|
+
----
|
1237
|
+
class Example < Lutaml::Model::Serializable
|
1238
|
+
attribute :name, :string
|
1239
|
+
attribute :description, :string
|
1240
|
+
attribute :title, :string
|
1241
|
+
attribute :note, :string
|
1242
|
+
|
1243
|
+
xml do
|
1244
|
+
root 'example'
|
1245
|
+
map_element :name, to: :name, cdata: true
|
1246
|
+
map_content to: :description, cdata: true
|
1247
|
+
map_element :title, to: :title, cdata: false
|
1248
|
+
map_element :note, to: :note, cdata: false
|
1249
|
+
end
|
1250
|
+
end
|
1251
|
+
----
|
1252
|
+
|
1253
|
+
[source,xml]
|
1254
|
+
----
|
1255
|
+
<example><name><![CDATA[John]]></name><![CDATA[here is the description]]><title><![CDATA[Lutaml]]></title><note>Careful</note></example>
|
1256
|
+
----
|
1257
|
+
|
1258
|
+
[source,ruby]
|
1259
|
+
----
|
1260
|
+
> Example.from_xml(xml)
|
1261
|
+
> #<Example:0x0000000104ac7240 @name="John" @description="here is the description" @title="Lutaml" @note="Careful">
|
1262
|
+
> Example.new(name: "John", description: "here is the description", title: "Lutaml", note: "Careful").to_xml
|
1263
|
+
> #<example><name><![CDATA[John]]></name><![CDATA[here is the description]]><title>Lutaml</title><note>Careful</note></example>
|
1264
|
+
----
|
1265
|
+
====
|
1266
|
+
|
901
1267
|
|
902
1268
|
==== Example for mapping
|
903
1269
|
|