lutaml-model 0.3.23 → 0.3.25
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 +35 -16
- data/README.adoc +274 -28
- data/lib/lutaml/model/attribute.rb +18 -8
- 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 +2 -0
- data/lib/lutaml/model/serialize.rb +7 -2
- 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/version.rb +1 -1
- data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +5 -2
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +2 -1
- data/lib/lutaml/model/xml_adapter/xml_document.rb +0 -2
- 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 +112 -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 +85 -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 +66 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 923c122ff4459f02c1949dc88ee8df94b93db6ce23c00c9da2159f6b376da3a0
|
4
|
+
data.tar.gz: aa537363d67a5383cdbd280cfa9ddee0a3c5de0cb151b79b2e195a030bc0061d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0da4f4a87c8fe4a3951d302df5797594675f83616ea52b4582f417683d4f55076c57ad3d6f3018119c0a6021497bf6832b150ebf695d1fa8d4304da2040b641
|
7
|
+
data.tar.gz: 0d4d6d783728b53d453c23e2cfcfcd08d9eac83d94b4652d3bdee95c19d9d9bf68e233dea6d29d9478d4214271dd800be93b8f8e11a59cf9f9cd46d744dd2002
|
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
|
@@ -74,13 +75,14 @@ Metrics/MethodLength:
|
|
74
75
|
Metrics/ParameterLists:
|
75
76
|
Max: 13
|
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
|
208
315
|
|
209
|
-
The
|
210
|
-
3.4 onwards, hence the `Decimal` type is only enabled when the `bigdecimal`
|
211
|
-
library is loaded.
|
316
|
+
The Decimal type is an optional type that is disabled by default.
|
212
317
|
|
213
|
-
|
214
|
-
|
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.
|
322
|
+
|
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
|
@@ -34,15 +34,23 @@ module Lutaml
|
|
34
34
|
|
35
35
|
def cast_type!(type)
|
36
36
|
case type
|
37
|
+
when Symbol
|
38
|
+
begin
|
39
|
+
Type.lookup(type)
|
40
|
+
rescue UnknownTypeError
|
41
|
+
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
42
|
+
end
|
43
|
+
when String
|
44
|
+
begin
|
45
|
+
Type.const_get(type)
|
46
|
+
rescue NameError
|
47
|
+
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
48
|
+
end
|
37
49
|
when Class
|
38
50
|
type
|
39
|
-
|
40
|
-
Type
|
41
|
-
when Symbol
|
42
|
-
Type.const_get(type.to_s.split("_").collect(&:capitalize).join)
|
51
|
+
else
|
52
|
+
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
43
53
|
end
|
44
|
-
rescue NameError
|
45
|
-
raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
|
46
54
|
end
|
47
55
|
|
48
56
|
def cast_value(value)
|
@@ -194,7 +202,9 @@ module Lutaml
|
|
194
202
|
elsif type <= Serialize
|
195
203
|
type.hash_representation(value, format, options)
|
196
204
|
else
|
197
|
-
|
205
|
+
# Convert to Value instance if not already
|
206
|
+
value = type.new(value) unless value.is_a?(Type::Value)
|
207
|
+
value.send(:"to_#{format}")
|
198
208
|
end
|
199
209
|
end
|
200
210
|
|
@@ -208,7 +218,7 @@ module Lutaml
|
|
208
218
|
elsif type <= Serialize && value.is_a?(Hash)
|
209
219
|
type.apply_mappings(value, format, options)
|
210
220
|
else
|
211
|
-
|
221
|
+
type.cast(value)
|
212
222
|
end
|
213
223
|
end
|
214
224
|
|
data/lib/lutaml/model/error.rb
CHANGED
@@ -11,3 +11,5 @@ require_relative "error/unknown_adapter_type_error"
|
|
11
11
|
require_relative "error/collection_count_out_of_range_error"
|
12
12
|
require_relative "error/validation_error"
|
13
13
|
require_relative "error/type_not_enabled_error"
|
14
|
+
require_relative "error/type_error"
|
15
|
+
require_relative "error/unknown_type_error"
|
@@ -35,7 +35,7 @@ module Lutaml
|
|
35
35
|
subclass.instance_variable_set(:@attributes,
|
36
36
|
Utils.deep_dup(@attributes))
|
37
37
|
subclass.instance_variable_set(:@mappings, Utils.deep_dup(@mappings))
|
38
|
-
subclass.instance_variable_set(:@model,
|
38
|
+
subclass.instance_variable_set(:@model, subclass)
|
39
39
|
end
|
40
40
|
|
41
41
|
def model(klass = nil)
|
@@ -310,7 +310,10 @@ module Lutaml
|
|
310
310
|
def apply_xml_mapping(doc, instance, options = {})
|
311
311
|
return instance unless doc
|
312
312
|
|
313
|
-
|
313
|
+
if options[:default_namespace].nil?
|
314
|
+
options[:default_namespace] =
|
315
|
+
mappings_for(:xml)&.namespace_uri
|
316
|
+
end
|
314
317
|
mappings = mappings_for(:xml).mappings
|
315
318
|
|
316
319
|
if doc.is_a?(Array)
|
@@ -398,6 +401,8 @@ module Lutaml
|
|
398
401
|
value.map do |v|
|
399
402
|
text_hash?(attr, v) ? v["text"] : v
|
400
403
|
end
|
404
|
+
elsif attr&.raw? && value
|
405
|
+
value.node.children.map(&:to_xml).join
|
401
406
|
elsif text_hash?(attr, value)
|
402
407
|
value["text"]
|
403
408
|
else
|