shale 0.8.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +24 -0
- data/README.md +313 -62
- data/exe/shaleb +19 -4
- data/lib/shale/adapter/csv.rb +48 -0
- data/lib/shale/adapter/nokogiri/document.rb +7 -2
- data/lib/shale/adapter/nokogiri.rb +11 -4
- data/lib/shale/adapter/ox.rb +10 -4
- data/lib/shale/adapter/rexml.rb +18 -4
- data/lib/shale/error.rb +20 -8
- data/lib/shale/mapper.rb +41 -6
- data/lib/shale/mapping/delegates.rb +95 -0
- data/lib/shale/mapping/descriptor/dict.rb +10 -1
- data/lib/shale/mapping/descriptor/xml.rb +13 -2
- data/lib/shale/mapping/dict.rb +15 -5
- data/lib/shale/mapping/dict_base.rb +12 -6
- data/lib/shale/mapping/dict_group.rb +1 -1
- data/lib/shale/mapping/validator.rb +10 -3
- data/lib/shale/mapping/xml.rb +22 -6
- data/lib/shale/mapping/xml_base.rb +21 -12
- data/lib/shale/schema/compiler/complex.rb +52 -8
- data/lib/shale/schema/compiler/xml_complex.rb +5 -4
- data/lib/shale/schema/json_compiler.rb +27 -13
- data/lib/shale/schema/json_generator.rb +2 -2
- data/lib/shale/schema/xml_compiler.rb +46 -18
- data/lib/shale/schema/xml_generator.rb +3 -3
- data/lib/shale/schema.rb +10 -4
- data/lib/shale/type/complex.rb +291 -34
- data/lib/shale/type/date.rb +11 -0
- data/lib/shale/type/time.rb +11 -0
- data/lib/shale/type/value.rb +22 -0
- data/lib/shale/utils.rb +22 -4
- data/lib/shale/version.rb +1 -1
- data/lib/shale.rb +30 -4
- data/shale.gemspec +2 -2
- metadata +7 -5
data/lib/shale/type/complex.rb
CHANGED
@@ -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'
|
@@ -13,11 +14,11 @@ module Shale
|
|
13
14
|
# @api private
|
14
15
|
class Complex < Value
|
15
16
|
class << self
|
16
|
-
%i[hash json yaml toml].each do |format|
|
17
|
+
%i[hash json yaml toml csv].each do |format|
|
17
18
|
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
18
19
|
# Convert Hash to Object using Hash/JSON/YAML/TOML mapping
|
19
20
|
#
|
20
|
-
# @param [Hash] hash Hash to convert
|
21
|
+
# @param [Hash, Array] hash Hash to convert
|
21
22
|
# @param [Array<Symbol>] only
|
22
23
|
# @param [Array<Symbol>] except
|
23
24
|
# @param [any] context
|
@@ -26,6 +27,18 @@ module Shale
|
|
26
27
|
#
|
27
28
|
# @api public
|
28
29
|
def of_#{format}(hash, only: nil, except: nil, context: nil)
|
30
|
+
#{
|
31
|
+
if format != :toml
|
32
|
+
<<~CODE
|
33
|
+
if hash.is_a?(Array)
|
34
|
+
return hash.map do |item|
|
35
|
+
of_#{format}(item, only: only, except: except, context: context)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
CODE
|
39
|
+
end
|
40
|
+
}
|
41
|
+
|
29
42
|
instance = model.new
|
30
43
|
|
31
44
|
attributes
|
@@ -35,6 +48,7 @@ module Shale
|
|
35
48
|
|
36
49
|
mapping_keys = #{format}_mapping.keys
|
37
50
|
grouping = Shale::Mapping::Group::DictGrouping.new
|
51
|
+
delegates = Shale::Mapping::Delegates.new
|
38
52
|
|
39
53
|
only = to_partial_render_attributes(only)
|
40
54
|
except = to_partial_render_attributes(except)
|
@@ -54,7 +68,8 @@ module Shale
|
|
54
68
|
mapper.send(mapping.method_from, instance, value)
|
55
69
|
end
|
56
70
|
else
|
57
|
-
|
71
|
+
receiver_attributes = get_receiver_attributes(mapping)
|
72
|
+
attribute = receiver_attributes[mapping.attribute]
|
58
73
|
next unless attribute
|
59
74
|
|
60
75
|
if only
|
@@ -67,20 +82,22 @@ module Shale
|
|
67
82
|
next if except.key?(attribute.name) && attribute_except.nil?
|
68
83
|
end
|
69
84
|
|
85
|
+
casted_value = nil
|
86
|
+
|
70
87
|
if value.nil?
|
71
|
-
|
88
|
+
casted_value = nil
|
72
89
|
elsif attribute.collection?
|
73
|
-
[
|
74
|
-
|
90
|
+
casted_value = (value.is_a?(Array) ? value : [value]).map do |val|
|
91
|
+
unless val.nil?
|
75
92
|
val = attribute.type.of_#{format}(
|
76
93
|
val,
|
77
94
|
only: attribute_only,
|
78
95
|
except: attribute_except,
|
79
96
|
context: context
|
80
97
|
)
|
81
|
-
end
|
82
98
|
|
83
|
-
|
99
|
+
attribute.type.cast(val)
|
100
|
+
end
|
84
101
|
end
|
85
102
|
else
|
86
103
|
val = attribute.type.of_#{format}(
|
@@ -89,11 +106,23 @@ module Shale
|
|
89
106
|
except: attribute_except,
|
90
107
|
context: context
|
91
108
|
)
|
92
|
-
|
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)
|
93
117
|
end
|
94
118
|
end
|
95
119
|
end
|
96
120
|
|
121
|
+
delegates.each do |delegate|
|
122
|
+
receiver = get_receiver(instance, delegate.receiver_attribute)
|
123
|
+
receiver.send(delegate.setter, delegate.value)
|
124
|
+
end
|
125
|
+
|
97
126
|
grouping.each do |group|
|
98
127
|
mapper = new
|
99
128
|
|
@@ -109,7 +138,7 @@ module Shale
|
|
109
138
|
|
110
139
|
# Convert Object to Hash using Hash/JSON/YAML/TOML mapping
|
111
140
|
#
|
112
|
-
# @param [any] instance Object to convert
|
141
|
+
# @param [any, Array<any>] instance Object to convert
|
113
142
|
# @param [Array<Symbol>] only
|
114
143
|
# @param [Array<Symbol>] except
|
115
144
|
# @param [any] context
|
@@ -120,6 +149,18 @@ module Shale
|
|
120
149
|
#
|
121
150
|
# @api public
|
122
151
|
def as_#{format}(instance, only: nil, except: nil, context: nil)
|
152
|
+
#{
|
153
|
+
if format != :toml
|
154
|
+
<<~CODE
|
155
|
+
if instance.is_a?(Array)
|
156
|
+
return instance.map do |item|
|
157
|
+
as_#{format}(item, only: only, except: except, context: context)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
CODE
|
161
|
+
end
|
162
|
+
}
|
163
|
+
|
123
164
|
unless instance.is_a?(model)
|
124
165
|
msg = "argument is a '\#{instance.class}' but should be a '\#{model}'"
|
125
166
|
raise IncorrectModelError, msg
|
@@ -143,7 +184,14 @@ module Shale
|
|
143
184
|
mapper.send(mapping.method_to, instance, hash)
|
144
185
|
end
|
145
186
|
else
|
146
|
-
|
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]
|
147
195
|
next unless attribute
|
148
196
|
|
149
197
|
if only
|
@@ -156,7 +204,7 @@ module Shale
|
|
156
204
|
next if except.key?(attribute.name) && attribute_except.nil?
|
157
205
|
end
|
158
206
|
|
159
|
-
value =
|
207
|
+
value = receiver.send(attribute.name) if receiver
|
160
208
|
|
161
209
|
if value.nil?
|
162
210
|
hash[mapping.name] = nil if mapping.render_nil?
|
@@ -312,6 +360,65 @@ module Shale
|
|
312
360
|
)
|
313
361
|
end
|
314
362
|
|
363
|
+
# Convert CSV to Object
|
364
|
+
#
|
365
|
+
# @param [String] csv CSV to convert
|
366
|
+
# @param [Array<Symbol>] only
|
367
|
+
# @param [Array<Symbol>] except
|
368
|
+
# @param [any] context
|
369
|
+
# @param [true, false] headers
|
370
|
+
# @param [Hash] csv_options
|
371
|
+
#
|
372
|
+
# @return [model instance]
|
373
|
+
#
|
374
|
+
# @api public
|
375
|
+
def from_csv(csv, only: nil, except: nil, context: nil, headers: false, **csv_options)
|
376
|
+
data = Shale.csv_adapter.load(csv, **csv_options.merge(headers: csv_mapping.keys.keys))
|
377
|
+
|
378
|
+
data.shift if headers
|
379
|
+
|
380
|
+
of_csv(
|
381
|
+
data,
|
382
|
+
only: only,
|
383
|
+
except: except,
|
384
|
+
context: context
|
385
|
+
)
|
386
|
+
end
|
387
|
+
|
388
|
+
# Convert Object to CSV
|
389
|
+
#
|
390
|
+
# @param [model instance] instance Object to convert
|
391
|
+
# @param [Array<Symbol>] only
|
392
|
+
# @param [Array<Symbol>] except
|
393
|
+
# @param [any] context
|
394
|
+
# @param [true, false] headers
|
395
|
+
# @param [Hash] csv_options
|
396
|
+
#
|
397
|
+
# @return [String]
|
398
|
+
#
|
399
|
+
# @api public
|
400
|
+
def to_csv(instance, only: nil, except: nil, context: nil, headers: false, **csv_options)
|
401
|
+
data = as_csv([*instance], only: only, except: except, context: context)
|
402
|
+
|
403
|
+
cols = csv_mapping.keys.values
|
404
|
+
|
405
|
+
if only
|
406
|
+
cols = cols.select { |e| only.include?(e.attribute) }
|
407
|
+
end
|
408
|
+
|
409
|
+
if except
|
410
|
+
cols = cols.reject { |e| except.include?(e.attribute) }
|
411
|
+
end
|
412
|
+
|
413
|
+
cols = cols.map(&:name)
|
414
|
+
|
415
|
+
if headers
|
416
|
+
data.prepend(cols.to_h { |e| [e, e] })
|
417
|
+
end
|
418
|
+
|
419
|
+
Shale.csv_adapter.dump(data, **csv_options.merge(headers: cols))
|
420
|
+
end
|
421
|
+
|
315
422
|
# Convert XML document to Object
|
316
423
|
#
|
317
424
|
# @param [Shale::Adapter::<XML adapter>::Node] element
|
@@ -331,6 +438,7 @@ module Shale
|
|
331
438
|
.each { |attr| instance.send(attr.setter, attr.default.call) }
|
332
439
|
|
333
440
|
grouping = Shale::Mapping::Group::XmlGrouping.new
|
441
|
+
delegates = Shale::Mapping::Delegates.new
|
334
442
|
|
335
443
|
only = to_partial_render_attributes(only)
|
336
444
|
except = to_partial_render_attributes(except)
|
@@ -350,16 +458,29 @@ module Shale
|
|
350
458
|
mapper.send(mapping.method_from, instance, value)
|
351
459
|
end
|
352
460
|
else
|
353
|
-
|
461
|
+
receiver_attributes = get_receiver_attributes(mapping)
|
462
|
+
attribute = receiver_attributes[mapping.attribute]
|
354
463
|
next unless attribute
|
355
464
|
|
356
465
|
next if only && !only.key?(attribute.name)
|
357
466
|
next if except&.key?(attribute.name)
|
358
467
|
|
468
|
+
casted_value = attribute.type.cast(value)
|
469
|
+
|
359
470
|
if attribute.collection?
|
360
|
-
|
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)
|
361
482
|
else
|
362
|
-
instance.send(attribute.setter,
|
483
|
+
instance.send(attribute.setter, casted_value)
|
363
484
|
end
|
364
485
|
end
|
365
486
|
end
|
@@ -378,7 +499,8 @@ module Shale
|
|
378
499
|
mapper.send(content_mapping.method_from, instance, element)
|
379
500
|
end
|
380
501
|
else
|
381
|
-
|
502
|
+
receiver_attributes = get_receiver_attributes(content_mapping)
|
503
|
+
attribute = receiver_attributes[content_mapping.attribute]
|
382
504
|
|
383
505
|
if attribute
|
384
506
|
skip = false
|
@@ -388,8 +510,13 @@ module Shale
|
|
388
510
|
skip = true if except&.key?(attribute.name)
|
389
511
|
|
390
512
|
unless skip
|
391
|
-
value = attribute.type.of_xml(element)
|
392
|
-
|
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
|
393
520
|
end
|
394
521
|
# rubocop:enable Metrics/BlockNesting
|
395
522
|
end
|
@@ -411,7 +538,8 @@ module Shale
|
|
411
538
|
mapper.send(mapping.method_from, instance, node)
|
412
539
|
end
|
413
540
|
else
|
414
|
-
|
541
|
+
receiver_attributes = get_receiver_attributes(mapping)
|
542
|
+
attribute = receiver_attributes[mapping.attribute]
|
415
543
|
next unless attribute
|
416
544
|
|
417
545
|
if only
|
@@ -431,14 +559,31 @@ module Shale
|
|
431
559
|
context: context
|
432
560
|
)
|
433
561
|
|
562
|
+
casted_value = attribute.type.cast(value)
|
563
|
+
|
434
564
|
if attribute.collection?
|
435
|
-
|
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)
|
436
576
|
else
|
437
|
-
instance.send(attribute.setter,
|
577
|
+
instance.send(attribute.setter, casted_value)
|
438
578
|
end
|
439
579
|
end
|
440
580
|
end
|
441
581
|
|
582
|
+
delegates.each do |delegate|
|
583
|
+
receiver = get_receiver(instance, delegate.receiver_attribute)
|
584
|
+
receiver.send(delegate.setter, delegate.value)
|
585
|
+
end
|
586
|
+
|
442
587
|
grouping.each do |group|
|
443
588
|
mapper = new
|
444
589
|
|
@@ -495,7 +640,8 @@ module Shale
|
|
495
640
|
_cdata = nil,
|
496
641
|
only: nil,
|
497
642
|
except: nil,
|
498
|
-
context: nil
|
643
|
+
context: nil,
|
644
|
+
version: nil
|
499
645
|
)
|
500
646
|
unless instance.is_a?(model)
|
501
647
|
msg = "argument is a '#{instance.class}' but should be a '#{model}'"
|
@@ -503,7 +649,7 @@ module Shale
|
|
503
649
|
end
|
504
650
|
|
505
651
|
unless doc
|
506
|
-
doc = Shale.xml_adapter.create_document
|
652
|
+
doc = Shale.xml_adapter.create_document(version)
|
507
653
|
|
508
654
|
element = as_xml(
|
509
655
|
instance,
|
@@ -542,13 +688,20 @@ module Shale
|
|
542
688
|
mapper.send(mapping.method_to, instance, element, doc)
|
543
689
|
end
|
544
690
|
else
|
545
|
-
|
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]
|
546
699
|
next unless attribute
|
547
700
|
|
548
701
|
next if only && !only.key?(attribute.name)
|
549
702
|
next if except&.key?(attribute.name)
|
550
703
|
|
551
|
-
value =
|
704
|
+
value = receiver.send(attribute.name) if receiver
|
552
705
|
|
553
706
|
if mapping.render_nil? || !value.nil?
|
554
707
|
doc.add_namespace(mapping.namespace.prefix, mapping.namespace.name)
|
@@ -571,7 +724,14 @@ module Shale
|
|
571
724
|
mapper.send(content_mapping.method_to, instance, element, doc)
|
572
725
|
end
|
573
726
|
else
|
574
|
-
|
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]
|
575
735
|
|
576
736
|
if attribute
|
577
737
|
skip = false
|
@@ -581,7 +741,7 @@ module Shale
|
|
581
741
|
skip = true if except&.key?(attribute.name)
|
582
742
|
|
583
743
|
unless skip
|
584
|
-
value =
|
744
|
+
value = receiver.send(attribute.name) if receiver
|
585
745
|
|
586
746
|
if content_mapping.cdata
|
587
747
|
doc.create_cdata(value.to_s, element)
|
@@ -606,7 +766,14 @@ module Shale
|
|
606
766
|
mapper.send(mapping.method_to, instance, element, doc)
|
607
767
|
end
|
608
768
|
else
|
609
|
-
|
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]
|
610
777
|
next unless attribute
|
611
778
|
|
612
779
|
if only
|
@@ -619,7 +786,7 @@ module Shale
|
|
619
786
|
next if except.key?(attribute.name) && attribute_except.nil?
|
620
787
|
end
|
621
788
|
|
622
|
-
value =
|
789
|
+
value = receiver.send(attribute.name) if receiver
|
623
790
|
|
624
791
|
if mapping.render_nil? || !value.nil?
|
625
792
|
doc.add_namespace(mapping.namespace.prefix, mapping.namespace.name)
|
@@ -680,6 +847,7 @@ module Shale
|
|
680
847
|
# @param [any] context
|
681
848
|
# @param [true, false] pretty
|
682
849
|
# @param [true, false] declaration
|
850
|
+
# @param [true, false, String] encoding
|
683
851
|
#
|
684
852
|
# @raise [AdapterError]
|
685
853
|
#
|
@@ -692,13 +860,15 @@ module Shale
|
|
692
860
|
except: nil,
|
693
861
|
context: nil,
|
694
862
|
pretty: false,
|
695
|
-
declaration: false
|
863
|
+
declaration: false,
|
864
|
+
encoding: false
|
696
865
|
)
|
697
866
|
validate_xml_adapter
|
698
867
|
Shale.xml_adapter.dump(
|
699
|
-
as_xml(instance, only: only, except: except, context: context),
|
868
|
+
as_xml(instance, only: only, except: except, context: context, version: declaration),
|
700
869
|
pretty: pretty,
|
701
|
-
declaration: declaration
|
870
|
+
declaration: declaration,
|
871
|
+
encoding: encoding
|
702
872
|
)
|
703
873
|
end
|
704
874
|
|
@@ -740,6 +910,64 @@ module Shale
|
|
740
910
|
end
|
741
911
|
end.to_h
|
742
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
|
743
971
|
end
|
744
972
|
|
745
973
|
# Convert Object to Hash
|
@@ -801,6 +1029,26 @@ module Shale
|
|
801
1029
|
self.class.to_toml(self, only: only, except: except, context: context)
|
802
1030
|
end
|
803
1031
|
|
1032
|
+
# Convert Object to CSV
|
1033
|
+
#
|
1034
|
+
# @param [Array<Symbol>] only
|
1035
|
+
# @param [Array<Symbol>] except
|
1036
|
+
# @param [any] context
|
1037
|
+
#
|
1038
|
+
# @return [String]
|
1039
|
+
#
|
1040
|
+
# @api public
|
1041
|
+
def to_csv(only: nil, except: nil, context: nil, headers: false, **csv_options)
|
1042
|
+
self.class.to_csv(
|
1043
|
+
self,
|
1044
|
+
only: only,
|
1045
|
+
except: except,
|
1046
|
+
context: context,
|
1047
|
+
headers: headers,
|
1048
|
+
**csv_options
|
1049
|
+
)
|
1050
|
+
end
|
1051
|
+
|
804
1052
|
# Convert Object to XML
|
805
1053
|
#
|
806
1054
|
# @param [Array<Symbol>] only
|
@@ -808,18 +1056,27 @@ module Shale
|
|
808
1056
|
# @param [any] context
|
809
1057
|
# @param [true, false] pretty
|
810
1058
|
# @param [true, false] declaration
|
1059
|
+
# @param [true, false, String] encoding
|
811
1060
|
#
|
812
1061
|
# @return [String]
|
813
1062
|
#
|
814
1063
|
# @api public
|
815
|
-
def to_xml(
|
1064
|
+
def to_xml(
|
1065
|
+
only: nil,
|
1066
|
+
except: nil,
|
1067
|
+
context: nil,
|
1068
|
+
pretty: false,
|
1069
|
+
declaration: false,
|
1070
|
+
encoding: false
|
1071
|
+
)
|
816
1072
|
self.class.to_xml(
|
817
1073
|
self,
|
818
1074
|
only: only,
|
819
1075
|
except: except,
|
820
1076
|
context: context,
|
821
1077
|
pretty: pretty,
|
822
|
-
declaration: declaration
|
1078
|
+
declaration: declaration,
|
1079
|
+
encoding: encoding
|
823
1080
|
)
|
824
1081
|
end
|
825
1082
|
end
|
data/lib/shale/type/date.rb
CHANGED
@@ -47,6 +47,17 @@ module Shale
|
|
47
47
|
value&.iso8601
|
48
48
|
end
|
49
49
|
|
50
|
+
# Use ISO 8601 format in CSV document
|
51
|
+
#
|
52
|
+
# @param [Date] value
|
53
|
+
#
|
54
|
+
# @return [String]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
def self.as_csv(value, **)
|
58
|
+
value&.iso8601
|
59
|
+
end
|
60
|
+
|
50
61
|
# Use ISO 8601 format in XML document
|
51
62
|
#
|
52
63
|
# @param [Date] value Value to convert to XML
|
data/lib/shale/type/time.rb
CHANGED
@@ -47,6 +47,17 @@ module Shale
|
|
47
47
|
value&.iso8601
|
48
48
|
end
|
49
49
|
|
50
|
+
# Use ISO 8601 format in CSV document
|
51
|
+
#
|
52
|
+
# @param [Time] value
|
53
|
+
#
|
54
|
+
# @return [String]
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
def self.as_csv(value, **)
|
58
|
+
value&.iso8601
|
59
|
+
end
|
60
|
+
|
50
61
|
# Use ISO 8601 format in XML document
|
51
62
|
#
|
52
63
|
# @param [Time] value Value to convert to XML
|
data/lib/shale/type/value.rb
CHANGED
@@ -111,6 +111,28 @@ module Shale
|
|
111
111
|
value
|
112
112
|
end
|
113
113
|
|
114
|
+
# Extract value from CSV document
|
115
|
+
#
|
116
|
+
# @param [any] value
|
117
|
+
#
|
118
|
+
# @return [any]
|
119
|
+
#
|
120
|
+
# @api private
|
121
|
+
def of_csv(value, **)
|
122
|
+
value
|
123
|
+
end
|
124
|
+
|
125
|
+
# Convert value to form accepted by CSV document
|
126
|
+
#
|
127
|
+
# @param [any] value
|
128
|
+
#
|
129
|
+
# @return [any]
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
def as_csv(value, **)
|
133
|
+
value
|
134
|
+
end
|
135
|
+
|
114
136
|
# Extract value from XML document
|
115
137
|
#
|
116
138
|
# @param [Shale::Adapter::<XML adapter>::Node] value
|
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
|
-
|
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
|
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
|
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