lutaml-model 0.3.29 → 0.4.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.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-repos.json +15 -0
  3. data/.github/workflows/dependent-tests.yml +14 -0
  4. data/.rubocop_todo.yml +15 -7
  5. data/README.adoc +6 -0
  6. data/lib/lutaml/model/attribute.rb +6 -2
  7. data/lib/lutaml/model/json_adapter/json_document.rb +1 -1
  8. data/lib/lutaml/model/json_adapter/multi_json_adapter.rb +1 -1
  9. data/lib/lutaml/model/json_adapter/standard_json_adapter.rb +1 -1
  10. data/lib/lutaml/model/serialize.rb +133 -33
  11. data/lib/lutaml/model/toml_adapter/toml_document.rb +1 -1
  12. data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +1 -1
  13. data/lib/lutaml/model/toml_adapter/tomlib_adapter.rb +1 -1
  14. data/lib/lutaml/model/type/time.rb +4 -4
  15. data/lib/lutaml/model/utils.rb +1 -1
  16. data/lib/lutaml/model/validation.rb +6 -2
  17. data/lib/lutaml/model/version.rb +1 -1
  18. data/lib/lutaml/model/xml_adapter/builder/nokogiri.rb +10 -0
  19. data/lib/lutaml/model/xml_adapter/builder/ox.rb +5 -1
  20. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +5 -3
  21. data/lib/lutaml/model/xml_adapter/oga_adapter.rb +1 -1
  22. data/lib/lutaml/model/xml_adapter/ox_adapter.rb +25 -10
  23. data/lib/lutaml/model/xml_adapter/xml_document.rb +12 -24
  24. data/lib/lutaml/model/yaml_adapter/standard_yaml_adapter.rb +1 -1
  25. data/lib/lutaml/model/yaml_adapter/yaml_document.rb +1 -1
  26. data/spec/fixtures/xml/latin_encoding.xml +5 -0
  27. data/spec/fixtures/xml/shift_jis.xml +4 -0
  28. data/spec/lutaml/model/custom_serialization_spec.rb +16 -0
  29. data/spec/lutaml/model/defaults_spec.rb +74 -0
  30. data/spec/lutaml/model/enum_spec.rb +131 -0
  31. data/spec/lutaml/model/mixed_content_spec.rb +190 -7
  32. data/spec/lutaml/model/render_nil_spec.rb +0 -2
  33. data/spec/lutaml/model/serializable_spec.rb +2 -2
  34. data/spec/lutaml/model/xml_mapping_spec.rb +679 -543
  35. metadata +7 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fba70c8a3fca206b3109603109812019773027580c47b2e3615d873df6e0ce2d
4
- data.tar.gz: 21313fbfb52c29c620eb1020672d337689eed963c25fe56c20baeef139e569b5
3
+ metadata.gz: fa60d5495804e6bfc0203ba1a237f38b89b9626d036beeb2be8ca7aebb22426f
4
+ data.tar.gz: 63d8d18127bee85cfaa73973f57554bc56385805dfeea1dad343c552277089d6
5
5
  SHA512:
6
- metadata.gz: 274739542b8473ae58b4fc6b78e1152aaef604a55f3c9db0ec5678743573d683b4afdd2429d97d6a24c03fdb2c14a72904bf4e057f480e6bafc635a21fde085a
7
- data.tar.gz: b1a7ad49313463676b3afe8732f94ed8ab37f79935f33449c70011bd8a8449934425aa27b9dd25d2a7e25a064afa1e665bf95f8b2eac4ec7cd63be4bcfdb7b8d
6
+ metadata.gz: 322a29941e7685a6e118d083f1b6e11ce6b8abc1cacb5508dd501f874c5ea3f2dc6f0e6719ce4895deb31c877319a3afe0e20d0820a81b8fd60d9fe59c408472
7
+ data.tar.gz: ce087acefc221ed41180658b8c40d4cff5c0196c208647b9d1c5d0d39799849860abf80b981fe753b30a1176ca8633ed696440beeddef5a4493c1e829340f250
@@ -0,0 +1,15 @@
1
+ {
2
+ "repo": [
3
+ "lutaml/genericode",
4
+ "lutaml/ogc-gml",
5
+ "metanorma/modspec-ruby",
6
+ "metanorma/rfcxml",
7
+ "metanorma/niso-jats",
8
+ "metanorma/sts-ruby",
9
+ "lutaml/metaschema",
10
+ "glossarist/termium",
11
+ "lutaml/oasis-etm",
12
+ "lutaml/ali",
13
+ "lutaml/reqif"
14
+ ]
15
+ }
@@ -0,0 +1,14 @@
1
+ name: dependent-gems-test
2
+
3
+ on:
4
+ push:
5
+ branches: [ main ]
6
+ tags: [ v* ]
7
+ pull_request:
8
+ workflow_dispatch:
9
+ repository_dispatch:
10
+ types: [ release-passed ]
11
+
12
+ jobs:
13
+ rake:
14
+ uses: metanorma/ci/.github/workflows/dependent-rake.yml@main
data/.rubocop_todo.yml CHANGED
@@ -1,12 +1,12 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2024-11-25 13:01:09 UTC using RuboCop version 1.66.1.
3
+ # on 2024-12-03 11:13:42 UTC using RuboCop version 1.66.1.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
7
7
  # versions of RuboCop, may require this file to be generated again.
8
8
 
9
- # Offense count: 185
9
+ # Offense count: 224
10
10
  # This cop supports safe autocorrection (--autocorrect).
11
11
  # Configuration parameters: Max, AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns.
12
12
  # URISchemes: http, https
@@ -51,7 +51,7 @@ Metrics/AbcSize:
51
51
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns, inherit_mode.
52
52
  # AllowedMethods: refine
53
53
  Metrics/BlockLength:
54
- Max: 47
54
+ Max: 46
55
55
 
56
56
  # Offense count: 28
57
57
  # Configuration parameters: AllowedMethods, AllowedPatterns, Max.
@@ -65,7 +65,7 @@ Metrics/CyclomaticComplexity:
65
65
  - 'lib/lutaml/model/xml_adapter/ox_adapter.rb'
66
66
  - 'lib/lutaml/model/xml_adapter/xml_document.rb'
67
67
 
68
- # Offense count: 55
68
+ # Offense count: 54
69
69
  # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns.
70
70
  Metrics/MethodLength:
71
71
  Max: 46
@@ -97,7 +97,15 @@ RSpec/ContextWording:
97
97
  - 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
98
98
  - 'spec/lutaml/model/xml_mapping_spec.rb'
99
99
 
100
- # Offense count: 127
100
+ # Offense count: 1
101
+ # This cop supports unsafe autocorrection (--autocorrect-all).
102
+ # Configuration parameters: SkipBlocks, EnforcedStyle, OnlyStaticConstants.
103
+ # SupportedStyles: described_class, explicit
104
+ RSpec/DescribedClass:
105
+ Exclude:
106
+ - 'spec/lutaml/model/xml_mapping_spec.rb'
107
+
108
+ # Offense count: 133
101
109
  # Configuration parameters: CountAsOne.
102
110
  RSpec/ExampleLength:
103
111
  Max: 54
@@ -127,7 +135,7 @@ RSpec/MultipleDescribes:
127
135
  - 'spec/lutaml/model/xml_adapter/xml_namespace_spec.rb'
128
136
  - 'spec/lutaml/model/xml_adapter_spec.rb'
129
137
 
130
- # Offense count: 153
138
+ # Offense count: 157
131
139
  RSpec/MultipleExpectations:
132
140
  Max: 14
133
141
 
@@ -136,7 +144,7 @@ RSpec/MultipleExpectations:
136
144
  RSpec/MultipleMemoizedHelpers:
137
145
  Max: 9
138
146
 
139
- # Offense count: 11
147
+ # Offense count: 15
140
148
  # Configuration parameters: AllowedGroups.
141
149
  RSpec/NestedGroups:
142
150
  Max: 4
data/README.adoc CHANGED
@@ -569,6 +569,12 @@ The values set inside the `values:` option can be of any type, but they must
569
569
  match the type of the attribute. The values are compared using the `==` operator,
570
570
  so the type must implement the `==` method.
571
571
 
572
+ Also, If all the elements in `values` directive are strings then `lutaml-model` add some enum convenience methods, for each of the value the following three methods are added
573
+
574
+ * `value1`: will return value if set
575
+ * `value1?`: will return true if value is set, false otherwise
576
+ * `value1=`: will set the value of `name_of_attribute` equal to `value1` if truthy value is given, and remove it otherwise.
577
+
572
578
  .Using the `values` directive to define acceptable values for an attribute (basic types)
573
579
  [example]
574
580
  ====
@@ -76,6 +76,10 @@ module Lutaml
76
76
  @raw
77
77
  end
78
78
 
79
+ def enum?
80
+ !enum_values.empty?
81
+ end
82
+
79
83
  def default
80
84
  value = if delegate
81
85
  type.attributes[to].default
@@ -98,7 +102,7 @@ module Lutaml
98
102
 
99
103
  def valid_value!(value)
100
104
  return true if value.nil? && !collection?
101
- return true if enum_values.empty?
105
+ return true unless enum?
102
106
 
103
107
  unless valid_value?(value)
104
108
  raise Lutaml::Model::InvalidValueError.new(name, value, enum_values)
@@ -208,7 +212,7 @@ module Lutaml
208
212
  serialize(v, format, options)
209
213
  end
210
214
  elsif type <= Serialize
211
- type.hash_representation(value, format, options)
215
+ type.public_send(:"as_#{format}", value, options)
212
216
  else
213
217
  # Convert to Value instance if not already
214
218
  value = type.new(value) unless value.is_a?(Type::Value)
@@ -7,7 +7,7 @@ module Lutaml
7
7
  module JsonAdapter
8
8
  # Base class for JSON documents
9
9
  class JsonDocument < JsonObject
10
- def self.parse(json)
10
+ def self.parse(json, _options = {})
11
11
  raise NotImplementedError, "Subclasses must implement `parse`."
12
12
  end
13
13
 
@@ -5,7 +5,7 @@ module Lutaml
5
5
  module Model
6
6
  module JsonAdapter
7
7
  class MultiJsonAdapter < JsonDocument
8
- def self.parse(json)
8
+ def self.parse(json, _options = {})
9
9
  data = MultiJson.load(json)
10
10
  new(data)
11
11
  end
@@ -5,7 +5,7 @@ module Lutaml
5
5
  module Model
6
6
  module JsonAdapter
7
7
  class StandardJsonAdapter < JsonDocument
8
- def self.parse(json)
8
+ def self.parse(json, _options = {})
9
9
  JSON.parse(json, create_additions: false)
10
10
  end
11
11
 
@@ -51,6 +51,7 @@ module Lutaml
51
51
  Utils.add_boolean_accessor_if_not_defined(klass, :ordered)
52
52
  Utils.add_boolean_accessor_if_not_defined(klass, :mixed)
53
53
  Utils.add_accessor_if_not_defined(klass, :element_order)
54
+ Utils.add_accessor_if_not_defined(klass, :encoding)
54
55
 
55
56
  Utils.add_method_if_not_defined(klass,
56
57
  :using_default_for) do |attribute_name|
@@ -80,16 +81,104 @@ module Lutaml
80
81
  attr = Attribute.new(name, type, options)
81
82
  attributes[name] = attr
82
83
 
83
- define_method(name) do
84
- instance_variable_get(:"@#{name}")
84
+ if attr.enum?
85
+ add_enum_methods_to_model(
86
+ model,
87
+ name,
88
+ options[:values],
89
+ collection: options[:collection],
90
+ )
91
+ else
92
+ define_method(name) do
93
+ instance_variable_get(:"@#{name}")
94
+ end
95
+
96
+ define_method(:"#{name}=") do |value|
97
+ value_set_for(name)
98
+ instance_variable_set(:"@#{name}", attr.cast_value(value))
99
+ end
85
100
  end
101
+ end
86
102
 
87
- define_method(:"#{name}=") do |value|
88
- value_set_for(name)
89
- instance_variable_set(:"@#{name}", attr.cast_value(value))
103
+ def add_enum_methods_to_model(klass, enum_name, values, collection: false)
104
+ add_enum_getter_if_not_defined(klass, enum_name, collection)
105
+ add_enum_setter_if_not_defined(klass, enum_name, values, collection)
106
+
107
+ return unless values.all?(::String)
108
+
109
+ values.each do |value|
110
+ Utils.add_method_if_not_defined(klass, "#{value}?") do
111
+ curr_value = public_send(:"#{enum_name}")
112
+
113
+ if collection
114
+ curr_value.include?(value)
115
+ else
116
+ curr_value == value
117
+ end
118
+ end
119
+
120
+ Utils.add_method_if_not_defined(klass, value.to_s) do
121
+ public_send(:"#{value}?")
122
+ end
123
+
124
+ Utils.add_method_if_not_defined(klass, "#{value}=") do |val|
125
+ value_set_for(enum_name)
126
+ enum_vals = public_send(:"#{enum_name}")
127
+
128
+ enum_vals = if !!val
129
+ if collection
130
+ enum_vals << value
131
+ else
132
+ [value]
133
+ end
134
+ elsif collection
135
+ enum_vals.delete(value)
136
+ enum_vals
137
+ else
138
+ []
139
+ end
140
+
141
+ instance_variable_set(:"@#{enum_name}", enum_vals)
142
+ end
143
+
144
+ Utils.add_method_if_not_defined(klass, "#{value}!") do
145
+ public_send(:"#{value}=", true)
146
+ end
90
147
  end
91
148
  end
92
149
 
150
+ def add_enum_getter_if_not_defined(klass, enum_name, collection)
151
+ Utils.add_method_if_not_defined(klass, enum_name) do
152
+ i = instance_variable_get(:"@#{enum_name}") || []
153
+
154
+ if !collection && i.is_a?(Array)
155
+ i.first
156
+ else
157
+ i.uniq
158
+ end
159
+ end
160
+ end
161
+
162
+ def add_enum_setter_if_not_defined(klass, enum_name, _values, collection)
163
+ Utils.add_method_if_not_defined(klass, "#{enum_name}=") do |value|
164
+ value = [value] unless value.is_a?(Array)
165
+
166
+ value_set_for(enum_name)
167
+
168
+ if collection
169
+ curr_value = public_send(:"#{enum_name}")
170
+
171
+ instance_variable_set(:"@#{enum_name}", curr_value + value)
172
+ else
173
+ instance_variable_set(:"@#{enum_name}", value)
174
+ end
175
+ end
176
+ end
177
+
178
+ def enums
179
+ attributes.select { |_, attr| attr.enum? }
180
+ end
181
+
93
182
  Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
94
183
  define_method(format) do |&block|
95
184
  klass = format == :xml ? XmlMapping : KeyValueMapping
@@ -101,23 +190,22 @@ module Lutaml
101
190
  end
102
191
  end
103
192
 
104
- define_method(:"from_#{format}") do |data|
193
+ define_method(:"from_#{format}") do |data, options = {}|
105
194
  adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
106
195
 
107
- doc = adapter.parse(data)
108
- public_send(:"of_#{format}", doc)
196
+ doc = adapter.parse(data, options)
197
+ public_send(:"of_#{format}", doc, options)
109
198
  end
110
199
 
111
- define_method(:"of_#{format}") do |doc|
200
+ define_method(:"of_#{format}") do |doc, options = {}|
112
201
  if doc.is_a?(Array)
113
- return doc.map do |item|
114
- send(:"of_#{format}", item)
115
- end
202
+ return doc.map { |item| send(:"of_#{format}", item) }
116
203
  end
117
204
 
118
205
  if format == :xml
119
206
  doc_hash = doc.parse_element(doc.root, self, :xml)
120
- apply_mappings(doc_hash, format)
207
+ options[:encoding] = doc.encoding
208
+ apply_mappings(doc_hash, format, options)
121
209
  else
122
210
  apply_mappings(doc.to_h, format)
123
211
  end
@@ -135,7 +223,7 @@ module Lutaml
135
223
  end
136
224
  end
137
225
 
138
- define_method(:"as_#{format}") do |instance|
226
+ define_method(:"as_#{format}") do |instance, options = {}|
139
227
  if instance.is_a?(Array)
140
228
  return instance.map { |item| public_send(:"as_#{format}", item) }
141
229
  end
@@ -147,7 +235,7 @@ module Lutaml
147
235
 
148
236
  return instance if format == :xml
149
237
 
150
- hash_representation(instance, format)
238
+ hash_representation(instance, format, options)
151
239
  end
152
240
  end
153
241
 
@@ -166,7 +254,7 @@ module Lutaml
166
254
  mappings.each_with_object({}) do |rule, hash|
167
255
  name = rule.to
168
256
  next if except&.include?(name) || (only && !only.include?(name))
169
- next if !rule.render_default? && instance.using_default?(rule.to)
257
+ next if !rule.custom_methods[:to] && (!rule.render_default? && instance.using_default?(rule.to))
170
258
 
171
259
  next handle_delegate(instance, rule, hash, format) if rule.delegate
172
260
 
@@ -176,15 +264,15 @@ module Lutaml
176
264
 
177
265
  value = instance.send(name)
178
266
 
179
- next if value.nil? && !rule.render_nil
267
+ next if Utils.blank?(value) && !rule.render_nil
180
268
 
181
269
  attribute = attributes[name]
182
270
 
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
271
+ hash[rule.from.to_s] = if rule.child_mappings
272
+ generate_hash_from_child_mappings(value, rule.child_mappings)
273
+ else
274
+ attribute.serialize(value, format, options)
275
+ end
188
276
  end
189
277
  end
190
278
 
@@ -194,7 +282,7 @@ module Lutaml
194
282
  return if value.nil? && !rule.render_nil
195
283
 
196
284
  attribute = instance.send(rule.delegate).class.attributes[name]
197
- hash[rule.from] = attribute.serialize(value, format)
285
+ hash[rule.from.to_s] = attribute.serialize(value, format)
198
286
  end
199
287
 
200
288
  def mappings_for(format)
@@ -315,6 +403,7 @@ module Lutaml
315
403
  end
316
404
 
317
405
  def apply_xml_mapping(doc, instance, options = {})
406
+ instance.encoding = options[:encoding]
318
407
  return instance unless doc
319
408
 
320
409
  if options[:default_namespace].nil?
@@ -347,6 +436,8 @@ module Lutaml
347
436
  mappings.each do |rule|
348
437
  raise "Attribute '#{rule.to}' not found in #{self}" unless valid_rule?(rule)
349
438
 
439
+ attr = attribute_for_rule(rule)
440
+
350
441
  value = if rule.raw_mapping?
351
442
  doc.node.inner_xml
352
443
  elsif rule.content_mapping?
@@ -355,10 +446,10 @@ module Lutaml
355
446
  doc.fetch(rule.namespaced_name(options[:default_namespace]))
356
447
  else
357
448
  defaults_used << rule.to
358
- rule.to_value_for(instance)
449
+ attr&.default || rule.to_value_for(instance)
359
450
  end
360
451
 
361
- value = normalize_xml_value(value, rule, options)
452
+ value = normalize_xml_value(value, rule, attr, options)
362
453
  rule.deserialize(instance, value, attributes, self)
363
454
  end
364
455
 
@@ -376,10 +467,10 @@ module Lutaml
376
467
 
377
468
  attr = attribute_for_rule(rule)
378
469
 
379
- value = if doc.key?(rule.name) || doc.key?(rule.name.to_sym)
380
- doc[rule.name] || doc[rule.name.to_sym]
470
+ value = if doc.key?(rule.name.to_s) || doc.key?(rule.name.to_sym)
471
+ doc[rule.name.to_s] || doc[rule.name.to_sym]
381
472
  else
382
- attr.default
473
+ attr&.default
383
474
  end
384
475
 
385
476
  if rule.custom_methods[:from]
@@ -399,9 +490,7 @@ module Lutaml
399
490
  instance
400
491
  end
401
492
 
402
- def normalize_xml_value(value, rule, options = {})
403
- attr = attribute_for_rule(rule)
404
-
493
+ def normalize_xml_value(value, rule, attr, options = {})
405
494
  value = [value].compact if attr&.collection? && !value.is_a?(Array)
406
495
 
407
496
  value = if value.is_a?(Array)
@@ -459,7 +548,7 @@ module Lutaml
459
548
  end
460
549
  end
461
550
 
462
- attr_accessor :element_order, :schema_location
551
+ attr_accessor :element_order, :schema_location, :encoding
463
552
  attr_writer :ordered, :mixed
464
553
 
465
554
  def initialize(attrs = {})
@@ -489,7 +578,9 @@ module Lutaml
489
578
  value = []
490
579
  end
491
580
 
492
- instance_variable_set(:"@#{name}", self.class.ensure_utf8(value))
581
+ default = using_default?(name)
582
+ public_send(:"#{name}=", self.class.ensure_utf8(value))
583
+ using_default_for(name) if default
493
584
  end
494
585
  end
495
586
 
@@ -538,6 +629,14 @@ module Lutaml
538
629
  hash[key.to_sym] || hash[key.to_s]
539
630
  end
540
631
 
632
+ def pretty_print_instance_variables
633
+ (instance_variables - %i[@using_default]).sort
634
+ end
635
+
636
+ def to_yaml_hash
637
+ self.class.as_yaml(self)
638
+ end
639
+
541
640
  Lutaml::Model::Config::AVAILABLE_FORMATS.each do |format|
542
641
  define_method(:"to_#{format}") do |options = {}|
543
642
  adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
@@ -548,6 +647,7 @@ module Lutaml
548
647
  options)
549
648
  end
550
649
 
650
+ options[:parse_encoding] = encoding if encoding
551
651
  adapter.new(representation).public_send(:"to_#{format}", options)
552
652
  end
553
653
  end
@@ -7,7 +7,7 @@ module Lutaml
7
7
  module TomlAdapter
8
8
  # Base class for TOML documents
9
9
  class TomlDocument < TomlObject
10
- def self.parse(toml)
10
+ def self.parse(toml, _options = {})
11
11
  raise NotImplementedError, "Subclasses must implement `parse`."
12
12
  end
13
13
 
@@ -5,7 +5,7 @@ module Lutaml
5
5
  module Model
6
6
  module TomlAdapter
7
7
  class TomlRbAdapter < TomlDocument
8
- def self.parse(toml)
8
+ def self.parse(toml, _options = {})
9
9
  data = TomlRB.parse(toml)
10
10
  new(data)
11
11
  end
@@ -5,7 +5,7 @@ module Lutaml
5
5
  module Model
6
6
  module TomlAdapter
7
7
  class TomlibAdapter < TomlDocument
8
- def self.parse(toml)
8
+ def self.parse(toml, _options = {})
9
9
  data = Tomlib.load(toml)
10
10
  new(data)
11
11
  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.29"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
@@ -51,6 +51,16 @@ module Lutaml
51
51
  end
52
52
  end
53
53
 
54
+ def add_xml_fragment(element, content)
55
+ if element.is_a?(self.class)
56
+ element = element.xml.parent
57
+ end
58
+
59
+ fragment = ::Nokogiri::XML::DocumentFragment.parse(content)
60
+
61
+ element.add_child(fragment)
62
+ end
63
+
54
64
  def add_text(element, text, cdata: false)
55
65
  return add_cdata(element, text) if cdata
56
66
 
@@ -66,10 +66,14 @@ module Lutaml
66
66
  xml.text(text)
67
67
  end
68
68
 
69
+ def add_xml_fragment(element, content)
70
+ element.raw(content)
71
+ end
72
+
69
73
  def add_text(element, text, cdata: false)
70
74
  return element.cdata(text) if cdata
71
75
 
72
- element << text
76
+ element.text(text)
73
77
  end
74
78
 
75
79
  def add_cdata(element, value)
@@ -6,10 +6,10 @@ module Lutaml
6
6
  module Model
7
7
  module XmlAdapter
8
8
  class NokogiriAdapter < XmlDocument
9
- def self.parse(xml)
10
- parsed = Nokogiri::XML(xml)
9
+ def self.parse(xml, options = {})
10
+ parsed = Nokogiri::XML(xml, nil, options[:encoding])
11
11
  root = NokogiriElement.new(parsed.root)
12
- new(root)
12
+ new(root, parsed.encoding)
13
13
  end
14
14
 
15
15
  def to_xml(options = {})
@@ -17,6 +17,8 @@ module Lutaml
17
17
 
18
18
  if options.key?(:encoding)
19
19
  builder_options[:encoding] = options[:encoding] unless options[:encoding].nil?
20
+ elsif options.key?(:parse_encoding)
21
+ builder_options[:encoding] = options[:parse_encoding]
20
22
  else
21
23
  builder_options[:encoding] = "UTF-8"
22
24
  end
@@ -5,7 +5,7 @@ module Lutaml
5
5
  module Model
6
6
  module XmlAdapter
7
7
  class OgaAdapter < XmlDocument
8
- def self.parse(xml)
8
+ def self.parse(xml, _options = {})
9
9
  parsed = Oga.parse_xml(xml)
10
10
  root = OgaElement.new(parsed)
11
11
  new(root)