shale 0.7.1 → 0.9.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 +19 -2
- data/README.md +184 -15
- 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/mapper.rb +40 -1
- data/lib/shale/mapping/descriptor/dict.rb +10 -1
- data/lib/shale/mapping/descriptor/xml.rb +19 -2
- data/lib/shale/mapping/dict.rb +14 -46
- data/lib/shale/mapping/dict_base.rb +76 -0
- data/lib/shale/mapping/dict_group.rb +41 -0
- data/lib/shale/mapping/group/dict.rb +55 -0
- data/lib/shale/mapping/group/dict_grouping.rb +41 -0
- data/lib/shale/mapping/group/xml.rb +43 -0
- data/lib/shale/mapping/group/xml_grouping.rb +27 -0
- data/lib/shale/mapping/xml.rb +40 -152
- data/lib/shale/mapping/xml_base.rb +227 -0
- data/lib/shale/mapping/xml_group.rb +70 -0
- data/lib/shale/type/complex.rb +198 -18
- 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/version.rb +1 -1
- data/lib/shale.rb +30 -4
- data/shale.gemspec +3 -3
- metadata +14 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3f880bc8d984c45e7e8a05bd155c1014df237e037263c7bf68a7ae82c0e49ea4
|
4
|
+
data.tar.gz: ba7a2f12bed87e048dd53d43900f1c8203d90f5f89a43b3824475ddda39eb07b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54023d4f621dae53537f73e0ad79d3736d5d52e605c1a7da2c4bdb06588b348feaae9066ce00b6b3aedc9e84ea23a425884eabf987717ffda6d3c22eccc6325f
|
7
|
+
data.tar.gz: e82cad292dab557891cad4f210bfb4a97d2096a6d2151a5a8aef020358accb5986e527e32a141a9a2e3a01d594e9a3adb42c0f7e8d17464517731136f63289a2
|
data/CHANGELOG.md
CHANGED
@@ -1,9 +1,26 @@
|
|
1
|
-
## [0.
|
1
|
+
## [0.9.0] - 2022-10-31
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- Support for CSV
|
5
|
+
- Allow to specify version and add encoding to XML declaration
|
6
|
+
- Support for mapping/generating collections
|
7
|
+
|
8
|
+
## [0.8.0] - 2022-08-30
|
9
|
+
|
10
|
+
### Added
|
11
|
+
- Allow to group mappings using `group` block
|
12
|
+
- Bring back Ruby 2.6 support
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
- Use anonymous module for attributes definition.
|
16
|
+
It allows to override accessors and `super` works as expected.
|
17
|
+
|
18
|
+
## [0.7.1] - 2022-08-12
|
2
19
|
|
3
20
|
### Fixed
|
4
21
|
- Fix broken handling of Date and Time types
|
5
22
|
|
6
|
-
## [0.7.0] -
|
23
|
+
## [0.7.0] - 2022-08-09
|
7
24
|
|
8
25
|
### Added
|
9
26
|
- `only: []` and `except: []` options that allow to controll what attributes are rendered/parsed
|
data/README.md
CHANGED
@@ -1,23 +1,23 @@
|
|
1
1
|
# Shale
|
2
2
|
|
3
|
-
Shale is a Ruby object mapper and serializer for JSON, YAML, TOML and XML.
|
4
|
-
It allows you to parse JSON, YAML, TOML and XML data and convert it into Ruby data structures,
|
5
|
-
as well as serialize data structures into JSON, YAML, TOML or XML.
|
3
|
+
Shale is a Ruby object mapper and serializer for JSON, YAML, TOML, CSV and XML.
|
4
|
+
It allows you to parse JSON, YAML, TOML, CSV and XML data and convert it into Ruby data structures,
|
5
|
+
as well as serialize data structures into JSON, YAML, TOML, CSV or XML.
|
6
6
|
|
7
7
|
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
8
8
|
|
9
9
|
## Features
|
10
10
|
|
11
|
-
* Convert JSON, YAML, TOML and XML to Ruby data model
|
12
|
-
* Convert Ruby data model to JSON, YAML, TOML and XML
|
11
|
+
* Convert JSON, YAML, TOML, CSV and XML to Ruby data model
|
12
|
+
* Convert Ruby data model to JSON, YAML, TOML, CSV and XML
|
13
13
|
* Generate JSON and XML Schema from Ruby models
|
14
14
|
* Compile JSON and XML Schema into Ruby models
|
15
|
-
* Out of the box support for JSON, YAML, Tomlib, toml-rb, Nokogiri, REXML and Ox parsers
|
15
|
+
* Out of the box support for JSON, YAML, Tomlib, toml-rb, CSV, Nokogiri, REXML and Ox parsers
|
16
16
|
* Support for custom adapters
|
17
17
|
|
18
18
|
## Installation
|
19
19
|
|
20
|
-
Shale supports Ruby (MRI) 2.
|
20
|
+
Shale supports Ruby (MRI) 2.6+
|
21
21
|
|
22
22
|
Add this line to your application's Gemfile:
|
23
23
|
|
@@ -51,9 +51,13 @@ $ gem install shale
|
|
51
51
|
* [Converting object to Hash](#converting-object-to-hash)
|
52
52
|
* [Converting XML to object](#converting-xml-to-object)
|
53
53
|
* [Converting object to XML](#converting-object-to-xml)
|
54
|
+
* [Converting CSV to object](#converting-csv-to-object)
|
55
|
+
* [Converting object to CSV](#converting-object-to-csv)
|
56
|
+
* [Converting collections](#converting-collections)
|
54
57
|
* [Mapping JSON keys to object attributes](#mapping-json-keys-to-object-attributes)
|
55
58
|
* [Mapping YAML keys to object attributes](#mapping-yaml-keys-to-object-attributes)
|
56
59
|
* [Mapping TOML keys to object attributes](#mapping-toml-keys-to-object-attributes)
|
60
|
+
* [Mapping CSV columns to object attributes](#mapping-csv-columns-to-object-attributes)
|
57
61
|
* [Mapping Hash keys to object attributes](#mapping-hash-keys-to-object-attributes)
|
58
62
|
* [Mapping XML elements and attributes to object attributes](#mapping-xml-elements-and-attributes-to-object-attributes)
|
59
63
|
* [Using XML namespaces](#using-xml-namespaces)
|
@@ -346,6 +350,70 @@ person.to_xml
|
|
346
350
|
# </person>
|
347
351
|
```
|
348
352
|
|
353
|
+
### Converting CSV to object
|
354
|
+
|
355
|
+
CSV represents a flat data structure, so you can't map properties to complex types directly,
|
356
|
+
but you can use methods to map properties to complex types
|
357
|
+
(see [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
|
358
|
+
section).
|
359
|
+
|
360
|
+
`.from_csv` method allways returns an array of records.
|
361
|
+
|
362
|
+
```ruby
|
363
|
+
people = Person.from_csv(<<~DATA)
|
364
|
+
John,Doe,50,false
|
365
|
+
DATA
|
366
|
+
```
|
367
|
+
|
368
|
+
### Converting object to CSV
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
people[0].to_csv # or Person.to_csv(people) if you want to convert a collection
|
372
|
+
|
373
|
+
# =>
|
374
|
+
#
|
375
|
+
# John,Doe,50,false
|
376
|
+
```
|
377
|
+
|
378
|
+
### Converting collections
|
379
|
+
|
380
|
+
Shale allows converting collections for formats that support it (JSON, YAML and CSV).
|
381
|
+
To convert Ruby array to JSON:
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
person1 = Person.new(name: 'John Doe')
|
385
|
+
person2 = Person.new(name: 'Joe Sixpack')
|
386
|
+
|
387
|
+
Person.to_json([person1, person2], pretty: true)
|
388
|
+
# or Person.to_yaml([person1, person2])
|
389
|
+
# or Person.to_csv([person1, person2])
|
390
|
+
|
391
|
+
# =>
|
392
|
+
#
|
393
|
+
# [
|
394
|
+
# { "name": "John Doe" },
|
395
|
+
# { "name": "Joe Sixpack" }
|
396
|
+
# ]
|
397
|
+
```
|
398
|
+
|
399
|
+
To convert JSON array to Ruby:
|
400
|
+
|
401
|
+
```ruby
|
402
|
+
Person.from_json(<<~JSON)
|
403
|
+
[
|
404
|
+
{ "name": "John Doe" },
|
405
|
+
{ "name": "Joe Sixpack" }
|
406
|
+
]
|
407
|
+
JSON
|
408
|
+
|
409
|
+
# =>
|
410
|
+
#
|
411
|
+
# [
|
412
|
+
# #<Person:0x00000001033dbce8 @name="John Doe">,
|
413
|
+
# #<Person:0x00000001033db4c8 @name="Joe Sixpack">
|
414
|
+
# ]
|
415
|
+
```
|
416
|
+
|
349
417
|
### Mapping JSON keys to object attributes
|
350
418
|
|
351
419
|
By default keys are named the same as attributes. To use custom keys use:
|
@@ -392,6 +460,24 @@ class Person < Shale::Mapper
|
|
392
460
|
end
|
393
461
|
```
|
394
462
|
|
463
|
+
### Mapping CSV columns to object attributes
|
464
|
+
|
465
|
+
For CSV the order of mapping matters, the first argument in the `map` method is only
|
466
|
+
used as a label in header row. So, in the example below the first column will be mapped
|
467
|
+
to `:first_name` attribute and the second column to `:last_name`.
|
468
|
+
|
469
|
+
```ruby
|
470
|
+
class Person < Shale::Mapper
|
471
|
+
attribute :first_name, Shale::Type::String
|
472
|
+
attribute :last_name, Shale::Type::String
|
473
|
+
|
474
|
+
csv do
|
475
|
+
map 'firstName', to: :first_name
|
476
|
+
map 'lastName', to: :last_name
|
477
|
+
end
|
478
|
+
end
|
479
|
+
```
|
480
|
+
|
395
481
|
### Mapping Hash keys to object attributes
|
396
482
|
|
397
483
|
```ruby
|
@@ -561,8 +647,9 @@ DATA
|
|
561
647
|
|
562
648
|
### Rendering nil values
|
563
649
|
|
564
|
-
|
565
|
-
by using `render_nil: true` on a mapping.
|
650
|
+
For JSON, YAML, TOML and XML by default, elements with `nil` value are not rendered.
|
651
|
+
You can change this behavior by using `render_nil: true` on a mapping.
|
652
|
+
For CSV the default is to render `nil` elements.
|
566
653
|
|
567
654
|
```ruby
|
568
655
|
class Person < Shale::Mapper
|
@@ -729,6 +816,55 @@ end
|
|
729
816
|
Person.new(password: 'secret').to_json(context: current_user)
|
730
817
|
```
|
731
818
|
|
819
|
+
If you want to work on multiple elements at a time you can group them using `group` block:
|
820
|
+
|
821
|
+
```ruby
|
822
|
+
class Person < Shale::Mapper
|
823
|
+
attribute :name, Shale::Type::String
|
824
|
+
|
825
|
+
json do
|
826
|
+
group from: :name_from_json, to: :name_to_json do
|
827
|
+
map 'first_name'
|
828
|
+
map 'last_name'
|
829
|
+
end
|
830
|
+
end
|
831
|
+
|
832
|
+
xml do
|
833
|
+
group from: :name_from_xml, to: :name_to_xml do
|
834
|
+
map_content
|
835
|
+
map_element 'first_name'
|
836
|
+
map_attribute 'last_name'
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
840
|
+
def name_from_json(model, value)
|
841
|
+
model.name = "#{value['first_name']} #{value['last_name']}"
|
842
|
+
end
|
843
|
+
|
844
|
+
def name_to_json(model, doc)
|
845
|
+
doc['first_name'] = model.name.split(' ')[0]
|
846
|
+
doc['last_name'] = model.name.split(' ')[1]
|
847
|
+
end
|
848
|
+
|
849
|
+
def name_from_xml(model, value)
|
850
|
+
# value => { content: ..., attributes: {}, elements: {} }
|
851
|
+
end
|
852
|
+
|
853
|
+
def name_to_xml(model, element, doc)
|
854
|
+
# ...
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
858
|
+
Person.from_json(<<~DATA)
|
859
|
+
{
|
860
|
+
"first_name": "John",
|
861
|
+
"last_name": "Doe"
|
862
|
+
}
|
863
|
+
DATA
|
864
|
+
|
865
|
+
# => #<Person:0x00007f9bc3086d60 @name="John Doe">
|
866
|
+
```
|
867
|
+
|
732
868
|
### Additional options
|
733
869
|
|
734
870
|
You can control which attributes to render and parse by
|
@@ -804,19 +940,52 @@ person.to_json(pretty: true)
|
|
804
940
|
# }
|
805
941
|
```
|
806
942
|
|
807
|
-
You can also add an XML declaration by passing `declaration: true`
|
943
|
+
You can also add an XML declaration by passing `declaration: true` and `encoding: true`
|
944
|
+
or if you want to spcify version: `declaration: '1.1'` and `encoding: 'ASCII'` to `#to_xml`
|
808
945
|
|
809
946
|
```ruby
|
810
|
-
person.to_xml(pretty: true, declaration: true)
|
947
|
+
person.to_xml(pretty: true, declaration: true, encoding: true)
|
811
948
|
|
812
949
|
# =>
|
813
950
|
#
|
814
|
-
# <?xml version="1.0"?>
|
951
|
+
# <?xml version="1.0" encoding="UTF-8"?>
|
815
952
|
# <Person>
|
816
953
|
# <Address city="London"/>
|
817
954
|
# </Person>
|
818
955
|
```
|
819
956
|
|
957
|
+
For CSV you can pass `headers: true` to indicate that the first row contains column
|
958
|
+
names and shouldn't be included in the returned collection. It also accepts all the options that
|
959
|
+
[CSV parser](https://ruby-doc.org/stdlib-3.1.2/libdoc/csv/rdoc/CSV.html#class-CSV-label-Options) accepts.
|
960
|
+
|
961
|
+
```ruby
|
962
|
+
class Person
|
963
|
+
attribute :first_name, Shale::Type::String
|
964
|
+
attribute :last_name, Shale::Type::String
|
965
|
+
end
|
966
|
+
|
967
|
+
people = Person.from_csv(<<~DATA, headers: true, col_sep: '|')
|
968
|
+
first_name|last_name
|
969
|
+
John|Doe
|
970
|
+
James|Sixpack
|
971
|
+
DATA
|
972
|
+
|
973
|
+
# =>
|
974
|
+
#
|
975
|
+
# [
|
976
|
+
# #<Person:0x0000000113d7a488 @first_name="John", @last_name="Doe">,
|
977
|
+
# #<Person:0x0000000113d7a488 @first_name="James", @last_name="Sixpack">
|
978
|
+
# ]
|
979
|
+
|
980
|
+
Person.to_csv(people, headers: true, col_sep: '|')
|
981
|
+
|
982
|
+
# =>
|
983
|
+
#
|
984
|
+
# first_name|last_name
|
985
|
+
# John|Doe
|
986
|
+
# James|Sixpack
|
987
|
+
```
|
988
|
+
|
820
989
|
### Using custom models
|
821
990
|
|
822
991
|
By default Shale combines mapper and model into one class. If you want to use your own classes
|
@@ -906,10 +1075,10 @@ end
|
|
906
1075
|
### Adapters
|
907
1076
|
|
908
1077
|
Shale uses adapters for parsing and generating documents.
|
909
|
-
By default Ruby's standard JSON, YAML parsers are used for handling JSON
|
1078
|
+
By default Ruby's standard JSON, YAML, CSV parsers are used for handling JSON YAML, CSV documents.
|
910
1079
|
|
911
|
-
You can change it by providing your own adapter. For JSON, YAML and
|
912
|
-
`.load` and `.dump` class methods.
|
1080
|
+
You can change it by providing your own adapter. For JSON, YAML, TOML and CSV adapter must
|
1081
|
+
implement `.load` and `.dump` class methods.
|
913
1082
|
|
914
1083
|
```ruby
|
915
1084
|
require 'shale'
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'csv'
|
4
|
+
|
5
|
+
module Shale
|
6
|
+
module Adapter
|
7
|
+
# CSV adapter
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class CSV
|
11
|
+
# Parse CSV into Array<Hash<String, any>>
|
12
|
+
#
|
13
|
+
# @param [String] csv CSV document
|
14
|
+
# @param [Array<String>] headers
|
15
|
+
# @param [Hash] options
|
16
|
+
#
|
17
|
+
# @return [Array<Hash<String, any>>]
|
18
|
+
#
|
19
|
+
# @api private
|
20
|
+
def self.load(csv, headers:, **options)
|
21
|
+
::CSV.parse(csv, headers: headers, **options).map(&:to_h)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Serialize Array<Hash<String, any>> into CSV
|
25
|
+
#
|
26
|
+
# @param [Array<Hash<String, any>>] obj Array<Hash<String, any>> object
|
27
|
+
# @param [Array<String>] headers
|
28
|
+
# @param [Hash] options
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
def self.dump(obj, headers:, **options)
|
34
|
+
::CSV.generate(**options) do |csv|
|
35
|
+
obj.each do |row|
|
36
|
+
values = []
|
37
|
+
|
38
|
+
headers.each do |header|
|
39
|
+
values << row[header] if row.key?(header)
|
40
|
+
end
|
41
|
+
|
42
|
+
csv << values
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -9,9 +9,14 @@ module Shale
|
|
9
9
|
class Document
|
10
10
|
# Initialize object
|
11
11
|
#
|
12
|
+
# @param [String, nil] version
|
13
|
+
#
|
12
14
|
# @api private
|
13
|
-
def initialize
|
14
|
-
|
15
|
+
def initialize(version = nil)
|
16
|
+
ver = nil
|
17
|
+
ver = version if version.is_a?(String)
|
18
|
+
|
19
|
+
@doc = ::Nokogiri::XML::Document.new(ver)
|
15
20
|
@namespaces = {}
|
16
21
|
end
|
17
22
|
|
@@ -37,12 +37,13 @@ module Shale
|
|
37
37
|
#
|
38
38
|
# @param [::Nokogiri::XML::Document] doc Nokogiri document
|
39
39
|
# @param [true, false] pretty
|
40
|
-
# @param [true, false] declaration
|
40
|
+
# @param [true, false, String] declaration
|
41
|
+
# @param [true, false, String] encoding
|
41
42
|
#
|
42
43
|
# @return [String]
|
43
44
|
#
|
44
45
|
# @api private
|
45
|
-
def self.dump(doc, pretty: false, declaration: false)
|
46
|
+
def self.dump(doc, pretty: false, declaration: false, encoding: false)
|
46
47
|
save_with = ::Nokogiri::XML::Node::SaveOptions::AS_XML
|
47
48
|
|
48
49
|
if pretty
|
@@ -53,6 +54,10 @@ module Shale
|
|
53
54
|
save_with |= ::Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
|
54
55
|
end
|
55
56
|
|
57
|
+
if encoding
|
58
|
+
doc.encoding = encoding == true ? 'UTF-8' : encoding
|
59
|
+
end
|
60
|
+
|
56
61
|
result = doc.to_xml(save_with: save_with)
|
57
62
|
|
58
63
|
unless pretty
|
@@ -64,9 +69,11 @@ module Shale
|
|
64
69
|
|
65
70
|
# Create Shale::Adapter::Nokogiri::Document instance
|
66
71
|
#
|
72
|
+
# @param [true, false, String, nil] declaration
|
73
|
+
#
|
67
74
|
# @api private
|
68
|
-
def self.create_document
|
69
|
-
Document.new
|
75
|
+
def self.create_document(version = nil)
|
76
|
+
Document.new(version)
|
70
77
|
end
|
71
78
|
end
|
72
79
|
end
|
data/lib/shale/adapter/ox.rb
CHANGED
@@ -31,12 +31,13 @@ module Shale
|
|
31
31
|
#
|
32
32
|
# @param [::Ox::Document, ::Ox::Element] doc Ox document
|
33
33
|
# @param [true, false] pretty
|
34
|
-
# @param [true, false] declaration
|
34
|
+
# @param [true, false, String] declaration
|
35
|
+
# @param [true, false, String] encoding
|
35
36
|
#
|
36
37
|
# @return [String]
|
37
38
|
#
|
38
39
|
# @api private
|
39
|
-
def self.dump(doc, pretty: false, declaration: false)
|
40
|
+
def self.dump(doc, pretty: false, declaration: false, encoding: false)
|
40
41
|
opts = { indent: -1, with_xml: false }
|
41
42
|
|
42
43
|
if pretty
|
@@ -44,7 +45,12 @@ module Shale
|
|
44
45
|
end
|
45
46
|
|
46
47
|
if declaration
|
47
|
-
doc[:version] = '1.0'
|
48
|
+
doc[:version] = declaration == true ? '1.0' : declaration
|
49
|
+
|
50
|
+
if encoding
|
51
|
+
doc[:encoding] = encoding == true ? 'UTF-8' : encoding
|
52
|
+
end
|
53
|
+
|
48
54
|
opts[:with_xml] = true
|
49
55
|
end
|
50
56
|
|
@@ -54,7 +60,7 @@ module Shale
|
|
54
60
|
# Create Shale::Adapter::Ox::Document instance
|
55
61
|
#
|
56
62
|
# @api private
|
57
|
-
def self.create_document
|
63
|
+
def self.create_document(_version = nil)
|
58
64
|
Document.new
|
59
65
|
end
|
60
66
|
end
|
data/lib/shale/adapter/rexml.rb
CHANGED
@@ -32,14 +32,28 @@ module Shale
|
|
32
32
|
#
|
33
33
|
# @param [::REXML::Document] doc REXML document
|
34
34
|
# @param [true, false] pretty
|
35
|
-
# @param [true, false] declaration
|
35
|
+
# @param [true, false, String] declaration
|
36
|
+
# @param [true, false, String] encoding
|
36
37
|
#
|
37
38
|
# @return [String]
|
38
39
|
#
|
39
40
|
# @api private
|
40
|
-
def self.dump(doc, pretty: false, declaration: false)
|
41
|
+
def self.dump(doc, pretty: false, declaration: false, encoding: false)
|
41
42
|
if declaration
|
42
|
-
|
43
|
+
ver = nil
|
44
|
+
enc = nil
|
45
|
+
|
46
|
+
if declaration.is_a?(String)
|
47
|
+
ver = declaration
|
48
|
+
end
|
49
|
+
|
50
|
+
if encoding == true
|
51
|
+
enc = 'UTF-8'
|
52
|
+
else
|
53
|
+
enc = encoding || nil
|
54
|
+
end
|
55
|
+
|
56
|
+
doc.add(::REXML::XMLDecl.new(ver, enc))
|
43
57
|
end
|
44
58
|
|
45
59
|
io = StringIO.new
|
@@ -58,7 +72,7 @@ module Shale
|
|
58
72
|
# Create Shale::Adapter::REXML::Document instance
|
59
73
|
#
|
60
74
|
# @api private
|
61
|
-
def self.create_document
|
75
|
+
def self.create_document(_version = nil)
|
62
76
|
Document.new
|
63
77
|
end
|
64
78
|
end
|
data/lib/shale/mapper.rb
CHANGED
@@ -49,6 +49,7 @@ module Shale
|
|
49
49
|
@json_mapping = Mapping::Dict.new
|
50
50
|
@yaml_mapping = Mapping::Dict.new
|
51
51
|
@toml_mapping = Mapping::Dict.new
|
52
|
+
@csv_mapping = Mapping::Dict.new(render_nil_default: true)
|
52
53
|
@xml_mapping = Mapping::Xml.new
|
53
54
|
|
54
55
|
class << self
|
@@ -87,6 +88,13 @@ module Shale
|
|
87
88
|
# @api public
|
88
89
|
attr_reader :toml_mapping
|
89
90
|
|
91
|
+
# Return CSV mapping object
|
92
|
+
#
|
93
|
+
# @return [Shale::Mapping::Dict]
|
94
|
+
#
|
95
|
+
# @api public
|
96
|
+
attr_reader :csv_mapping
|
97
|
+
|
90
98
|
# Return XML mapping object
|
91
99
|
#
|
92
100
|
# @return [Shale::Mapping::XML]
|
@@ -98,6 +106,10 @@ module Shale
|
|
98
106
|
def inherited(subclass)
|
99
107
|
super
|
100
108
|
|
109
|
+
attributes_module = Module.new
|
110
|
+
subclass.instance_variable_set('@attributes_module', attributes_module)
|
111
|
+
subclass.include(attributes_module)
|
112
|
+
|
101
113
|
subclass.instance_variable_set('@model', subclass)
|
102
114
|
subclass.instance_variable_set('@attributes', @attributes.dup)
|
103
115
|
|
@@ -105,12 +117,14 @@ module Shale
|
|
105
117
|
subclass.instance_variable_set('@__json_mapping_init', @json_mapping.dup)
|
106
118
|
subclass.instance_variable_set('@__yaml_mapping_init', @yaml_mapping.dup)
|
107
119
|
subclass.instance_variable_set('@__toml_mapping_init', @toml_mapping.dup)
|
120
|
+
subclass.instance_variable_set('@__csv_mapping_init', @csv_mapping.dup)
|
108
121
|
subclass.instance_variable_set('@__xml_mapping_init', @xml_mapping.dup)
|
109
122
|
|
110
123
|
subclass.instance_variable_set('@hash_mapping', @hash_mapping.dup)
|
111
124
|
subclass.instance_variable_set('@json_mapping', @json_mapping.dup)
|
112
125
|
subclass.instance_variable_set('@yaml_mapping', @yaml_mapping.dup)
|
113
126
|
subclass.instance_variable_set('@toml_mapping', @toml_mapping.dup)
|
127
|
+
subclass.instance_variable_set('@csv_mapping', @csv_mapping.dup)
|
114
128
|
|
115
129
|
xml_mapping = @xml_mapping.dup
|
116
130
|
xml_mapping.root(Utils.underscore(subclass.name || ''))
|
@@ -169,9 +183,10 @@ module Shale
|
|
169
183
|
@json_mapping.map(name.to_s, to: name) unless @json_mapping.finalized?
|
170
184
|
@yaml_mapping.map(name.to_s, to: name) unless @yaml_mapping.finalized?
|
171
185
|
@toml_mapping.map(name.to_s, to: name) unless @toml_mapping.finalized?
|
186
|
+
@csv_mapping.map(name.to_s, to: name) unless @csv_mapping.finalized?
|
172
187
|
@xml_mapping.map_element(name.to_s, to: name) unless @xml_mapping.finalized?
|
173
188
|
|
174
|
-
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
189
|
+
@attributes_module.class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
175
190
|
attr_reader :#{name}
|
176
191
|
|
177
192
|
def #{name}=(val)
|
@@ -276,6 +291,30 @@ module Shale
|
|
276
291
|
@toml_mapping.instance_eval(&block)
|
277
292
|
end
|
278
293
|
|
294
|
+
# Define CSV mapping
|
295
|
+
#
|
296
|
+
# @param [Proc] block
|
297
|
+
#
|
298
|
+
# @example
|
299
|
+
# calss Person < Shale::Mapper
|
300
|
+
# attribute :first_name, Shale::Type::String
|
301
|
+
# attribute :last_name, Shale::Type::String
|
302
|
+
# attribute :age, Shale::Type::Integer
|
303
|
+
#
|
304
|
+
# csv do
|
305
|
+
# map 'first_name', to: :first_name
|
306
|
+
# map 'last_name', to: :last_name
|
307
|
+
# map 'age', to: :age
|
308
|
+
# end
|
309
|
+
# end
|
310
|
+
#
|
311
|
+
# @api public
|
312
|
+
def csv(&block)
|
313
|
+
@csv_mapping = @__csv_mapping_init.dup
|
314
|
+
@csv_mapping.finalize!
|
315
|
+
@csv_mapping.instance_eval(&block)
|
316
|
+
end
|
317
|
+
|
279
318
|
# Define XML mapping
|
280
319
|
#
|
281
320
|
# @param [Proc] block
|
@@ -35,17 +35,26 @@ module Shale
|
|
35
35
|
# @api private
|
36
36
|
attr_reader :method_to
|
37
37
|
|
38
|
+
# Return group name
|
39
|
+
#
|
40
|
+
# @return [String]
|
41
|
+
#
|
42
|
+
# @api private
|
43
|
+
attr_reader :group
|
44
|
+
|
38
45
|
# Initialize instance
|
39
46
|
#
|
40
47
|
# @param [String] name
|
41
48
|
# @param [Symbol, nil] attribute
|
42
49
|
# @param [Hash, nil] methods
|
50
|
+
# @param [String, nil] group
|
43
51
|
# @param [true, false] render_nil
|
44
52
|
#
|
45
53
|
# @api private
|
46
|
-
def initialize(name:, attribute:, methods:, render_nil:)
|
54
|
+
def initialize(name:, attribute:, methods:, group:, render_nil:)
|
47
55
|
@name = name
|
48
56
|
@attribute = attribute
|
57
|
+
@group = group
|
49
58
|
@render_nil = render_nil
|
50
59
|
|
51
60
|
if methods
|
@@ -28,13 +28,21 @@ module Shale
|
|
28
28
|
# @param [String] name
|
29
29
|
# @param [Symbol, String] attribute
|
30
30
|
# @param [Hash, nil] methods
|
31
|
+
# @param [String, nil] group
|
31
32
|
# @param [Shale::Mapping::XmlNamespace] namespace
|
32
33
|
# @param [true, false] cdata
|
33
34
|
# @param [true, false] render_nil
|
34
35
|
#
|
35
36
|
# @api private
|
36
|
-
def initialize(name:, attribute:, methods:, namespace:, cdata:, render_nil:)
|
37
|
-
super(
|
37
|
+
def initialize(name:, attribute:, methods:, group:, namespace:, cdata:, render_nil:)
|
38
|
+
super(
|
39
|
+
name: name,
|
40
|
+
attribute: attribute,
|
41
|
+
methods: methods,
|
42
|
+
group: group,
|
43
|
+
render_nil: render_nil
|
44
|
+
)
|
45
|
+
|
38
46
|
@namespace = namespace
|
39
47
|
@cdata = cdata
|
40
48
|
end
|
@@ -47,6 +55,15 @@ module Shale
|
|
47
55
|
def prefixed_name
|
48
56
|
[namespace.prefix, name].compact.join(':')
|
49
57
|
end
|
58
|
+
|
59
|
+
# Return name with XML namespace
|
60
|
+
#
|
61
|
+
# @return [String]
|
62
|
+
#
|
63
|
+
# @api private
|
64
|
+
def namespaced_name
|
65
|
+
[namespace.name, name].compact.join(':')
|
66
|
+
end
|
50
67
|
end
|
51
68
|
end
|
52
69
|
end
|