lutaml-model 0.3.24 → 0.3.25
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.
- 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 +6 -1
- 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"
|
@@ -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
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
module Type
|
4
|
+
class Boolean < Value
|
5
|
+
def self.cast(value)
|
6
|
+
return nil if value.nil?
|
7
|
+
return true if value == true || value.to_s.match?(/^(true|t|yes|y|1)$/i)
|
8
|
+
return false if value == false || value.to_s.match?(/^(false|f|no|n|0)$/i)
|
9
|
+
|
10
|
+
value
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.serialize(value)
|
14
|
+
return nil if value.nil?
|
15
|
+
|
16
|
+
cast(value) # Return actual boolean instead of string
|
17
|
+
end
|
18
|
+
|
19
|
+
# Format-specific serialization methods
|
20
|
+
def to_xml
|
21
|
+
value.to_s
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_json(*_args)
|
25
|
+
value
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_yaml
|
29
|
+
value
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_toml
|
33
|
+
value.to_s
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|