lutaml-model 0.3.30 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +34 -18
  3. data/README.adoc +172 -8
  4. data/lib/lutaml/model/attribute.rb +6 -2
  5. data/lib/lutaml/model/key_value_mapping.rb +0 -1
  6. data/lib/lutaml/model/key_value_mapping_rule.rb +3 -1
  7. data/lib/lutaml/model/mapping_rule.rb +14 -2
  8. data/lib/lutaml/model/serialize.rb +174 -61
  9. data/lib/lutaml/model/type/decimal.rb +5 -0
  10. data/lib/lutaml/model/type/time.rb +4 -4
  11. data/lib/lutaml/model/utils.rb +1 -1
  12. data/lib/lutaml/model/validation.rb +6 -2
  13. data/lib/lutaml/model/version.rb +1 -1
  14. data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +1 -0
  15. data/lib/lutaml/model/xml_adapter/builder/oga.rb +180 -0
  16. data/lib/lutaml/model/xml_adapter/builder/ox.rb +1 -0
  17. data/lib/lutaml/model/xml_adapter/oga/document.rb +20 -0
  18. data/lib/lutaml/model/xml_adapter/oga/element.rb +117 -0
  19. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +77 -44
  20. data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -9
  21. data/lib/lutaml/model/xml_mapping.rb +0 -1
  22. data/lib/lutaml/model/xml_mapping_rule.rb +16 -4
  23. data/spec/address_spec.rb +1 -0
  24. data/spec/fixtures/sample_model.rb +7 -0
  25. data/spec/lutaml/model/custom_model_spec.rb +47 -1
  26. data/spec/lutaml/model/custom_serialization_spec.rb +16 -0
  27. data/spec/lutaml/model/enum_spec.rb +131 -0
  28. data/spec/lutaml/model/included_spec.rb +192 -0
  29. data/spec/lutaml/model/mixed_content_spec.rb +48 -32
  30. data/spec/lutaml/model/multiple_mapping_spec.rb +329 -0
  31. data/spec/lutaml/model/ordered_content_spec.rb +1 -1
  32. data/spec/lutaml/model/render_nil_spec.rb +3 -2
  33. data/spec/lutaml/model/serializable_spec.rb +3 -3
  34. data/spec/lutaml/model/type/boolean_spec.rb +62 -0
  35. data/spec/lutaml/model/xml_adapter/oga_adapter_spec.rb +11 -11
  36. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +1 -1
  37. data/spec/lutaml/model/xml_adapter_spec.rb +2 -2
  38. data/spec/lutaml/model/xml_mapping_spec.rb +24 -9
  39. data/spec/sample_model_spec.rb +114 -0
  40. metadata +9 -2
@@ -21,6 +21,7 @@ module Lutaml
21
21
 
22
22
  def self.included(base)
23
23
  base.extend(ClassMethods)
24
+ base.initialize_attrs(base)
24
25
  end
25
26
 
26
27
  module ClassMethods
@@ -28,14 +29,18 @@ module Lutaml
28
29
 
29
30
  def inherited(subclass)
30
31
  super
32
+ subclass.initialize_attrs(self)
33
+ end
31
34
 
32
- @mappings ||= {}
33
- @attributes ||= {}
35
+ def included(base)
36
+ base.extend(ClassMethods)
37
+ base.initialize_attrs(self)
38
+ end
34
39
 
35
- subclass.instance_variable_set(:@attributes,
36
- Utils.deep_dup(@attributes))
37
- subclass.instance_variable_set(:@mappings, Utils.deep_dup(@mappings))
38
- subclass.instance_variable_set(:@model, subclass)
40
+ def initialize_attrs(source_class)
41
+ @mappings = Utils.deep_dup(source_class.instance_variable_get(:@mappings)) || {}
42
+ @attributes = Utils.deep_dup(source_class.instance_variable_get(:@attributes)) || {}
43
+ instance_variable_set(:@model, self)
39
44
  end
40
45
 
41
46
  def model(klass = nil)
@@ -81,16 +86,104 @@ module Lutaml
81
86
  attr = Attribute.new(name, type, options)
82
87
  attributes[name] = attr
83
88
 
84
- define_method(name) do
85
- instance_variable_get(:"@#{name}")
89
+ if attr.enum?
90
+ add_enum_methods_to_model(
91
+ model,
92
+ name,
93
+ options[:values],
94
+ collection: options[:collection],
95
+ )
96
+ else
97
+ define_method(name) do
98
+ instance_variable_get(:"@#{name}")
99
+ end
100
+
101
+ define_method(:"#{name}=") do |value|
102
+ value_set_for(name)
103
+ instance_variable_set(:"@#{name}", attr.cast_value(value))
104
+ end
86
105
  end
106
+ end
107
+
108
+ def add_enum_methods_to_model(klass, enum_name, values, collection: false)
109
+ add_enum_getter_if_not_defined(klass, enum_name, collection)
110
+ add_enum_setter_if_not_defined(klass, enum_name, values, collection)
87
111
 
88
- define_method(:"#{name}=") do |value|
89
- value_set_for(name)
90
- instance_variable_set(:"@#{name}", attr.cast_value(value))
112
+ return unless values.all?(::String)
113
+
114
+ values.each do |value|
115
+ Utils.add_method_if_not_defined(klass, "#{value}?") do
116
+ curr_value = public_send(:"#{enum_name}")
117
+
118
+ if collection
119
+ curr_value.include?(value)
120
+ else
121
+ curr_value == value
122
+ end
123
+ end
124
+
125
+ Utils.add_method_if_not_defined(klass, value.to_s) do
126
+ public_send(:"#{value}?")
127
+ end
128
+
129
+ Utils.add_method_if_not_defined(klass, "#{value}=") do |val|
130
+ value_set_for(enum_name)
131
+ enum_vals = public_send(:"#{enum_name}")
132
+
133
+ enum_vals = if !!val
134
+ if collection
135
+ enum_vals << value
136
+ else
137
+ [value]
138
+ end
139
+ elsif collection
140
+ enum_vals.delete(value)
141
+ enum_vals
142
+ else
143
+ []
144
+ end
145
+
146
+ instance_variable_set(:"@#{enum_name}", enum_vals)
147
+ end
148
+
149
+ Utils.add_method_if_not_defined(klass, "#{value}!") do
150
+ public_send(:"#{value}=", true)
151
+ end
91
152
  end
92
153
  end
93
154
 
155
+ def add_enum_getter_if_not_defined(klass, enum_name, collection)
156
+ Utils.add_method_if_not_defined(klass, enum_name) do
157
+ i = instance_variable_get(:"@#{enum_name}") || []
158
+
159
+ if !collection && i.is_a?(Array)
160
+ i.first
161
+ else
162
+ i.uniq
163
+ end
164
+ end
165
+ end
166
+
167
+ def add_enum_setter_if_not_defined(klass, enum_name, _values, collection)
168
+ Utils.add_method_if_not_defined(klass, "#{enum_name}=") do |value|
169
+ value = [value] unless value.is_a?(Array)
170
+
171
+ value_set_for(enum_name)
172
+
173
+ if collection
174
+ curr_value = public_send(:"#{enum_name}")
175
+
176
+ instance_variable_set(:"@#{enum_name}", curr_value + value)
177
+ else
178
+ instance_variable_set(:"@#{enum_name}", value)
179
+ end
180
+ end
181
+ end
182
+
183
+ def enums
184
+ attributes.select { |_, attr| attr.enum? }
185
+ end
186
+
94
187
  Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
95
188
  define_method(format) do |&block|
96
189
  klass = format == :xml ? XmlMapping : KeyValueMapping
@@ -135,7 +228,7 @@ module Lutaml
135
228
  end
136
229
  end
137
230
 
138
- define_method(:"as_#{format}") do |instance|
231
+ define_method(:"as_#{format}") do |instance, options = {}|
139
232
  if instance.is_a?(Array)
140
233
  return instance.map { |item| public_send(:"as_#{format}", item) }
141
234
  end
@@ -147,7 +240,7 @@ module Lutaml
147
240
 
148
241
  return instance if format == :xml
149
242
 
150
- hash_representation(instance, format)
243
+ hash_representation(instance, format, options)
151
244
  end
152
245
  end
153
246
 
@@ -166,7 +259,7 @@ module Lutaml
166
259
  mappings.each_with_object({}) do |rule, hash|
167
260
  name = rule.to
168
261
  next if except&.include?(name) || (only && !only.include?(name))
169
- next if !rule.render_default? && instance.using_default?(rule.to)
262
+ next if !rule.custom_methods[:to] && (!rule.render_default? && instance.using_default?(rule.to))
170
263
 
171
264
  next handle_delegate(instance, rule, hash, format) if rule.delegate
172
265
 
@@ -176,15 +269,18 @@ module Lutaml
176
269
 
177
270
  value = instance.send(name)
178
271
 
179
- next if value.nil? && !rule.render_nil
180
-
181
272
  attribute = attributes[name]
182
273
 
183
- hash[rule.from] = if rule.child_mappings
184
- generate_hash_from_child_mappings(value, rule.child_mappings)
185
- else
186
- attribute.serialize(value, format, options)
187
- end
274
+ value = if rule.child_mappings
275
+ generate_hash_from_child_mappings(value, rule.child_mappings)
276
+ else
277
+ attribute.serialize(value, format, options)
278
+ end
279
+
280
+ next if Utils.blank?(value) && !rule.render_nil
281
+
282
+ rule_from_name = rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
283
+ hash[rule_from_name] = value
188
284
  end
189
285
  end
190
286
 
@@ -194,39 +290,14 @@ module Lutaml
194
290
  return if value.nil? && !rule.render_nil
195
291
 
196
292
  attribute = instance.send(rule.delegate).class.attributes[name]
197
- hash[rule.from] = attribute.serialize(value, format)
293
+ rule_from_name = rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
294
+ hash[rule_from_name] = attribute.serialize(value, format)
198
295
  end
199
296
 
200
297
  def mappings_for(format)
201
298
  mappings[format] || default_mappings(format)
202
299
  end
203
300
 
204
- def attr_value(attrs, name, attr_rule)
205
- value = if attrs.key?(name.to_sym)
206
- attrs[name.to_sym]
207
- elsif attrs.key?(name.to_s)
208
- attrs[name.to_s]
209
- else
210
- attr_rule.default
211
- end
212
-
213
- if attr_rule.collection? || value.is_a?(Array)
214
- (value || []).map do |v|
215
- if v.is_a?(Hash)
216
- attr_rule.type.new(v)
217
- else
218
- # TODO: This code is problematic because Type.cast does not know
219
- # about all the types.
220
- Lutaml::Model::Type.cast(v, attr_rule.type)
221
- end
222
- end
223
- else
224
- # TODO: This code is problematic because Type.cast does not know
225
- # about all the types.
226
- Lutaml::Model::Type.cast(value, attr_rule.type)
227
- end
228
- end
229
-
230
301
  def default_mappings(format)
231
302
  klass = format == :xml ? XmlMapping : KeyValueMapping
232
303
 
@@ -325,8 +396,7 @@ module Lutaml
325
396
  mappings = mappings_for(:xml).mappings
326
397
 
327
398
  if doc.is_a?(Array)
328
- raise "May be `collection: true` is" \
329
- "missing for #{self} in #{options[:caller_class]}"
399
+ raise "May be `collection: true` is missing for #{self} in #{options[:caller_class]}"
330
400
  end
331
401
 
332
402
  if instance.respond_to?(:ordered=) && doc.is_a?(Lutaml::Model::MappingHash)
@@ -350,17 +420,18 @@ module Lutaml
350
420
 
351
421
  attr = attribute_for_rule(rule)
352
422
 
423
+ namespaced_names = rule.namespaced_names(options[:default_namespace])
424
+
353
425
  value = if rule.raw_mapping?
354
426
  doc.node.inner_xml
355
427
  elsif rule.content_mapping?
356
428
  doc[rule.content_key]
357
- elsif doc.key_exist?(rule.namespaced_name(options[:default_namespace]))
358
- doc.fetch(rule.namespaced_name(options[:default_namespace]))
429
+ elsif key = (namespaced_names & doc.keys).first
430
+ doc[key]
359
431
  else
360
432
  defaults_used << rule.to
361
433
  attr&.default || rule.to_value_for(instance)
362
434
  end
363
-
364
435
  value = normalize_xml_value(value, rule, attr, options)
365
436
  rule.deserialize(instance, value, attributes, self)
366
437
  end
@@ -379,13 +450,19 @@ module Lutaml
379
450
 
380
451
  attr = attribute_for_rule(rule)
381
452
 
382
- value = if doc.key?(rule.name) || doc.key?(rule.name.to_sym)
383
- doc[rule.name] || doc[rule.name.to_sym]
384
- else
385
- attr.default
386
- end
453
+ names = rule.multiple_mappings? ? rule.name : [rule.name]
387
454
 
388
- if rule.custom_methods[:from]
455
+ value = names.collect do |rule_name|
456
+ if doc.key?(rule_name.to_s)
457
+ doc[rule_name.to_s]
458
+ elsif doc.key?(rule_name.to_sym)
459
+ doc[rule_name.to_sym]
460
+ else
461
+ attr&.default
462
+ end
463
+ end.compact.first
464
+
465
+ if rule.using_custom_methods?
389
466
  if Utils.present?(value)
390
467
  value = new.send(rule.custom_methods[:from], instance, value)
391
468
  end
@@ -479,7 +556,7 @@ module Lutaml
479
556
 
480
557
  self.class.attributes.each do |name, attr|
481
558
  value = if attrs.key?(name) || attrs.key?(name.to_s)
482
- self.class.attr_value(attrs, name, attr)
559
+ attr_value(attrs, name, attr)
483
560
  else
484
561
  using_default_for(name)
485
562
  attr.default
@@ -490,7 +567,35 @@ module Lutaml
490
567
  value = []
491
568
  end
492
569
 
493
- instance_variable_set(:"@#{name}", self.class.ensure_utf8(value))
570
+ default = using_default?(name)
571
+ public_send(:"#{name}=", self.class.ensure_utf8(value))
572
+ using_default_for(name) if default
573
+ end
574
+ end
575
+
576
+ def attr_value(attrs, name, attr_rule)
577
+ value = if attrs.key?(name.to_sym)
578
+ attrs[name.to_sym]
579
+ elsif attrs.key?(name.to_s)
580
+ attrs[name.to_s]
581
+ else
582
+ attr_rule.default
583
+ end
584
+
585
+ if attr_rule.collection? || value.is_a?(Array)
586
+ (value || []).map do |v|
587
+ if v.is_a?(Hash)
588
+ attr_rule.type.new(v)
589
+ else
590
+ # TODO: This code is problematic because Type.cast does not know
591
+ # about all the types.
592
+ Lutaml::Model::Type.cast(v, attr_rule.type)
593
+ end
594
+ end
595
+ else
596
+ # TODO: This code is problematic because Type.cast does not know
597
+ # about all the types.
598
+ Lutaml::Model::Type.cast(value, attr_rule.type)
494
599
  end
495
600
  end
496
601
 
@@ -539,6 +644,14 @@ module Lutaml
539
644
  hash[key.to_sym] || hash[key.to_s]
540
645
  end
541
646
 
647
+ def pretty_print_instance_variables
648
+ (instance_variables - %i[@using_default]).sort
649
+ end
650
+
651
+ def to_yaml_hash
652
+ self.class.as_yaml(self)
653
+ end
654
+
542
655
  Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
543
656
  define_method(:"to_#{format}") do |options = {}|
544
657
  adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
@@ -36,6 +36,11 @@ module Lutaml
36
36
  raise TypeNotEnabledError.new("Decimal", value)
37
37
  end
38
38
  end
39
+
40
+ # Override to avoid serializing ruby object in YAML
41
+ def to_yaml
42
+ value&.to_s("F")
43
+ end
39
44
  end
40
45
  end
41
46
  end
@@ -34,10 +34,10 @@ module Lutaml
34
34
  # value&.iso8601
35
35
  # end
36
36
 
37
- # # YAML timestamp format (native)
38
- # def to_yaml
39
- # value
40
- # end
37
+ # YAML timestamp format (native)
38
+ def to_yaml
39
+ value&.iso8601
40
+ end
41
41
 
42
42
  # # TOML time format (HH:MM:SS.mmm)
43
43
  # def to_toml
@@ -39,7 +39,7 @@ module Lutaml
39
39
  end
40
40
 
41
41
  def blank?(value)
42
- value.respond_to?(:empty?) ? value.empty? : !value
42
+ value.respond_to?(:empty?) ? value.empty? : value.nil?
43
43
  end
44
44
 
45
45
  def add_method_if_not_defined(klass, method_name, &block)
@@ -4,9 +4,13 @@ module Lutaml
4
4
  def validate
5
5
  errors = []
6
6
  self.class.attributes.each do |name, attr|
7
- value = instance_variable_get(:"@#{name}")
7
+ value = public_send(:"#{name}")
8
8
  begin
9
- attr.validate_value!(value)
9
+ if value.respond_to?(:validate!)
10
+ value.validate!
11
+ else
12
+ attr.validate_value!(value)
13
+ end
10
14
  rescue Lutaml::Model::InvalidValueError,
11
15
  Lutaml::Model::CollectionCountOutOfRangeError,
12
16
  PatternNotMatchedError => e
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.3.30"
5
+ VERSION = "0.5.0"
6
6
  end
7
7
  end
@@ -39,6 +39,7 @@ module Lutaml
39
39
  )
40
40
  add_namespace_prefix(prefix)
41
41
 
42
+ element_name = element_name.first if element_name.is_a?(Array)
42
43
  element_name = "#{element_name}_" if respond_to?(element_name)
43
44
 
44
45
  if block_given?
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module XmlAdapter
6
+ module Builder
7
+ class Oga
8
+ def self.build(options = {}, &block)
9
+ if block_given?
10
+ XmlAdapter::Builder::Oga.new(options, &block)
11
+ else
12
+ XmlAdapter::Builder::Oga.new(options)
13
+ end
14
+ end
15
+
16
+ attr_reader :document, :current_node, :options
17
+
18
+ def initialize(options = {})
19
+ @document = XmlAdapter::Oga::Document.new
20
+ @current_node = @document
21
+ @options = options
22
+ yield(self) if block_given?
23
+ end
24
+
25
+ def create_element(name, attributes = {}, &block)
26
+ if @current_namespace && !name.start_with?("#{@current_namespace}:")
27
+ name = "#{@current_namespace}:#{name}"
28
+ end
29
+
30
+ if block_given?
31
+ element(name, attributes, &block)
32
+ else
33
+ element(name, attributes)
34
+ end
35
+ end
36
+
37
+ def element(name, attributes = {})
38
+ oga_element = ::Oga::XML::Element.new(name: name)
39
+ if block_given?
40
+ element_attributes(oga_element, attributes)
41
+ @current_node.children << oga_element
42
+ # Save previous node to reset the pointer for the rest of the iteration
43
+ previous_node = @current_node
44
+ # Set current node to new element as pointer for the block
45
+ @current_node = oga_element
46
+ yield(self)
47
+ # Reset the pointer for the rest of the iterations
48
+ @current_node = previous_node
49
+ end
50
+ oga_element
51
+ end
52
+
53
+ def add_element(oga_element, child)
54
+ if child.is_a?(String)
55
+ current_element = oga_element.is_a?(XmlAdapter::Oga::Document) ? current_node : oga_element
56
+ add_xml_fragment(current_element, child)
57
+ elsif oga_element.is_a?(XmlAdapter::Oga::Document)
58
+ oga_element.children.last.children << child
59
+ else
60
+ oga_element.children << child
61
+ end
62
+ end
63
+
64
+ def add_attribute(element, name, value)
65
+ attribute = ::Oga::XML::Attribute.new(
66
+ name: name,
67
+ value: value.to_s,
68
+ )
69
+ if element.is_a?(XmlAdapter::Oga::Document)
70
+ element.children.last.attributes << attribute
71
+ else
72
+ element.attributes << attribute
73
+ end
74
+ end
75
+
76
+ def create_and_add_element(
77
+ element_name,
78
+ prefix: (prefix_unset = true
79
+ nil),
80
+ attributes: {},
81
+ &block
82
+ )
83
+ @current_namespace = nil if prefix.nil? && !prefix_unset
84
+ prefixed_name = if prefix
85
+ "#{prefix}:#{element_name}"
86
+ elsif @current_namespace && !element_name.start_with?("#{@current_namespace}:")
87
+ "#{@current_namespace}:#{element_name}"
88
+ else
89
+ element_name
90
+ end
91
+
92
+ if block_given?
93
+ element(prefixed_name, attributes, &block)
94
+ else
95
+ element(prefixed_name, attributes)
96
+ end
97
+ end
98
+
99
+ def <<(text)
100
+ @current_node.text(text)
101
+ end
102
+
103
+ def add_xml_fragment(element, content)
104
+ fragment = "<fragment>#{content}</fragment>"
105
+ parsed_fragment = ::Oga.parse_xml(fragment)
106
+ parsed_children = parsed_fragment.children.first.children
107
+ if element.is_a?(XmlAdapter::Oga::Document)
108
+ element.children.last.children += parsed_children
109
+ else
110
+ element.children += parsed_children
111
+ end
112
+ end
113
+
114
+ def add_text(element, text, cdata: false)
115
+ return add_cdata(element, text) if cdata
116
+
117
+ oga_text = ::Oga::XML::Text.new(text: text.to_s)
118
+ if element.is_a?(XmlAdapter::Oga::Document)
119
+ children = element.children
120
+ children.empty? ? children << oga_text : children.last.children << oga_text
121
+ else
122
+ element.children << oga_text
123
+ end
124
+ end
125
+
126
+ def add_cdata(element, value)
127
+ oga_cdata = ::Oga::XML::CData.new(text: value.to_s)
128
+ if element.is_a?(XmlAdapter::Oga::Document)
129
+ element.children.last.children << oga_cdata
130
+ else
131
+ element.children << oga_cdata
132
+ end
133
+ end
134
+
135
+ def add_namespace_prefix(prefix)
136
+ @current_namespace = prefix
137
+ self
138
+ end
139
+
140
+ def parent
141
+ @document
142
+ end
143
+
144
+ def text(value = nil)
145
+ return @current_node.inner_text if value.nil?
146
+
147
+ str = value.is_a?(Array) ? value.join : value
148
+ @current_node.children << ::Oga::XML::Text.new(text: str)
149
+ end
150
+
151
+ def method_missing(method_name, *args)
152
+ if block_given?
153
+ @current_node.public_send(method_name, *args) do
154
+ yield(self)
155
+ end
156
+ else
157
+ @current_node.public_send(method_name, *args)
158
+ end
159
+ end
160
+
161
+ def respond_to_missing?(method_name, include_private = false)
162
+ @current_node.respond_to?(method_name) || super
163
+ end
164
+
165
+ private
166
+
167
+ def element_attributes(oga_element, attributes)
168
+ oga_element.attributes = attributes.map do |name, value|
169
+ ::Oga::XML::Attribute.new(
170
+ name: name,
171
+ value: value,
172
+ element: oga_element,
173
+ )
174
+ end
175
+ end
176
+ end
177
+ end
178
+ end
179
+ end
180
+ end
@@ -43,6 +43,7 @@ module Lutaml
43
43
  end
44
44
 
45
45
  def create_and_add_element(element_name, prefix: nil, attributes: {})
46
+ element_name = element_name.first if element_name.is_a?(Array)
46
47
  prefixed_name = if prefix
47
48
  "#{prefix}:#{element_name}"
48
49
  elsif @current_namespace && !element_name.start_with?("#{@current_namespace}:")
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Lutaml
4
+ module Model
5
+ module XmlAdapter
6
+ module Oga
7
+ class Document < ::Oga::XML::Document
8
+ def initialize(options = {})
9
+ super
10
+ end
11
+
12
+ def text(value = nil)
13
+ children << ::Oga::XML::Text.new(text: value)
14
+ self
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end