shale 0.1.0 → 0.2.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 +9 -0
- data/README.md +106 -5
- data/lib/shale/error.rb +6 -0
- data/lib/shale/mapper.rb +2 -2
- data/lib/shale/mapping/base.rb +32 -0
- data/lib/shale/mapping/key_value.rb +9 -3
- data/lib/shale/mapping/xml.rb +14 -5
- data/lib/shale/type/composite.rb +326 -0
- data/lib/shale/version.rb +1 -1
- metadata +4 -3
- data/lib/shale/type/complex.rb +0 -427
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ab65ef1fa452cb7d3810b6a996597190541fcbc5e4d5832901f8608bb8ddbf29
|
4
|
+
data.tar.gz: 241f5a1b333205a53cb598e51c8cd639a82f6f7661437f44d20fc3a0674935df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a6107d0c547481b76ad03573a880001043b030825eccccdb11e5627dfc817467bedce0c77f0c2ca52f267a2791855c5b37aa6a518347be908458bbeff42e1f50
|
7
|
+
data.tar.gz: c7e883f3a41049a72904ca94702eadcb2c568b1d8c7717ad2000340b21c53b5f9c90bd1a981e57866bb704e9ddcfb36d713b74061ed59eb3d065df9fbc5fbcba
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
## [0.2.0] - 2022-01-20
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- Support for using methods to extract/generate data from/to document
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
- deduplicate code
|
8
|
+
- Rename `Shale::Type::Complex` -> `Shale::Type::Composite`
|
9
|
+
|
1
10
|
## [0.1.0] - 2021-11-30
|
2
11
|
|
3
12
|
First public release
|
data/README.md
CHANGED
@@ -48,7 +48,7 @@ end
|
|
48
48
|
```
|
49
49
|
|
50
50
|
- `default: -> { 'value' }` - add a default value to attribute (it must be a proc that returns value)
|
51
|
-
- `collection: true` -
|
51
|
+
- `collection: true` - indicates that a attribute is a collection
|
52
52
|
|
53
53
|
### Creating objects
|
54
54
|
|
@@ -327,6 +327,100 @@ DATA
|
|
327
327
|
- `map_attribute` - map element's attribute to attribute
|
328
328
|
- `map_content` - map first text node to attribute
|
329
329
|
|
330
|
+
### Using methods to extract and generate data
|
331
|
+
|
332
|
+
If you need full controll over extracting and generating data from/to document,
|
333
|
+
you can use methods to do so:
|
334
|
+
|
335
|
+
```ruby
|
336
|
+
class Person < Shale::Mapper
|
337
|
+
attribute :hobbies, Shale::Type::String, collection: true
|
338
|
+
attribute :street, Shale::Type::String
|
339
|
+
attribute :city, Shale::Type::String
|
340
|
+
|
341
|
+
json do
|
342
|
+
map 'hobbies', using: { from: :hobbies_from_json, to: :hobbies_to_json }
|
343
|
+
map 'address', using: { from: :address_from_json, to: :address_to_json }
|
344
|
+
end
|
345
|
+
|
346
|
+
xml do
|
347
|
+
root 'Person'
|
348
|
+
|
349
|
+
map_attribute 'hobbies', using: { from: :hobbies_from_xml, to: :hobbies_to_xml }
|
350
|
+
map_element 'Address', using: { from: :address_from_xml, to: :address_to_xml }
|
351
|
+
end
|
352
|
+
|
353
|
+
def hobbies_from_json(value)
|
354
|
+
self.hobbies = value.split(',').map(&:strip)
|
355
|
+
end
|
356
|
+
|
357
|
+
def hobbies_to_json
|
358
|
+
hobbies.join(', ')
|
359
|
+
end
|
360
|
+
|
361
|
+
def address_from_json(value)
|
362
|
+
self.street = value['street']
|
363
|
+
self.city = value['city']
|
364
|
+
end
|
365
|
+
|
366
|
+
def address_to_json
|
367
|
+
{ 'street' => street, 'city' => city }
|
368
|
+
end
|
369
|
+
|
370
|
+
def hobbies_from_xml(value)
|
371
|
+
self.hobbies = value.split(',').map(&:strip)
|
372
|
+
end
|
373
|
+
|
374
|
+
def hobbies_to_xml(element, doc)
|
375
|
+
doc.add_attribute(element, 'hobbies', hobbies.join(', '))
|
376
|
+
end
|
377
|
+
|
378
|
+
def address_from_xml(node)
|
379
|
+
self.street = node.children.find { |e| e.name == 'Street' }.text
|
380
|
+
self.city = node.children.find { |e| e.name == 'City' }.text
|
381
|
+
end
|
382
|
+
|
383
|
+
def address_to_xml(parent, doc)
|
384
|
+
street_element = doc.create_element('Street')
|
385
|
+
doc.add_text(street_element, street.to_s)
|
386
|
+
|
387
|
+
city_element = doc.create_element('City')
|
388
|
+
doc.add_text(city_element, city.to_s)
|
389
|
+
|
390
|
+
address_element = doc.create_element('Address')
|
391
|
+
doc.add_element(address_element, street_element)
|
392
|
+
doc.add_element(address_element, city_element)
|
393
|
+
doc.add_element(parent, address_element)
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
397
|
+
person = Person.from_json(<<~DATA)
|
398
|
+
{
|
399
|
+
"hobbies": "Singing, Dancing, Running",
|
400
|
+
"address": {
|
401
|
+
"street": "Oxford Street",
|
402
|
+
"city": "London"
|
403
|
+
}
|
404
|
+
}
|
405
|
+
DATA
|
406
|
+
|
407
|
+
person = Person.from_xml(<<~DATA)
|
408
|
+
<Person hobbies="Singing, Dancing, Running">
|
409
|
+
<Address>
|
410
|
+
<Street>Oxford Street</Street>
|
411
|
+
<City>London</City>
|
412
|
+
</Address>
|
413
|
+
</person>
|
414
|
+
DATA
|
415
|
+
|
416
|
+
# =>
|
417
|
+
#
|
418
|
+
# #<Person:0x00007f9bc3086d60
|
419
|
+
# @hobbies=["Singing", "Dancing", "Running"],
|
420
|
+
# @street="Oxford Street",
|
421
|
+
# @city="London">
|
422
|
+
```
|
423
|
+
|
330
424
|
### Supported types
|
331
425
|
|
332
426
|
Shale supports these types out of the box:
|
@@ -355,10 +449,10 @@ end
|
|
355
449
|
### Adapters
|
356
450
|
|
357
451
|
Shale uses adapters for parsing and generating documents.
|
358
|
-
By default Ruby's standard JSON parser is used for
|
452
|
+
By default Ruby's standard JSON parser is used for handling JSON documents, YAML for YAML and
|
359
453
|
REXML for XML.
|
360
454
|
|
361
|
-
You can change it by providing your own adapter. For JSON and YAML adapter must implement
|
455
|
+
You can change it by providing your own adapter. For JSON and YAML, adapter must implement
|
362
456
|
`.load` and `.dump` class methods.
|
363
457
|
|
364
458
|
```ruby
|
@@ -369,15 +463,22 @@ Shale.json_adapter = MultiJson
|
|
369
463
|
Shale.yaml_adapter = MyYamlAdapter
|
370
464
|
```
|
371
465
|
|
372
|
-
For XML,
|
466
|
+
For XML, Shale provides adapters for most popular Ruby XML parsers:
|
373
467
|
|
374
468
|
```ruby
|
375
469
|
require 'shale'
|
376
470
|
|
471
|
+
# REXML is used by default:
|
472
|
+
|
473
|
+
require 'shale/adapter/rexml'
|
474
|
+
Shale.xml_adapter = Shale::Adapter::REXML
|
475
|
+
|
476
|
+
# if you want to use Nokogiri:
|
477
|
+
|
377
478
|
require 'shale/adapter/nokogiri'
|
378
479
|
Shale.xml_adapter = Shale::Adapter::Nokogiri
|
379
480
|
|
380
|
-
# or if you want to use Ox
|
481
|
+
# or if you want to use Ox:
|
381
482
|
|
382
483
|
require 'shale/adapter/ox'
|
383
484
|
Shale.xml_adapter = Shale::Adapter::Ox
|
data/lib/shale/error.rb
CHANGED
data/lib/shale/mapper.rb
CHANGED
@@ -5,7 +5,7 @@ require_relative 'error'
|
|
5
5
|
require_relative 'utils'
|
6
6
|
require_relative 'mapping/key_value'
|
7
7
|
require_relative 'mapping/xml'
|
8
|
-
require_relative 'type/
|
8
|
+
require_relative 'type/composite'
|
9
9
|
|
10
10
|
module Shale
|
11
11
|
# Base class used for mapping
|
@@ -42,7 +42,7 @@ module Shale
|
|
42
42
|
# person.to_json
|
43
43
|
#
|
44
44
|
# @api public
|
45
|
-
class Mapper < Type::
|
45
|
+
class Mapper < Type::Composite
|
46
46
|
@attributes = {}
|
47
47
|
@hash_mapping = Mapping::KeyValue.new
|
48
48
|
@json_mapping = Mapping::KeyValue.new
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../error'
|
4
|
+
|
5
|
+
module Shale
|
6
|
+
module Mapping
|
7
|
+
class Base
|
8
|
+
private
|
9
|
+
|
10
|
+
# Validate correctness of argument passed to map functions
|
11
|
+
#
|
12
|
+
# @param [String] key
|
13
|
+
# @param [Symbol] to
|
14
|
+
# @param [Hash] using
|
15
|
+
#
|
16
|
+
# @raise [IncorrectMappingArgumentsError] when arguments are incorrect
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
def validate_arguments(key, to, using)
|
20
|
+
if to.nil? && using.nil?
|
21
|
+
msg = ":to or :using argument is required for mapping '#{key}'"
|
22
|
+
raise IncorrectMappingArgumentsError, msg
|
23
|
+
end
|
24
|
+
|
25
|
+
if !using.nil? && (using[:from].nil? || using[:to].nil?)
|
26
|
+
msg = ":using argument for mapping '#{key}' requires :to and :from keys"
|
27
|
+
raise IncorrectMappingArgumentsError, msg
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'base'
|
4
|
+
|
3
5
|
module Shale
|
4
6
|
module Mapping
|
5
7
|
# Mapping for key/value serialization formats (Hash/JSON/YAML)
|
6
8
|
#
|
7
9
|
# @api private
|
8
|
-
class KeyValue
|
10
|
+
class KeyValue < Base
|
9
11
|
# Return keys mapping hash
|
10
12
|
#
|
11
13
|
# @return [Hash]
|
@@ -17,6 +19,7 @@ module Shale
|
|
17
19
|
#
|
18
20
|
# @api private
|
19
21
|
def initialize
|
22
|
+
super
|
20
23
|
@keys = {}
|
21
24
|
end
|
22
25
|
|
@@ -25,9 +28,12 @@ module Shale
|
|
25
28
|
# @param [String] key Document's key
|
26
29
|
# @param [Symbol] to Object's attribute
|
27
30
|
#
|
31
|
+
# @raise [IncorrectMappingArgumentsError] when arguments are incorrect
|
32
|
+
#
|
28
33
|
# @api private
|
29
|
-
def map(key, to:)
|
30
|
-
|
34
|
+
def map(key, to: nil, using: nil)
|
35
|
+
validate_arguments(key, to, using)
|
36
|
+
@keys[key] = to || using
|
31
37
|
end
|
32
38
|
|
33
39
|
# @api private
|
data/lib/shale/mapping/xml.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require_relative 'base'
|
4
|
+
|
3
5
|
module Shale
|
4
6
|
module Mapping
|
5
|
-
class Xml
|
7
|
+
class Xml < Base
|
6
8
|
# Return elements mapping hash
|
7
9
|
#
|
8
10
|
# @return [Hash]
|
@@ -28,6 +30,7 @@ module Shale
|
|
28
30
|
#
|
29
31
|
# @api private
|
30
32
|
def initialize
|
33
|
+
super
|
31
34
|
@elements = {}
|
32
35
|
@attributes = {}
|
33
36
|
@content = nil
|
@@ -39,9 +42,12 @@ module Shale
|
|
39
42
|
# @param [String] element Document's element
|
40
43
|
# @param [Symbol] to Object's attribute
|
41
44
|
#
|
45
|
+
# @raise [IncorrectMappingArgumentsError] when arguments are incorrect
|
46
|
+
#
|
42
47
|
# @api private
|
43
|
-
def map_element(element, to:)
|
44
|
-
|
48
|
+
def map_element(element, to: nil, using: nil)
|
49
|
+
validate_arguments(element, to, using)
|
50
|
+
@elements[element] = to || using
|
45
51
|
end
|
46
52
|
|
47
53
|
# Map document's attribute to object's attribute
|
@@ -49,9 +55,12 @@ module Shale
|
|
49
55
|
# @param [String] attribute Document's attribute
|
50
56
|
# @param [Symbol] to Object's attribute
|
51
57
|
#
|
58
|
+
# @raise [IncorrectMappingArgumentsError] when arguments are incorrect
|
59
|
+
#
|
52
60
|
# @api private
|
53
|
-
def map_attribute(attribute, to:)
|
54
|
-
|
61
|
+
def map_attribute(attribute, to: nil, using: nil)
|
62
|
+
validate_arguments(attribute, to, using)
|
63
|
+
@attributes[attribute] = to || using
|
55
64
|
end
|
56
65
|
|
57
66
|
# Map document's content to object's attribute
|
@@ -0,0 +1,326 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'base'
|
4
|
+
|
5
|
+
module Shale
|
6
|
+
module Type
|
7
|
+
# Build composite object. Don't use it directly.
|
8
|
+
# It serves as a base type class for @see Shale::Mapper
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
class Composite < Base
|
12
|
+
class << self
|
13
|
+
%i[hash json yaml].each do |format|
|
14
|
+
class_eval(<<-RUBY, __FILE__, __LINE__ + 1)
|
15
|
+
# Convert Hash to Object using Hash/JSON/YAML mapping
|
16
|
+
#
|
17
|
+
# @param [Hash] hash Hash to convert
|
18
|
+
#
|
19
|
+
# @return [Shale::Mapper]
|
20
|
+
#
|
21
|
+
# @api public
|
22
|
+
def out_of_#{format}(hash)
|
23
|
+
instance = new
|
24
|
+
|
25
|
+
mapping_keys = #{format}_mapping.keys
|
26
|
+
|
27
|
+
hash.each do |key, value|
|
28
|
+
mapping = mapping_keys[key]
|
29
|
+
next unless mapping
|
30
|
+
|
31
|
+
if mapping.is_a?(Hash)
|
32
|
+
instance.send(mapping[:from], value)
|
33
|
+
else
|
34
|
+
attribute = attributes[mapping]
|
35
|
+
next unless attribute
|
36
|
+
|
37
|
+
if value.nil?
|
38
|
+
instance.public_send("\#{attribute.name}=", nil)
|
39
|
+
next
|
40
|
+
end
|
41
|
+
|
42
|
+
if attribute.collection?
|
43
|
+
[*value].each do |val|
|
44
|
+
val = val ? attribute.type.out_of_#{format}(val) : val
|
45
|
+
instance.public_send(attribute.name) << attribute.type.cast(val)
|
46
|
+
end
|
47
|
+
else
|
48
|
+
val = attribute.type.out_of_#{format}(value)
|
49
|
+
instance.public_send("\#{attribute.name}=", val)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
instance
|
55
|
+
end
|
56
|
+
|
57
|
+
# Convert Object to Hash using Hash/JSON/YAML mapping
|
58
|
+
#
|
59
|
+
# @param [Shale::Type::Base] instance Object to convert
|
60
|
+
#
|
61
|
+
# @return [Hash]
|
62
|
+
#
|
63
|
+
# @api public
|
64
|
+
def as_#{format}(instance)
|
65
|
+
hash = {}
|
66
|
+
|
67
|
+
instance.class.#{format}_mapping.keys.each do |key, attr|
|
68
|
+
if attr.is_a?(Hash)
|
69
|
+
hash[key] = instance.send(attr[:to])
|
70
|
+
else
|
71
|
+
attribute = instance.class.attributes[attr]
|
72
|
+
next unless attribute
|
73
|
+
|
74
|
+
value = instance.public_send(attribute.name)
|
75
|
+
|
76
|
+
if value.nil?
|
77
|
+
hash[key] = nil
|
78
|
+
next
|
79
|
+
end
|
80
|
+
|
81
|
+
if attribute.collection?
|
82
|
+
hash[key] = [*value].map { |v| v ? attribute.type.as_#{format}(v) : v }
|
83
|
+
else
|
84
|
+
hash[key] = attribute.type.as_#{format}(value)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
hash
|
90
|
+
end
|
91
|
+
RUBY
|
92
|
+
end
|
93
|
+
|
94
|
+
alias from_hash out_of_hash
|
95
|
+
|
96
|
+
alias to_hash as_hash
|
97
|
+
|
98
|
+
# Convert JSON to Object
|
99
|
+
#
|
100
|
+
# @param [String] json JSON to convert
|
101
|
+
#
|
102
|
+
# @return [Shale::Mapper]
|
103
|
+
#
|
104
|
+
# @api public
|
105
|
+
def from_json(json)
|
106
|
+
out_of_json(Shale.json_adapter.load(json))
|
107
|
+
end
|
108
|
+
|
109
|
+
# Convert Object to JSON
|
110
|
+
#
|
111
|
+
# @param [Shale::Type::Base] instance Object to convert
|
112
|
+
#
|
113
|
+
# @return [String]
|
114
|
+
#
|
115
|
+
# @api public
|
116
|
+
def to_json(instance)
|
117
|
+
Shale.json_adapter.dump(as_json(instance))
|
118
|
+
end
|
119
|
+
|
120
|
+
# Convert YAML to Object
|
121
|
+
#
|
122
|
+
# @param [String] yaml YAML to convert
|
123
|
+
#
|
124
|
+
# @return [Shale::Mapper]
|
125
|
+
#
|
126
|
+
# @api public
|
127
|
+
def from_yaml(yaml)
|
128
|
+
out_of_yaml(Shale.yaml_adapter.load(yaml))
|
129
|
+
end
|
130
|
+
|
131
|
+
# Convert Object to YAML
|
132
|
+
#
|
133
|
+
# @param [Shale::Type::Base] instance Object to convert
|
134
|
+
#
|
135
|
+
# @return [String]
|
136
|
+
#
|
137
|
+
# @api public
|
138
|
+
def to_yaml(instance)
|
139
|
+
Shale.yaml_adapter.dump(as_yaml(instance))
|
140
|
+
end
|
141
|
+
|
142
|
+
# Convert XML document to Object
|
143
|
+
#
|
144
|
+
# @param [Shale::Adapter::<XML adapter>::Node] xml XML to convert
|
145
|
+
#
|
146
|
+
# @return [Shale::Mapper]
|
147
|
+
#
|
148
|
+
# @api public
|
149
|
+
def out_of_xml(element)
|
150
|
+
instance = new
|
151
|
+
|
152
|
+
element.attributes.each do |key, value|
|
153
|
+
mapping = xml_mapping.attributes[key.to_s]
|
154
|
+
next unless mapping
|
155
|
+
|
156
|
+
if mapping.is_a?(Hash)
|
157
|
+
instance.send(mapping[:from], value)
|
158
|
+
else
|
159
|
+
attribute = attributes[mapping]
|
160
|
+
next unless attribute
|
161
|
+
|
162
|
+
if attribute.collection?
|
163
|
+
instance.public_send(attribute.name) << attribute.type.cast(value)
|
164
|
+
else
|
165
|
+
instance.public_send("#{attribute.name}=", value)
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
if xml_mapping.content
|
171
|
+
attribute = attributes[xml_mapping.content]
|
172
|
+
|
173
|
+
if attribute
|
174
|
+
instance.public_send("#{attribute.name}=", attribute.type.out_of_xml(element))
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
element.children.each do |node|
|
179
|
+
mapping = xml_mapping.elements[node.name]
|
180
|
+
next unless mapping
|
181
|
+
|
182
|
+
if mapping.is_a?(Hash)
|
183
|
+
instance.send(mapping[:from], node)
|
184
|
+
else
|
185
|
+
attribute = attributes[mapping]
|
186
|
+
next unless attribute
|
187
|
+
|
188
|
+
if attribute.collection?
|
189
|
+
value = attribute.type.out_of_xml(node)
|
190
|
+
instance.public_send(attribute.name) << attribute.type.cast(value)
|
191
|
+
else
|
192
|
+
instance.public_send("#{attribute.name}=", attribute.type.out_of_xml(node))
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
instance
|
198
|
+
end
|
199
|
+
|
200
|
+
# Convert XML to Object
|
201
|
+
#
|
202
|
+
# @param [String] xml XML to convert
|
203
|
+
#
|
204
|
+
# @return [Shale::Mapper]
|
205
|
+
#
|
206
|
+
# @api public
|
207
|
+
def from_xml(xml)
|
208
|
+
out_of_xml(Shale.xml_adapter.load(xml))
|
209
|
+
end
|
210
|
+
|
211
|
+
# Convert Object to XML document
|
212
|
+
#
|
213
|
+
# @param [Shale::Type::Base] instance Object to convert
|
214
|
+
# @param [String, nil] node_name XML node name
|
215
|
+
# @param [Shale::Adapter::<xml adapter>::Document, nil] doc Object to convert
|
216
|
+
#
|
217
|
+
# @return [::REXML::Document, ::Nokogiri::Document, ::Ox::Document]
|
218
|
+
#
|
219
|
+
# @api public
|
220
|
+
def as_xml(instance, node_name = nil, doc = nil)
|
221
|
+
unless doc
|
222
|
+
doc = Shale.xml_adapter.create_document
|
223
|
+
doc.add_element(doc.doc, as_xml(instance, instance.class.xml_mapping.root, doc))
|
224
|
+
return doc.doc
|
225
|
+
end
|
226
|
+
|
227
|
+
element = doc.create_element(node_name)
|
228
|
+
|
229
|
+
xml_mapping.attributes.each do |xml_attr, obj_attr|
|
230
|
+
if obj_attr.is_a?(Hash)
|
231
|
+
instance.send(obj_attr[:to], element, doc)
|
232
|
+
else
|
233
|
+
attribute = instance.class.attributes[obj_attr]
|
234
|
+
next unless attribute
|
235
|
+
|
236
|
+
value = instance.public_send(attribute.name)
|
237
|
+
|
238
|
+
if value && !value.empty?
|
239
|
+
doc.add_attribute(element, xml_attr, value)
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
if xml_mapping.content
|
245
|
+
attribute = instance.class.attributes[xml_mapping.content]
|
246
|
+
|
247
|
+
if attribute
|
248
|
+
value = instance.public_send(attribute.name)
|
249
|
+
doc.add_text(element, value.to_s) if value
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
xml_mapping.elements.each do |xml_name, obj_attr|
|
254
|
+
if obj_attr.is_a?(Hash)
|
255
|
+
instance.send(obj_attr[:to], element, doc)
|
256
|
+
else
|
257
|
+
attribute = instance.class.attributes[obj_attr]
|
258
|
+
next unless attribute
|
259
|
+
|
260
|
+
value = instance.public_send(attribute.name)
|
261
|
+
next if value.nil?
|
262
|
+
|
263
|
+
if attribute.collection?
|
264
|
+
[*value].each do |v|
|
265
|
+
next if v.nil?
|
266
|
+
doc.add_element(element, attribute.type.as_xml(v, xml_name, doc))
|
267
|
+
end
|
268
|
+
else
|
269
|
+
doc.add_element(element, attribute.type.as_xml(value, xml_name, doc))
|
270
|
+
end
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
element
|
275
|
+
end
|
276
|
+
|
277
|
+
# Convert Object to XML
|
278
|
+
#
|
279
|
+
# @param [Shale::Type::Base] instance Object to convert
|
280
|
+
#
|
281
|
+
# @return [String]
|
282
|
+
#
|
283
|
+
# @api public
|
284
|
+
def to_xml(instance)
|
285
|
+
Shale.xml_adapter.dump(as_xml(instance))
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
# Convert Object to Hash
|
290
|
+
#
|
291
|
+
# @return [Hash]
|
292
|
+
#
|
293
|
+
# @api public
|
294
|
+
def to_hash
|
295
|
+
self.class.to_hash(self)
|
296
|
+
end
|
297
|
+
|
298
|
+
# Convert Object to JSON
|
299
|
+
#
|
300
|
+
# @return [String]
|
301
|
+
#
|
302
|
+
# @api public
|
303
|
+
def to_json
|
304
|
+
self.class.to_json(self)
|
305
|
+
end
|
306
|
+
|
307
|
+
# Convert Object to YAML
|
308
|
+
#
|
309
|
+
# @return [String]
|
310
|
+
#
|
311
|
+
# @api public
|
312
|
+
def to_yaml
|
313
|
+
self.class.to_yaml(self)
|
314
|
+
end
|
315
|
+
|
316
|
+
# Convert Object to XML
|
317
|
+
#
|
318
|
+
# @return [String]
|
319
|
+
#
|
320
|
+
# @api public
|
321
|
+
def to_xml
|
322
|
+
self.class.to_xml(self)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
data/lib/shale/version.rb
CHANGED
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.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kamil Giszczak
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-20 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Ruby object mapper and serializer for XML, JSON and YAML.
|
14
14
|
email:
|
@@ -28,11 +28,12 @@ files:
|
|
28
28
|
- lib/shale/attribute.rb
|
29
29
|
- lib/shale/error.rb
|
30
30
|
- lib/shale/mapper.rb
|
31
|
+
- lib/shale/mapping/base.rb
|
31
32
|
- lib/shale/mapping/key_value.rb
|
32
33
|
- lib/shale/mapping/xml.rb
|
33
34
|
- lib/shale/type/base.rb
|
34
35
|
- lib/shale/type/boolean.rb
|
35
|
-
- lib/shale/type/
|
36
|
+
- lib/shale/type/composite.rb
|
36
37
|
- lib/shale/type/date.rb
|
37
38
|
- lib/shale/type/float.rb
|
38
39
|
- lib/shale/type/integer.rb
|
data/lib/shale/type/complex.rb
DELETED
@@ -1,427 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module Shale
|
6
|
-
module Type
|
7
|
-
# Build complex object. Don't use it directly.
|
8
|
-
# It serves as a base type class for @see Shale::Mapper
|
9
|
-
#
|
10
|
-
# @api private
|
11
|
-
class Complex < Base
|
12
|
-
class << self
|
13
|
-
# Convert Hash to Object
|
14
|
-
#
|
15
|
-
# @param [Hash] hash Hash to convert
|
16
|
-
#
|
17
|
-
# @return [Shale::Mapper]
|
18
|
-
#
|
19
|
-
# @api public
|
20
|
-
def out_of_hash(hash)
|
21
|
-
instance = new
|
22
|
-
|
23
|
-
hash.each do |key, value|
|
24
|
-
mapping = hash_mapping.keys[key]
|
25
|
-
next unless mapping
|
26
|
-
|
27
|
-
attribute = attributes[mapping]
|
28
|
-
next unless attribute
|
29
|
-
|
30
|
-
if value.nil?
|
31
|
-
instance.public_send("#{attribute.name}=", nil)
|
32
|
-
next
|
33
|
-
end
|
34
|
-
|
35
|
-
if attribute.collection?
|
36
|
-
[*value].each do |val|
|
37
|
-
val = val ? attribute.type.out_of_hash(val) : val
|
38
|
-
instance.public_send(attribute.name) << attribute.type.cast(val)
|
39
|
-
end
|
40
|
-
else
|
41
|
-
instance.public_send("#{attribute.name}=", attribute.type.out_of_hash(value))
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
instance
|
46
|
-
end
|
47
|
-
|
48
|
-
alias from_hash out_of_hash
|
49
|
-
|
50
|
-
# Convert Object to Hash
|
51
|
-
#
|
52
|
-
# @param [Shale::Type::Base] instance Object to convert
|
53
|
-
#
|
54
|
-
# @return [Hash]
|
55
|
-
#
|
56
|
-
# @api public
|
57
|
-
def as_hash(instance)
|
58
|
-
hash = {}
|
59
|
-
|
60
|
-
instance.class.hash_mapping.keys.each do |key, attr|
|
61
|
-
attribute = instance.class.attributes[attr]
|
62
|
-
next unless attribute
|
63
|
-
|
64
|
-
value = instance.public_send(attribute.name)
|
65
|
-
|
66
|
-
if value.nil?
|
67
|
-
hash[key] = nil
|
68
|
-
next
|
69
|
-
end
|
70
|
-
|
71
|
-
if attribute.collection?
|
72
|
-
hash[key] = [*value].map { |v| v ? attribute.type.as_hash(v) : v }
|
73
|
-
else
|
74
|
-
hash[key] = attribute.type.as_hash(value)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
hash
|
79
|
-
end
|
80
|
-
|
81
|
-
alias to_hash as_hash
|
82
|
-
|
83
|
-
# Convert JSON document to Object
|
84
|
-
#
|
85
|
-
# @param [Hash] hash JSON document to convert
|
86
|
-
#
|
87
|
-
# @return [Shale::Mapper]
|
88
|
-
#
|
89
|
-
# @api public
|
90
|
-
def out_of_json(hash)
|
91
|
-
instance = new
|
92
|
-
|
93
|
-
hash.each do |key, value|
|
94
|
-
mapping = json_mapping.keys[key]
|
95
|
-
next unless mapping
|
96
|
-
|
97
|
-
attribute = attributes[mapping]
|
98
|
-
next unless attribute
|
99
|
-
|
100
|
-
if value.nil?
|
101
|
-
instance.public_send("#{attribute.name}=", nil)
|
102
|
-
next
|
103
|
-
end
|
104
|
-
|
105
|
-
if attribute.collection?
|
106
|
-
[*value].each do |val|
|
107
|
-
val = val ? attribute.type.out_of_json(val) : val
|
108
|
-
instance.public_send(attribute.name) << attribute.type.cast(val)
|
109
|
-
end
|
110
|
-
else
|
111
|
-
instance.public_send("#{attribute.name}=", attribute.type.out_of_json(value))
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
instance
|
116
|
-
end
|
117
|
-
|
118
|
-
# Convert JSON to Object
|
119
|
-
#
|
120
|
-
# @param [String] json JSON to convert
|
121
|
-
#
|
122
|
-
# @return [Shale::Mapper]
|
123
|
-
#
|
124
|
-
# @api public
|
125
|
-
def from_json(json)
|
126
|
-
out_of_json(Shale.json_adapter.load(json))
|
127
|
-
end
|
128
|
-
|
129
|
-
# Convert Object to JSON document
|
130
|
-
#
|
131
|
-
# @param [Shale::Type::Base] instance Object to convert
|
132
|
-
#
|
133
|
-
# @return [Hash]
|
134
|
-
#
|
135
|
-
# @api public
|
136
|
-
def as_json(instance)
|
137
|
-
hash = {}
|
138
|
-
|
139
|
-
instance.class.json_mapping.keys.each do |key, attr|
|
140
|
-
attribute = instance.class.attributes[attr]
|
141
|
-
next unless attribute
|
142
|
-
|
143
|
-
value = instance.public_send(attribute.name)
|
144
|
-
|
145
|
-
if value.nil?
|
146
|
-
hash[key] = nil
|
147
|
-
next
|
148
|
-
end
|
149
|
-
|
150
|
-
if attribute.collection?
|
151
|
-
hash[key] = [*value].map { |v| v ? attribute.type.as_json(v) : v }
|
152
|
-
else
|
153
|
-
hash[key] = attribute.type.as_json(value)
|
154
|
-
end
|
155
|
-
end
|
156
|
-
|
157
|
-
hash
|
158
|
-
end
|
159
|
-
|
160
|
-
# Convert Object to JSON
|
161
|
-
#
|
162
|
-
# @param [Shale::Type::Base] instance Object to convert
|
163
|
-
#
|
164
|
-
# @return [String]
|
165
|
-
#
|
166
|
-
# @api public
|
167
|
-
def to_json(instance)
|
168
|
-
Shale.json_adapter.dump(as_json(instance))
|
169
|
-
end
|
170
|
-
|
171
|
-
# Convert YAML document to Object
|
172
|
-
#
|
173
|
-
# @param [Hash] hash YAML document to convert
|
174
|
-
#
|
175
|
-
# @return [Shale::Mapper]
|
176
|
-
#
|
177
|
-
# @api public
|
178
|
-
def out_of_yaml(hash)
|
179
|
-
instance = new
|
180
|
-
|
181
|
-
hash.each do |key, value|
|
182
|
-
mapping = yaml_mapping.keys[key]
|
183
|
-
next unless mapping
|
184
|
-
|
185
|
-
attribute = attributes[mapping]
|
186
|
-
next unless attribute
|
187
|
-
|
188
|
-
if value.nil?
|
189
|
-
instance.public_send("#{attribute.name}=", nil)
|
190
|
-
next
|
191
|
-
end
|
192
|
-
|
193
|
-
if attribute.collection?
|
194
|
-
[*value].each do |val|
|
195
|
-
val = val ? attribute.type.out_of_yaml(val) : val
|
196
|
-
instance.public_send(attribute.name) << attribute.type.cast(val)
|
197
|
-
end
|
198
|
-
else
|
199
|
-
instance.public_send("#{attribute.name}=", attribute.type.out_of_yaml(value))
|
200
|
-
end
|
201
|
-
end
|
202
|
-
|
203
|
-
instance
|
204
|
-
end
|
205
|
-
|
206
|
-
# Convert YAML to Object
|
207
|
-
#
|
208
|
-
# @param [String] yaml YAML to convert
|
209
|
-
#
|
210
|
-
# @return [Shale::Mapper]
|
211
|
-
#
|
212
|
-
# @api public
|
213
|
-
def from_yaml(yaml)
|
214
|
-
out_of_yaml(Shale.yaml_adapter.load(yaml))
|
215
|
-
end
|
216
|
-
|
217
|
-
# Convert Object to YAML document
|
218
|
-
#
|
219
|
-
# @param [Shale::Type::Base] instance Object to convert
|
220
|
-
#
|
221
|
-
# @return [Hash]
|
222
|
-
#
|
223
|
-
# @api public
|
224
|
-
def as_yaml(instance)
|
225
|
-
hash = {}
|
226
|
-
|
227
|
-
instance.class.yaml_mapping.keys.each do |key, attr|
|
228
|
-
attribute = instance.class.attributes[attr]
|
229
|
-
next unless attribute
|
230
|
-
|
231
|
-
value = instance.public_send(attribute.name)
|
232
|
-
|
233
|
-
if value.nil?
|
234
|
-
hash[key] = nil
|
235
|
-
next
|
236
|
-
end
|
237
|
-
|
238
|
-
if attribute.collection?
|
239
|
-
hash[key] = [*value].map { |v| v ? attribute.type.as_yaml(v) : v }
|
240
|
-
else
|
241
|
-
hash[key] = attribute.type.as_yaml(value)
|
242
|
-
end
|
243
|
-
end
|
244
|
-
|
245
|
-
hash
|
246
|
-
end
|
247
|
-
|
248
|
-
# Convert Object to YAML
|
249
|
-
#
|
250
|
-
# @param [Shale::Type::Base] instance Object to convert
|
251
|
-
#
|
252
|
-
# @return [String]
|
253
|
-
#
|
254
|
-
# @api public
|
255
|
-
def to_yaml(instance)
|
256
|
-
Shale.yaml_adapter.dump(as_yaml(instance))
|
257
|
-
end
|
258
|
-
|
259
|
-
# Convert XML document to Object
|
260
|
-
#
|
261
|
-
# @param [Shale::Adapter::<XML adapter>::Node] xml XML to convert
|
262
|
-
#
|
263
|
-
# @return [Shale::Mapper]
|
264
|
-
#
|
265
|
-
# @api public
|
266
|
-
def out_of_xml(element)
|
267
|
-
instance = new
|
268
|
-
|
269
|
-
element.attributes.each do |key, value|
|
270
|
-
mapping = xml_mapping.attributes[key.to_s]
|
271
|
-
next unless mapping
|
272
|
-
|
273
|
-
attribute = attributes[mapping]
|
274
|
-
next unless attribute
|
275
|
-
|
276
|
-
if attribute.collection?
|
277
|
-
instance.public_send(attribute.name) << attribute.type.cast(value)
|
278
|
-
else
|
279
|
-
instance.public_send("#{attribute.name}=", value)
|
280
|
-
end
|
281
|
-
end
|
282
|
-
|
283
|
-
if xml_mapping.content
|
284
|
-
attribute = attributes[xml_mapping.content]
|
285
|
-
|
286
|
-
if attribute
|
287
|
-
instance.public_send("#{attribute.name}=", attribute.type.out_of_xml(element))
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
element.children.each do |node|
|
292
|
-
mapping = xml_mapping.elements[node.name]
|
293
|
-
next unless mapping
|
294
|
-
|
295
|
-
attribute = attributes[mapping]
|
296
|
-
next unless attribute
|
297
|
-
|
298
|
-
if attribute.collection?
|
299
|
-
value = attribute.type.out_of_xml(node)
|
300
|
-
instance.public_send(attribute.name) << attribute.type.cast(value)
|
301
|
-
else
|
302
|
-
instance.public_send("#{attribute.name}=", attribute.type.out_of_xml(node))
|
303
|
-
end
|
304
|
-
end
|
305
|
-
|
306
|
-
instance
|
307
|
-
end
|
308
|
-
|
309
|
-
# Convert XML to Object
|
310
|
-
#
|
311
|
-
# @param [String] xml XML to convert
|
312
|
-
#
|
313
|
-
# @return [Shale::Mapper]
|
314
|
-
#
|
315
|
-
# @api public
|
316
|
-
def from_xml(xml)
|
317
|
-
out_of_xml(Shale.xml_adapter.load(xml))
|
318
|
-
end
|
319
|
-
|
320
|
-
# Convert Object to XML document
|
321
|
-
#
|
322
|
-
# @param [Shale::Type::Base] instance Object to convert
|
323
|
-
# @param [String, nil] node_name XML node name
|
324
|
-
# @param [Shale::Adapter::<xml adapter>::Document, nil] doc Object to convert
|
325
|
-
#
|
326
|
-
# @return [::REXML::Document, ::Nokogiri::Document, ::Ox::Document]
|
327
|
-
#
|
328
|
-
# @api public
|
329
|
-
def as_xml(instance, node_name = nil, doc = nil)
|
330
|
-
unless doc
|
331
|
-
doc = Shale.xml_adapter.create_document
|
332
|
-
doc.add_element(doc.doc, as_xml(instance, instance.class.xml_mapping.root, doc))
|
333
|
-
return doc.doc
|
334
|
-
end
|
335
|
-
|
336
|
-
element = doc.create_element(node_name)
|
337
|
-
|
338
|
-
xml_mapping.attributes.each do |xml_attr, obj_attr|
|
339
|
-
attribute = instance.class.attributes[obj_attr]
|
340
|
-
next unless attribute
|
341
|
-
|
342
|
-
value = instance.public_send(attribute.name)
|
343
|
-
|
344
|
-
if value && !value.empty?
|
345
|
-
doc.add_attribute(element, xml_attr, value)
|
346
|
-
end
|
347
|
-
end
|
348
|
-
|
349
|
-
if xml_mapping.content
|
350
|
-
attribute = instance.class.attributes[xml_mapping.content]
|
351
|
-
|
352
|
-
if attribute
|
353
|
-
value = instance.public_send(attribute.name)
|
354
|
-
doc.add_text(element, value.to_s) if value
|
355
|
-
end
|
356
|
-
end
|
357
|
-
|
358
|
-
xml_mapping.elements.each do |xml_name, obj_attr|
|
359
|
-
attribute = instance.class.attributes[obj_attr]
|
360
|
-
next unless attribute
|
361
|
-
|
362
|
-
value = instance.public_send(attribute.name)
|
363
|
-
next if value.nil?
|
364
|
-
|
365
|
-
if attribute.collection?
|
366
|
-
[*value].each do |v|
|
367
|
-
next if v.nil?
|
368
|
-
doc.add_element(element, attribute.type.as_xml(v, xml_name, doc))
|
369
|
-
end
|
370
|
-
else
|
371
|
-
doc.add_element(element, attribute.type.as_xml(value, xml_name, doc))
|
372
|
-
end
|
373
|
-
end
|
374
|
-
|
375
|
-
element
|
376
|
-
end
|
377
|
-
|
378
|
-
# Convert Object to XML
|
379
|
-
#
|
380
|
-
# @param [Shale::Type::Base] instance Object to convert
|
381
|
-
#
|
382
|
-
# @return [String]
|
383
|
-
#
|
384
|
-
# @api public
|
385
|
-
def to_xml(instance)
|
386
|
-
Shale.xml_adapter.dump(as_xml(instance))
|
387
|
-
end
|
388
|
-
end
|
389
|
-
|
390
|
-
# Convert Object to Hash
|
391
|
-
#
|
392
|
-
# @return [Hash]
|
393
|
-
#
|
394
|
-
# @api public
|
395
|
-
def to_hash
|
396
|
-
self.class.to_hash(self)
|
397
|
-
end
|
398
|
-
|
399
|
-
# Convert Object to JSON
|
400
|
-
#
|
401
|
-
# @return [String]
|
402
|
-
#
|
403
|
-
# @api public
|
404
|
-
def to_json
|
405
|
-
self.class.to_json(self)
|
406
|
-
end
|
407
|
-
|
408
|
-
# Convert Object to YAML
|
409
|
-
#
|
410
|
-
# @return [String]
|
411
|
-
#
|
412
|
-
# @api public
|
413
|
-
def to_yaml
|
414
|
-
self.class.to_yaml(self)
|
415
|
-
end
|
416
|
-
|
417
|
-
# Convert Object to XML
|
418
|
-
#
|
419
|
-
# @return [String]
|
420
|
-
#
|
421
|
-
# @api public
|
422
|
-
def to_xml
|
423
|
-
self.class.to_xml(self)
|
424
|
-
end
|
425
|
-
end
|
426
|
-
end
|
427
|
-
end
|