lutaml-model 0.3.5 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
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
- The name "LutaML" comes from the Latin word for clay, "Lutum", and "ML"
17
- for Markup Language. Just as clay can be molded and modeled into beautiful and
18
- practical end products, the Lutaml::Model gem is used for data modeling,
19
- allowing you to shape and structure your data into useful forms.
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: Lutaml::Model is designed to be compatible with the
23
- https://www.shalerb.org[Shale] data modeling API. Shale is an amazing Ruby data
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
 
@@ -1338,11 +1338,11 @@ class CustomCeramic < Lutaml::Model::Serializable
1338
1338
  end
1339
1339
 
1340
1340
  def name_to_json(model, value)
1341
- "Masterpiece: #{value}"
1341
+ doc["name"] = "Masterpiece: #{value}"
1342
1342
  end
1343
1343
 
1344
1344
  def name_from_json(model, doc)
1345
- doc['name'].sub(/^Masterpiece: /, '')
1345
+ model.name = value.sub(/^JSON Masterpiece: /, '')
1346
1346
  end
1347
1347
  end
1348
1348
  ----
@@ -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 default configuration is as follows:
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,400 @@ 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, doc)
1975
+ doc["name"] = "JSON Masterpiece: #{model.name}"
1976
+ end
1977
+
1978
+ def name_from_json(model, value)
1979
+ model.name = value.sub(/^JSON Masterpiece: /, "")
1980
+ end
1981
+
1982
+ def color_to_json(model, doc)
1983
+ doc["color"] = model.color.upcase
1984
+ end
1985
+
1986
+ def color_from_json(model, value)
1987
+ model.color = value.downcase
1988
+ end
1989
+
1990
+ def description_to_json(model, doc)
1991
+ doc["description"] = "JSON Description: #{model.description}"
1992
+ end
1993
+
1994
+ def description_from_json(model, value)
1995
+ model.description = value.sub(/^JSON Description: /, "")
1996
+ end
1997
+
1998
+ def name_to_xml(model, parent, doc)
1999
+ el = doc.create_element("Name")
2000
+ doc.add_text(el, "XML Masterpiece: #{model.name}")
2001
+ doc.add_element(parent, el)
2002
+ end
2003
+
2004
+ def name_from_xml(model, value)
2005
+ model.name = value.sub(/^XML Masterpiece: /, "")
2006
+ end
2007
+
2008
+ def color_to_xml(model, parent, doc)
2009
+ color_element = doc.create_element("Color")
2010
+ doc.add_text(color_element, model.color.upcase)
2011
+ doc.add_element(parent, color_element)
2012
+ end
2013
+
2014
+ def color_from_xml(model, value)
2015
+ model.color = value.downcase
2016
+ end
2017
+
2018
+ def description_to_xml(model, parent, doc)
2019
+ doc.add_text(parent, "XML Description: #{model.description}")
2020
+ end
2021
+
2022
+ def description_from_xml(model, value)
2023
+ model.description = value.join.strip.sub(/^XML Description: /, "")
2024
+ end
2025
+ end
2026
+ ----
2027
+
2028
+ Custom serialization methods in Shale do not map to specific attributes, but
2029
+ allow the user to specify where the data goes.
2030
+
2031
+ [source,ruby]
2032
+ ----
2033
+ class Example < Shale::Mapper
2034
+ attribute :name, Shale::Type::String
2035
+ attribute :size, Shale::Type::Integer
2036
+ attribute :color, Shale::Type::String
2037
+ attribute :description, Shale::Type::String
2038
+
2039
+ json do
2040
+ map "name", using: { from: :name_from_json, to: :name_to_json }
2041
+ map "size", to: :size
2042
+ map "color", using: { from: :color_from_json, to: :color_to_json }
2043
+ map "description", to: :description, using: { from: :description_from_json, to: :description_to_json }
2044
+ end
2045
+
2046
+ xml do
2047
+ root "CustomSerialization"
2048
+ map_element "Name", using: { from: :name_from_xml, to: :name_to_xml }
2049
+ map_attribute "Size", to: :size
2050
+ map_element "Color", using: { from: :color_from_xml, to: :color_to_xml }
2051
+ map_content to: :description, using: { from: :description_from_xml, to: :description_to_xml }
2052
+ end
2053
+
2054
+ def name_to_json(model, doc)
2055
+ doc['name'] = "JSON Masterpiece: #{model.name}"
2056
+ end
2057
+
2058
+ def name_from_json(model, value)
2059
+ model.name = value.sub(/^JSON Masterpiece: /, "")
2060
+ end
2061
+
2062
+ def color_to_json(model, doc)
2063
+ doc['color'] = model.color.upcase
2064
+ end
2065
+
2066
+ def color_from_json(model, doc)
2067
+ model.color = doc['color'].downcase
2068
+ end
2069
+
2070
+ def description_to_json(model, doc)
2071
+ doc['description'] = "JSON Description: #{model.description}"
2072
+ end
2073
+
2074
+ def description_from_json(model, doc)
2075
+ model.description = doc['description'].sub(/^JSON Description: /, "")
2076
+ end
2077
+
2078
+ def name_from_xml(model, node)
2079
+ model.name = node.text.sub(/^XML Masterpiece: /, "")
2080
+ end
2081
+
2082
+ def name_to_xml(model, parent, doc)
2083
+ name_element = doc.create_element('Name')
2084
+ doc.add_text(name_element, model.street.to_s)
2085
+ doc.add_element(parent, name_element)
2086
+ end
2087
+ end
2088
+ ----
2089
+
2090
+ NOTE: There are cases where the Shale implementation of custom methods work
2091
+ differently from the Lutaml::Model implementation. In these cases, you will need
2092
+ to adjust the custom methods accordingly.
2093
+
2094
+ Actions:
2095
+
2096
+ * Replace the `using` keyword with the `with` keyword.
2097
+ * Adjust the custom methods.
2098
+
2099
+
2100
+ == About LutaML
2101
+
2102
+ The name "LutaML" is pronounced as "Looh-tah-mel".
2103
+
2104
+ The name "LutaML" comes from the Latin word for clay, "Lutum", and "ML"
2105
+ for "Markup Language". Just as clay can be molded and modeled into beautiful and
2106
+ practical end products, the Lutaml::Model gem is used for data modeling,
2107
+ allowing you to shape and structure your data into useful forms.
2108
+
2109
+
2110
+
1702
2111
  == License and Copyright
1703
2112
 
1704
2113
  This project is licensed under the BSD 2-clause License.
@@ -40,6 +40,33 @@ module Lutaml
40
40
  def render_nil?
41
41
  options.fetch(:render_nil, false)
42
42
  end
43
+
44
+ def serialize(value, format, options = {})
45
+ if value.is_a?(Array)
46
+ value.map do |v|
47
+ serialize(v, format, options)
48
+ end
49
+ elsif type <= Serialize
50
+ type.hash_representation(value, format, options)
51
+ else
52
+ type.serialize(value)
53
+ end
54
+ end
55
+
56
+ def cast(value, format, options = {})
57
+ value ||= [] if collection?
58
+ instance = options[:instance]
59
+
60
+ if value.is_a?(Array)
61
+ value.map do |v|
62
+ cast(v, format, instance: instance)
63
+ end
64
+ elsif type <= Serialize
65
+ type.apply_mappings(value, format, options)
66
+ else
67
+ Lutaml::Model::Type.cast(value, type)
68
+ end
69
+ end
43
70
  end
44
71
  end
45
72
  end
@@ -3,11 +3,92 @@ module Lutaml
3
3
  module Config
4
4
  extend self
5
5
 
6
- attr_accessor :xml_adapter, :json_adapter, :yaml_adapter, :toml_adapter
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
@@ -6,3 +6,4 @@ module Lutaml
6
6
  end
7
7
 
8
8
  require_relative "error/invalid_value_error"
9
+ require_relative "error/unknown_adapter_type_error"