lutaml-model 0.3.5 → 0.3.6
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 +4 -3
- data/README.adoc +424 -19
- data/lib/lutaml/model/config.rb +82 -1
- data/lib/lutaml/model/error/unknown_adapter_type_error.rb +16 -0
- data/lib/lutaml/model/error.rb +1 -0
- data/lib/lutaml/model/serialize.rb +4 -6
- data/lib/lutaml/model/type.rb +0 -24
- data/lib/lutaml/model/version.rb +1 -1
- data/lib/lutaml/model.rb +2 -2
- metadata +3 -3
- data/lib/lutaml/model/type/json.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45fc67305626a2fe4d38db31804a618fd887a9855ff8ddb7ef2d0d0e8cc8b9b8
|
4
|
+
data.tar.gz: 7ffbc1f1b1958d05ebbf82889b1b54f032df48a4ed9052f3a963b896bfb43238
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 37a4e64c1b61b6a1112329b18ea4bd9b544fe125859cd81a1054b162817e1ffb22e03f4168836ce8890e6f6cba6f137c78110522322c0a0fe6fde3e121c533d4
|
7
|
+
data.tar.gz: bd3bfb6600103c25034c3604fcdc83947d8eb3a14b23ec54e93186f66d273a5270a923394bd5f00e5caf3d58c9ff7c65efce9cd5f7a874ac22f4582711622d27
|
data/.rubocop_todo.yml
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# This configuration was generated by
|
2
2
|
# `rubocop --auto-gen-config`
|
3
|
-
# on 2024-08-
|
3
|
+
# on 2024-08-19 03:44:26 UTC using RuboCop version 1.65.1.
|
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
|
@@ -14,7 +14,7 @@ Gemspec/RequireMFA:
|
|
14
14
|
Exclude:
|
15
15
|
- 'lutaml-model.gemspec'
|
16
16
|
|
17
|
-
# Offense count:
|
17
|
+
# Offense count: 78
|
18
18
|
# This cop supports safe autocorrection (--autocorrect).
|
19
19
|
# Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
|
20
20
|
# URISchemes: http, https
|
@@ -28,6 +28,7 @@ Layout/LineLength:
|
|
28
28
|
- 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
|
29
29
|
- 'lib/lutaml/model/xml_adapter/xml_document.rb'
|
30
30
|
- 'spec/lutaml/model/comparable_model_spec.rb'
|
31
|
+
- 'spec/lutaml/model/custom_serialization_spec.rb'
|
31
32
|
- 'spec/lutaml/model/delegation_spec.rb'
|
32
33
|
- 'spec/lutaml/model/schema/json_schema_spec.rb'
|
33
34
|
- 'spec/lutaml/model/serializable_spec.rb'
|
@@ -102,7 +103,7 @@ RSpec/ContextWording:
|
|
102
103
|
- 'spec/lutaml/model/xml_adapter/oga_adapter_spec.rb'
|
103
104
|
- 'spec/lutaml/model/xml_adapter/ox_adapter_spec.rb'
|
104
105
|
|
105
|
-
# Offense count:
|
106
|
+
# Offense count: 73
|
106
107
|
# Configuration parameters: CountAsOne.
|
107
108
|
RSpec/ExampleLength:
|
108
109
|
Max: 57
|
data/README.adoc
CHANGED
@@ -13,16 +13,13 @@ objects to and from various formats such as JSON, XML, YAML, and TOML. It uses
|
|
13
13
|
an adapter pattern to support multiple libraries for each format, providing
|
14
14
|
flexibility and extensibility for your data modeling needs.
|
15
15
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
16
|
+
NOTE: Lutaml::Model is designed to be mostly compatible with the data modeling
|
17
|
+
API of https://www.shalerb.org[Shale], an impressive Ruby data modeller.
|
18
|
+
Lutaml::Model is meant to address advanced needs not currently addressed by
|
19
|
+
Shale.
|
21
20
|
|
22
|
-
NOTE:
|
23
|
-
|
24
|
-
modeller. Lutaml::Model is meant to address needs that are not currently
|
25
|
-
addressed by Shale.
|
21
|
+
NOTE: Instructions on how to migrate from Shale to Lutaml::Model are provided in
|
22
|
+
<<migrate-from-shale>>.
|
26
23
|
|
27
24
|
|
28
25
|
== Data modeling in a nutshell
|
@@ -80,6 +77,7 @@ There are two ways to define a data model in Lutaml::Model:
|
|
80
77
|
* Inheriting from the `Lutaml::Model::Serializable` class
|
81
78
|
* Including the `Lutaml::Model::Serialize` module
|
82
79
|
|
80
|
+
[[define-through-inheritance]]
|
83
81
|
==== Definition through inheritance
|
84
82
|
|
85
83
|
The simplest way to define a model is to create a class that inherits from
|
@@ -98,6 +96,7 @@ class Kiln < Lutaml::Model::Serializable
|
|
98
96
|
end
|
99
97
|
----
|
100
98
|
|
99
|
+
[[define-through-inclusion]]
|
101
100
|
==== Definition through inclusion
|
102
101
|
|
103
102
|
If the model class already has a super class that it inherits from, the model
|
@@ -169,12 +168,6 @@ attribute :name_of_attribute, {symbol | string | class}
|
|
169
168
|
| `Boolean` | `:boolean` | `Lutaml::Model::Type::Boolean` | `Boolean`
|
170
169
|
| `Decimal` | `:decimal` | `Lutaml::Model::Type::Decimal` | `::BigDecimal`
|
171
170
|
| `Hash` | `:hash` | `Lutaml::Model::Type::Hash` | `::Hash`
|
172
|
-
| `Uuid` | `:uuid` | `Lutaml::Model::Type::Uuid` | `::String`
|
173
|
-
| `Symbol` | `:symbol` | `Lutaml::Model::Type::Symbol` | `Symbol`
|
174
|
-
| `Binary` | `:binary` | `Lutaml::Model::Type::Binary` | `Binary`
|
175
|
-
| `Url` | `:url` | `Lutaml::Model::Type::Url` | `::URI`
|
176
|
-
| `IpAddress` | `:ip_address` | `Lutaml::Model::Type::IpAddress` | `::IPAddr`
|
177
|
-
| `Json` | `:json` | `Lutaml::Model::Type::Json` | `::JSON`
|
178
171
|
|
179
172
|
|===
|
180
173
|
|
@@ -238,6 +231,7 @@ end
|
|
238
231
|
----
|
239
232
|
====
|
240
233
|
|
234
|
+
[[attribute-enumeration]]
|
241
235
|
=== Attribute as an enumeration
|
242
236
|
|
243
237
|
An attribute can be defined as an enumeration by using the `values` directive.
|
@@ -777,6 +771,7 @@ end
|
|
777
771
|
----
|
778
772
|
====
|
779
773
|
|
774
|
+
[[namespace-inherit]]
|
780
775
|
===== Namespace with `inherit` option
|
781
776
|
|
782
777
|
The `inherit` option is used at the element level to inherit the namespace from
|
@@ -838,7 +833,7 @@ end
|
|
838
833
|
----
|
839
834
|
====
|
840
835
|
|
841
|
-
|
836
|
+
[[mixed-content]]
|
842
837
|
==== Mixed content
|
843
838
|
|
844
839
|
===== General
|
@@ -859,6 +854,8 @@ To map this to Lutaml::Model we can use the `mixed` option in either way:
|
|
859
854
|
* when defining the model;
|
860
855
|
* when referencing the model.
|
861
856
|
|
857
|
+
NOTE: This feature is not supported by Shale.
|
858
|
+
|
862
859
|
|
863
860
|
===== Specifying the `mixed` option at `root`
|
864
861
|
|
@@ -1071,7 +1068,7 @@ end
|
|
1071
1068
|
----
|
1072
1069
|
====
|
1073
1070
|
|
1074
|
-
|
1071
|
+
[[separate-serialization-model]]
|
1075
1072
|
=== Separate serialization model
|
1076
1073
|
|
1077
1074
|
The `Serialize` module can be used to define only serialization mappings for a
|
@@ -1283,6 +1280,9 @@ end
|
|
1283
1280
|
----
|
1284
1281
|
====
|
1285
1282
|
|
1283
|
+
NOTE: The corresponding keyword used by Shale is `receiver:` instead of
|
1284
|
+
`delegate:`.
|
1285
|
+
|
1286
1286
|
|
1287
1287
|
==== Attribute serialization with custom methods
|
1288
1288
|
|
@@ -1365,7 +1365,7 @@ end
|
|
1365
1365
|
====
|
1366
1366
|
|
1367
1367
|
|
1368
|
-
|
1368
|
+
[[attribute-extraction]]
|
1369
1369
|
==== Attribute extraction
|
1370
1370
|
|
1371
1371
|
NOTE: This feature is for key-value data model serialization only.
|
@@ -1561,7 +1561,7 @@ serialization format.
|
|
1561
1561
|
You will need to specify the configuration for the adapter you want to use. The
|
1562
1562
|
easiest way is to copy and paste the following configuration into your code.
|
1563
1563
|
|
1564
|
-
The
|
1564
|
+
The configuration is as follows:
|
1565
1565
|
|
1566
1566
|
[source,ruby]
|
1567
1567
|
----
|
@@ -1579,6 +1579,21 @@ Lutaml::Model::Config.configure do |config|
|
|
1579
1579
|
end
|
1580
1580
|
----
|
1581
1581
|
|
1582
|
+
You can also provide the adapter type by using symbols like
|
1583
|
+
|
1584
|
+
[source,ruby]
|
1585
|
+
----
|
1586
|
+
require 'lutaml/model'
|
1587
|
+
|
1588
|
+
Lutaml::Model::Config.configure do |config|
|
1589
|
+
config.xml_adapter_type = :nokogiri # can be one of [:nokogiri, :ox, :oga]
|
1590
|
+
config.yaml_adapter_type = :standard_yaml
|
1591
|
+
config.json_adapter_type = :standard_json # can be one of [:standard_json, :multi_json]
|
1592
|
+
config.toml_adapter_type = :toml_rb # can be one of [:toml_rb, :tomlib]
|
1593
|
+
end
|
1594
|
+
----
|
1595
|
+
|
1596
|
+
NOTE: By default `yaml_adapter_type` and `json_adapter_type` are set to `:standard_yaml` and `:standard_json` respectively.
|
1582
1597
|
|
1583
1598
|
=== XML
|
1584
1599
|
|
@@ -1699,6 +1714,396 @@ Lutaml::Model::Config.configure do |config|
|
|
1699
1714
|
end
|
1700
1715
|
----
|
1701
1716
|
|
1717
|
+
|
1718
|
+
== Comparison with Shale
|
1719
|
+
|
1720
|
+
Lutaml::Model is a serialization library that is similar to Shale, but with some
|
1721
|
+
differences in implementation.
|
1722
|
+
|
1723
|
+
[cols="a,a,a,a",options="header"]
|
1724
|
+
|===
|
1725
|
+
| Feature | Lutaml::Model | Shale | Notes
|
1726
|
+
|
1727
|
+
| Data model definition
|
1728
|
+
|
|
1729
|
+
3 types:
|
1730
|
+
|
1731
|
+
* <<define-through-inheritance,Inherit from `Lutaml::Model::Serializable`>>
|
1732
|
+
* <<define-through-inclusion,Include `Lutaml::Model::Serialize`>>
|
1733
|
+
* <<separate-serialization-model,Separate serialization model class>>
|
1734
|
+
|
|
1735
|
+
2 types:
|
1736
|
+
|
1737
|
+
* Inherit from `Shale::Mapper`
|
1738
|
+
* Custom model class
|
1739
|
+
|
|
1740
|
+
|
1741
|
+
| Value types
|
1742
|
+
| `Lutaml::Model::Type` includes: `Integer`, `String`, `Float`, `Boolean`, `Date`, `DateTime`, `Time`, `Hash`.
|
1743
|
+
| `Shale::Type` includes: `Integer`, `String`, `Float`, `Boolean`, `Date`, `Time`.
|
1744
|
+
| Lutaml::Model supports the additional value types `DateTime` and `Hash`.
|
1745
|
+
|
1746
|
+
| Configuration
|
1747
|
+
| `Lutaml::Model::Config`
|
1748
|
+
| `Shale.{type}_adapter`
|
1749
|
+
| Lutaml::Model uses a configuration block to set the serialization adapters.
|
1750
|
+
|
1751
|
+
| Custom serialization methods
|
1752
|
+
| `:with`, on individual attributes
|
1753
|
+
| `:using`, on entire object/document
|
1754
|
+
| Lutaml::Model uses the `:with` keyword for custom serialization methods.
|
1755
|
+
|
1756
|
+
| Serialization formats
|
1757
|
+
| XML, YAML, JSON, TOML
|
1758
|
+
| XML, YAML, JSON, TOML, CSV
|
1759
|
+
| Lutaml::Model does not support CSV.
|
1760
|
+
|
1761
|
+
| Adapter support
|
1762
|
+
| XML (Nokogiri, Ox, Oga), YAML, JSON (JSON, MultiJson), TOML (Toml-rb, Tomlib)
|
1763
|
+
| XML (Nokogiri, Ox), YAML, JSON (JSON, MultiJson), TOML (Toml-rb, Tomlib), CSV
|
1764
|
+
| Lutaml::Model does not support CSV.
|
1765
|
+
|
1766
|
+
4+h| XML features
|
1767
|
+
|
1768
|
+
| XML mixed content support
|
1769
|
+
| Yes. Supports the following kind of XML through <<mixed-content,mixed content>> support.
|
1770
|
+
|
1771
|
+
[source,xml]
|
1772
|
+
----
|
1773
|
+
<description>My name is
|
1774
|
+
<bold>John Doe</bold>,
|
1775
|
+
and I'm <i>28</i>
|
1776
|
+
years old</description>
|
1777
|
+
----
|
1778
|
+
| No. Shale's `map_content` only supports the first text node.
|
1779
|
+
|
|
1780
|
+
|
1781
|
+
| XML namespace inheritance
|
1782
|
+
| Yes. Supports the <<namespace-inherit,`inherit`>> option to inherit the
|
1783
|
+
namespace from the root element.
|
1784
|
+
| No.
|
1785
|
+
|
|
1786
|
+
|
1787
|
+
4+h| Attribute features
|
1788
|
+
|
1789
|
+
| Attribute delegation
|
1790
|
+
| `:delegate` option to delegate attribute mappings to a model.
|
1791
|
+
| `:receiver` option to delegate attribute mappings to a model.
|
1792
|
+
|
|
1793
|
+
|
1794
|
+
| Enumerations
|
1795
|
+
| Yes. Supports enumerations as value types through the
|
1796
|
+
<<attribute-enumeration,`values:` option>>.
|
1797
|
+
| No.
|
1798
|
+
| Lutaml::Model supports enumerations as value types.
|
1799
|
+
|
1800
|
+
| Attribute extraction
|
1801
|
+
| Yes. Supports <<attribute-extraction,attribute extraction>> from key-value
|
1802
|
+
data models.
|
1803
|
+
| No.
|
1804
|
+
| Lutaml::Model supports attribute extraction from key-value data models.
|
1805
|
+
|
1806
|
+
|
1807
|
+
|===
|
1808
|
+
|
1809
|
+
|
1810
|
+
[[migrate-from-shale]]
|
1811
|
+
== Migration steps from Shale
|
1812
|
+
|
1813
|
+
The following sections provide a guide for migrating from Shale to Lutaml::Model.
|
1814
|
+
|
1815
|
+
=== Step 1: Replace inheritance class
|
1816
|
+
|
1817
|
+
`Lutaml::Model` uses `Lutaml::Model::Serializable` as the base inheritance class.
|
1818
|
+
|
1819
|
+
[source,ruby]
|
1820
|
+
----
|
1821
|
+
class Example < Lutaml::Model::Serializable
|
1822
|
+
# ...
|
1823
|
+
end
|
1824
|
+
----
|
1825
|
+
|
1826
|
+
[NOTE]
|
1827
|
+
====
|
1828
|
+
`Lutaml::Model` also supports an inclusion method as in the following example,
|
1829
|
+
which is not supported by Shale. This is useful for cases where you want to
|
1830
|
+
include the serialization methods in a class that already inherits from another
|
1831
|
+
class.
|
1832
|
+
|
1833
|
+
[source,ruby]
|
1834
|
+
----
|
1835
|
+
class Example
|
1836
|
+
include Lutaml::Model::Serialize
|
1837
|
+
# ...
|
1838
|
+
end
|
1839
|
+
----
|
1840
|
+
====
|
1841
|
+
|
1842
|
+
Shale uses `Shale::Mapper` as the base inheritance class.
|
1843
|
+
|
1844
|
+
[source,ruby]
|
1845
|
+
----
|
1846
|
+
class Example < Shale::Mapper
|
1847
|
+
# ...
|
1848
|
+
end
|
1849
|
+
----
|
1850
|
+
|
1851
|
+
Actions:
|
1852
|
+
|
1853
|
+
* Replace mentions of `Shale::Mapper` with `Lutaml::Model::Serializable`.
|
1854
|
+
* Potentially replace inheritance with inclusion for suitable cases.
|
1855
|
+
|
1856
|
+
|
1857
|
+
=== Step 2: Replace value type definitions
|
1858
|
+
|
1859
|
+
Value types in `Lutaml::Model` are under the `Lutaml::Model::Type` module.
|
1860
|
+
|
1861
|
+
[source,ruby]
|
1862
|
+
----
|
1863
|
+
class Example < Lutaml::Model::Serializable
|
1864
|
+
attribute :length, Lutaml::Model::Type::Integer
|
1865
|
+
attribute :description, Lutaml::Model::Type::String
|
1866
|
+
end
|
1867
|
+
----
|
1868
|
+
|
1869
|
+
[NOTE]
|
1870
|
+
====
|
1871
|
+
`Lutaml::Model` also supports specifying predefined value types as strings or
|
1872
|
+
symbols, which is not supported by Shale.
|
1873
|
+
|
1874
|
+
[source,ruby]
|
1875
|
+
----
|
1876
|
+
class Example < Lutaml::Model::Serializable
|
1877
|
+
attribute :length, :integer
|
1878
|
+
attribute :description, "String"
|
1879
|
+
end
|
1880
|
+
----
|
1881
|
+
====
|
1882
|
+
|
1883
|
+
Value types in Shale are under the `Shale::Type` module.
|
1884
|
+
|
1885
|
+
[source,ruby]
|
1886
|
+
----
|
1887
|
+
class Example < Shale::Mapper
|
1888
|
+
attribute :length, Shale::Type::Integer
|
1889
|
+
attribute :description, Shale::Type::String
|
1890
|
+
end
|
1891
|
+
----
|
1892
|
+
|
1893
|
+
Action:
|
1894
|
+
|
1895
|
+
* Replace mentions of `Shale::Type` with `Lutaml::Model::Type`.
|
1896
|
+
* Potentially replace value type definitions with strings or symbols.
|
1897
|
+
|
1898
|
+
|
1899
|
+
=== Step 3: Configure serialization adapters
|
1900
|
+
|
1901
|
+
`Lutaml::Model` uses a configuration block to set the serialization adapters.
|
1902
|
+
|
1903
|
+
[source,ruby]
|
1904
|
+
----
|
1905
|
+
require 'lutaml/model/xml_adapter/nokogiri_adapter'
|
1906
|
+
Lutaml::Model::Config.configure do |config|
|
1907
|
+
config.xml_adapter = Lutaml::Model::XmlAdapter::NokogiriAdapter
|
1908
|
+
end
|
1909
|
+
----
|
1910
|
+
|
1911
|
+
The equivalent for Shale is this:
|
1912
|
+
|
1913
|
+
[source,ruby]
|
1914
|
+
----
|
1915
|
+
require 'shale/adapter/nokogiri'
|
1916
|
+
Shale.xml_adapter = Shale::Adapter::Nokogiri
|
1917
|
+
----
|
1918
|
+
|
1919
|
+
|
1920
|
+
Here are places that this code may reside at:
|
1921
|
+
|
1922
|
+
* If your code is a standalone Ruby script, this code will be present in your code.
|
1923
|
+
* If your code is organized in a Ruby gem, this code will be specified somewhere referenced by `lib/your_gem_name.rb`.
|
1924
|
+
* If your code contains tests or specs, they will be in the test setup file, e.g. RSpec `spec/spec_helper.rb`.
|
1925
|
+
|
1926
|
+
Actions:
|
1927
|
+
|
1928
|
+
* Replace the Shale configuration block with the `Lutaml::Model::Config`
|
1929
|
+
configuration block.
|
1930
|
+
|
1931
|
+
* Replace the Shale adapter with the `Lutaml::Model` adapter.
|
1932
|
+
|
1933
|
+
|
1934
|
+
|
1935
|
+
=== Step 4: Rewrite custom serialization methods
|
1936
|
+
|
1937
|
+
There is an implementation difference between Lutaml::Model and Shale for custom
|
1938
|
+
serialization methods.
|
1939
|
+
|
1940
|
+
Custom serialization methods in `Lutaml::Model` map to individual attributes.
|
1941
|
+
|
1942
|
+
For custom serialization methods, Lutaml::Model uses the `:with` keyword
|
1943
|
+
instead of the `:using` keyword used by Shale.
|
1944
|
+
|
1945
|
+
[source,ruby]
|
1946
|
+
----
|
1947
|
+
class Example < Lutaml::Model::Serializable
|
1948
|
+
attribute :name, :string
|
1949
|
+
attribute :size, :integer
|
1950
|
+
attribute :color, :string
|
1951
|
+
attribute :description, :string
|
1952
|
+
|
1953
|
+
json do
|
1954
|
+
map "name", to: :name, with: { to: :name_to_json, from: :name_from_json }
|
1955
|
+
map "size", to: :size
|
1956
|
+
map "color", to: :color,
|
1957
|
+
with: { to: :color_to_json, from: :color_from_json }
|
1958
|
+
map "description", to: :description,
|
1959
|
+
with: { to: :description_to_json, from: :description_from_json }
|
1960
|
+
end
|
1961
|
+
|
1962
|
+
xml do
|
1963
|
+
root "CustomSerialization"
|
1964
|
+
map_element "Name", to: :name,
|
1965
|
+
with: { to: :name_to_xml, from: :name_from_xml }
|
1966
|
+
map_attribute "Size", to: :size
|
1967
|
+
map_element "Color", to: :color,
|
1968
|
+
with: { to: :color_to_xml, from: :color_from_xml }
|
1969
|
+
map_content to: :description,
|
1970
|
+
with: { to: :description_to_xml,
|
1971
|
+
from: :description_from_xml }
|
1972
|
+
end
|
1973
|
+
|
1974
|
+
def name_to_json(_model, value)
|
1975
|
+
"JSON Masterpiece: #{value}"
|
1976
|
+
end
|
1977
|
+
|
1978
|
+
def name_from_json(_model, doc)
|
1979
|
+
doc["name"].sub(/^JSON Masterpiece: /, "")
|
1980
|
+
end
|
1981
|
+
|
1982
|
+
def color_to_json(_model, value)
|
1983
|
+
value.upcase
|
1984
|
+
end
|
1985
|
+
|
1986
|
+
def color_from_json(_model, doc)
|
1987
|
+
doc["color"].downcase
|
1988
|
+
end
|
1989
|
+
|
1990
|
+
def description_to_json(_model, value)
|
1991
|
+
"JSON Description: #{value}"
|
1992
|
+
end
|
1993
|
+
|
1994
|
+
def description_from_json(_model, doc)
|
1995
|
+
doc["description"].sub(/^JSON Description: /, "")
|
1996
|
+
end
|
1997
|
+
|
1998
|
+
def name_to_xml(_model, value)
|
1999
|
+
"XML Masterpiece: #{value}"
|
2000
|
+
end
|
2001
|
+
|
2002
|
+
def name_from_xml(_model, value)
|
2003
|
+
value.sub(/^XML Masterpiece: /, "")
|
2004
|
+
end
|
2005
|
+
|
2006
|
+
def color_to_xml(_model, value)
|
2007
|
+
value.upcase
|
2008
|
+
end
|
2009
|
+
|
2010
|
+
def color_from_xml(_model, value)
|
2011
|
+
value.downcase
|
2012
|
+
end
|
2013
|
+
|
2014
|
+
def description_to_xml(_model, value)
|
2015
|
+
"XML Description: #{value}"
|
2016
|
+
end
|
2017
|
+
|
2018
|
+
def description_from_xml(_model, value)
|
2019
|
+
value.sub(/^XML Description: /, "")
|
2020
|
+
end
|
2021
|
+
end
|
2022
|
+
----
|
2023
|
+
|
2024
|
+
Custom serialization methods in Shale do not map to specific attributes, but
|
2025
|
+
allow the user to specify where the data goes.
|
2026
|
+
|
2027
|
+
[source,ruby]
|
2028
|
+
----
|
2029
|
+
class Example < Shale::Mapper
|
2030
|
+
attribute :name, Shale::Type::String
|
2031
|
+
attribute :size, Shale::Type::Integer
|
2032
|
+
attribute :color, Shale::Type::String
|
2033
|
+
attribute :description, Shale::Type::String
|
2034
|
+
|
2035
|
+
json do
|
2036
|
+
map "name", using: { from: :name_from_json, to: :name_to_json }
|
2037
|
+
map "size", to: :size
|
2038
|
+
map "color", using: { from: :color_from_json, to: :color_to_json }
|
2039
|
+
map "description", to: :description, using: { from: :description_from_json, to: :description_to_json }
|
2040
|
+
end
|
2041
|
+
|
2042
|
+
xml do
|
2043
|
+
root "CustomSerialization"
|
2044
|
+
map_element "Name", using: { from: :name_from_xml, to: :name_to_xml }
|
2045
|
+
map_attribute "Size", to: :size
|
2046
|
+
map_element "Color", using: { from: :color_from_xml, to: :color_to_xml }
|
2047
|
+
map_content to: :description, using: { from: :description_from_xml, to: :description_to_xml }
|
2048
|
+
end
|
2049
|
+
|
2050
|
+
def name_to_json(model, doc)
|
2051
|
+
doc['name'] = "JSON Masterpiece: #{model.name}"
|
2052
|
+
end
|
2053
|
+
|
2054
|
+
def name_from_json(model, value)
|
2055
|
+
model.name = value.sub(/^JSON Masterpiece: /, "")
|
2056
|
+
end
|
2057
|
+
|
2058
|
+
def color_to_json(model, doc)
|
2059
|
+
doc['color'] = model.color.upcase
|
2060
|
+
end
|
2061
|
+
|
2062
|
+
def color_from_json(model, doc)
|
2063
|
+
model.color = doc['color'].downcase
|
2064
|
+
end
|
2065
|
+
|
2066
|
+
def description_to_json(model, doc)
|
2067
|
+
doc['description'] = "JSON Description: #{model.description}"
|
2068
|
+
end
|
2069
|
+
|
2070
|
+
def description_from_json(model, doc)
|
2071
|
+
model.description = doc['description'].sub(/^JSON Description: /, "")
|
2072
|
+
end
|
2073
|
+
|
2074
|
+
def name_from_xml(model, node)
|
2075
|
+
model.name = node.text.sub(/^XML Masterpiece: /, "")
|
2076
|
+
end
|
2077
|
+
|
2078
|
+
def name_to_xml(model, parent, doc)
|
2079
|
+
name_element = doc.create_element('Name')
|
2080
|
+
doc.add_text(name_element, model.street.to_s)
|
2081
|
+
doc.add_element(parent, name_element)
|
2082
|
+
end
|
2083
|
+
end
|
2084
|
+
----
|
2085
|
+
|
2086
|
+
NOTE: There are cases where the Shale implementation of custom methods work
|
2087
|
+
differently from the Lutaml::Model implementation. In these cases, you will need
|
2088
|
+
to adjust the custom methods accordingly.
|
2089
|
+
|
2090
|
+
Actions:
|
2091
|
+
|
2092
|
+
* Replace the `using` keyword with the `with` keyword.
|
2093
|
+
* Adjust the custom methods.
|
2094
|
+
|
2095
|
+
|
2096
|
+
== About LutaML
|
2097
|
+
|
2098
|
+
The name "LutaML" is pronounced as "Looh-tah-mel".
|
2099
|
+
|
2100
|
+
The name "LutaML" comes from the Latin word for clay, "Lutum", and "ML"
|
2101
|
+
for "Markup Language". Just as clay can be molded and modeled into beautiful and
|
2102
|
+
practical end products, the Lutaml::Model gem is used for data modeling,
|
2103
|
+
allowing you to shape and structure your data into useful forms.
|
2104
|
+
|
2105
|
+
|
2106
|
+
|
1702
2107
|
== License and Copyright
|
1703
2108
|
|
1704
2109
|
This project is licensed under the BSD 2-clause License.
|
data/lib/lutaml/model/config.rb
CHANGED
@@ -3,11 +3,92 @@ module Lutaml
|
|
3
3
|
module Config
|
4
4
|
extend self
|
5
5
|
|
6
|
-
|
6
|
+
# Default values are set for these so the readers are defined below
|
7
|
+
attr_writer :json_adapter, :yaml_adapter
|
8
|
+
|
9
|
+
attr_accessor :xml_adapter, :toml_adapter
|
10
|
+
|
11
|
+
AVAILABLE_FORMATS = %i[xml json yaml toml].freeze
|
7
12
|
|
8
13
|
def configure
|
9
14
|
yield self
|
10
15
|
end
|
16
|
+
|
17
|
+
# This will generate the following methods
|
18
|
+
#
|
19
|
+
# xml_adapter_type=
|
20
|
+
# @params:
|
21
|
+
# one of [:nokogiri, :ox, :oga]
|
22
|
+
# @example
|
23
|
+
# Lutaml::Model::Config.xml_adapter = :nokogiri
|
24
|
+
#
|
25
|
+
# json_adapter_type=
|
26
|
+
# @params:
|
27
|
+
# one of [:standard_json, :multi_json]
|
28
|
+
# if not set, :standard_json will be used by default
|
29
|
+
# @example
|
30
|
+
# Lutaml::Model::Config.json_adapter = :standard_json
|
31
|
+
#
|
32
|
+
# yaml_adapter_type=
|
33
|
+
# @params:
|
34
|
+
# one of [:standard_yaml]
|
35
|
+
# if not set, :standard_yaml will be used by default
|
36
|
+
# @example
|
37
|
+
# Lutaml::Model::Config.yaml_adapter = :standard_yaml
|
38
|
+
#
|
39
|
+
# toml_adapter_type=
|
40
|
+
# @params
|
41
|
+
# one of [:tomlib, :toml_rb]
|
42
|
+
# @example
|
43
|
+
# Lutaml::Model::Config.toml_adapter = :tomlib
|
44
|
+
AVAILABLE_FORMATS.each do |adapter_name|
|
45
|
+
define_method(:"#{adapter_name}_adapter_type=") do |type_name|
|
46
|
+
adapter = "#{adapter_name}_adapter"
|
47
|
+
type = "#{type_name}_adapter"
|
48
|
+
|
49
|
+
begin
|
50
|
+
adapter_file = File.join(adapter, type)
|
51
|
+
require_relative adapter_file
|
52
|
+
rescue LoadError
|
53
|
+
raise(
|
54
|
+
Lutaml::Model::UnknownAdapterTypeError.new(
|
55
|
+
adapter_name,
|
56
|
+
type_name,
|
57
|
+
),
|
58
|
+
cause: nil,
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
instance_variable_set(
|
63
|
+
:"@#{adapter}",
|
64
|
+
Lutaml::Model.const_get(to_class_name(adapter))
|
65
|
+
.const_get(to_class_name(type)),
|
66
|
+
)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Return JSON adapter. By default StandardJsonAdapter is used
|
71
|
+
#
|
72
|
+
# @example
|
73
|
+
# Lutaml::Model::Config.json_adapter
|
74
|
+
# # => Lutaml::Model::YamlAdapter::StandardJsonAdapter
|
75
|
+
def json_adapter
|
76
|
+
@json_adapter || Lutaml::Model::JsonAdapter::StandardJsonAdapter
|
77
|
+
end
|
78
|
+
|
79
|
+
# Return YAML adapter. By default StandardYamlAdapter is used
|
80
|
+
#
|
81
|
+
# @example
|
82
|
+
# Lutaml::Model::Config.yaml_adapter
|
83
|
+
# # => Lutaml::Model::YamlAdapter::StandardYamlAdapter
|
84
|
+
def yaml_adapter
|
85
|
+
@yaml_adapter || Lutaml::Model::YamlAdapter::StandardYamlAdapter
|
86
|
+
end
|
87
|
+
|
88
|
+
# @api private
|
89
|
+
def to_class_name(str)
|
90
|
+
str.to_s.split("_").map(&:capitalize).join
|
91
|
+
end
|
11
92
|
end
|
12
93
|
end
|
13
94
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Lutaml
|
2
|
+
module Model
|
3
|
+
class UnknownAdapterTypeError < Error
|
4
|
+
def initialize(adapter_name, type_name)
|
5
|
+
@adapter_name = adapter_name
|
6
|
+
@type_name = type_name
|
7
|
+
|
8
|
+
super()
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_s
|
12
|
+
"Unknown type: `#{@type_name}` for `#{@adapter_name}` adapter"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/lutaml/model/error.rb
CHANGED
@@ -13,8 +13,6 @@ require_relative "comparable_model"
|
|
13
13
|
module Lutaml
|
14
14
|
module Model
|
15
15
|
module Serialize
|
16
|
-
FORMATS = %i[xml json yaml toml].freeze
|
17
|
-
|
18
16
|
include ComparableModel
|
19
17
|
|
20
18
|
def self.included(base)
|
@@ -73,7 +71,7 @@ module Lutaml
|
|
73
71
|
attr.options[:values].include?(value)
|
74
72
|
end
|
75
73
|
|
76
|
-
|
74
|
+
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
77
75
|
define_method(format) do |&block|
|
78
76
|
klass = format == :xml ? XmlMapping : KeyValueMapping
|
79
77
|
mappings[format] = klass.new
|
@@ -432,14 +430,14 @@ module Lutaml
|
|
432
430
|
end
|
433
431
|
|
434
432
|
def key_exist?(hash, key)
|
435
|
-
hash.key?(key
|
433
|
+
hash.key?(key.to_sym) || hash.key?(key.to_s)
|
436
434
|
end
|
437
435
|
|
438
436
|
def key_value(hash, key)
|
439
|
-
hash[key
|
437
|
+
hash[key.to_sym] || hash[key.to_s]
|
440
438
|
end
|
441
439
|
|
442
|
-
|
440
|
+
Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
|
443
441
|
define_method(:"to_#{format}") do |options = {}|
|
444
442
|
validate
|
445
443
|
adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
|
data/lib/lutaml/model/type.rb
CHANGED
@@ -1,8 +1,5 @@
|
|
1
1
|
require "date"
|
2
2
|
require "bigdecimal"
|
3
|
-
require "securerandom"
|
4
|
-
require "uri"
|
5
|
-
require "ipaddr"
|
6
3
|
|
7
4
|
module Lutaml
|
8
5
|
module Model
|
@@ -20,12 +17,6 @@ module Lutaml
|
|
20
17
|
Boolean
|
21
18
|
Decimal
|
22
19
|
Hash
|
23
|
-
Uuid
|
24
|
-
Symbol
|
25
|
-
Binary
|
26
|
-
Url
|
27
|
-
IpAddress
|
28
|
-
Json
|
29
20
|
).each do |t|
|
30
21
|
class_eval <<~HEREDOC, __FILE__, __LINE__ + 1
|
31
22
|
class #{t} # class Integer
|
@@ -72,18 +63,6 @@ module Lutaml
|
|
72
63
|
BigDecimal(value.to_s)
|
73
64
|
when "Hash"
|
74
65
|
normalize_hash(Hash(value))
|
75
|
-
when "Uuid"
|
76
|
-
UUID_REGEX.match?(value) ? value : SecureRandom.uuid
|
77
|
-
when "Symbol"
|
78
|
-
value.to_sym
|
79
|
-
when "Binary"
|
80
|
-
value.force_encoding("BINARY")
|
81
|
-
when "Url"
|
82
|
-
URI.parse(value.to_s)
|
83
|
-
when "IpAddress"
|
84
|
-
IPAddr.new(value.to_s)
|
85
|
-
when "Json"
|
86
|
-
Json.cast(value)
|
87
66
|
else
|
88
67
|
value
|
89
68
|
end
|
@@ -107,8 +86,6 @@ module Lutaml
|
|
107
86
|
value.to_s("F")
|
108
87
|
when "Hash"
|
109
88
|
Hash(value)
|
110
|
-
when "Json"
|
111
|
-
value.to_json
|
112
89
|
else
|
113
90
|
value.to_s
|
114
91
|
end
|
@@ -145,4 +122,3 @@ end
|
|
145
122
|
|
146
123
|
require_relative "type/time_without_date"
|
147
124
|
require_relative "type/date_time"
|
148
|
-
require_relative "type/json"
|
data/lib/lutaml/model/version.rb
CHANGED
data/lib/lutaml/model.rb
CHANGED
@@ -3,8 +3,8 @@
|
|
3
3
|
require_relative "model/version"
|
4
4
|
require_relative "model/type"
|
5
5
|
require_relative "model/serializable"
|
6
|
-
require_relative "model/json_adapter"
|
7
|
-
require_relative "model/yaml_adapter"
|
6
|
+
require_relative "model/json_adapter/standard_json_adapter"
|
7
|
+
require_relative "model/yaml_adapter/standard_yaml_adapter"
|
8
8
|
require_relative "model/xml_adapter"
|
9
9
|
require_relative "model/toml_adapter"
|
10
10
|
require_relative "model/error"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lutaml-model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ribose Inc.
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-08-
|
11
|
+
date: 2024-08-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -56,6 +56,7 @@ files:
|
|
56
56
|
- lib/lutaml/model/config.rb
|
57
57
|
- lib/lutaml/model/error.rb
|
58
58
|
- lib/lutaml/model/error/invalid_value_error.rb
|
59
|
+
- lib/lutaml/model/error/unknown_adapter_type_error.rb
|
59
60
|
- lib/lutaml/model/json_adapter.rb
|
60
61
|
- lib/lutaml/model/json_adapter/json_document.rb
|
61
62
|
- lib/lutaml/model/json_adapter/json_object.rb
|
@@ -80,7 +81,6 @@ files:
|
|
80
81
|
- lib/lutaml/model/toml_adapter/tomlib_adapter.rb
|
81
82
|
- lib/lutaml/model/type.rb
|
82
83
|
- lib/lutaml/model/type/date_time.rb
|
83
|
-
- lib/lutaml/model/type/json.rb
|
84
84
|
- lib/lutaml/model/type/time_without_date.rb
|
85
85
|
- lib/lutaml/model/version.rb
|
86
86
|
- lib/lutaml/model/xml_adapter.rb
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require "json"
|
2
|
-
|
3
|
-
module Lutaml
|
4
|
-
module Model
|
5
|
-
module Type
|
6
|
-
# JSON representation
|
7
|
-
class Json
|
8
|
-
attr_reader :value
|
9
|
-
|
10
|
-
def initialize(value)
|
11
|
-
@value = value
|
12
|
-
end
|
13
|
-
|
14
|
-
def to_json(*_args)
|
15
|
-
@value.to_json
|
16
|
-
end
|
17
|
-
|
18
|
-
def ==(other)
|
19
|
-
@value == (other.is_a?(::Hash) ? other : other.value)
|
20
|
-
end
|
21
|
-
|
22
|
-
def self.cast(value)
|
23
|
-
return value if value.is_a?(self) || value.nil?
|
24
|
-
|
25
|
-
new(::JSON.parse(value))
|
26
|
-
end
|
27
|
-
|
28
|
-
def self.serialize(value)
|
29
|
-
value.to_json
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|