lutaml-model 0.5.2 → 0.5.4

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/dependent-tests.yml +2 -0
  3. data/.rubocop_todo.yml +39 -13
  4. data/Gemfile +1 -0
  5. data/README.adoc +430 -52
  6. data/lib/lutaml/model/constants.rb +7 -0
  7. data/lib/lutaml/model/error/type/invalid_value_error.rb +19 -0
  8. data/lib/lutaml/model/error.rb +1 -0
  9. data/lib/lutaml/model/key_value_mapping.rb +31 -2
  10. data/lib/lutaml/model/mapping_hash.rb +8 -0
  11. data/lib/lutaml/model/mapping_rule.rb +8 -0
  12. data/lib/lutaml/model/schema/templates/simple_type.rb +247 -0
  13. data/lib/lutaml/model/schema/xml_compiler.rb +720 -0
  14. data/lib/lutaml/model/schema.rb +5 -0
  15. data/lib/lutaml/model/serialize.rb +33 -13
  16. data/lib/lutaml/model/toml_adapter/toml_rb_adapter.rb +1 -2
  17. data/lib/lutaml/model/type/hash.rb +11 -11
  18. data/lib/lutaml/model/utils.rb +7 -0
  19. data/lib/lutaml/model/version.rb +1 -1
  20. data/lib/lutaml/model/xml_adapter/nokogiri_adapter.rb +5 -1
  21. data/lib/lutaml/model/xml_adapter/xml_document.rb +11 -15
  22. data/lib/lutaml/model/xml_mapping.rb +4 -2
  23. data/lib/lutaml/model/xml_mapping_rule.rb +1 -4
  24. data/lib/lutaml/model.rb +1 -0
  25. data/spec/fixtures/xml/invalid_math_document.xml +4 -0
  26. data/spec/fixtures/xml/math_document_schema.xsd +56 -0
  27. data/spec/fixtures/xml/test_schema.xsd +53 -0
  28. data/spec/fixtures/xml/valid_math_document.xml +4 -0
  29. data/spec/lutaml/model/cdata_spec.rb +2 -2
  30. data/spec/lutaml/model/custom_model_spec.rb +7 -20
  31. data/spec/lutaml/model/key_value_mapping_spec.rb +27 -0
  32. data/spec/lutaml/model/map_all_spec.rb +188 -0
  33. data/spec/lutaml/model/mixed_content_spec.rb +15 -15
  34. data/spec/lutaml/model/render_nil_spec.rb +29 -0
  35. data/spec/lutaml/model/schema/xml_compiler_spec.rb +1431 -0
  36. data/spec/lutaml/model/with_child_mapping_spec.rb +2 -2
  37. data/spec/lutaml/model/xml_adapter/xml_namespace_spec.rb +52 -0
  38. data/spec/lutaml/model/xml_mapping_spec.rb +108 -1
  39. metadata +12 -2
@@ -2,6 +2,7 @@ require_relative "schema/json_schema"
2
2
  require_relative "schema/xsd_schema"
3
3
  require_relative "schema/relaxng_schema"
4
4
  require_relative "schema/yaml_schema"
5
+ require_relative "schema/xml_compiler"
5
6
 
6
7
  module Lutaml
7
8
  module Model
@@ -21,6 +22,10 @@ module Lutaml
21
22
  def self.to_yaml(klass, options = {})
22
23
  YamlSchema.generate(klass, options)
23
24
  end
25
+
26
+ def self.from_xml(xml, options = {})
27
+ XmlCompiler.to_models(xml, options)
28
+ end
24
29
  end
25
30
  end
26
31
  end
@@ -269,17 +269,22 @@ module Lutaml
269
269
 
270
270
  value = instance.send(name)
271
271
 
272
+ if rule.raw_mapping?
273
+ adapter = Lutaml::Model::Config.send(:"#{format}_adapter")
274
+ return adapter.parse(value, options)
275
+ end
276
+
272
277
  attribute = attributes[name]
273
278
 
274
- next hash.merge!(generate_hash_from_child_mappings(value, format, rule.root_mappings)) if rule.root_mapping?
279
+ next hash.merge!(generate_hash_from_child_mappings(attribute, value, format, rule.root_mappings)) if rule.root_mapping?
275
280
 
276
281
  value = if rule.child_mappings
277
- generate_hash_from_child_mappings(value, format, rule.child_mappings)
282
+ generate_hash_from_child_mappings(attribute, value, format, rule.child_mappings)
278
283
  else
279
284
  attribute.serialize(value, format, options)
280
285
  end
281
286
 
282
- next if Utils.blank?(value) && !rule.render_nil
287
+ next unless rule.render?(value)
283
288
 
284
289
  rule_from_name = rule.multiple_mappings? ? rule.from.first.to_s : rule.from.to_s
285
290
  hash[rule_from_name] = value
@@ -346,7 +351,7 @@ module Lutaml
346
351
  end
347
352
  end
348
353
 
349
- def generate_hash_from_child_mappings(value, format, child_mappings)
354
+ def generate_hash_from_child_mappings(attr, value, format, child_mappings)
350
355
  return value unless child_mappings
351
356
 
352
357
  hash = {}
@@ -365,7 +370,11 @@ module Lutaml
365
370
  value.each do |child_obj|
366
371
  map_key = nil
367
372
  map_value = {}
373
+ mapping_rules = attr.type.mappings_for(format)
374
+
368
375
  child_mappings.each do |attr_name, path|
376
+ mapping_rule = mapping_rules.find_by_to(attr_name)
377
+
369
378
  attr_value = child_obj.send(attr_name)
370
379
 
371
380
  attr_value = if attr_value.is_a?(Lutaml::Model::Serialize)
@@ -376,7 +385,7 @@ module Lutaml
376
385
  attr_value
377
386
  end
378
387
 
379
- next if Utils.blank?(attr_value)
388
+ next unless mapping_rule&.render?(attr_value)
380
389
 
381
390
  if path == :key
382
391
  map_key = attr_value
@@ -446,11 +455,11 @@ module Lutaml
446
455
  instance.mixed = mappings_for(:xml).mixed_content? || options[:mixed_content]
447
456
  end
448
457
 
449
- if doc["__schema_location"]
458
+ if doc["attributes"]&.key?("__schema_location")
450
459
  instance.schema_location = Lutaml::Model::SchemaLocation.new(
451
- schema_location: doc["__schema_location"][:schema_location],
452
- prefix: doc["__schema_location"][:prefix],
453
- namespace: doc["__schema_location"][:namespace],
460
+ schema_location: doc["attributes"]["__schema_location"][:schema_location],
461
+ prefix: doc["attributes"]["__schema_location"][:prefix],
462
+ namespace: doc["attributes"]["__schema_location"][:namespace],
454
463
  )
455
464
  end
456
465
 
@@ -461,18 +470,17 @@ module Lutaml
461
470
 
462
471
  attr = attribute_for_rule(rule)
463
472
 
464
- namespaced_names = rule.namespaced_names(options[:default_namespace])
465
-
466
473
  value = if rule.raw_mapping?
467
474
  doc.node.inner_xml
468
475
  elsif rule.content_mapping?
469
476
  doc[rule.content_key]
470
- elsif key = (namespaced_names & doc.keys).first
471
- doc[key]
477
+ elsif val = value_for_rule(doc, rule, options)
478
+ val
472
479
  else
473
480
  defaults_used << rule.to
474
481
  attr&.default || rule.to_value_for(instance)
475
482
  end
483
+
476
484
  value = normalize_xml_value(value, rule, attr, options)
477
485
  rule.deserialize(instance, value, attributes, self)
478
486
  end
@@ -484,6 +492,15 @@ module Lutaml
484
492
  instance
485
493
  end
486
494
 
495
+ def value_for_rule(doc, rule, options)
496
+ rule_names = rule.namespaced_names(options[:default_namespace])
497
+ hash = rule.attribute? ? doc["attributes"] : doc["elements"]
498
+ return unless hash
499
+
500
+ value_key = rule_names.find { |name| hash.key_exist?(name) }
501
+ hash.fetch(value_key) if value_key
502
+ end
503
+
487
504
  def apply_hash_mapping(doc, instance, format, options = {})
488
505
  mappings = options[:mappings] || mappings_for(format).mappings
489
506
  mappings.each do |rule|
@@ -496,6 +513,9 @@ module Lutaml
496
513
  value = names.collect do |rule_name|
497
514
  if rule.root_mapping?
498
515
  doc
516
+ elsif rule.raw_mapping?
517
+ adapter = Lutaml::Model::Config.public_send(:"#{format}_adapter")
518
+ adapter.new(doc).public_send(:"to_#{format}")
499
519
  elsif doc.key?(rule_name.to_s)
500
520
  doc[rule_name.to_s]
501
521
  elsif doc.key?(rule_name.to_sym)
@@ -6,8 +6,7 @@ module Lutaml
6
6
  module TomlAdapter
7
7
  class TomlRbAdapter < TomlDocument
8
8
  def self.parse(toml, _options = {})
9
- data = TomlRB.parse(toml)
10
- new(data)
9
+ TomlRB.parse(toml)
11
10
  end
12
11
 
13
12
  def to_toml(*)
@@ -19,18 +19,18 @@ module Lutaml
19
19
 
20
20
  hash = hash.to_h if hash.is_a?(Lutaml::Model::MappingHash)
21
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
22
+ normalized_hash = hash.transform_values do |value|
23
+ normalize_value(value)
33
24
  end
25
+
26
+ normalized_hash["elements"] || normalized_hash
27
+ end
28
+
29
+ def self.normalize_value(value)
30
+ return value unless value.is_a?(::Hash)
31
+
32
+ nested = normalize_hash(value)
33
+ nested.is_a?(::Hash) ? nested.except("text") : nested
34
34
  end
35
35
 
36
36
  def self.serialize(value)
@@ -42,6 +42,13 @@ module Lutaml
42
42
  value.respond_to?(:empty?) ? value.empty? : value.nil?
43
43
  end
44
44
 
45
+ def empty_collection?(collection)
46
+ return false if collection.nil?
47
+ return false unless [Array, Hash].include?(collection.class)
48
+
49
+ collection.empty?
50
+ end
51
+
45
52
  def add_method_if_not_defined(klass, method_name, &block)
46
53
  unless klass.method_defined?(method_name)
47
54
  klass.class_eval do
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Lutaml
4
4
  module Model
5
- VERSION = "0.5.2"
5
+ VERSION = "0.5.4"
6
6
  end
7
7
  end
@@ -124,7 +124,11 @@ module Lutaml
124
124
  end
125
125
 
126
126
  attributes = {}
127
- node.attributes.transform_values do |attr|
127
+
128
+ # Using `attribute_nodes` instead of `attributes` because
129
+ # `attribute_nodes` handles name collisions as well
130
+ # More info: https://devdocs.io/nokogiri/nokogiri/xml/node#method-i-attributes
131
+ node.attribute_nodes.each do |attr|
128
132
  name = if attr.namespace
129
133
  "#{attr.namespace.prefix}:#{attr.name}"
130
134
  else
@@ -77,26 +77,21 @@ module Lutaml
77
77
  result.node = element
78
78
  result.item_order = element.order
79
79
 
80
- element.children.each_with_object(result) do |child, hash|
80
+ element.children.each do |child|
81
81
  if klass&.<= Serialize
82
82
  attr = klass.attribute_for_child(child.name,
83
83
  format)
84
84
  end
85
85
 
86
- value = if child.text?
87
- child.text
88
- else
89
- parse_element(child, attr&.type || klass, format)
90
- end
91
-
92
- hash[child.namespaced_name] = if hash[child.namespaced_name]
93
- [hash[child.namespaced_name], value].flatten
94
- else
95
- value
96
- end
86
+ next result.assign_or_append_value(child.name, child.text) if child.text?
87
+
88
+ result["elements"] ||= Lutaml::Model::MappingHash.new
89
+ result["elements"].assign_or_append_value(child.namespaced_name, parse_element(child, attr&.type || klass, format))
97
90
  end
98
91
 
99
- result.merge(attributes_hash(element))
92
+ result["attributes"] = attributes_hash(element) if element.attributes&.any?
93
+
94
+ result
100
95
  end
101
96
 
102
97
  def attributes_hash(element)
@@ -167,8 +162,9 @@ module Lutaml
167
162
  def add_value(xml, value, attribute, cdata: false)
168
163
  if !value.nil?
169
164
  serialized_value = attribute.type.serialize(value)
170
-
171
- if attribute.type == Lutaml::Model::Type::Hash
165
+ if attribute.raw?
166
+ xml.add_xml_fragment(xml, value)
167
+ elsif attribute.type == Lutaml::Model::Type::Hash
172
168
  serialized_value.each do |key, val|
173
169
  xml.create_and_add_element(key) do |element|
174
170
  element.text(val)
@@ -149,10 +149,10 @@ module Lutaml
149
149
  prefix: (prefix_set = false
150
150
  nil)
151
151
  )
152
- validate!("__raw_mapping", to, with, type: TYPES[:all_content])
152
+ validate!(Constants::RAW_MAPPING_KEY, to, with, type: TYPES[:all_content])
153
153
 
154
154
  rule = XmlMappingRule.new(
155
- "__raw_mapping",
155
+ Constants::RAW_MAPPING_KEY,
156
156
  to: to,
157
157
  render_nil: render_nil,
158
158
  render_default: render_default,
@@ -168,6 +168,8 @@ module Lutaml
168
168
  @raw_mapping = rule
169
169
  end
170
170
 
171
+ alias map_all_content map_all
172
+
171
173
  def validate!(key, to, with, type: nil)
172
174
  validate_mappings!(type)
173
175
 
@@ -59,10 +59,6 @@ module Lutaml
59
59
  name.nil?
60
60
  end
61
61
 
62
- def raw_mapping?
63
- name == "__raw_mapping"
64
- end
65
-
66
62
  def content_key
67
63
  cdata ? "#cdata-section" : "text"
68
64
  end
@@ -111,6 +107,7 @@ module Lutaml
111
107
  prefix: prefix.dup,
112
108
  mixed_content: mixed_content,
113
109
  namespace_set: namespace_set?,
110
+ attribute: attribute,
114
111
  prefix_set: prefix_set?,
115
112
  default_namespace: default_namespace.dup,
116
113
  )
data/lib/lutaml/model.rb CHANGED
@@ -10,6 +10,7 @@ require_relative "model/yaml_adapter/standard_yaml_adapter"
10
10
  require_relative "model/xml_adapter"
11
11
  require_relative "model/toml_adapter"
12
12
  require_relative "model/error"
13
+ require_relative "model/constants"
13
14
 
14
15
  module Lutaml
15
16
  module Model
@@ -0,0 +1,4 @@
1
+ <MathDocument>
2
+ <Title>Example Title</Title>
3
+ <IPV4Address>Example Address</IPV4Address>
4
+ </MathDocument>
@@ -0,0 +1,56 @@
1
+ <schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns="http://schemas.openxmlformats.org/officeDocument/2006/math">
2
+ <xs:simpleType name="StringDatatype">
3
+ <xs:annotation>
4
+ <xs:documentation>
5
+ A string data type to be used for the example.
6
+ </xs:documentation>
7
+ </xs:annotation>
8
+ <xs:restriction base="xs:string"></xs:restriction>
9
+ </xs:simpleType>
10
+
11
+ <xs:simpleType name="IPV4AddressDatatype">
12
+ <xs:annotation>
13
+ <xs:documentation>
14
+ An Internet Protocol version 4 address represented using dotted-quad syntax as defined in section 3.2 of RFC2673.
15
+ </xs:documentation>
16
+ </xs:annotation>
17
+ <xs:restriction base="StringDatatype">
18
+ <xs:pattern value="((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]).){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])" />
19
+ </xs:restriction>
20
+ </xs:simpleType>
21
+
22
+ <xs:simpleType name="whiteSpaces">
23
+ <xs:annotation>
24
+ <xs:documentation>
25
+ A non-empty string of Unicode characters with leading and trailing whitespace
26
+ disallowed. Whitespace is: U+9, U+10, U+32 or [ \n\t]+
27
+ </xs:documentation>
28
+ </xs:annotation>
29
+ <xs:restriction base="xs:string">
30
+ <xs:annotation>
31
+ <xs:documentation>
32
+ The 'string' datatype restricts the XSD type by prohibiting leading
33
+ and trailing whitespace, and something (not only whitespace) is required.
34
+ </xs:documentation>
35
+ </xs:annotation>
36
+ <xs:pattern value="\S(.*\S)?">
37
+ <xs:annotation>
38
+ <xs:documentation>
39
+ This pattern ensures that leading and trailing whitespace is
40
+ disallowed. This helps to even the user experience between implementations
41
+ related to whitespace.
42
+ </xs:documentation>
43
+ </xs:annotation>
44
+ </xs:pattern>
45
+ </xs:restriction>
46
+ </xs:simpleType>
47
+
48
+ <xs:complexType name="MathDocument">
49
+ <xs:choice>
50
+ <xs:element name="Title" type="whiteSpaces"/>
51
+ <xs:element name="IPV4Address" type="IPV4AddressDatatype"/>
52
+ </xs:choice>
53
+ </xs:complexType>
54
+
55
+ <xs:element name="MathDocument" type="MathDocument"/>
56
+ </schema>
@@ -0,0 +1,53 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified" attributeFormDefault="qualified" blockDefault="#all" targetNamespace="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">
3
+ <xsd:simpleType name="ST_Integer255">
4
+ <xsd:annotation>
5
+ <xsd:documentation>Integer value (1 to 255)</xsd:documentation>
6
+ </xsd:annotation>
7
+ <xsd:restriction base="xsd:integer">
8
+ <xsd:minInclusive value="1" />
9
+ <xsd:maxInclusive value="255" />
10
+ </xsd:restriction>
11
+ </xsd:simpleType>
12
+ <xsd:complexType name="CT_Integer255">
13
+ <xsd:attribute name="val" type="ST_Integer255" use="required">
14
+ <xsd:annotation>
15
+ <xsd:documentation>Value</xsd:documentation>
16
+ </xsd:annotation>
17
+ </xsd:attribute>
18
+ </xsd:complexType>
19
+ <xsd:simpleType name="ST_Integer2">
20
+ <xsd:annotation>
21
+ <xsd:documentation>Integer value (-2 to 2)</xsd:documentation>
22
+ </xsd:annotation>
23
+ <xsd:restriction base="xsd:integer">
24
+ <xsd:minInclusive value="-2" />
25
+ <xsd:maxInclusive value="2" />
26
+ </xsd:restriction>
27
+ </xsd:simpleType>
28
+ <xsd:complexType name="CT_Integer2">
29
+ <xsd:attribute name="val" type="ST_Integer2" use="required">
30
+ <xsd:annotation>
31
+ <xsd:documentation>Value</xsd:documentation>
32
+ </xsd:annotation>
33
+ </xsd:attribute>
34
+ </xsd:complexType>
35
+ <xsd:element name="MathTest" type="CT_Integer255">
36
+ <xsd:annotation>
37
+ <xsd:documentation>Main Test class</xsd:documentation>
38
+ </xsd:annotation>
39
+ </xsd:element>
40
+ <xsd:element name="MathTest1" type="CT_Integer2">
41
+ <xsd:annotation>
42
+ <xsd:documentation>Main Test class</xsd:documentation>
43
+ </xsd:annotation>
44
+ </xsd:element>
45
+ <xsd:complexType name="CT_MathTest">
46
+ <xsd:group>
47
+ <xsd:sequence>
48
+ <xsd:element ref="MathTest" />
49
+ <xsd:element ref="MathTest1" />
50
+ </xsd:sequence>
51
+ </xsd:group>
52
+ </xsd:complexType>
53
+ </xsd:schema>
@@ -0,0 +1,4 @@
1
+ <MathDocument>
2
+ <Title>Example Title</Title>
3
+ <IPV4Address>192.168.1.1</IPV4Address>
4
+ </MathDocument>
@@ -118,8 +118,8 @@ module CDATA
118
118
  def child_from_xml(model, value)
119
119
  model.child_mapper ||= CustomModelChild.new
120
120
 
121
- model.child_mapper.street = value["street"].text
122
- model.child_mapper.city = value["city"].text
121
+ model.child_mapper.street = value["elements"]["street"].text
122
+ model.child_mapper.city = value["elements"]["city"].text
123
123
  end
124
124
  end
125
125
 
@@ -56,8 +56,8 @@ class CustomModelParentMapper < Lutaml::Model::Serializable
56
56
  def child_from_xml(model, value)
57
57
  model.child_mapper ||= CustomModelChild.new
58
58
 
59
- model.child_mapper.street = value["street"].text
60
- model.child_mapper.city = value["city"].text
59
+ model.child_mapper.street = value["elements"]["street"].text
60
+ model.child_mapper.city = value["elements"]["city"].text
61
61
  end
62
62
  end
63
63
 
@@ -126,10 +126,10 @@ module CustomModelSpecs
126
126
 
127
127
  def bibdata_from_xml(model, value)
128
128
  model.bibdata = BibliographicItem.new(
129
- "type" => value["type"],
130
- "title" => value["title"],
131
- "language" => value["title"]["language"],
132
- "schema_version" => value["schema-version"],
129
+ "type" => value["attributes"]["type"],
130
+ "title" => value["elements"]["title"],
131
+ "language" => value["elements"]["title"]["attributes"]["language"],
132
+ "schema_version" => value["attributes"]["schema-version"],
133
133
  )
134
134
  end
135
135
 
@@ -410,22 +410,9 @@ RSpec.describe "CustomModel" do
410
410
  </MixedWithNestedContent>
411
411
  XML
412
412
 
413
- expected_xml = <<~XML
414
- <MixedWithNestedContent>
415
- <street>
416
- A &lt;p&gt;b&lt;/p&gt; B &lt;p&gt;c&lt;/p&gt; C
417
- </street>
418
- <bibdata type="collection" schema-version="v1.2.8">
419
- <title language="en">
420
- JCGM Collection 1
421
- </title>
422
- </bibdata>
423
- </MixedWithNestedContent>
424
- XML
425
-
426
413
  bibdata = CustomModelSpecs::MixedWithNestedContent.from_xml(xml)
427
414
 
428
- expect(bibdata.to_xml).to be_equivalent_to(expected_xml)
415
+ expect(bibdata.to_xml).to be_equivalent_to(xml)
429
416
  end
430
417
  end
431
418
  end
@@ -83,4 +83,31 @@ RSpec.describe Lutaml::Model::KeyValueMapping do
83
83
  expect(m.custom_methods.object_id).not_to eq(dup_m.custom_methods.object_id)
84
84
  end
85
85
  end
86
+
87
+ context "with map_all option" do
88
+ before do
89
+ mapping.map_all(
90
+ render_nil: true,
91
+ delegate: :container,
92
+ )
93
+ end
94
+
95
+ it "handles JSON mapping" do
96
+ expect(mapping.mappings[0].render_nil).to be true
97
+ expect(mapping.mappings[0].delegate).to eq(:container)
98
+ expect(mapping.mappings[0].raw_mapping?).to be true
99
+ end
100
+
101
+ it "handles YAML mapping" do
102
+ expect(mapping.mappings[0].render_nil).to be true
103
+ expect(mapping.mappings[0].delegate).to eq(:container)
104
+ expect(mapping.mappings[0].raw_mapping?).to be true
105
+ end
106
+
107
+ it "handles TOML mapping" do
108
+ expect(mapping.mappings[0].render_nil).to be true
109
+ expect(mapping.mappings[0].delegate).to eq(:container)
110
+ expect(mapping.mappings[0].raw_mapping?).to be true
111
+ end
112
+ end
86
113
  end