lutaml-model 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +27 -0
- data/.github/workflows/rake.yml +15 -0
- data/.github/workflows/release.yml +23 -0
- data/.gitignore +11 -0
- data/.rspec +3 -0
- data/.rubocop.yml +12 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +12 -0
- data/README.adoc +380 -0
- data/Rakefile +12 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/lib/lutaml/model/attribute.rb +26 -0
- data/lib/lutaml/model/config.rb +14 -0
- data/lib/lutaml/model/json_adapter/multi_json.rb +20 -0
- data/lib/lutaml/model/json_adapter/standard.rb +20 -0
- data/lib/lutaml/model/json_adapter.rb +38 -0
- data/lib/lutaml/model/key_value_mapping.rb +20 -0
- data/lib/lutaml/model/key_value_mapping_rule.rb +10 -0
- data/lib/lutaml/model/mapping_rule.rb +34 -0
- data/lib/lutaml/model/schema/json_schema.rb +63 -0
- data/lib/lutaml/model/schema/relaxng_schema.rb +50 -0
- data/lib/lutaml/model/schema/xsd_schema.rb +54 -0
- data/lib/lutaml/model/schema/yaml_schema.rb +47 -0
- data/lib/lutaml/model/schema.rb +27 -0
- data/lib/lutaml/model/serializable.rb +10 -0
- data/lib/lutaml/model/serialize.rb +179 -0
- data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +20 -0
- data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +20 -0
- data/lib/lutaml/model/toml_adapter.rb +37 -0
- data/lib/lutaml/model/type/time_without_date.rb +17 -0
- data/lib/lutaml/model/type.rb +114 -0
- data/lib/lutaml/model/version.rb +7 -0
- data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +117 -0
- data/lib/lutaml/model/xml_adapter/oga_adapter.rb +75 -0
- data/lib/lutaml/model/xml_adapter/ox_adapter.rb +75 -0
- data/lib/lutaml/model/xml_adapter.rb +60 -0
- data/lib/lutaml/model/xml_mapping.rb +65 -0
- data/lib/lutaml/model/xml_mapping_rule.rb +16 -0
- data/lib/lutaml/model/yaml_adapter.rb +24 -0
- data/lib/lutaml/model.rb +21 -0
- data/lutaml-model.gemspec +50 -0
- data/sig/lutaml/model.rbs +6 -0
- metadata +228 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
# lib/lutaml/model/json_adapter/standard.rb
|
2
|
+
require "json"
|
3
|
+
require_relative "../json_adapter"
|
4
|
+
|
5
|
+
module Lutaml
|
6
|
+
module Model
|
7
|
+
module JsonAdapter
|
8
|
+
class StandardDocument < Document
|
9
|
+
def self.parse(json)
|
10
|
+
attributes = JSON.parse(json, create_additions: false)
|
11
|
+
new(attributes)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_json(*args)
|
15
|
+
JSON.generate(@attributes, *args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# lib/lutaml/model/json_adapter.rb
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Lutaml
|
5
|
+
module Model
|
6
|
+
module JsonAdapter
|
7
|
+
class JsonObject
|
8
|
+
attr_reader :attributes
|
9
|
+
|
10
|
+
def initialize(attributes = {})
|
11
|
+
@attributes = attributes
|
12
|
+
end
|
13
|
+
|
14
|
+
def [](key)
|
15
|
+
@attributes[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def []=(key, value)
|
19
|
+
@attributes[key] = value
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_h
|
23
|
+
@attributes
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Document < JsonObject
|
28
|
+
def self.parse(json)
|
29
|
+
raise NotImplementedError, "Subclasses must implement `parse`."
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_json(*args)
|
33
|
+
raise NotImplementedError, "Subclasses must implement `to_json`."
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# lib/lutaml/model/key_value_mapping.rb
|
2
|
+
require_relative "key_value_mapping_rule"
|
3
|
+
|
4
|
+
module Lutaml
|
5
|
+
module Model
|
6
|
+
class KeyValueMapping
|
7
|
+
attr_reader :mappings
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@mappings = []
|
11
|
+
end
|
12
|
+
|
13
|
+
def map(name, to:, render_nil: false, with: {}, delegate: nil)
|
14
|
+
@mappings << KeyValueMappingRule.new(name, to: to, render_nil: render_nil, with: with, delegate: delegate)
|
15
|
+
end
|
16
|
+
|
17
|
+
alias map_element map
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# lib/lutaml/model/mapping_rule.rb
|
2
|
+
module Lutaml
|
3
|
+
module Model
|
4
|
+
class MappingRule
|
5
|
+
attr_reader :name, :to, :render_nil, :custom_methods, :delegate
|
6
|
+
|
7
|
+
def initialize(name, to:, render_nil: false, with: {}, delegate: nil)
|
8
|
+
@name = name
|
9
|
+
@to = to
|
10
|
+
@render_nil = render_nil
|
11
|
+
@custom_methods = with
|
12
|
+
@delegate = delegate
|
13
|
+
end
|
14
|
+
|
15
|
+
alias from name
|
16
|
+
|
17
|
+
def serialize(model, value)
|
18
|
+
if custom_methods[:to]
|
19
|
+
model.send(custom_methods[:to], model, value)
|
20
|
+
else
|
21
|
+
value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def deserialize(model, doc)
|
26
|
+
if custom_methods[:from]
|
27
|
+
model.send(custom_methods[:from], model, doc)
|
28
|
+
else
|
29
|
+
doc[name.to_s]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# lib/lutaml/model/schema/json_schema.rb
|
2
|
+
require "json"
|
3
|
+
|
4
|
+
module Lutaml
|
5
|
+
module Model
|
6
|
+
module Schema
|
7
|
+
class JsonSchema
|
8
|
+
def self.generate(klass, options = {})
|
9
|
+
schema = {
|
10
|
+
"$schema" => "https://json-schema.org/draft/2020-12/schema",
|
11
|
+
"$id" => options[:id],
|
12
|
+
"description" => options[:description],
|
13
|
+
"$ref" => "#/$defs/#{klass.name}",
|
14
|
+
"$defs" => generate_definitions(klass),
|
15
|
+
}.compact
|
16
|
+
|
17
|
+
options[:pretty] ? JSON.pretty_generate(schema) : schema.to_json
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def self.generate_definitions(klass)
|
23
|
+
{
|
24
|
+
klass.name => {
|
25
|
+
"type" => "object",
|
26
|
+
"properties" => generate_properties(klass),
|
27
|
+
"required" => klass.attributes.keys,
|
28
|
+
},
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.generate_properties(klass)
|
33
|
+
klass.attributes.each_with_object({}) do |(name, attr), properties|
|
34
|
+
properties[name] = generate_property_schema(attr)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.generate_property_schema(attr)
|
39
|
+
{ "type" => get_json_type(attr.type) }
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.get_json_type(type)
|
43
|
+
case type
|
44
|
+
when Lutaml::Model::Type::String
|
45
|
+
"string"
|
46
|
+
when Lutaml::Model::Type::Integer
|
47
|
+
"integer"
|
48
|
+
when Lutaml::Model::Type::Boolean
|
49
|
+
"boolean"
|
50
|
+
when Lutaml::Model::Type::Float
|
51
|
+
"number"
|
52
|
+
when Lutaml::Model::Type::Array
|
53
|
+
"array"
|
54
|
+
when Lutaml::Model::Type::Hash
|
55
|
+
"object"
|
56
|
+
else
|
57
|
+
"string" # Default to string for unknown types
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# lib/lutaml/model/schema/relaxng_schema.rb
|
2
|
+
require "nokogiri"
|
3
|
+
|
4
|
+
module Lutaml
|
5
|
+
module Model
|
6
|
+
module Schema
|
7
|
+
class RelaxngSchema
|
8
|
+
def self.generate(klass, options = {})
|
9
|
+
schema = Nokogiri::XML::Builder.new do |xml|
|
10
|
+
xml.element(name: klass.name) do
|
11
|
+
xml.complexType do
|
12
|
+
xml.sequence do
|
13
|
+
generate_elements(klass, xml)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
schema.to_xml
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def self.generate_elements(klass, xml)
|
24
|
+
klass.attributes.each do |name, attr|
|
25
|
+
xml.element(name: name, type: get_relaxng_type(attr.type))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.get_relaxng_type(type)
|
30
|
+
case type
|
31
|
+
when Lutaml::Model::Type::String
|
32
|
+
"string"
|
33
|
+
when Lutaml::Model::Type::Integer
|
34
|
+
"integer"
|
35
|
+
when Lutaml::Model::Type::Boolean
|
36
|
+
"boolean"
|
37
|
+
when Lutaml::Model::Type::Float
|
38
|
+
"float"
|
39
|
+
when Lutaml::Model::Type::Array
|
40
|
+
"array"
|
41
|
+
when Lutaml::Model::Type::Hash
|
42
|
+
"object"
|
43
|
+
else
|
44
|
+
"string" # Default to string for unknown types
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# lib/lutaml/model/schema/xsd_schema.rb
|
2
|
+
require "nokogiri"
|
3
|
+
|
4
|
+
module Lutaml
|
5
|
+
module Model
|
6
|
+
module Schema
|
7
|
+
class XsdSchema
|
8
|
+
def self.generate(klass, options = {})
|
9
|
+
schema = Nokogiri::XML::Builder.new do |xml|
|
10
|
+
xml.schema(xmlns: "http://www.w3.org/2001/XMLSchema") do
|
11
|
+
xml.element(name: klass.name) do
|
12
|
+
xml.complexType do
|
13
|
+
xml.sequence do
|
14
|
+
generate_elements(klass, xml)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
schema.to_xml
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def self.generate_elements(klass, xml)
|
26
|
+
klass.attributes.each do |name, attr|
|
27
|
+
xml.element(name: name, type: get_xsd_type(attr.type))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.get_xsd_type(type)
|
32
|
+
case type
|
33
|
+
when Lutaml::Model::Type::String
|
34
|
+
"xs:string"
|
35
|
+
when Lutaml::Model::Type::Integer
|
36
|
+
"xs:integer"
|
37
|
+
when Lutaml::Model::Type::Boolean
|
38
|
+
"xs:boolean"
|
39
|
+
when Lutaml::Model::Type::Float
|
40
|
+
"xs:float"
|
41
|
+
when Lutaml::Model::Type::Decimal
|
42
|
+
"xs:decimal"
|
43
|
+
when Lutaml::Model::Type::Array
|
44
|
+
"xs:array"
|
45
|
+
when Lutaml::Model::Type::Hash
|
46
|
+
"xs:object"
|
47
|
+
else
|
48
|
+
"xs:string" # Default to string for unknown types
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# lib/lutaml/model/schema/yaml_schema.rb
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
module Lutaml
|
5
|
+
module Model
|
6
|
+
module Schema
|
7
|
+
class YamlSchema
|
8
|
+
def self.generate(klass, options = {})
|
9
|
+
schema = {
|
10
|
+
"type" => "map",
|
11
|
+
"mapping" => generate_mapping(klass),
|
12
|
+
}
|
13
|
+
YAML.dump(schema)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def self.generate_mapping(klass)
|
19
|
+
klass.attributes.each_with_object({}) do |(name, attr), mapping|
|
20
|
+
mapping[name.to_s] = { "type" => get_yaml_type(attr.type) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.get_yaml_type(type)
|
25
|
+
case type
|
26
|
+
when Lutaml::Model::Type::String
|
27
|
+
"str"
|
28
|
+
when Lutaml::Model::Type::Integer
|
29
|
+
"int"
|
30
|
+
when Lutaml::Model::Type::Boolean
|
31
|
+
"bool"
|
32
|
+
when Lutaml::Model::Type::Float
|
33
|
+
"float"
|
34
|
+
when Lutaml::Model::Type::Decimal
|
35
|
+
"float" # YAML does not have a separate decimal type, so we use float
|
36
|
+
when Lutaml::Model::Type::Array
|
37
|
+
"seq"
|
38
|
+
when Lutaml::Model::Type::Hash
|
39
|
+
"map"
|
40
|
+
else
|
41
|
+
"str" # Default to string for unknown types
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# lib/lutaml/model/schema.rb
|
2
|
+
require_relative "schema/json_schema"
|
3
|
+
require_relative "schema/xsd_schema"
|
4
|
+
require_relative "schema/relaxng_schema"
|
5
|
+
require_relative "schema/yaml_schema"
|
6
|
+
|
7
|
+
module Lutaml
|
8
|
+
module Model
|
9
|
+
module Schema
|
10
|
+
def self.to_json(klass, options = {})
|
11
|
+
JsonSchema.generate(klass, options)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.to_xsd(klass, options = {})
|
15
|
+
XsdSchema.generate(klass, options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.to_relaxng(klass, options = {})
|
19
|
+
RelaxngSchema.generate(klass, options)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.to_yaml(klass, options = {})
|
23
|
+
YamlSchema.generate(klass, options)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# lib/lutaml/model/serialize.rb
|
2
|
+
require_relative "json_adapter/standard"
|
3
|
+
require_relative "json_adapter/multi_json"
|
4
|
+
require_relative "yaml_adapter"
|
5
|
+
require_relative "xml_adapter"
|
6
|
+
require_relative "toml_adapter/toml_rb_adapter"
|
7
|
+
require_relative "toml_adapter/tomlib_adapter"
|
8
|
+
require_relative "config"
|
9
|
+
require_relative "type"
|
10
|
+
require_relative "attribute"
|
11
|
+
require_relative "mapping_rule"
|
12
|
+
require_relative "xml_mapping"
|
13
|
+
require_relative "key_value_mapping"
|
14
|
+
require_relative "json_adapter"
|
15
|
+
|
16
|
+
module Lutaml
|
17
|
+
module Model
|
18
|
+
module Serialize
|
19
|
+
FORMATS = %i[xml json yaml toml].freeze
|
20
|
+
|
21
|
+
def self.included(base)
|
22
|
+
base.extend(ClassMethods)
|
23
|
+
end
|
24
|
+
|
25
|
+
module ClassMethods
|
26
|
+
attr_accessor :attributes, :mappings
|
27
|
+
|
28
|
+
def attribute(name, type, options = {})
|
29
|
+
self.attributes ||= {}
|
30
|
+
attr = Attribute.new(name, type, options)
|
31
|
+
attributes[name] = attr
|
32
|
+
|
33
|
+
define_method(name) do
|
34
|
+
instance_variable_get("@#{name}")
|
35
|
+
end
|
36
|
+
|
37
|
+
define_method("#{name}=") do |value|
|
38
|
+
instance_variable_set("@#{name}", value)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
FORMATS.each do |format|
|
43
|
+
define_method(format) do |&block|
|
44
|
+
self.mappings ||= {}
|
45
|
+
klass = format == :xml ? XmlMapping : KeyValueMapping
|
46
|
+
self.mappings[format] = klass.new
|
47
|
+
self.mappings[format].instance_eval(&block)
|
48
|
+
end
|
49
|
+
|
50
|
+
define_method("from_#{format}") do |data|
|
51
|
+
adapter = Lutaml::Model::Config.send("#{format}_adapter")
|
52
|
+
doc = adapter.parse(data)
|
53
|
+
mapped_attrs = apply_mappings(doc.to_h, format)
|
54
|
+
apply_content_mapping(doc, mapped_attrs) if format == :xml
|
55
|
+
new(mapped_attrs)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def mappings_for(format)
|
60
|
+
self.mappings[format] || default_mappings(format)
|
61
|
+
end
|
62
|
+
|
63
|
+
def default_mappings(format)
|
64
|
+
klass = format == :xml ? XmlMapping : KeyValueMapping
|
65
|
+
klass.new.tap do |mapping|
|
66
|
+
attributes&.each do |name, attr|
|
67
|
+
mapping.map_element(name.to_s, to: name, render_nil: attr.render_nil?)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def apply_mappings(doc, format)
|
73
|
+
mappings = mappings_for(format).mappings
|
74
|
+
mappings.each_with_object({}) do |rule, hash|
|
75
|
+
attr = attributes[rule.to]
|
76
|
+
raise "Attribute '#{rule.to}' not found in #{self}" unless attr
|
77
|
+
|
78
|
+
value = doc[rule.name]
|
79
|
+
if attr.collection?
|
80
|
+
value = (value || []).map { |v| attr.type <= Serialize ? attr.type.new(v) : v }
|
81
|
+
elsif value.is_a?(Hash) && attr.type <= Serialize
|
82
|
+
value = attr.type.new(value)
|
83
|
+
end
|
84
|
+
hash[rule.to] = value
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def apply_content_mapping(doc, mapped_attrs)
|
89
|
+
content_mapping = mappings_for(:xml).content_mapping
|
90
|
+
return unless content_mapping
|
91
|
+
|
92
|
+
content = doc.root.children.select(&:text?).map(&:text)
|
93
|
+
mapped_attrs[content_mapping.to] = content
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize(attrs = {})
|
98
|
+
return self unless self.class.attributes
|
99
|
+
|
100
|
+
self.class.attributes.each do |name, attr|
|
101
|
+
value = attrs.key?(name) ? attrs[name] : attr.default
|
102
|
+
value = if attr.collection?
|
103
|
+
(value || []).map { |v| Lutaml::Model::Type.cast(v, attr.type) }
|
104
|
+
else
|
105
|
+
Lutaml::Model::Type.cast(value, attr.type)
|
106
|
+
end
|
107
|
+
send("#{name}=", ensure_utf8(value))
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# TODO: Make this work
|
112
|
+
# FORMATS.each do |format|
|
113
|
+
# define_method("to_#{format}") do |options = {}|
|
114
|
+
# adapter = Lutaml::Model::Config.send("#{format}_adapter")
|
115
|
+
# representation = format == :yaml ? self : hash_representation(format, options)
|
116
|
+
# adapter.new(representation).send("to_#{format}", options)
|
117
|
+
# end
|
118
|
+
# end
|
119
|
+
|
120
|
+
def to_xml(options = {})
|
121
|
+
adapter = Lutaml::Model::Config.xml_adapter
|
122
|
+
adapter.new(self).to_xml(options)
|
123
|
+
end
|
124
|
+
|
125
|
+
def to_json(options = {})
|
126
|
+
adapter = Lutaml::Model::Config.json_adapter
|
127
|
+
adapter.new(hash_representation(:json, options)).to_json(options)
|
128
|
+
end
|
129
|
+
|
130
|
+
def to_yaml(options = {})
|
131
|
+
adapter = Lutaml::Model::Config.yaml_adapter
|
132
|
+
adapter.to_yaml(self, options)
|
133
|
+
end
|
134
|
+
|
135
|
+
def to_toml(options = {})
|
136
|
+
adapter = Lutaml::Model::Config.toml_adapter
|
137
|
+
adapter.new(hash_representation(:toml, options)).to_toml
|
138
|
+
end
|
139
|
+
|
140
|
+
# TODO: END Make this work
|
141
|
+
|
142
|
+
def hash_representation(format, options = {})
|
143
|
+
only = options[:only]
|
144
|
+
except = options[:except]
|
145
|
+
mappings = self.class.mappings_for(format).mappings
|
146
|
+
|
147
|
+
mappings.each_with_object({}) do |rule, hash|
|
148
|
+
name = rule.to
|
149
|
+
next if except&.include?(name) || (only && !only.include?(name))
|
150
|
+
|
151
|
+
value = send(name)
|
152
|
+
next if value.nil? && !rule.render_nil
|
153
|
+
|
154
|
+
hash[rule.from] = case value
|
155
|
+
when Array
|
156
|
+
value.map { |v| v.is_a?(Serialize) ? v.hash_representation(format, options) : v }
|
157
|
+
else
|
158
|
+
value.is_a?(Serialize) ? value.hash_representation(format, options) : value
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
private
|
164
|
+
|
165
|
+
def ensure_utf8(value)
|
166
|
+
case value
|
167
|
+
when String
|
168
|
+
value.encode("UTF-8", invalid: :replace, undef: :replace, replace: "")
|
169
|
+
when Array
|
170
|
+
value.map { |v| ensure_utf8(v) }
|
171
|
+
when Hash
|
172
|
+
value.transform_keys { |k| ensure_utf8(k) }.transform_values { |v| ensure_utf8(v) }
|
173
|
+
else
|
174
|
+
value
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# lib/lutaml/model/toml_adapter/toml_rb_adapter.rb
|
2
|
+
require "toml-rb"
|
3
|
+
require_relative "../toml_adapter"
|
4
|
+
|
5
|
+
module Lutaml
|
6
|
+
module Model
|
7
|
+
module TomlAdapter
|
8
|
+
class TomlRbDocument < Document
|
9
|
+
def self.parse(toml)
|
10
|
+
data = TomlRB.parse(toml)
|
11
|
+
new(data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_toml(*args)
|
15
|
+
TomlRB.dump(to_h, *args)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# lib/lutaml/model/toml_adapter/tomlib_adapter.rb
|
2
|
+
require "tomlib"
|
3
|
+
require_relative "../toml_adapter"
|
4
|
+
|
5
|
+
module Lutaml
|
6
|
+
module Model
|
7
|
+
module TomlAdapter
|
8
|
+
class TomlibDocument < Document
|
9
|
+
def self.parse(toml)
|
10
|
+
data = Tomlib::Parser.new(toml).parsed
|
11
|
+
new(data)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_toml(*args)
|
15
|
+
Tomlib::Generator.new(to_h).toml_str
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# lib/lutaml/model/toml_adapter.rb
|
2
|
+
|
3
|
+
module Lutaml
|
4
|
+
module Model
|
5
|
+
module TomlAdapter
|
6
|
+
class TomlObject
|
7
|
+
attr_reader :attributes
|
8
|
+
|
9
|
+
def initialize(attributes = {})
|
10
|
+
@attributes = attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
@attributes[key]
|
15
|
+
end
|
16
|
+
|
17
|
+
def []=(key, value)
|
18
|
+
@attributes[key] = value
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_h
|
22
|
+
@attributes
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Document < TomlObject
|
27
|
+
def self.parse(toml)
|
28
|
+
raise NotImplementedError, "Subclasses must implement `parse`."
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_toml(*args)
|
32
|
+
raise NotImplementedError, "Subclasses must implement `to_toml`."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# lib/lutaml/model/type/time_without_date.rb
|
2
|
+
module Lutaml
|
3
|
+
module Model
|
4
|
+
module Type
|
5
|
+
class TimeWithoutDate
|
6
|
+
def self.cast(value)
|
7
|
+
parsed_time = ::Time.parse(value.to_s)
|
8
|
+
parsed_time.strftime("%H:%M:%S")
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.serialize(value)
|
12
|
+
value.strftime("%H:%M:%S")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|