lutaml-model 0.1.0
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 +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
|