lutaml-model 0.3.24 → 0.3.25

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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +35 -16
  3. data/README.adoc +274 -28
  4. data/lib/lutaml/model/attribute.rb +18 -8
  5. data/lib/lutaml/model/error/type_error.rb +9 -0
  6. data/lib/lutaml/model/error/unknown_type_error.rb +9 -0
  7. data/lib/lutaml/model/error/validation_error.rb +0 -1
  8. data/lib/lutaml/model/error.rb +2 -0
  9. data/lib/lutaml/model/serialize.rb +6 -1
  10. data/lib/lutaml/model/type/boolean.rb +38 -0
  11. data/lib/lutaml/model/type/date.rb +35 -0
  12. data/lib/lutaml/model/type/date_time.rb +32 -4
  13. data/lib/lutaml/model/type/decimal.rb +42 -0
  14. data/lib/lutaml/model/type/float.rb +37 -0
  15. data/lib/lutaml/model/type/hash.rb +62 -0
  16. data/lib/lutaml/model/type/integer.rb +41 -0
  17. data/lib/lutaml/model/type/string.rb +49 -0
  18. data/lib/lutaml/model/type/time.rb +49 -0
  19. data/lib/lutaml/model/type/time_without_date.rb +37 -5
  20. data/lib/lutaml/model/type/value.rb +52 -0
  21. data/lib/lutaml/model/type.rb +50 -114
  22. data/lib/lutaml/model/version.rb +1 -1
  23. data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +5 -2
  24. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +2 -1
  25. data/lib/lutaml/model/xml_adapter/xml_document.rb +0 -2
  26. data/lutaml-model.gemspec +1 -1
  27. data/spec/address_spec.rb +170 -0
  28. data/spec/fixtures/address.rb +33 -0
  29. data/spec/fixtures/person.rb +73 -0
  30. data/spec/fixtures/sample_model.rb +40 -0
  31. data/spec/fixtures/vase.rb +38 -0
  32. data/spec/fixtures/xml/special_char.xml +13 -0
  33. data/spec/lutaml/model/attribute_spec.rb +112 -0
  34. data/spec/lutaml/model/collection_spec.rb +299 -0
  35. data/spec/lutaml/model/comparable_model_spec.rb +106 -0
  36. data/spec/lutaml/model/custom_model_spec.rb +410 -0
  37. data/spec/lutaml/model/custom_serialization_spec.rb +170 -0
  38. data/spec/lutaml/model/defaults_spec.rb +221 -0
  39. data/spec/lutaml/model/delegation_spec.rb +340 -0
  40. data/spec/lutaml/model/inheritance_spec.rb +92 -0
  41. data/spec/lutaml/model/json_adapter_spec.rb +37 -0
  42. data/spec/lutaml/model/key_value_mapping_spec.rb +86 -0
  43. data/spec/lutaml/model/map_content_spec.rb +118 -0
  44. data/spec/lutaml/model/mixed_content_spec.rb +625 -0
  45. data/spec/lutaml/model/namespace_spec.rb +57 -0
  46. data/spec/lutaml/model/ordered_content_spec.rb +83 -0
  47. data/spec/lutaml/model/render_nil_spec.rb +138 -0
  48. data/spec/lutaml/model/schema/json_schema_spec.rb +79 -0
  49. data/spec/lutaml/model/schema/relaxng_schema_spec.rb +60 -0
  50. data/spec/lutaml/model/schema/xsd_schema_spec.rb +55 -0
  51. data/spec/lutaml/model/schema/yaml_schema_spec.rb +47 -0
  52. data/spec/lutaml/model/serializable_spec.rb +297 -0
  53. data/spec/lutaml/model/serializable_validation_spec.rb +85 -0
  54. data/spec/lutaml/model/simple_model_spec.rb +314 -0
  55. data/spec/lutaml/model/toml_adapter_spec.rb +39 -0
  56. data/spec/lutaml/model/type/boolean_spec.rb +54 -0
  57. data/spec/lutaml/model/type/date_spec.rb +118 -0
  58. data/spec/lutaml/model/type/date_time_spec.rb +127 -0
  59. data/spec/lutaml/model/type/decimal_spec.rb +125 -0
  60. data/spec/lutaml/model/type/float_spec.rb +191 -0
  61. data/spec/lutaml/model/type/hash_spec.rb +63 -0
  62. data/spec/lutaml/model/type/integer_spec.rb +145 -0
  63. data/spec/lutaml/model/type/string_spec.rb +150 -0
  64. data/spec/lutaml/model/type/time_spec.rb +142 -0
  65. data/spec/lutaml/model/type/time_without_date_spec.rb +125 -0
  66. data/spec/lutaml/model/type_spec.rb +276 -0
  67. data/spec/lutaml/model/utils_spec.rb +79 -0
  68. data/spec/lutaml/model/validation_spec.rb +83 -0
  69. data/spec/lutaml/model/with_child_mapping_spec.rb +174 -0
  70. data/spec/lutaml/model/xml_adapter/nokogiri_adapter_spec.rb +56 -0
  71. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +56 -0
  72. data/spec/lutaml/model/xml_adapter/ox_adapter_spec.rb +61 -0
  73. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +251 -0
  74. data/spec/lutaml/model/xml_adapter_spec.rb +178 -0
  75. data/spec/lutaml/model/xml_mapping_spec.rb +863 -0
  76. data/spec/lutaml/model/yaml_adapter_spec.rb +30 -0
  77. data/spec/lutaml/model_spec.rb +1 -0
  78. data/spec/person_spec.rb +161 -0
  79. data/spec/spec_helper.rb +33 -0
  80. metadata +66 -2
@@ -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
@@ -1,16 +1,48 @@
1
+ require "time"
2
+
1
3
  module Lutaml
2
4
  module Model
3
5
  module Type
4
- # Time representation without date
5
- class TimeWithoutDate
6
+ class TimeWithoutDate < Value
7
+ # TODO: we probably want to do something like this because using
8
+ # Time.parse will set the date to today.
9
+ #
10
+ # time = ::Time.parse(value.to_s)
11
+ # ::Time.new(1, 1, 1, time.hour, time.min, time.sec)
12
+
6
13
  def self.cast(value)
7
- return if value.nil?
14
+ return nil if value.nil?
8
15
 
9
- ::Time.parse(value.to_s)
16
+ case value
17
+ when ::Time then value
18
+ else ::Time.parse(value.to_s)
19
+ end
20
+ rescue ArgumentError
21
+ nil
10
22
  end
11
23
 
12
24
  def self.serialize(value)
13
- value.strftime("%H:%M:%S")
25
+ return nil if value.nil?
26
+
27
+ value = cast(value)
28
+ value.strftime("%H:%M:%S") # Format as HH:MM:SS
29
+ end
30
+
31
+ # Instance methods for format-specific serialization
32
+ def to_xml
33
+ self.class.serialize(value)
34
+ end
35
+
36
+ def to_json(*_args)
37
+ self.class.serialize(value)
38
+ end
39
+
40
+ def to_yaml
41
+ self.class.serialize(value)
42
+ end
43
+
44
+ def to_toml
45
+ value.strftime("%H:%M:%S.%L") # Include milliseconds for TOML
14
46
  end
15
47
  end
16
48
  end
@@ -0,0 +1,52 @@
1
+ require_relative "../config"
2
+
3
+ module Lutaml
4
+ module Model
5
+ module Type
6
+ # Base class for all value types
7
+ class Value
8
+ attr_reader :value
9
+
10
+ def initialize(value)
11
+ @value = self.class.cast(value)
12
+ end
13
+
14
+ def self.cast(value)
15
+ return nil if value.nil?
16
+
17
+ value
18
+ end
19
+
20
+ def self.serialize(value)
21
+ return nil if value.nil?
22
+
23
+ new(value).to_s
24
+ end
25
+
26
+ # Instance methods for serialization
27
+ def to_s
28
+ value.to_s
29
+ end
30
+
31
+ # Format-specific instance methods
32
+ ::Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
33
+ define_method(:"to_#{format}") do
34
+ value
35
+ end
36
+ end
37
+
38
+ # Class-level format conversion
39
+ def self.from_format(value, format)
40
+ new(send(:"from_#{format}", value))
41
+ end
42
+
43
+ # Default format-specific class methods that can be overridden
44
+ ::Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
45
+ define_singleton_method(:"from_#{format}") do |value|
46
+ cast(value)
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,136 +1,72 @@
1
- require "date"
2
- require "bigdecimal"
3
-
4
1
  module Lutaml
5
2
  module Model
6
3
  module Type
7
- # This module provides a set of methods to cast and serialize values
8
-
9
- # TODO: Make Boolean a separate class
10
-
11
- %w(
12
- String
13
- Integer
14
- Float
15
- Date
16
- Time
17
- Boolean
18
- Decimal
19
- Hash
20
- ).each do |t|
21
- class_eval <<~HEREDOC, __FILE__, __LINE__ + 1
22
- class #{t} # class Integer
23
- def self.cast(value) # def self.cast(value)
24
- return if value.nil? # return if value.nil?
25
- #
26
- Type.cast(value, #{t}) # Type.cast(value, Integer)
27
- end # end
28
-
29
- def self.serialize(value) # def self.serialize(value)
30
- return if value.nil? # return if value.nil?
31
- #
32
- Type.serialize(value, #{t}) # Type.serialize(value, Integer)
33
- end # end
34
- end # end
35
- HEREDOC
36
- end
37
-
38
- UUID_REGEX = /\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\z/
39
-
40
- def self.cast(value, type)
41
- return if value.nil?
42
-
43
- case type.to_s.split("::").last
44
- when "String"
45
- value.to_s
46
- when "Integer"
47
- value.to_i
48
- when "Float"
49
- value.to_f
50
- when "Date"
51
- begin
52
- ::Date.parse(value.to_s)
53
- rescue ArgumentError
54
- nil
55
- end
56
- when "DateTime"
57
- DateTime.cast(value)
58
- when "Time"
59
- ::Time.parse(value.to_s)
60
- when "TimeWithoutDate"
61
- TimeWithoutDate.cast(value)
62
- when "Boolean"
63
- to_boolean(value)
64
- when "Decimal"
65
- unless defined?(BigDecimal)
66
- raise Lutaml::Model::TypeNotEnabledError.new("Decimal", value)
4
+ TYPE_CODES = {
5
+ string: "Lutaml::Model::Type::String",
6
+ integer: "Lutaml::Model::Type::Integer",
7
+ float: "Lutaml::Model::Type::Float",
8
+ decimal: "Lutaml::Model::Type::Decimal",
9
+ date: "Lutaml::Model::Type::Date",
10
+ time: "Lutaml::Model::Type::Time",
11
+ date_time: "Lutaml::Model::Type::DateTime",
12
+ time_without_date: "Lutaml::Model::Type::TimeWithoutDate",
13
+ boolean: "Lutaml::Model::Type::Boolean",
14
+ hash: "Lutaml::Model::Type::Hash",
15
+ }.freeze
16
+
17
+ class << self
18
+ def register_builtin_types
19
+ TYPE_CODES.each do |type_name, type_class|
20
+ register(type_name, const_get(type_class))
67
21
  end
68
-
69
- BigDecimal(value.to_s)
70
- when "Hash"
71
- normalize_hash(Hash(value))
72
- else
73
- value
74
22
  end
75
- end
76
-
77
- def self.serialize(value, type)
78
- return if value.nil?
79
23
 
80
- case type.to_s.split("::").last
81
- when "Date"
82
- value.iso8601
83
- when "DateTime"
84
- DateTime.serialize(value)
85
- when "Integer"
86
- value.to_i
87
- when "Float"
88
- value.to_f
89
- when "Boolean"
90
- to_boolean(value)
91
- when "Decimal"
92
- unless defined?(BigDecimal)
93
- raise Lutaml::Model::TypeNotEnabledError.new("Decimal", value)
24
+ def register(type_name, type_class)
25
+ unless type_class < Value
26
+ raise TypeError,
27
+ "class '#{type_class}' is not a valid Lutaml::Model::Type::Value"
94
28
  end
95
29
 
96
- value.to_s("F")
97
- when "Hash"
98
- Hash(value)
99
- else
100
- value.to_s
30
+ @registry ||= {}
31
+ @registry[type_name.to_sym] = type_class
101
32
  end
102
- end
103
33
 
104
- def self.to_boolean(value)
105
- if value == true || value.to_s =~ (/^(true|t|yes|y|1)$/i)
106
- return true
107
- end
34
+ def lookup(type_name)
35
+ @registry ||= {}
36
+ klass = @registry[type_name.to_sym]
108
37
 
109
- if value == false || value.nil? || value.to_s =~ (/^(false|f|no|n|0)$/i)
110
- return false
111
- end
38
+ raise UnknownTypeError.new(type_name) unless klass
112
39
 
113
- raise ArgumentError.new("invalid value for Boolean: \"#{value}\"")
114
- end
40
+ klass
41
+ end
115
42
 
116
- def self.normalize_hash(hash)
117
- return hash["text"] if hash.keys == ["text"]
43
+ def cast(value, type)
44
+ return nil if value.nil?
118
45
 
119
- hash = hash.to_h if hash.is_a?(Lutaml::Model::MappingHash)
46
+ type.cast(value)
47
+ end
120
48
 
121
- hash.filter_map do |key, value|
122
- next if key == "text"
49
+ def serialize(value, type)
50
+ return nil if value.nil?
123
51
 
124
- if value.is_a?(::Hash)
125
- [key, normalize_hash(value)]
126
- else
127
- [key, value]
128
- end
129
- end.to_h
52
+ type.serialize(value)
53
+ end
130
54
  end
131
55
  end
132
56
  end
133
57
  end
134
58
 
135
- require_relative "type/time_without_date"
59
+ # Register built-in types
60
+ require_relative "type/value"
61
+ require_relative "type/string"
62
+ require_relative "type/integer"
63
+ require_relative "type/float"
64
+ require_relative "type/date"
65
+ require_relative "type/time"
136
66
  require_relative "type/date_time"
67
+ require_relative "type/time_without_date"
68
+ require_relative "type/boolean"
69
+ require_relative "type/decimal"
70
+ require_relative "type/hash"
71
+
72
+ Lutaml::Model::Type.register_builtin_types
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.3.24"
5
+ VERSION = "0.3.25"
6
6
  end
7
7
  end
@@ -39,6 +39,8 @@ module Lutaml
39
39
  )
40
40
  add_namespace_prefix(prefix)
41
41
 
42
+ element_name = "#{element_name}_" if respond_to?(element_name)
43
+
42
44
  if block_given?
43
45
  public_send(element_name, attributes) do
44
46
  xml.parent.namespace = nil if prefix.nil? && !prefix_unset
@@ -54,7 +56,8 @@ module Lutaml
54
56
  element = element.xml.parent
55
57
  end
56
58
 
57
- element << text.to_s
59
+ text_node = ::Nokogiri::XML::Text.new(text.to_s, element)
60
+ element.add_child(text_node)
58
61
  end
59
62
 
60
63
  def add_namespace_prefix(prefix)
@@ -64,7 +67,7 @@ module Lutaml
64
67
  end
65
68
 
66
69
  def method_missing(method_name, *args, &block)
67
- if block
70
+ if block_given?
68
71
  xml.public_send(method_name, *args, &block)
69
72
  else
70
73
  xml.public_send(method_name, *args)