shale 0.9.0 → 1.0.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.
@@ -139,55 +139,63 @@ module Shale
139
139
  <%- unless type.references.empty? -%>
140
140
 
141
141
  <%- type.references.each do |property| -%>
142
- require_relative '<%= property.type.file_name %>'
142
+ require_relative '<%= type.relative_path(property.type.file_name) %>'
143
143
  <%- end -%>
144
144
  <%- end -%>
145
145
 
146
- class <%= type.name %> < Shale::Mapper
146
+ <%- type.modules.each_with_index do |name, i| -%>
147
+ <%= ' ' * i %>module <%= name %>
148
+ <%- end -%>
149
+ <%- indent = ' ' * type.modules.length -%>
150
+ <%= indent %>class <%= type.root_name %> < Shale::Mapper
147
151
  <%- type.properties.select(&:content?).each do |property| -%>
148
- attribute :<%= property.attribute_name %>, <%= property.type.name -%>
152
+ <%= indent %>attribute :<%= property.attribute_name %>, <%= property.type.name -%>
149
153
  <%- if property.collection? %>, collection: true<% end -%>
150
154
  <%- unless property.default.nil? %>, default: -> { <%= property.default %> }<% end %>
151
155
  <%- end -%>
152
156
  <%- type.properties.select(&:attribute?).each do |property| -%>
153
- attribute :<%= property.attribute_name %>, <%= property.type.name -%>
157
+ <%= indent %>attribute :<%= property.attribute_name %>, <%= property.type.name -%>
154
158
  <%- if property.collection? %>, collection: true<% end -%>
155
159
  <%- unless property.default.nil? %>, default: -> { <%= property.default %> }<% end %>
156
160
  <%- end -%>
157
161
  <%- type.properties.select(&:element?).each do |property| -%>
158
- attribute :<%= property.attribute_name %>, <%= property.type.name -%>
162
+ <%= indent %>attribute :<%= property.attribute_name %>, <%= property.type.name -%>
159
163
  <%- if property.collection? %>, collection: true<% end -%>
160
164
  <%- unless property.default.nil? %>, default: -> { <%= property.default %> }<% end %>
161
165
  <%- end -%>
162
166
 
163
- xml do
164
- root '<%= type.root %>'
167
+ <%= indent %>xml do
168
+ <%= indent %>root '<%= type.root %>'
165
169
  <%- if type.namespace -%>
166
- namespace '<%= type.namespace %>', '<%= type.prefix %>'
170
+ <%= indent %>namespace '<%= type.namespace %>', '<%= type.prefix %>'
167
171
  <%- end -%>
168
172
  <%- unless type.properties.empty? -%>
169
173
 
170
174
  <%- type.properties.select(&:content?).each do |property| -%>
171
- map_content to: :<%= property.attribute_name %>
175
+ <%= indent %>map_content to: :<%= property.attribute_name %>
172
176
  <%- end -%>
173
177
  <%- type.properties.select(&:attribute?).each do |property| -%>
174
- map_attribute '<%= property.mapping_name %>', to: :<%= property.attribute_name -%>
178
+ <%= indent %>map_attribute '<%= property.mapping_name %>', to: :<%= property.attribute_name -%>
175
179
  <%- if property.namespace %>, prefix: '<%= property.prefix %>'<%- end -%>
176
180
  <%- if property.namespace %>, namespace: '<%= property.namespace %>'<% end %>
177
181
  <%- end -%>
178
182
  <%- type.properties.select(&:element?).each do |property| -%>
179
- map_element '<%= property.mapping_name %>', to: :<%= property.attribute_name -%>
183
+ <%= indent %>map_element '<%= property.mapping_name %>', to: :<%= property.attribute_name -%>
180
184
  <%- if type.namespace != property.namespace %>, prefix: <%= property.prefix ? "'\#{property.prefix}'" : 'nil' %><%- end -%>
181
185
  <%- if type.namespace != property.namespace %>, namespace: <%= property.namespace ? "'\#{property.namespace}'" : 'nil' %><% end %>
182
186
  <%- end -%>
183
187
  <%- end -%>
184
- end
185
- end
188
+ <%= indent %>end
189
+ <%= indent %>end
190
+ <%- type.modules.length.times do |i| -%>
191
+ <%= ' ' * (type.modules.length - i - 1) %>end
192
+ <%- end -%>
186
193
  TEMPLATE
187
194
 
188
195
  # Generate Shale models from XML Schema and return them as a Ruby Array of objects
189
196
  #
190
197
  # @param [Array<String>] schemas
198
+ # @param [Hash<String, String>, nil] namespace_mapping
191
199
  #
192
200
  # @raise [AdapterError] when XML adapter is not set or Ox adapter is used
193
201
  # @raise [SchemaError] when XML Schema has errors
@@ -198,7 +206,7 @@ module Shale
198
206
  # Shale::Schema::XMLCompiler.new.as_models([schema1, schema2])
199
207
  #
200
208
  # @api public
201
- def as_models(schemas)
209
+ def as_models(schemas, namespace_mapping: nil)
202
210
  unless Shale.xml_adapter
203
211
  raise AdapterError, XML_ADAPTER_NOT_SET_MESSAGE
204
212
  end
@@ -212,6 +220,7 @@ module Shale
212
220
  Shale.xml_adapter.load(schema)
213
221
  end
214
222
 
223
+ @namespace_mapping = namespace_mapping || {}
215
224
  @elements_repository = {}
216
225
  @attributes_repository = {}
217
226
  @simple_types_repository = {}
@@ -244,7 +253,7 @@ module Shale
244
253
  duplicates[type.name] += 1
245
254
 
246
255
  if total_duplicates[type.name] > 1
247
- type.name = format("#{type.name}%d", duplicates[type.name])
256
+ type.root_name = format("#{type.root_name}%d", duplicates[type.name])
248
257
  end
249
258
  end
250
259
 
@@ -256,6 +265,7 @@ module Shale
256
265
  # Generate Shale models from XML Schema
257
266
  #
258
267
  # @param [Array<String>] schemas
268
+ # @param [Hash<String, String>, nil] namespace_mapping
259
269
  #
260
270
  # @raise [SchemaError] when XML Schema has errors
261
271
  #
@@ -265,8 +275,8 @@ module Shale
265
275
  # Shale::Schema::XMLCompiler.new.to_models([schema1, schema2])
266
276
  #
267
277
  # @api public
268
- def to_models(schemas)
269
- types = as_models(schemas)
278
+ def to_models(schemas, namespace_mapping: nil)
279
+ types = as_models(schemas, namespace_mapping: namespace_mapping)
270
280
 
271
281
  types.to_h do |type|
272
282
  [type.file_name, MODEL_TEMPLATE.result(binding)]
@@ -415,6 +425,10 @@ module Shale
415
425
  type = type.sub(/^#{prefix}/, name)
416
426
  end
417
427
 
428
+ if namespaces.key?('xmlns') && !type.include?(':')
429
+ type = "#{namespaces['xmlns']}:#{type}"
430
+ end
431
+
418
432
  type
419
433
  end
420
434
 
@@ -535,7 +549,13 @@ module Shale
535
549
  name = node.attributes['name'] || node.parent.attributes['name']
536
550
  prefix, namespace = resolve_complex_type_namespace(node)
537
551
 
538
- @complex_types[id] = Compiler::XMLComplex.new(id, name, prefix, namespace)
552
+ @complex_types[id] = Compiler::XMLComplex.new(
553
+ id,
554
+ name,
555
+ prefix,
556
+ namespace,
557
+ @namespace_mapping[namespace]
558
+ )
539
559
  end
540
560
  end
541
561
  end
@@ -571,6 +591,14 @@ module Shale
571
591
  infer_type_from_xs_type(type, namespaces)
572
592
  end
573
593
 
594
+ # Infer type from XSD type
595
+ #
596
+ # @param [String] type
597
+ # @param [Hash<String, String>] namespaces
598
+ #
599
+ # @return [Shale::Schema::Compiler::<any>]
600
+ #
601
+ # @api private
574
602
  def infer_type_from_xs_type(type, namespaces)
575
603
  type = replace_ns_prefixes(type, namespaces)
576
604
 
@@ -84,7 +84,7 @@ module Shale
84
84
 
85
85
  root_element = TypedElement.new(
86
86
  name: klass.xml_mapping.unprefixed_root,
87
- type: [default_namespace.prefix, schematize(klass.name)].compact.join(':'),
87
+ type: [default_namespace.prefix, schematize(klass.model.name)].compact.join(':'),
88
88
  required: true
89
89
  )
90
90
 
@@ -195,7 +195,7 @@ module Shale
195
195
  end
196
196
 
197
197
  complex = ComplexType.new(
198
- schematize(type.name),
198
+ schematize(type.model.name),
199
199
  children,
200
200
  mixed: !type.xml_mapping.content.nil?
201
201
  )
@@ -287,7 +287,7 @@ module Shale
287
287
  # @api private
288
288
  def get_xml_type_for_attribute(type, namespace)
289
289
  if mapper_type?(type)
290
- [namespace.prefix, schematize(type.name)].compact.join(':')
290
+ [namespace.prefix, schematize(type.model.name)].compact.join(':')
291
291
  else
292
292
  "xs:#{self.class.get_xml_type(type)}"
293
293
  end
data/lib/shale/schema.rb CHANGED
@@ -38,6 +38,7 @@ module Shale
38
38
  #
39
39
  # @param [Array<String>] schemas
40
40
  # @param [String, nil] root_name
41
+ # @param [Hash<String, String>, nil] namespace_mapping
41
42
  #
42
43
  # @return [Array<String>]
43
44
  #
@@ -46,8 +47,12 @@ module Shale
46
47
  # # => [model1, model2, model3]
47
48
  #
48
49
  # @api public
49
- def self.from_json(schemas, root_name: nil)
50
- JSONCompiler.new.to_models(schemas, root_name: root_name)
50
+ def self.from_json(schemas, root_name: nil, namespace_mapping: nil)
51
+ JSONCompiler.new.to_models(
52
+ schemas,
53
+ root_name: root_name,
54
+ namespace_mapping: namespace_mapping
55
+ )
51
56
  end
52
57
 
53
58
  # Generate XML Schema from Shale model
@@ -71,6 +76,7 @@ module Shale
71
76
  # Generate Shale model from XML Schema
72
77
  #
73
78
  # @param [Array<String>] schemas
79
+ # @param [Hash<String, String>, nil] namespace_mapping
74
80
  #
75
81
  # @return [Array<String>]
76
82
  #
@@ -79,8 +85,8 @@ module Shale
79
85
  # # => [model1, model2, model3]
80
86
  #
81
87
  # @api public
82
- def self.from_xml(schemas)
83
- XMLCompiler.new.to_models(schemas)
88
+ def self.from_xml(schemas, namespace_mapping: nil)
89
+ XMLCompiler.new.to_models(schemas, namespace_mapping: namespace_mapping)
84
90
  end
85
91
  end
86
92
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../error'
4
+ require_relative '../mapping/delegates'
4
5
  require_relative '../mapping/group/dict_grouping'
5
6
  require_relative '../mapping/group/xml_grouping'
6
7
  require_relative 'value'
@@ -47,6 +48,7 @@ module Shale
47
48
 
48
49
  mapping_keys = #{format}_mapping.keys
49
50
  grouping = Shale::Mapping::Group::DictGrouping.new
51
+ delegates = Shale::Mapping::Delegates.new
50
52
 
51
53
  only = to_partial_render_attributes(only)
52
54
  except = to_partial_render_attributes(except)
@@ -66,7 +68,8 @@ module Shale
66
68
  mapper.send(mapping.method_from, instance, value)
67
69
  end
68
70
  else
69
- attribute = attributes[mapping.attribute]
71
+ receiver_attributes = get_receiver_attributes(mapping)
72
+ attribute = receiver_attributes[mapping.attribute]
70
73
  next unless attribute
71
74
 
72
75
  if only
@@ -79,20 +82,22 @@ module Shale
79
82
  next if except.key?(attribute.name) && attribute_except.nil?
80
83
  end
81
84
 
85
+ casted_value = nil
86
+
82
87
  if value.nil?
83
- instance.send(attribute.setter, nil)
88
+ casted_value = nil
84
89
  elsif attribute.collection?
85
- [*value].each do |val|
86
- if val
90
+ casted_value = (value.is_a?(Array) ? value : [value]).map do |val|
91
+ unless val.nil?
87
92
  val = attribute.type.of_#{format}(
88
93
  val,
89
94
  only: attribute_only,
90
95
  except: attribute_except,
91
96
  context: context
92
97
  )
93
- end
94
98
 
95
- instance.send(attribute.name) << attribute.type.cast(val)
99
+ attribute.type.cast(val)
100
+ end
96
101
  end
97
102
  else
98
103
  val = attribute.type.of_#{format}(
@@ -101,11 +106,23 @@ module Shale
101
106
  except: attribute_except,
102
107
  context: context
103
108
  )
104
- instance.send(attribute.setter, attribute.type.cast(val))
109
+
110
+ casted_value = attribute.type.cast(val)
111
+ end
112
+
113
+ if mapping.receiver
114
+ delegates.add(attributes[mapping.receiver], attribute.setter, casted_value)
115
+ else
116
+ instance.send(attribute.setter, casted_value)
105
117
  end
106
118
  end
107
119
  end
108
120
 
121
+ delegates.each do |delegate|
122
+ receiver = get_receiver(instance, delegate.receiver_attribute)
123
+ receiver.send(delegate.setter, delegate.value)
124
+ end
125
+
109
126
  grouping.each do |group|
110
127
  mapper = new
111
128
 
@@ -167,7 +184,14 @@ module Shale
167
184
  mapper.send(mapping.method_to, instance, hash)
168
185
  end
169
186
  else
170
- attribute = attributes[mapping.attribute]
187
+ if mapping.receiver
188
+ receiver = instance.send(mapping.receiver)
189
+ else
190
+ receiver = instance
191
+ end
192
+
193
+ receiver_attributes = get_receiver_attributes(mapping)
194
+ attribute = receiver_attributes[mapping.attribute]
171
195
  next unless attribute
172
196
 
173
197
  if only
@@ -180,7 +204,7 @@ module Shale
180
204
  next if except.key?(attribute.name) && attribute_except.nil?
181
205
  end
182
206
 
183
- value = instance.send(attribute.name)
207
+ value = receiver.send(attribute.name) if receiver
184
208
 
185
209
  if value.nil?
186
210
  hash[mapping.name] = nil if mapping.render_nil?
@@ -414,6 +438,7 @@ module Shale
414
438
  .each { |attr| instance.send(attr.setter, attr.default.call) }
415
439
 
416
440
  grouping = Shale::Mapping::Group::XmlGrouping.new
441
+ delegates = Shale::Mapping::Delegates.new
417
442
 
418
443
  only = to_partial_render_attributes(only)
419
444
  except = to_partial_render_attributes(except)
@@ -433,16 +458,29 @@ module Shale
433
458
  mapper.send(mapping.method_from, instance, value)
434
459
  end
435
460
  else
436
- attribute = attributes[mapping.attribute]
461
+ receiver_attributes = get_receiver_attributes(mapping)
462
+ attribute = receiver_attributes[mapping.attribute]
437
463
  next unless attribute
438
464
 
439
465
  next if only && !only.key?(attribute.name)
440
466
  next if except&.key?(attribute.name)
441
467
 
468
+ casted_value = attribute.type.cast(value)
469
+
442
470
  if attribute.collection?
443
- instance.send(attribute.name) << attribute.type.cast(value)
471
+ if mapping.receiver
472
+ delegates.add_collection(
473
+ attributes[mapping.receiver],
474
+ attribute.setter,
475
+ casted_value
476
+ )
477
+ else
478
+ instance.send(attribute.name) << casted_value
479
+ end
480
+ elsif mapping.receiver
481
+ delegates.add(attributes[mapping.receiver], attribute.setter, casted_value)
444
482
  else
445
- instance.send(attribute.setter, attribute.type.cast(value))
483
+ instance.send(attribute.setter, casted_value)
446
484
  end
447
485
  end
448
486
  end
@@ -461,7 +499,8 @@ module Shale
461
499
  mapper.send(content_mapping.method_from, instance, element)
462
500
  end
463
501
  else
464
- attribute = attributes[content_mapping.attribute]
502
+ receiver_attributes = get_receiver_attributes(content_mapping)
503
+ attribute = receiver_attributes[content_mapping.attribute]
465
504
 
466
505
  if attribute
467
506
  skip = false
@@ -471,8 +510,13 @@ module Shale
471
510
  skip = true if except&.key?(attribute.name)
472
511
 
473
512
  unless skip
474
- value = attribute.type.of_xml(element)
475
- instance.send(attribute.setter, attribute.type.cast(value))
513
+ value = attribute.type.cast(attribute.type.of_xml(element))
514
+
515
+ if content_mapping.receiver
516
+ delegates.add(attributes[content_mapping.receiver], attribute.setter, value)
517
+ else
518
+ instance.send(attribute.setter, value)
519
+ end
476
520
  end
477
521
  # rubocop:enable Metrics/BlockNesting
478
522
  end
@@ -494,7 +538,8 @@ module Shale
494
538
  mapper.send(mapping.method_from, instance, node)
495
539
  end
496
540
  else
497
- attribute = attributes[mapping.attribute]
541
+ receiver_attributes = get_receiver_attributes(mapping)
542
+ attribute = receiver_attributes[mapping.attribute]
498
543
  next unless attribute
499
544
 
500
545
  if only
@@ -514,14 +559,31 @@ module Shale
514
559
  context: context
515
560
  )
516
561
 
562
+ casted_value = attribute.type.cast(value)
563
+
517
564
  if attribute.collection?
518
- instance.send(attribute.name) << attribute.type.cast(value)
565
+ if mapping.receiver
566
+ delegates.add_collection(
567
+ attributes[mapping.receiver],
568
+ attribute.setter,
569
+ casted_value
570
+ )
571
+ else
572
+ instance.send(attribute.name) << casted_value
573
+ end
574
+ elsif mapping.receiver
575
+ delegates.add(attributes[mapping.receiver], attribute.setter, casted_value)
519
576
  else
520
- instance.send(attribute.setter, attribute.type.cast(value))
577
+ instance.send(attribute.setter, casted_value)
521
578
  end
522
579
  end
523
580
  end
524
581
 
582
+ delegates.each do |delegate|
583
+ receiver = get_receiver(instance, delegate.receiver_attribute)
584
+ receiver.send(delegate.setter, delegate.value)
585
+ end
586
+
525
587
  grouping.each do |group|
526
588
  mapper = new
527
589
 
@@ -626,13 +688,20 @@ module Shale
626
688
  mapper.send(mapping.method_to, instance, element, doc)
627
689
  end
628
690
  else
629
- attribute = attributes[mapping.attribute]
691
+ if mapping.receiver
692
+ receiver = instance.send(mapping.receiver)
693
+ else
694
+ receiver = instance
695
+ end
696
+
697
+ receiver_attributes = get_receiver_attributes(mapping)
698
+ attribute = receiver_attributes[mapping.attribute]
630
699
  next unless attribute
631
700
 
632
701
  next if only && !only.key?(attribute.name)
633
702
  next if except&.key?(attribute.name)
634
703
 
635
- value = instance.send(attribute.name)
704
+ value = receiver.send(attribute.name) if receiver
636
705
 
637
706
  if mapping.render_nil? || !value.nil?
638
707
  doc.add_namespace(mapping.namespace.prefix, mapping.namespace.name)
@@ -655,7 +724,14 @@ module Shale
655
724
  mapper.send(content_mapping.method_to, instance, element, doc)
656
725
  end
657
726
  else
658
- attribute = attributes[content_mapping.attribute]
727
+ if content_mapping.receiver
728
+ receiver = instance.send(content_mapping.receiver)
729
+ else
730
+ receiver = instance
731
+ end
732
+
733
+ receiver_attributes = get_receiver_attributes(content_mapping)
734
+ attribute = receiver_attributes[content_mapping.attribute]
659
735
 
660
736
  if attribute
661
737
  skip = false
@@ -665,7 +741,7 @@ module Shale
665
741
  skip = true if except&.key?(attribute.name)
666
742
 
667
743
  unless skip
668
- value = instance.send(attribute.name)
744
+ value = receiver.send(attribute.name) if receiver
669
745
 
670
746
  if content_mapping.cdata
671
747
  doc.create_cdata(value.to_s, element)
@@ -690,7 +766,14 @@ module Shale
690
766
  mapper.send(mapping.method_to, instance, element, doc)
691
767
  end
692
768
  else
693
- attribute = attributes[mapping.attribute]
769
+ if mapping.receiver
770
+ receiver = instance.send(mapping.receiver)
771
+ else
772
+ receiver = instance
773
+ end
774
+
775
+ receiver_attributes = get_receiver_attributes(mapping)
776
+ attribute = receiver_attributes[mapping.attribute]
694
777
  next unless attribute
695
778
 
696
779
  if only
@@ -703,7 +786,7 @@ module Shale
703
786
  next if except.key?(attribute.name) && attribute_except.nil?
704
787
  end
705
788
 
706
- value = instance.send(attribute.name)
789
+ value = receiver.send(attribute.name) if receiver
707
790
 
708
791
  if mapping.render_nil? || !value.nil?
709
792
  doc.add_namespace(mapping.namespace.prefix, mapping.namespace.name)
@@ -827,6 +910,64 @@ module Shale
827
910
  end
828
911
  end.to_h
829
912
  end
913
+
914
+ # Get receiver attributes for given mapping
915
+ #
916
+ # @param [Shale::Mapping::Descriptor::Dict] mapping
917
+ #
918
+ # @raise [AttributeNotDefinedError]
919
+ # @raise [NotAShaleMapperError]
920
+ #
921
+ # @return [Hash<Symbol, Shale::Attribute>]
922
+ #
923
+ # @api private
924
+ def get_receiver_attributes(mapping)
925
+ return attributes unless mapping.receiver
926
+
927
+ receiver_attribute = attributes[mapping.receiver]
928
+
929
+ unless receiver_attribute
930
+ msg = "attribute '#{mapping.receiver}' is not defined on #{self} mapper"
931
+ raise AttributeNotDefinedError, msg
932
+ end
933
+
934
+ unless receiver_attribute.type < Mapper
935
+ msg = "attribute '#{mapping.receiver}' is not a descendant of Shale::Mapper type"
936
+ raise NotAShaleMapperError, msg
937
+ end
938
+
939
+ if receiver_attribute.collection?
940
+ msg = "attribute '#{mapping.receiver}' can't be a collection"
941
+ raise NotAShaleMapperError, msg
942
+ end
943
+
944
+ receiver_attribute.type.attributes
945
+ end
946
+
947
+ # Get receiver for given mapping
948
+ #
949
+ # @param [any] instance
950
+ # @param [Shale::Attribute] receiver_attribute
951
+ #
952
+ # @return [Array]
953
+ #
954
+ # @api private
955
+ def get_receiver(instance, receiver_attribute)
956
+ receiver = instance.send(receiver_attribute.name)
957
+
958
+ unless receiver
959
+ receiver = receiver_attribute.type.model.new
960
+
961
+ receiver_attribute.type.attributes
962
+ .values
963
+ .select { |attr| attr.default }
964
+ .each { |attr| receiver.send(attr.setter, attr.default.call) }
965
+
966
+ instance.send(receiver_attribute.setter, receiver)
967
+ end
968
+
969
+ receiver
970
+ end
830
971
  end
831
972
 
832
973
  # Convert Object to Hash
data/lib/shale/utils.rb CHANGED
@@ -5,6 +5,21 @@ module Shale
5
5
  #
6
6
  # @api private
7
7
  module Utils
8
+ # Upcase first letter of a string
9
+ #
10
+ # @param [String] val
11
+ #
12
+ # @example
13
+ # Shale::Utils.upcase_first('fooBar')
14
+ # # => 'FooBar'
15
+ #
16
+ # @api private
17
+ def self.upcase_first(str)
18
+ return '' unless str
19
+ return '' if str.empty?
20
+ str[0].upcase + str[1..-1]
21
+ end
22
+
8
23
  # Convert string to Ruby's class naming convention
9
24
  #
10
25
  # @param [String] val
@@ -15,13 +30,14 @@ module Shale
15
30
  #
16
31
  # @api private
17
32
  def self.classify(str)
18
- str = str.to_s.sub(/.*\./, '')
33
+ # names may include a period, which will need to be stripped out
34
+ str = str.to_s.gsub(/\./, '')
19
35
 
20
- str = str.sub(/^[a-z\d]*/) { |match| match.capitalize || match }
36
+ str = str.sub(/^[a-z\d]*/) { |match| upcase_first(match) || match }
21
37
 
22
- str.gsub(%r{(?:_|-|(/))([a-z\d]*)}i) do
38
+ str.gsub('::', '/').gsub(%r{(?:_|-|(/))([a-z\d]*)}i) do
23
39
  word = Regexp.last_match(2)
24
- substituted = word.capitalize || word
40
+ substituted = upcase_first(word) || word
25
41
  Regexp.last_match(1) ? "::#{substituted}" : substituted
26
42
  end
27
43
  end
@@ -36,6 +52,8 @@ module Shale
36
52
  #
37
53
  # @api private
38
54
  def self.snake_case(str)
55
+ # XML elements allow periods and hyphens
56
+ str = str.to_s.gsub('.', '_')
39
57
  return str.to_s unless /[A-Z-]|::/.match?(str)
40
58
  word = str.to_s.gsub('::', '/')
41
59
  word = word.gsub(/([A-Z]+)(?=[A-Z][a-z])|([a-z\d])(?=[A-Z])/) do
data/lib/shale/version.rb CHANGED
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Shale
4
4
  # @api private
5
- VERSION = '0.9.0'
5
+ VERSION = '1.0.0'
6
6
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shale
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kamil Giszczak
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2022-10-31 00:00:00.000000000 Z
11
+ date: 2023-07-15 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Ruby object mapper and serializer for XML, JSON, TOML, CSV and YAML.
14
14
  email:
@@ -38,6 +38,7 @@ files:
38
38
  - lib/shale/attribute.rb
39
39
  - lib/shale/error.rb
40
40
  - lib/shale/mapper.rb
41
+ - lib/shale/mapping/delegates.rb
41
42
  - lib/shale/mapping/descriptor/dict.rb
42
43
  - lib/shale/mapping/descriptor/xml.rb
43
44
  - lib/shale/mapping/descriptor/xml_namespace.rb
@@ -124,7 +125,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
125
  - !ruby/object:Gem::Version
125
126
  version: '0'
126
127
  requirements: []
127
- rubygems_version: 3.3.7
128
+ rubygems_version: 3.4.10
128
129
  signing_key:
129
130
  specification_version: 4
130
131
  summary: Ruby object mapper and serializer for XML, JSON, TOML, CSV and YAML.