lutaml-model 0.3.24 → 0.3.26

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +36 -17
  3. data/README.adoc +396 -30
  4. data/lib/lutaml/model/attribute.rb +52 -27
  5. data/lib/lutaml/model/error/pattern_not_matched_error.rb +17 -0
  6. data/lib/lutaml/model/error/type_error.rb +9 -0
  7. data/lib/lutaml/model/error/unknown_type_error.rb +9 -0
  8. data/lib/lutaml/model/error/validation_error.rb +0 -1
  9. data/lib/lutaml/model/error.rb +3 -0
  10. data/lib/lutaml/model/mapping_hash.rb +8 -0
  11. data/lib/lutaml/model/serialize.rb +10 -5
  12. data/lib/lutaml/model/type/boolean.rb +38 -0
  13. data/lib/lutaml/model/type/date.rb +35 -0
  14. data/lib/lutaml/model/type/date_time.rb +32 -4
  15. data/lib/lutaml/model/type/decimal.rb +42 -0
  16. data/lib/lutaml/model/type/float.rb +37 -0
  17. data/lib/lutaml/model/type/hash.rb +62 -0
  18. data/lib/lutaml/model/type/integer.rb +41 -0
  19. data/lib/lutaml/model/type/string.rb +49 -0
  20. data/lib/lutaml/model/type/time.rb +49 -0
  21. data/lib/lutaml/model/type/time_without_date.rb +37 -5
  22. data/lib/lutaml/model/type/value.rb +52 -0
  23. data/lib/lutaml/model/type.rb +50 -114
  24. data/lib/lutaml/model/validation.rb +2 -1
  25. data/lib/lutaml/model/version.rb +1 -1
  26. data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +12 -3
  27. data/lib/lutaml/model/xml_adapter/builder/ox.rb +7 -1
  28. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +3 -1
  29. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +12 -8
  30. data/lib/lutaml/model/xml_adapter/xml_document.rb +6 -8
  31. data/lib/lutaml/model/xml_mapping.rb +6 -2
  32. data/lib/lutaml/model/xml_mapping_rule.rb +7 -1
  33. data/lutaml-model.gemspec +1 -1
  34. data/spec/address_spec.rb +170 -0
  35. data/spec/fixtures/address.rb +33 -0
  36. data/spec/fixtures/person.rb +73 -0
  37. data/spec/fixtures/sample_model.rb +40 -0
  38. data/spec/fixtures/vase.rb +38 -0
  39. data/spec/fixtures/xml/special_char.xml +13 -0
  40. data/spec/lutaml/model/attribute_spec.rb +139 -0
  41. data/spec/lutaml/model/cdata_spec.rb +520 -0
  42. data/spec/lutaml/model/collection_spec.rb +299 -0
  43. data/spec/lutaml/model/comparable_model_spec.rb +106 -0
  44. data/spec/lutaml/model/custom_model_spec.rb +410 -0
  45. data/spec/lutaml/model/custom_serialization_spec.rb +170 -0
  46. data/spec/lutaml/model/defaults_spec.rb +221 -0
  47. data/spec/lutaml/model/delegation_spec.rb +340 -0
  48. data/spec/lutaml/model/inheritance_spec.rb +92 -0
  49. data/spec/lutaml/model/json_adapter_spec.rb +37 -0
  50. data/spec/lutaml/model/key_value_mapping_spec.rb +86 -0
  51. data/spec/lutaml/model/map_content_spec.rb +118 -0
  52. data/spec/lutaml/model/mixed_content_spec.rb +625 -0
  53. data/spec/lutaml/model/namespace_spec.rb +57 -0
  54. data/spec/lutaml/model/ordered_content_spec.rb +83 -0
  55. data/spec/lutaml/model/render_nil_spec.rb +138 -0
  56. data/spec/lutaml/model/schema/json_schema_spec.rb +79 -0
  57. data/spec/lutaml/model/schema/relaxng_schema_spec.rb +60 -0
  58. data/spec/lutaml/model/schema/xsd_schema_spec.rb +55 -0
  59. data/spec/lutaml/model/schema/yaml_schema_spec.rb +47 -0
  60. data/spec/lutaml/model/serializable_spec.rb +297 -0
  61. data/spec/lutaml/model/serializable_validation_spec.rb +90 -0
  62. data/spec/lutaml/model/simple_model_spec.rb +314 -0
  63. data/spec/lutaml/model/toml_adapter_spec.rb +39 -0
  64. data/spec/lutaml/model/type/boolean_spec.rb +54 -0
  65. data/spec/lutaml/model/type/date_spec.rb +118 -0
  66. data/spec/lutaml/model/type/date_time_spec.rb +127 -0
  67. data/spec/lutaml/model/type/decimal_spec.rb +125 -0
  68. data/spec/lutaml/model/type/float_spec.rb +191 -0
  69. data/spec/lutaml/model/type/hash_spec.rb +63 -0
  70. data/spec/lutaml/model/type/integer_spec.rb +145 -0
  71. data/spec/lutaml/model/type/string_spec.rb +150 -0
  72. data/spec/lutaml/model/type/time_spec.rb +142 -0
  73. data/spec/lutaml/model/type/time_without_date_spec.rb +125 -0
  74. data/spec/lutaml/model/type_spec.rb +276 -0
  75. data/spec/lutaml/model/utils_spec.rb +79 -0
  76. data/spec/lutaml/model/validation_spec.rb +83 -0
  77. data/spec/lutaml/model/with_child_mapping_spec.rb +174 -0
  78. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +56 -0
  79. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +56 -0
  80. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +61 -0
  81. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +251 -0
  82. data/spec/lutaml/model/xml_adapter_spec.rb +178 -0
  83. data/spec/lutaml/model/xml_mapping_spec.rb +863 -0
  84. data/spec/lutaml/model/yaml_adapter_spec.rb +30 -0
  85. data/spec/lutaml/model_spec.rb +1 -0
  86. data/spec/person_spec.rb +161 -0
  87. data/spec/spec_helper.rb +33 -0
  88. metadata +68 -2
@@ -9,6 +9,7 @@ module Lutaml
9
9
  delegate
10
10
  collection
11
11
  values
12
+ pattern
12
13
  ].freeze
13
14
 
14
15
  def initialize(name, type, options = {})
@@ -34,15 +35,23 @@ module Lutaml
34
35
 
35
36
  def cast_type!(type)
36
37
  case type
38
+ when Symbol
39
+ begin
40
+ Type.lookup(type)
41
+ rescue UnknownTypeError
42
+ raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
43
+ end
44
+ when String
45
+ begin
46
+ Type.const_get(type)
47
+ rescue NameError
48
+ raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
49
+ end
37
50
  when Class
38
51
  type
39
- when String
40
- Type.const_get(type)
41
- when Symbol
42
- Type.const_get(type.to_s.split("_").collect(&:capitalize).join)
52
+ else
53
+ raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
43
54
  end
44
- rescue NameError
45
- raise ArgumentError, "Unknown Lutaml::Model::Type: #{type}"
46
55
  end
47
56
 
48
57
  def cast_value(value)
@@ -79,23 +88,12 @@ module Lutaml
79
88
  cast_value(value)
80
89
  end
81
90
 
82
- def enum_values
83
- @options.key?(:values) ? @options[:values] : []
91
+ def pattern
92
+ options[:pattern]
84
93
  end
85
94
 
86
- # Check if the value to be assigned is valid for the attribute
87
- #
88
- # Currently there are 2 validations
89
- # 1. Value should be from the values list if they are defined
90
- # e.g values: ["foo", "bar"] is set then any other value for this
91
- # attribute will raise `Lutaml::Model::InvalidValueError`
92
- #
93
- # 2. Value count should be between the collection range if defined
94
- # e.g if collection: 0..5 is set then the value greater then 5
95
- # will raise `Lutaml::Model::CollectionCountOutOfRangeError`
96
- def validate_value!(value)
97
- valid_value!(value)
98
- valid_collection!(value)
95
+ def enum_values
96
+ @options.key?(:values) ? @options[:values] : []
99
97
  end
100
98
 
101
99
  def valid_value!(value)
@@ -115,14 +113,32 @@ module Lutaml
115
113
  options[:values].include?(value)
116
114
  end
117
115
 
118
- def validate_value!(value)
119
- # return true if none of the validations are present
120
- return true if enum_values.empty? && singular?
116
+ def valid_pattern!(value)
117
+ return true unless type == Lutaml::Model::Type::String
118
+ return true unless pattern
119
+
120
+ unless pattern.match?(value)
121
+ raise Lutaml::Model::PatternNotMatchedError.new(name, pattern, value)
122
+ end
123
+
124
+ true
125
+ end
121
126
 
127
+ # Check if the value to be assigned is valid for the attribute
128
+ #
129
+ # Currently there are 2 validations
130
+ # 1. Value should be from the values list if they are defined
131
+ # e.g values: ["foo", "bar"] is set then any other value for this
132
+ # attribute will raise `Lutaml::Model::InvalidValueError`
133
+ #
134
+ # 2. Value count should be between the collection range if defined
135
+ # e.g if collection: 0..5 is set then the value greater then 5
136
+ # will raise `Lutaml::Model::CollectionCountOutOfRangeError`
137
+ def validate_value!(value)
122
138
  # Use the default value if the value is nil
123
139
  value = default if value.nil?
124
140
 
125
- valid_value!(value) && valid_collection!(value)
141
+ valid_value!(value) && valid_collection!(value) && valid_pattern!(value)
126
142
  end
127
143
 
128
144
  def validate_collection_range
@@ -194,7 +210,9 @@ module Lutaml
194
210
  elsif type <= Serialize
195
211
  type.hash_representation(value, format, options)
196
212
  else
197
- type.serialize(value)
213
+ # Convert to Value instance if not already
214
+ value = type.new(value) unless value.is_a?(Type::Value)
215
+ value.send(:"to_#{format}")
198
216
  end
199
217
  end
200
218
 
@@ -208,7 +226,7 @@ module Lutaml
208
226
  elsif type <= Serialize && value.is_a?(Hash)
209
227
  type.apply_mappings(value, format, options)
210
228
  else
211
- Lutaml::Model::Type.cast(value, type)
229
+ type.cast(value)
212
230
  end
213
231
  end
214
232
 
@@ -219,6 +237,13 @@ module Lutaml
219
237
  raise StandardError,
220
238
  "Invalid options given for `#{name}` #{invalid_opts}"
221
239
  end
240
+
241
+ if options.key?(:pattern) && type != Lutaml::Model::Type::String
242
+ raise StandardError,
243
+ "Invalid option `pattern` given for `#{name}`, `pattern` is only allowed for :string type"
244
+ end
245
+
246
+ true
222
247
  end
223
248
 
224
249
  def validate_type!(type)
@@ -0,0 +1,17 @@
1
+ module Lutaml
2
+ module Model
3
+ class PatternNotMatchedError < Error
4
+ def initialize(attr_name, pattern, value)
5
+ @attr_name = attr_name
6
+ @pattern = pattern
7
+ @value = value
8
+
9
+ super()
10
+ end
11
+
12
+ def to_s
13
+ "#{@attr_name}: \"#{@value}\" does not match #{@pattern.inspect}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Lutaml
2
+ module Model
3
+ class TypeError < Error
4
+ def initialize(message)
5
+ super("Type Error: #{message}")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Lutaml
2
+ module Model
3
+ class UnknownTypeError < Error
4
+ def initialize(type_name)
5
+ super("Unknown type '#{type_name}'")
6
+ end
7
+ end
8
+ end
9
+ end
@@ -1,4 +1,3 @@
1
- # lib/lutaml/model/error/validation_error.rb
2
1
  module Lutaml
3
2
  module Model
4
3
  class ValidationError < Error
@@ -7,7 +7,10 @@ end
7
7
 
8
8
  require_relative "error/invalid_value_error"
9
9
  require_relative "error/incorrect_mapping_argument_error"
10
+ require_relative "error/pattern_not_matched_error"
10
11
  require_relative "error/unknown_adapter_type_error"
11
12
  require_relative "error/collection_count_out_of_range_error"
12
13
  require_relative "error/validation_error"
13
14
  require_relative "error/type_not_enabled_error"
15
+ require_relative "error/type_error"
16
+ require_relative "error/unknown_type_error"
@@ -28,6 +28,14 @@ module Lutaml
28
28
  @item_order = order
29
29
  end
30
30
 
31
+ def text
32
+ self["#cdata-section"] || self["text"]
33
+ end
34
+
35
+ def text?
36
+ key?("#cdata-section") || key?("text")
37
+ end
38
+
31
39
  def ordered?
32
40
  @ordered
33
41
  end
@@ -310,7 +310,10 @@ module Lutaml
310
310
  def apply_xml_mapping(doc, instance, options = {})
311
311
  return instance unless doc
312
312
 
313
- options[:default_namespace] = mappings_for(:xml)&.namespace_uri if options[:default_namespace].nil?
313
+ if options[:default_namespace].nil?
314
+ options[:default_namespace] =
315
+ mappings_for(:xml)&.namespace_uri
316
+ end
314
317
  mappings = mappings_for(:xml).mappings
315
318
 
316
319
  if doc.is_a?(Array)
@@ -340,7 +343,7 @@ module Lutaml
340
343
  value = if rule.raw_mapping?
341
344
  doc.node.inner_xml
342
345
  elsif rule.content_mapping?
343
- doc["text"]
346
+ doc[rule.content_key]
344
347
  elsif doc.key_exist?(rule.namespaced_name(options[:default_namespace]))
345
348
  doc.fetch(rule.namespaced_name(options[:default_namespace]))
346
349
  else
@@ -396,10 +399,12 @@ module Lutaml
396
399
 
397
400
  value = if value.is_a?(Array)
398
401
  value.map do |v|
399
- text_hash?(attr, v) ? v["text"] : v
402
+ text_hash?(attr, v) ? v.text : v
400
403
  end
404
+ elsif attr&.raw? && value
405
+ value.node.children.map(&:to_xml).join
401
406
  elsif text_hash?(attr, value)
402
- value["text"]
407
+ value.text
403
408
  else
404
409
  value
405
410
  end
@@ -423,7 +428,7 @@ module Lutaml
423
428
 
424
429
  def text_hash?(attr, value)
425
430
  return false unless value.is_a?(Hash)
426
- return value.keys == ["text"] unless attr
431
+ return value.one? && value.text? unless attr
427
432
 
428
433
  !(attr.type <= Serialize) && attr.type != Lutaml::Model::Type::Hash
429
434
  end
@@ -0,0 +1,38 @@
1
+ module Lutaml
2
+ module Model
3
+ module Type
4
+ class Boolean < Value
5
+ def self.cast(value)
6
+ return nil if value.nil?
7
+ return true if value == true || value.to_s.match?(/^(true|t|yes|y|1)$/i)
8
+ return false if value == false || value.to_s.match?(/^(false|f|no|n|0)$/i)
9
+
10
+ value
11
+ end
12
+
13
+ def self.serialize(value)
14
+ return nil if value.nil?
15
+
16
+ cast(value) # Return actual boolean instead of string
17
+ end
18
+
19
+ # Format-specific serialization methods
20
+ def to_xml
21
+ value.to_s
22
+ end
23
+
24
+ def to_json(*_args)
25
+ value
26
+ end
27
+
28
+ def to_yaml
29
+ value
30
+ end
31
+
32
+ def to_toml
33
+ value.to_s
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ module Lutaml
2
+ module Model
3
+ module Type
4
+ class Date < Value
5
+ def self.cast(value)
6
+ return nil if value.nil?
7
+
8
+ case value
9
+ when ::DateTime, ::Time
10
+ value.to_date
11
+ when ::Date
12
+ value
13
+ else
14
+ ::Date.parse(value.to_s)
15
+ end
16
+ rescue ArgumentError
17
+ nil
18
+ end
19
+
20
+ # xs:date format
21
+ def self.serialize(value)
22
+ return nil if value.nil?
23
+
24
+ value&.iso8601
25
+ end
26
+
27
+ # This is to handle where Ruby's YAML safe_load does not handle
28
+ # the Date/Time classes
29
+ def to_yaml
30
+ value&.iso8601.to_s
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -4,15 +4,43 @@ module Lutaml
4
4
  module Model
5
5
  module Type
6
6
  # Date and time representation
7
- class DateTime
7
+ class DateTime < Value
8
8
  def self.cast(value)
9
- return if value.nil?
9
+ return nil if value.nil?
10
10
 
11
- ::DateTime.parse(value.to_s).new_offset(0)
11
+ case value
12
+ when ::DateTime then value
13
+ when ::Time then value.to_datetime
14
+ else ::DateTime.parse(value.to_s)
15
+ end
16
+ rescue ArgumentError
17
+ nil
12
18
  end
13
19
 
14
20
  def self.serialize(value)
15
- value.iso8601
21
+ return nil if value.nil?
22
+
23
+ cast(value)&.iso8601
24
+ end
25
+
26
+ # xs:dateTime format (ISO8601 with timezone)
27
+ def to_xml
28
+ value&.iso8601
29
+ end
30
+
31
+ # RFC3339 (ISO8601 with timezone)
32
+ def to_json(*_args)
33
+ value&.iso8601
34
+ end
35
+
36
+ # YAML timestamp format (native)
37
+ def to_yaml
38
+ value&.iso8601
39
+ end
40
+
41
+ # TOML datetime format (RFC3339)
42
+ def to_toml
43
+ value&.iso8601
16
44
  end
17
45
  end
18
46
  end
@@ -0,0 +1,42 @@
1
+ module Lutaml
2
+ module Model
3
+ module Type
4
+ class Decimal < Value
5
+ def self.cast(value)
6
+ return nil if value.nil?
7
+
8
+ check_dependencies!(value)
9
+ case value
10
+ when BigDecimal
11
+ # If already a BigDecimal, return as-is
12
+ value
13
+ else
14
+ # Convert to string first to handle various input types
15
+ BigDecimal(value.to_s)
16
+ end
17
+ rescue ArgumentError
18
+ nil
19
+ end
20
+
21
+ # # xs:decimal format
22
+ def self.serialize(value)
23
+ return nil if value.nil?
24
+
25
+ check_dependencies!(value)
26
+ value = cast(value)
27
+ value.to_s("F") # Use fixed-point notation to match test expectations
28
+ end
29
+
30
+ def self.from_xml(value)
31
+ cast(value.text)
32
+ end
33
+
34
+ def self.check_dependencies!(value)
35
+ unless defined?(BigDecimal)
36
+ raise TypeNotEnabledError.new("Decimal", value)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,37 @@
1
+ module Lutaml
2
+ module Model
3
+ module Type
4
+ class Float < Value
5
+ def self.cast(value)
6
+ return nil if value.nil?
7
+
8
+ value.to_f
9
+ end
10
+
11
+ def self.serialize(value)
12
+ return nil if value.nil?
13
+
14
+ cast(value)
15
+ end
16
+
17
+ # Instance methods for specific formats
18
+ # xs:float format
19
+ def to_xml
20
+ value.to_s
21
+ end
22
+
23
+ def to_yaml
24
+ value
25
+ end
26
+
27
+ def to_json(*_args)
28
+ value
29
+ end
30
+
31
+ def to_yaml
32
+ value
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,62 @@
1
+ module Lutaml
2
+ module Model
3
+ module Type
4
+ class Hash < Value
5
+ def self.cast(value)
6
+ return nil if value.nil?
7
+
8
+ hash = if value.respond_to?(:to_h)
9
+ value.to_h
10
+ else
11
+ Hash(value)
12
+ end
13
+
14
+ normalize_hash(hash)
15
+ end
16
+
17
+ def self.normalize_hash(hash)
18
+ return hash["text"] if hash.keys == ["text"]
19
+
20
+ hash = hash.to_h if hash.is_a?(Lutaml::Model::MappingHash)
21
+
22
+ hash = hash.except("text")
23
+
24
+ hash.transform_values do |value|
25
+ if value.is_a?(::Hash)
26
+ # Only process if value is a Hash
27
+ nested = normalize_hash(value)
28
+ # Only include non-text nodes in nested hashes if it's a hash
29
+ nested.is_a?(::Hash) ? nested.except("text") : nested
30
+ else
31
+ value
32
+ end
33
+ end
34
+ end
35
+
36
+ def self.serialize(value)
37
+ return nil if value.nil?
38
+ return value if value.is_a?(Hash)
39
+
40
+ value.respond_to?(:to_h) ? value.to_h : Hash(value)
41
+ end
42
+
43
+ # Format-specific serialization methods
44
+ def to_xml
45
+ value
46
+ end
47
+
48
+ def to_json(*_args)
49
+ value
50
+ end
51
+
52
+ def to_yaml
53
+ value
54
+ end
55
+
56
+ def to_toml
57
+ value
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,41 @@
1
+ module Lutaml
2
+ module Model
3
+ module Type
4
+ class Integer < Value
5
+ def self.cast(value)
6
+ return nil if value.nil?
7
+ return 1 if value === true
8
+ return 0 if value === false
9
+
10
+ case value
11
+ when ::String
12
+ if value.match?(/^0[0-7]+$/) # Octal
13
+ value.to_i(8)
14
+ elsif value.match?(/^-?\d+(\.\d+)?(e-?\d+)?$/i) # Float/exponential
15
+ Float(value).to_i
16
+ else
17
+ begin
18
+ Integer(value, 10)
19
+ rescue StandardError
20
+ nil
21
+ end
22
+ end
23
+ else
24
+ begin
25
+ Integer(value)
26
+ rescue StandardError
27
+ nil
28
+ end
29
+ end
30
+ end
31
+
32
+ # Override serialize to return Integer instead of String
33
+ def self.serialize(value)
34
+ return nil if value.nil?
35
+
36
+ cast(value)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,49 @@
1
+ module Lutaml
2
+ module Model
3
+ module Type
4
+ class String < Value
5
+ def self.cast(value)
6
+ return nil if value.nil?
7
+
8
+ value.to_s
9
+ end
10
+
11
+ # xs:string format
12
+ def to_xml
13
+ value&.to_s
14
+ end
15
+
16
+ # JSON string
17
+ def to_json(*_args)
18
+ value
19
+ end
20
+
21
+ # YAML string
22
+ def to_yaml
23
+ value
24
+ end
25
+
26
+ # TOML string
27
+ def to_toml
28
+ value&.to_s
29
+ end
30
+
31
+ def self.from_xml(value)
32
+ cast(value)
33
+ end
34
+
35
+ def self.from_json(value)
36
+ cast(value)
37
+ end
38
+
39
+ def self.from_yaml(value)
40
+ cast(value)
41
+ end
42
+
43
+ def self.from_toml(value)
44
+ cast(value)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ require "time"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Type
6
+ class Time < Value
7
+ def self.cast(value)
8
+ return nil if value.nil?
9
+
10
+ case value
11
+ when ::Time then value
12
+ when ::DateTime then value.to_time
13
+ else ::Time.parse(value.to_s)
14
+ end
15
+ rescue ArgumentError
16
+ nil
17
+ end
18
+
19
+ def self.serialize(value)
20
+ return nil if value.nil?
21
+
22
+ value = cast(value)
23
+ # value&.strftime("%Y-%m-%dT%H:%M:%S%:z")
24
+ value&.iso8601
25
+ end
26
+
27
+ # # xs:time format (HH:MM:SS.mmm±HH:MM)
28
+ # def to_xml
29
+ # value&.strftime("%H:%M:%S%:z")
30
+ # end
31
+
32
+ # # ISO8601 time format
33
+ # def to_json
34
+ # value&.iso8601
35
+ # end
36
+
37
+ # # YAML timestamp format (native)
38
+ # def to_yaml
39
+ # value
40
+ # end
41
+
42
+ # # TOML time format (HH:MM:SS.mmm)
43
+ # def to_toml
44
+ # value&.strftime("%H:%M:%S.%L")
45
+ # end
46
+ end
47
+ end
48
+ end
49
+ end