lutaml-model 0.8.11 → 0.8.13
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/.github/workflows/opal.yml +31 -0
- data/.rspec-opal +5 -0
- data/.rubocop_todo.yml +45 -34
- data/README.adoc +126 -104
- data/RELEASE_NOTES.adoc +3 -3
- data/benchmark/quick_benchmark.rb +2 -2
- data/benchmark/serialization_benchmark.rb +4 -4
- data/docs/_guides/advanced-mapping.adoc +1 -1
- data/docs/_guides/character-encoding.adoc +3 -3
- data/docs/_guides/index.adoc +4 -0
- data/docs/_guides/missing-values-handling.adoc +6 -6
- data/docs/_guides/ooxml-examples.adoc +7 -7
- data/docs/_guides/opal.adoc +221 -0
- data/docs/_guides/value-transformations.adoc +7 -7
- data/docs/_guides/xml/namespace-presentation.adoc +1 -1
- data/docs/_guides/xml/namespace-semantics.adoc +15 -15
- data/docs/_guides/xml/type-namespaces.adoc +9 -9
- data/docs/_guides/xml-mapping.adoc +32 -26
- data/docs/_guides/xml-namespace-qualification.adoc +4 -4
- data/docs/_guides/xml-namespaces.adoc +2 -2
- data/docs/_guides/xml_mappings/04_xml_namespace_class.adoc +18 -18
- data/docs/_guides/xml_mappings/05_common_patterns.adoc +16 -16
- data/docs/_guides/xml_mappings/06_migration_guide.adoc +5 -5
- data/docs/_guides/xml_mappings/07_best_practices.adoc +13 -12
- data/docs/_migrations/0-8-0-namespace-restructuring.adoc +2 -2
- data/docs/_pages/attributes.adoc +2 -2
- data/docs/_pages/collections.adoc +26 -20
- data/docs/_pages/configuration.adoc +9 -4
- data/docs/_pages/consolidation-mapping.adoc +4 -4
- data/docs/_pages/importable_models.adoc +14 -13
- data/docs/_pages/index.adoc +1 -0
- data/docs/_pages/quick-start.adoc +1 -1
- data/docs/_pages/serialization_adapters.adoc +3 -2
- data/docs/_pages/value_types.adoc +10 -10
- data/docs/_references/custom_registers.adoc +7 -7
- data/docs/_references/format-independent-features.adoc +4 -4
- data/docs/_references/instance-serialization.adoc +1 -1
- data/docs/_references/parent-root-context.adoc +3 -3
- data/docs/_tutorials/basic-model-definition.adoc +1 -1
- data/docs/_tutorials/first-xml-serialization.adoc +4 -4
- data/docs/_tutorials/lutaml-xml-architecture.adoc +4 -4
- data/docs/_tutorials/validation-basics.adoc +1 -1
- data/docs/_tutorials/working-with-collections.adoc +2 -2
- data/docs/_tutorials/xml-namespaces-basics.adoc +1 -1
- data/docs/_tutorials/xml-schema-primer-style-guide.adoc +29 -29
- data/docs/cli_compare.adoc +1 -1
- data/docs/index.adoc +2 -1
- data/docs/namespace-management.adoc +14 -14
- data/lib/lutaml/hash_format/adapter/mapping.rb +2 -4
- data/lib/lutaml/json/adapter/mapping.rb +2 -4
- data/lib/lutaml/jsonl/adapter/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/hash/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/json/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/jsonl/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/toml/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/yaml/mapping.rb +2 -4
- data/lib/lutaml/key_value/adapter/yamls/mapping.rb +2 -4
- data/lib/lutaml/key_value/mapping.rb +35 -10
- data/lib/lutaml/model/adapter_resolver.rb +5 -8
- data/lib/lutaml/model/collection.rb +11 -11
- data/lib/lutaml/model/error/no_root_mapping_error.rb +6 -5
- data/lib/lutaml/model/error/no_root_namespace_error.rb +6 -5
- data/lib/lutaml/model/error/type_only_mapping_error.rb +13 -0
- data/lib/lutaml/model/error/type_only_namespace_error.rb +12 -0
- data/lib/lutaml/model/mapping/mapping.rb +12 -0
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +3 -0
- data/lib/lutaml/toml/adapter/mapping.rb +2 -4
- data/lib/lutaml/xml/adapter/base_adapter.rb +0 -9
- data/lib/lutaml/xml/adapter/nokogiri_adapter.rb +0 -1
- data/lib/lutaml/xml/adapter/oga_adapter.rb +0 -1
- data/lib/lutaml/xml/adapter/ox_adapter.rb +0 -1
- data/lib/lutaml/xml/adapter/rexml_adapter.rb +0 -1
- data/lib/lutaml/xml/adapter/xml_serializer.rb +42 -22
- data/lib/lutaml/xml/adapter.rb +4 -0
- data/lib/lutaml/xml/builder/base.rb +64 -25
- data/lib/lutaml/xml/builder/nokogiri.rb +0 -2
- data/lib/lutaml/xml/builder/oga.rb +0 -2
- data/lib/lutaml/xml/builder/ox.rb +0 -2
- data/lib/lutaml/xml/builder/rexml.rb +0 -2
- data/lib/lutaml/xml/builder.rb +1 -0
- data/lib/lutaml/xml/configurable.rb +2 -2
- data/lib/lutaml/xml/declaration_handler.rb +3 -105
- data/lib/lutaml/xml/mapping.rb +3 -3
- data/lib/lutaml/xml/schema/xsd/documentation.rb +1 -1
- data/lib/lutaml/xml/schema/xsd.rb +5 -4
- data/lib/lutaml/xml/schema.rb +8 -5
- data/lib/lutaml/xml/serialization/collection_ext.rb +7 -7
- data/lib/lutaml/xml/serialization/format_conversion.rb +1 -1
- data/lib/lutaml/xml/serialization/instance_methods.rb +1 -1
- data/lib/lutaml/xml/xml_orderable.rb +17 -0
- data/lib/lutaml/xml.rb +9 -13
- data/lib/lutaml/yaml/adapter/mapping.rb +2 -4
- data/lib/lutaml/yamls/adapter/mapping.rb +7 -3
- data/lib/tasks/memory_profile.rb +2 -2
- data/lib/tasks/performance_benchmark.rb +5 -5
- data/lutaml-model.gemspec +1 -1
- data/spec/lutaml/key_value/transformation/rule_compiler_spec.rb +1 -1
- data/spec/lutaml/key_value/transformation/value_serializer_spec.rb +1 -1
- data/spec/lutaml/model/attribute_collection_spec.rb +1 -1
- data/spec/lutaml/model/cli_spec.rb +1 -1
- data/spec/lutaml/model/collection_spec.rb +1 -1
- data/spec/lutaml/model/collection_validation_spec.rb +6 -6
- data/spec/lutaml/model/consolidation_spec.rb +8 -8
- data/spec/lutaml/model/custom_collection_spec.rb +3 -3
- data/spec/lutaml/model/default_register_spec.rb +23 -23
- data/spec/lutaml/model/delegation_spec.rb +3 -10
- data/spec/lutaml/model/derived_attribute_serialization_spec.rb +1 -1
- data/spec/lutaml/model/dynamic_attribute_spec.rb +2 -2
- data/spec/lutaml/model/enum_spec.rb +1 -1
- data/spec/lutaml/model/group_spec.rb +12 -12
- data/spec/lutaml/model/lazy_collection_spec.rb +4 -4
- data/spec/lutaml/model/mixed_content_spec.rb +2 -2
- data/spec/lutaml/model/namespace_versioning_spec.rb +4 -4
- data/spec/lutaml/model/opal_smoke_spec.rb +117 -0
- data/spec/lutaml/model/processing_instruction_spec.rb +11 -11
- data/spec/lutaml/model/register_methods_spec.rb +2 -2
- data/spec/lutaml/model/render_empty_spec.rb +1 -1
- data/spec/lutaml/model/serialize_perf_guard_spec.rb +1 -1
- data/spec/lutaml/model/transform_dynamic_attributes_spec.rb +1 -1
- data/spec/lutaml/model/transformation_builder_spec.rb +2 -2
- data/spec/lutaml/model/xml_decoupling_spec.rb +3 -3
- data/spec/lutaml/model/xsd_patterns_spec.rb +2 -3
- data/spec/lutaml/xml/adapter/order_spec.rb +1 -1
- data/spec/lutaml/xml/clear_parse_state_spec.rb +1 -1
- data/spec/lutaml/xml/content_model_validation_spec.rb +4 -2
- data/spec/lutaml/xml/doubly_defined_namespace_spec.rb +5 -5
- data/spec/lutaml/xml/enhanced_mapping_spec.rb +2 -1
- data/spec/lutaml/xml/entity_fragmentation_spec.rb +5 -5
- data/spec/lutaml/xml/indent_spec.rb +109 -0
- data/spec/lutaml/xml/line_ending_spec.rb +66 -0
- data/spec/lutaml/xml/mapping_finalization_guard_spec.rb +2 -2
- data/spec/lutaml/xml/model_transform_guard_spec.rb +4 -4
- data/spec/lutaml/xml/namespace_alias_spec.rb +4 -4
- data/spec/lutaml/xml/namespace_aware_parsing_spec.rb +3 -3
- data/spec/lutaml/xml/namespace_bound_element_roundtrip_spec.rb +2 -2
- data/spec/lutaml/xml/namespace_format_preservation_spec.rb +1 -1
- data/spec/lutaml/xml/namespace_inheritance_spec.rb +3 -3
- data/spec/lutaml/xml/namespace_preservation_spec.rb +5 -5
- data/spec/lutaml/xml/opal_xml_spec.rb +145 -0
- data/spec/lutaml/xml/pipeline_integration_spec.rb +145 -0
- data/spec/lutaml/xml/schema_primer_spec.rb +5 -5
- data/spec/lutaml/xml/transformation_spec.rb +20 -20
- data/spec/lutaml/xml/type_namespace/collector_spec.rb +1 -1
- data/spec/lutaml/xml/type_namespace/planner_spec.rb +3 -3
- data/spec/lutaml/xml/xml_spec.rb +64 -13
- data/spec/support/opal.rb +6 -0
- metadata +16 -4
data/RELEASE_NOTES.adoc
CHANGED
|
@@ -26,7 +26,7 @@ The `namespace_scope` directive allows you to control WHERE namespace declaratio
|
|
|
26
26
|
----
|
|
27
27
|
class Vcard < Lutaml::Model::Serializable
|
|
28
28
|
xml do
|
|
29
|
-
|
|
29
|
+
element "vCard"
|
|
30
30
|
namespace VcardNamespace
|
|
31
31
|
|
|
32
32
|
# Consolidate namespaces at root
|
|
@@ -145,7 +145,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
145
145
|
attribute :coll, :string, collection: true
|
|
146
146
|
|
|
147
147
|
xml do
|
|
148
|
-
|
|
148
|
+
element "some-model"
|
|
149
149
|
map_element 'collection', to: :coll
|
|
150
150
|
end
|
|
151
151
|
end
|
|
@@ -209,7 +209,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
209
209
|
attribute :coll, :string, collection: true
|
|
210
210
|
|
|
211
211
|
xml do
|
|
212
|
-
|
|
212
|
+
element "some-model"
|
|
213
213
|
map_element 'collection', to: :coll, render_nil: :omit
|
|
214
214
|
end
|
|
215
215
|
|
|
@@ -30,7 +30,7 @@ class Item < Lutaml::Model::Serializable
|
|
|
30
30
|
attribute :value, :float
|
|
31
31
|
|
|
32
32
|
xml do
|
|
33
|
-
|
|
33
|
+
element "item"
|
|
34
34
|
map_attribute "id", to: :id
|
|
35
35
|
map_element "name", to: :name
|
|
36
36
|
map_element "value", to: :value
|
|
@@ -47,7 +47,7 @@ class Container < Lutaml::Model::Serializable
|
|
|
47
47
|
attribute :items, Item, collection: true
|
|
48
48
|
|
|
49
49
|
xml do
|
|
50
|
-
|
|
50
|
+
element "container"
|
|
51
51
|
map_element "item", to: :items
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -49,7 +49,7 @@ class SimpleModel < Lutaml::Model::Serializable
|
|
|
49
49
|
attribute :created_at, :date_time
|
|
50
50
|
|
|
51
51
|
xml do
|
|
52
|
-
|
|
52
|
+
element "simple"
|
|
53
53
|
map_attribute "id", to: :id
|
|
54
54
|
map_element "name", to: :name
|
|
55
55
|
map_element "active", to: :active
|
|
@@ -79,7 +79,7 @@ class AddressModel < Lutaml::Model::Serializable
|
|
|
79
79
|
attribute :postal_code, :string
|
|
80
80
|
|
|
81
81
|
xml do
|
|
82
|
-
|
|
82
|
+
element "address"
|
|
83
83
|
map_element "street", to: :street
|
|
84
84
|
map_element "city", to: :city
|
|
85
85
|
map_element "country", to: :country
|
|
@@ -106,7 +106,7 @@ class PersonModel < Lutaml::Model::Serializable
|
|
|
106
106
|
attribute :tags, :string, collection: true
|
|
107
107
|
|
|
108
108
|
xml do
|
|
109
|
-
|
|
109
|
+
element "person"
|
|
110
110
|
map_attribute "id", to: :id
|
|
111
111
|
map_element "first_name", to: :first_name
|
|
112
112
|
map_element "last_name", to: :last_name
|
|
@@ -136,7 +136,7 @@ class DepartmentModel < Lutaml::Model::Serializable
|
|
|
136
136
|
attribute :employees, PersonModel, collection: true
|
|
137
137
|
|
|
138
138
|
xml do
|
|
139
|
-
|
|
139
|
+
element "department"
|
|
140
140
|
map_element "name", to: :name
|
|
141
141
|
map_element "code", to: :code
|
|
142
142
|
map_element "employee", to: :employees
|
|
@@ -47,7 +47,7 @@ class CustomModel < Lutaml::Model::Serializable
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
xml do
|
|
50
|
-
|
|
50
|
+
element "CustomModel"
|
|
51
51
|
map_element ["name", "custom-name"], with: { to: :name_to_xml, from: :name_from_xml }
|
|
52
52
|
map_element ["color", "shade"], with: { to: :color_to_xml, from: :color_from_xml }
|
|
53
53
|
map_attribute ["id", "identifier"], to: :id
|
|
@@ -79,7 +79,7 @@ class JapaneseCeramic < Lutaml::Model::Serializable
|
|
|
79
79
|
attribute :description, :string
|
|
80
80
|
|
|
81
81
|
xml do
|
|
82
|
-
|
|
82
|
+
element "JapaneseCeramic"
|
|
83
83
|
map_attribute 'glazeType', to: :glaze_type
|
|
84
84
|
map_element 'description', to: :description
|
|
85
85
|
end
|
|
@@ -140,7 +140,7 @@ class Ceramic < Lutaml::Model::Serializable
|
|
|
140
140
|
attribute :temperature, :integer
|
|
141
141
|
|
|
142
142
|
xml do
|
|
143
|
-
|
|
143
|
+
element "ceramic"
|
|
144
144
|
map_element 'potter', to: :potter
|
|
145
145
|
map_content to: :description
|
|
146
146
|
end
|
|
@@ -191,7 +191,7 @@ class JapaneseCeramic < Lutaml::Model::Serializable
|
|
|
191
191
|
attribute :description, :string
|
|
192
192
|
|
|
193
193
|
xml do
|
|
194
|
-
|
|
194
|
+
element "JapaneseCeramic"
|
|
195
195
|
map_attribute 'glazeType', to: :glaze_type
|
|
196
196
|
map_element 'description', to: :description
|
|
197
197
|
end
|
data/docs/_guides/index.adoc
CHANGED
|
@@ -46,6 +46,10 @@ See link:../references/rdf-namespaces[RDF Namespaces] for namespace classes,
|
|
|
46
46
|
* link:../consolidation-mapping[Consolidation Mapping] - Group sibling elements into structured models
|
|
47
47
|
* link:../document-validation[Document Validation] - Document-level validation with rules, profiles, and remediation
|
|
48
48
|
|
|
49
|
+
== Runtime environments
|
|
50
|
+
|
|
51
|
+
* link:../opal[Opal Usage Guide] - Run Lutaml::Model in the browser via Opal (Ruby to JavaScript)
|
|
52
|
+
|
|
49
53
|
== By task
|
|
50
54
|
|
|
51
55
|
=== I want to serialize to XML
|
|
@@ -1246,7 +1246,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
1246
1246
|
attribute :coll, :string, collection: true
|
|
1247
1247
|
|
|
1248
1248
|
xml do
|
|
1249
|
-
|
|
1249
|
+
element "some-model"
|
|
1250
1250
|
map_element 'collection', to: :coll, render_nil: :omit
|
|
1251
1251
|
end
|
|
1252
1252
|
|
|
@@ -1285,7 +1285,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
1285
1285
|
attribute :coll, :string, collection: true
|
|
1286
1286
|
|
|
1287
1287
|
xml do
|
|
1288
|
-
|
|
1288
|
+
element "some-model"
|
|
1289
1289
|
map_element 'collection', to: :coll, render_nil: :as_nil
|
|
1290
1290
|
end
|
|
1291
1291
|
|
|
@@ -1327,7 +1327,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
1327
1327
|
attribute :coll, :string, collection: true
|
|
1328
1328
|
|
|
1329
1329
|
xml do
|
|
1330
|
-
|
|
1330
|
+
element "some-model"
|
|
1331
1331
|
map_element 'collection', to: :coll, render_nil: :as_blank # <1>
|
|
1332
1332
|
end
|
|
1333
1333
|
|
|
@@ -1424,7 +1424,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
1424
1424
|
attribute :coll, :string, collection: true
|
|
1425
1425
|
|
|
1426
1426
|
xml do
|
|
1427
|
-
|
|
1427
|
+
element "some-model"
|
|
1428
1428
|
map_element 'collection', to: :coll, render_empty: :omit
|
|
1429
1429
|
end
|
|
1430
1430
|
|
|
@@ -1461,7 +1461,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
1461
1461
|
attribute :coll, :string, collection: true
|
|
1462
1462
|
|
|
1463
1463
|
xml do
|
|
1464
|
-
|
|
1464
|
+
element "some-model"
|
|
1465
1465
|
map_element 'collection', to: :coll, render_empty: :as_nil
|
|
1466
1466
|
end
|
|
1467
1467
|
|
|
@@ -1503,7 +1503,7 @@ class SomeModel < Lutaml::Model::Serializable
|
|
|
1503
1503
|
attribute :coll, :string, collection: true
|
|
1504
1504
|
|
|
1505
1505
|
xml do
|
|
1506
|
-
|
|
1506
|
+
element "some-model"
|
|
1507
1507
|
map_element 'collection', to: :coll, render_empty: :as_blank # <1>
|
|
1508
1508
|
end
|
|
1509
1509
|
|
|
@@ -101,7 +101,7 @@ class PersonName < Lutaml::Model::Serializable
|
|
|
101
101
|
attribute :suffix, :string
|
|
102
102
|
|
|
103
103
|
xml do
|
|
104
|
-
|
|
104
|
+
element "personName"
|
|
105
105
|
map_element "givenName", to: :given_name
|
|
106
106
|
map_element "surname", to: :surname
|
|
107
107
|
map_attribute "prefix", to: :prefix
|
|
@@ -114,7 +114,7 @@ class Contact < Lutaml::Model::Serializable
|
|
|
114
114
|
attribute :person_name, PersonName
|
|
115
115
|
|
|
116
116
|
xml do
|
|
117
|
-
|
|
117
|
+
element "ContactInfo"
|
|
118
118
|
map_element "personName", to: :person_name
|
|
119
119
|
end
|
|
120
120
|
end
|
|
@@ -233,7 +233,7 @@ class DctermsCreated < Lutaml::Model::Serializable
|
|
|
233
233
|
attribute :type, XsiTypeType
|
|
234
234
|
|
|
235
235
|
xml do
|
|
236
|
-
|
|
236
|
+
element "created"
|
|
237
237
|
namespace DctermsNamespace
|
|
238
238
|
map_attribute "type", to: :type
|
|
239
239
|
map_content to: :value
|
|
@@ -246,7 +246,7 @@ class DctermsModified < Lutaml::Model::Serializable
|
|
|
246
246
|
attribute :type, XsiTypeType
|
|
247
247
|
|
|
248
248
|
xml do
|
|
249
|
-
|
|
249
|
+
element "modified"
|
|
250
250
|
namespace DctermsNamespace
|
|
251
251
|
map_attribute "type", to: :type
|
|
252
252
|
map_content to: :value
|
|
@@ -263,7 +263,7 @@ class CoreProperties < Lutaml::Model::Serializable
|
|
|
263
263
|
attribute :modified, DctermsModified
|
|
264
264
|
|
|
265
265
|
xml do
|
|
266
|
-
|
|
266
|
+
element "coreProperties"
|
|
267
267
|
map_element "title", to: :title
|
|
268
268
|
map_element "creator", to: :creator
|
|
269
269
|
map_element "lastModifiedBy", to: :last_modified_by
|
|
@@ -420,7 +420,7 @@ class Settings < Lutaml::Model::Serializable
|
|
|
420
420
|
attribute :chart_tracking, :boolean
|
|
421
421
|
|
|
422
422
|
xml do
|
|
423
|
-
|
|
423
|
+
element "settings"
|
|
424
424
|
namespace WNamespace
|
|
425
425
|
namespace_scope [W14Namespace, W15Namespace]
|
|
426
426
|
|
|
@@ -655,7 +655,7 @@ class Settings < Lutaml::Model::Serializable
|
|
|
655
655
|
attribute :w15_doc_id, W15DocId
|
|
656
656
|
|
|
657
657
|
xml do
|
|
658
|
-
|
|
658
|
+
element "settings"
|
|
659
659
|
namespace WNamespace
|
|
660
660
|
namespace_scope [W14Namespace, W15Namespace]
|
|
661
661
|
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Opal Usage Guide
|
|
3
|
+
parent: Guides
|
|
4
|
+
nav_order: 99
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
= Opal Usage Guide
|
|
8
|
+
|
|
9
|
+
:toc:
|
|
10
|
+
:toclevels: 3
|
|
11
|
+
|
|
12
|
+
== Overview
|
|
13
|
+
|
|
14
|
+
https://opalrb.com[Opal] is a Ruby-to-JavaScript compiler that allows Ruby code to run in the browser. Lutaml::Model supports running under Opal, enabling XML/JSON/YAML serialization in client-side applications.
|
|
15
|
+
|
|
16
|
+
The XML parsing layer is provided by the https://github.com/lutaml/moxml[moxml] gem (v0.2+), which detects the Opal runtime and uses the REXML adapter automatically.
|
|
17
|
+
|
|
18
|
+
== How it works
|
|
19
|
+
|
|
20
|
+
Opal compiles Ruby source code to JavaScript. Under Opal:
|
|
21
|
+
|
|
22
|
+
. `RUBY_ENGINE` equals `"opal"`
|
|
23
|
+
. `Lutaml::Model::RuntimeCompatibility.opal?` returns `true`
|
|
24
|
+
. Adapter selection is adjusted automatically
|
|
25
|
+
. Gems with C extensions (Nokogiri, Ox, Oj, tomlib) are not available
|
|
26
|
+
. REXML is used for XML because Opal reimplements `strscan` and `stringio` in its stdlib, enabling REXML (pure Ruby) to compile cleanly to JavaScript
|
|
27
|
+
|
|
28
|
+
No configuration is needed -- the runtime is detected and adapters are selected automatically.
|
|
29
|
+
|
|
30
|
+
== Supported features
|
|
31
|
+
|
|
32
|
+
=== Fully supported
|
|
33
|
+
|
|
34
|
+
* Model definition with `attribute`, types, collections, defaults
|
|
35
|
+
* XML serialization/deserialization (via REXML adapter)
|
|
36
|
+
** Element and attribute mapping
|
|
37
|
+
** Nested elements and collections
|
|
38
|
+
** Mixed content
|
|
39
|
+
** Namespaces (parsing and serialization)
|
|
40
|
+
** CDATA sections
|
|
41
|
+
** Processing instructions
|
|
42
|
+
** Comments
|
|
43
|
+
** Entity references
|
|
44
|
+
** XML declarations and doctypes
|
|
45
|
+
* JSON serialization/deserialization (via standard adapter)
|
|
46
|
+
* YAML serialization/deserialization (via standard adapter)
|
|
47
|
+
* Hash transformation
|
|
48
|
+
* Custom types
|
|
49
|
+
* Model import (`import_model`)
|
|
50
|
+
* Value transformations
|
|
51
|
+
|
|
52
|
+
=== Not available
|
|
53
|
+
|
|
54
|
+
[cols="1,3",options="header"]
|
|
55
|
+
|===
|
|
56
|
+
| Feature | Reason
|
|
57
|
+
| XSD schema generation | Requires Nokogiri
|
|
58
|
+
| RELAX NG generation | Requires Nokogiri
|
|
59
|
+
| XML schema compilation | Requires Nokogiri + native parsing
|
|
60
|
+
| TOML serialization | Both tomlib and toml-rb require native extensions
|
|
61
|
+
| Oj / MultiJson adapters | Require native extensions
|
|
62
|
+
| Ox / Nokogiri / Oga adapters | Require native extensions or C extensions
|
|
63
|
+
| XPath queries | REXML XPath requires features not yet in Opal's stdlib
|
|
64
|
+
| Liquid templating | Uses file-system-based template loading (Phase 1: skipped)
|
|
65
|
+
| Canon XML equivalence | Uses Nokogiri for XML parsing
|
|
66
|
+
|===
|
|
67
|
+
|
|
68
|
+
== Setup
|
|
69
|
+
|
|
70
|
+
=== Gemfile
|
|
71
|
+
|
|
72
|
+
Add Opal gems to your Gemfile:
|
|
73
|
+
|
|
74
|
+
[source,ruby]
|
|
75
|
+
----
|
|
76
|
+
gem "lutaml-model"
|
|
77
|
+
|
|
78
|
+
group :opal do
|
|
79
|
+
gem "opal", "~> 1.8"
|
|
80
|
+
gem "opal-rspec", "~> 1.0"
|
|
81
|
+
gem "opal-sprockets"
|
|
82
|
+
end
|
|
83
|
+
----
|
|
84
|
+
|
|
85
|
+
=== Rake task
|
|
86
|
+
|
|
87
|
+
Add an Opal RSpec task to your Rakefile:
|
|
88
|
+
|
|
89
|
+
[source,ruby]
|
|
90
|
+
----
|
|
91
|
+
begin
|
|
92
|
+
require "opal/rspec/rake_task"
|
|
93
|
+
rescue LoadError
|
|
94
|
+
# Opal not available
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
namespace :spec do
|
|
98
|
+
if defined?(Opal::RSpec::RakeTask)
|
|
99
|
+
desc "Run Opal (JavaScript) tests"
|
|
100
|
+
Opal::RSpec::RakeTask.new(:opal) do |server, runner|
|
|
101
|
+
server.append_path "lib"
|
|
102
|
+
runner.default_path = "spec"
|
|
103
|
+
runner.pattern = "spec/**/*_spec.{rb,opal}"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
----
|
|
108
|
+
|
|
109
|
+
=== Test configuration
|
|
110
|
+
|
|
111
|
+
Create `spec/support/opal.rb` for Opal-specific test patches:
|
|
112
|
+
|
|
113
|
+
[source,ruby]
|
|
114
|
+
----
|
|
115
|
+
# frozen_string_literal: true
|
|
116
|
+
|
|
117
|
+
if RUBY_ENGINE == "opal"
|
|
118
|
+
Lutaml::Model::Config.xml_adapter_type = :rexml
|
|
119
|
+
end
|
|
120
|
+
----
|
|
121
|
+
|
|
122
|
+
Create `.rspec-opal`:
|
|
123
|
+
|
|
124
|
+
----
|
|
125
|
+
--default-path=spec
|
|
126
|
+
--pattern='spec/**/*_spec.{rb,opal}'
|
|
127
|
+
-I lib
|
|
128
|
+
--opal-opt=-g,lutaml-model
|
|
129
|
+
-I spec
|
|
130
|
+
--require=spec_helper
|
|
131
|
+
--require=support/opal
|
|
132
|
+
----
|
|
133
|
+
|
|
134
|
+
=== CI workflow
|
|
135
|
+
|
|
136
|
+
Add `.github/workflows/opal.yml`:
|
|
137
|
+
|
|
138
|
+
[source,yaml]
|
|
139
|
+
----
|
|
140
|
+
name: opal
|
|
141
|
+
on:
|
|
142
|
+
push:
|
|
143
|
+
branches: [main]
|
|
144
|
+
pull_request:
|
|
145
|
+
|
|
146
|
+
jobs:
|
|
147
|
+
test:
|
|
148
|
+
runs-on: ubuntu-latest
|
|
149
|
+
steps:
|
|
150
|
+
- uses: actions/checkout@v4
|
|
151
|
+
with:
|
|
152
|
+
submodules: "recursive"
|
|
153
|
+
- uses: ruby/setup-ruby@v1
|
|
154
|
+
with:
|
|
155
|
+
ruby-version: "3.3"
|
|
156
|
+
bundler-cache: true
|
|
157
|
+
- uses: actions/setup-node@v4
|
|
158
|
+
with:
|
|
159
|
+
node-version: "18"
|
|
160
|
+
- name: Run Opal tests
|
|
161
|
+
run: bundle exec rake spec:opal
|
|
162
|
+
----
|
|
163
|
+
|
|
164
|
+
== Example: XML round-trip in the browser
|
|
165
|
+
|
|
166
|
+
[source,ruby]
|
|
167
|
+
----
|
|
168
|
+
class Person
|
|
169
|
+
include Lutaml::Model::Serialize
|
|
170
|
+
|
|
171
|
+
attribute :name, :string
|
|
172
|
+
attribute :age, :integer
|
|
173
|
+
|
|
174
|
+
xml do
|
|
175
|
+
element "person"
|
|
176
|
+
map_element "name", to: :name
|
|
177
|
+
map_element "age", to: :age
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Parse XML
|
|
182
|
+
person = Person.from_xml('<person><name>Alice</name><age>30</age></person>')
|
|
183
|
+
person.name # => "Alice"
|
|
184
|
+
person.age # => 30
|
|
185
|
+
|
|
186
|
+
# Serialize to XML
|
|
187
|
+
person.to_xml # => "<person><name>Alice</name><age>30</age></person>"
|
|
188
|
+
|
|
189
|
+
# JSON and YAML also work
|
|
190
|
+
person.to_json # => '{"name":"Alice","age":30}'
|
|
191
|
+
person.to_yaml # => "---\nname: Alice\nage: 30\n"
|
|
192
|
+
----
|
|
193
|
+
|
|
194
|
+
== Architecture
|
|
195
|
+
|
|
196
|
+
Under Opal, the library uses a JRuby-like pattern for dependency management:
|
|
197
|
+
|
|
198
|
+
. *Dependencies stay in the gemspec.* Gems like Nokogiri, Ox, and Oga remain listed as dependencies -- they are simply not loadable under Opal.
|
|
199
|
+
. *Requires are guarded with `RUBY_ENGINE`.* Code that depends on native gems uses `RUBY_ENGINE == "opal"` checks instead of silent `rescue LoadError`.
|
|
200
|
+
. *No gem splitting.* The same gem works on both MRI and Opal.
|
|
201
|
+
|
|
202
|
+
Key components:
|
|
203
|
+
* `Lutaml::Model::RuntimeCompatibility` -- detects the runtime (opal, windows, native)
|
|
204
|
+
* `Lutaml::Model::AdapterResolver` -- selects adapters based on runtime capabilities
|
|
205
|
+
* `Moxml::Config::OPAL_DEFAULT_ADAPTER` -- set to `:rexml`
|
|
206
|
+
* `Moxml::Adapter::OPAL_AVAILABLE_ADAPTERS` -- set to `%i[rexml]`
|
|
207
|
+
|
|
208
|
+
== Dependencies
|
|
209
|
+
|
|
210
|
+
The following gems in the lutaml ecosystem support Opal:
|
|
211
|
+
|
|
212
|
+
* **lutaml-model** -- Core model library (this gem)
|
|
213
|
+
* **moxml** (v0.2+) -- XML parsing abstraction with REXML adapter for Opal
|
|
214
|
+
* **canon** -- XML comparison (uses moxml under Opal; comparison features work but Nokogiri-specific features do not)
|
|
215
|
+
|
|
216
|
+
== Limitations
|
|
217
|
+
|
|
218
|
+
* **No XPath** -- REXML's XPath module has dependencies not yet reimplemented in Opal's stdlib.
|
|
219
|
+
* **No schema generation** -- XSD, RELAX NG generation, and schema compilation require Nokogiri.
|
|
220
|
+
* **No TOML** -- Both TOML adapters (tomlib, toml-rb) require native extensions.
|
|
221
|
+
* **No Liquid** -- Template rendering via Liquid is skipped in Opal (file-system dependency). May be addressed in a future phase using liquidjs via Opal's JavaScript bridge.
|
|
@@ -275,7 +275,7 @@ class Event < Lutaml::Model::Serializable
|
|
|
275
275
|
attribute :event_date, MultiFormatDate
|
|
276
276
|
|
|
277
277
|
xml do
|
|
278
|
-
|
|
278
|
+
element "event"
|
|
279
279
|
map_element "eventDate", to: :event_date
|
|
280
280
|
end
|
|
281
281
|
|
|
@@ -402,7 +402,7 @@ class Document < Lutaml::Model::Serializable
|
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
xml do
|
|
405
|
-
|
|
405
|
+
element "document"
|
|
406
406
|
map_element "pubDate", to: :publication_date
|
|
407
407
|
end
|
|
408
408
|
|
|
@@ -505,7 +505,7 @@ class Document < Lutaml::Model::Serializable
|
|
|
505
505
|
|
|
506
506
|
# XML wants YYYYMMDD
|
|
507
507
|
xml do
|
|
508
|
-
|
|
508
|
+
element "document"
|
|
509
509
|
map_element "pubDate", to: :publication_date, transform: {
|
|
510
510
|
export: ->(date) { date&.strftime("%Y%m%d") },
|
|
511
511
|
import: ->(str) {
|
|
@@ -749,7 +749,7 @@ class Product < Lutaml::Model::Serializable
|
|
|
749
749
|
end
|
|
750
750
|
|
|
751
751
|
xml do
|
|
752
|
-
|
|
752
|
+
element "product"
|
|
753
753
|
map_element "name", to: :name, transform: {
|
|
754
754
|
export: ->(value) { "XML:#{value}" },
|
|
755
755
|
import: ->(value) { value.gsub("XML:", "") }
|
|
@@ -898,7 +898,7 @@ class Event < Lutaml::Model::Serializable
|
|
|
898
898
|
attribute :name, :string
|
|
899
899
|
|
|
900
900
|
xml do
|
|
901
|
-
|
|
901
|
+
element "event"
|
|
902
902
|
map_element "eventDate", to: :event_date
|
|
903
903
|
map_element "name", to: :name
|
|
904
904
|
end
|
|
@@ -1029,7 +1029,7 @@ class Schedule < Lutaml::Model::Serializable
|
|
|
1029
1029
|
attribute :activity, :string
|
|
1030
1030
|
|
|
1031
1031
|
xml do
|
|
1032
|
-
|
|
1032
|
+
element "schedule"
|
|
1033
1033
|
map_element "date", to: :week_date
|
|
1034
1034
|
map_element "activity", to: :activity
|
|
1035
1035
|
end
|
|
@@ -1100,7 +1100,7 @@ class BlogPost < Lutaml::Model::Serializable
|
|
|
1100
1100
|
|
|
1101
1101
|
# XML API requires YYYYMMDD
|
|
1102
1102
|
xml do
|
|
1103
|
-
|
|
1103
|
+
element "post"
|
|
1104
1104
|
map_element "publishDate", to: :published_on, transform: {
|
|
1105
1105
|
export: ->(date) { date&.strftime("%Y%m%d") },
|
|
1106
1106
|
import: ->(str) {
|
|
@@ -185,7 +185,7 @@ class NamespacedItem < Lutaml::Model::Serializable
|
|
|
185
185
|
attribute :alt_name, SecondNamespacedName # SecondNamespace
|
|
186
186
|
|
|
187
187
|
xml do
|
|
188
|
-
|
|
188
|
+
element "item"
|
|
189
189
|
namespace SecondNamespace # Root uses SecondNamespace
|
|
190
190
|
map_element "name", to: :name
|
|
191
191
|
map_element "alt_name", to: :alt_name
|
|
@@ -43,7 +43,7 @@ class NativeItem < Lutaml::Model::Serializable
|
|
|
43
43
|
attribute :name, :string # Native type with no inherent namespace
|
|
44
44
|
|
|
45
45
|
xml do
|
|
46
|
-
|
|
46
|
+
element "first_item"
|
|
47
47
|
namespace FirstItemNamespace
|
|
48
48
|
map_element "name", to: :name
|
|
49
49
|
end
|
|
@@ -97,7 +97,7 @@ class NamespacedItem < Lutaml::Model::Serializable
|
|
|
97
97
|
attribute :alt_name, SecondNamespacedName # Has SecondNamespace
|
|
98
98
|
|
|
99
99
|
xml do
|
|
100
|
-
|
|
100
|
+
element "second_item"
|
|
101
101
|
namespace SecondNamespace
|
|
102
102
|
map_element "name", to: :name
|
|
103
103
|
map_element "alt_name", to: :alt_name
|
|
@@ -127,7 +127,7 @@ class NestedItem < Lutaml::Model::Serializable
|
|
|
127
127
|
attribute :value, :string
|
|
128
128
|
|
|
129
129
|
xml do
|
|
130
|
-
|
|
130
|
+
element "nested"
|
|
131
131
|
namespace FirstNamespace
|
|
132
132
|
map_element "value", to: :value
|
|
133
133
|
end
|
|
@@ -137,7 +137,7 @@ class Container < Lutaml::Model::Serializable
|
|
|
137
137
|
attribute :item, NestedItem
|
|
138
138
|
|
|
139
139
|
xml do
|
|
140
|
-
|
|
140
|
+
element "container"
|
|
141
141
|
namespace SecondNamespace
|
|
142
142
|
map_element "item", to: :item
|
|
143
143
|
end
|
|
@@ -162,7 +162,7 @@ end
|
|
|
162
162
|
[source,ruby]
|
|
163
163
|
----
|
|
164
164
|
xml do
|
|
165
|
-
|
|
165
|
+
element "item"
|
|
166
166
|
namespace MyNamespace
|
|
167
167
|
map_attribute "id", to: :id # Unqualified (no namespace)
|
|
168
168
|
map_element "name", to: :name # Qualified (uses MyNamespace)
|
|
@@ -200,7 +200,7 @@ class Item < Lutaml::Model::Serializable
|
|
|
200
200
|
attribute :value, :integer
|
|
201
201
|
|
|
202
202
|
xml do
|
|
203
|
-
|
|
203
|
+
element "item"
|
|
204
204
|
namespace MyNamespace
|
|
205
205
|
map_attribute "id", to: :id
|
|
206
206
|
map_attribute "value", to: :value
|
|
@@ -280,7 +280,7 @@ class Spacing < Lutaml::Model::Serializable
|
|
|
280
280
|
attribute :before, :integer
|
|
281
281
|
|
|
282
282
|
xml do
|
|
283
|
-
|
|
283
|
+
element "spacing"
|
|
284
284
|
namespace WordProcessingML
|
|
285
285
|
map_attribute "val", to: :val
|
|
286
286
|
map_attribute "after", to: :after
|
|
@@ -330,7 +330,7 @@ class Item < Lutaml::Model::Serializable
|
|
|
330
330
|
attribute :explicit, :string
|
|
331
331
|
|
|
332
332
|
xml do
|
|
333
|
-
|
|
333
|
+
element "item"
|
|
334
334
|
namespace Namespace1
|
|
335
335
|
map_attribute "normal", to: :normal # Uses form_default (unqualified)
|
|
336
336
|
map_attribute "explicit", to: :explicit, namespace: Namespace2 # Explicit wins
|
|
@@ -397,7 +397,7 @@ class NativeItemNames < Lutaml::Model::Serializable
|
|
|
397
397
|
attribute :name, :string, collection: true
|
|
398
398
|
|
|
399
399
|
xml do
|
|
400
|
-
|
|
400
|
+
element "item_names"
|
|
401
401
|
namespace FirstItemNamespace
|
|
402
402
|
map_element "name", to: :name
|
|
403
403
|
end
|
|
@@ -422,7 +422,7 @@ class NativeItemCollection < Lutaml::Model::Serializable
|
|
|
422
422
|
attribute :items, NativeItem, collection: true
|
|
423
423
|
|
|
424
424
|
xml do
|
|
425
|
-
|
|
425
|
+
element "items"
|
|
426
426
|
namespace FirstItemNamespace
|
|
427
427
|
map_element "item", to: :items
|
|
428
428
|
end
|
|
@@ -441,7 +441,7 @@ class Container < Lutaml::Model::Serializable
|
|
|
441
441
|
attribute :items, BaseItem, collection: true, polymorphic: [TypeA, TypeB]
|
|
442
442
|
|
|
443
443
|
xml do
|
|
444
|
-
|
|
444
|
+
element "container"
|
|
445
445
|
map_element "item", to: :items
|
|
446
446
|
end
|
|
447
447
|
end
|
|
@@ -557,7 +557,7 @@ class Child < Lutaml::Model::Serializable
|
|
|
557
557
|
attribute :value, :string
|
|
558
558
|
|
|
559
559
|
xml do
|
|
560
|
-
|
|
560
|
+
element "child"
|
|
561
561
|
# No namespace declared - will inherit from parent
|
|
562
562
|
map_element "value", to: :value
|
|
563
563
|
end
|
|
@@ -567,7 +567,7 @@ class Parent < Lutaml::Model::Serializable
|
|
|
567
567
|
attribute :child, Child
|
|
568
568
|
|
|
569
569
|
xml do
|
|
570
|
-
|
|
570
|
+
element "parent"
|
|
571
571
|
namespace ParentNamespace
|
|
572
572
|
map_element "child", to: :child
|
|
573
573
|
end
|
|
@@ -594,7 +594,7 @@ class Wrapper < Lutaml::Model::Serializable
|
|
|
594
594
|
attribute :items, NamespacedItem, collection: true
|
|
595
595
|
|
|
596
596
|
xml do
|
|
597
|
-
|
|
597
|
+
element "wrapper"
|
|
598
598
|
namespace WrapperNamespace
|
|
599
599
|
map_element "item", to: :items
|
|
600
600
|
end
|
|
@@ -635,7 +635,7 @@ class PlainItem < Lutaml::Model::Serializable
|
|
|
635
635
|
attribute :name, :string
|
|
636
636
|
|
|
637
637
|
xml do
|
|
638
|
-
|
|
638
|
+
element "item"
|
|
639
639
|
# No namespace declared
|
|
640
640
|
map_element "name", to: :name
|
|
641
641
|
end
|