shale 0.8.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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