shale 0.1.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 +7 -0
- data/CHANGELOG.md +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +392 -0
- data/lib/shale/adapter/json.rb +34 -0
- data/lib/shale/adapter/nokogiri.rb +176 -0
- data/lib/shale/adapter/ox.rb +162 -0
- data/lib/shale/adapter/rexml.rb +163 -0
- data/lib/shale/attribute.rb +47 -0
- data/lib/shale/error.rb +33 -0
- data/lib/shale/mapper.rb +292 -0
- data/lib/shale/mapping/key_value.rb +40 -0
- data/lib/shale/mapping/xml.rb +87 -0
- data/lib/shale/type/base.rb +118 -0
- data/lib/shale/type/boolean.rb +33 -0
- data/lib/shale/type/complex.rb +427 -0
- data/lib/shale/type/date.rb +29 -0
- data/lib/shale/type/float.rb +29 -0
- data/lib/shale/type/integer.rb +21 -0
- data/lib/shale/type/string.rb +21 -0
- data/lib/shale/type/time.rb +29 -0
- data/lib/shale/utils.rb +25 -0
- data/lib/shale/version.rb +6 -0
- data/lib/shale.rb +127 -0
- data/shale.gemspec +26 -0
- metadata +72 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4c96481848145b9736af4cc830c84c04970042fdc61f7d348aa64099065319ef
|
4
|
+
data.tar.gz: 25739ce30a7f4e66040b5dbff394da06a5c578065af0cd0b03bd0a56c7d78cbf
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f205c1407868c6469922b7553f9aef3cdb0dc70502981d56eeef5b08703c793213f63c2a898568f372bc4e9519bcbfa771eefd6cfd663746e5b4180d1738c351
|
7
|
+
data.tar.gz: 95d7e2c6f88d7d5099c1ff62345509e83faee23a3bdd16e77d8eea05fb061e0eeb76fabbba5dc50d7c15ea675d2d1430570a2ac4134416c3d03d905d4ef59528
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2021 TODO: Write your name
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,392 @@
|
|
1
|
+
# Shale
|
2
|
+
|
3
|
+
Shale is a object mapper and serializer for JSON, YAML and XML.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Shale supports Ruby (MRI) 2.6+
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'shale'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
```
|
18
|
+
$ bundle install
|
19
|
+
```
|
20
|
+
|
21
|
+
Or install it yourself as:
|
22
|
+
|
23
|
+
```
|
24
|
+
$ gem install shale
|
25
|
+
```
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
### Simple use case
|
30
|
+
|
31
|
+
```ruby
|
32
|
+
require 'shale'
|
33
|
+
|
34
|
+
class Address < Shale::Mapper
|
35
|
+
attribute :city, Shale::Type::String
|
36
|
+
attribute :street, Shale::Type::String
|
37
|
+
attribute :zip, Shale::Type::String
|
38
|
+
end
|
39
|
+
|
40
|
+
class Person < Shale::Mapper
|
41
|
+
attribute :first_name, Shale::Type::String
|
42
|
+
attribute :last_name, Shale::Type::String
|
43
|
+
attribute :age, Shale::Type::Date
|
44
|
+
attribute :married, Shale::Type::Boolean, default: -> { false }
|
45
|
+
attribute :hobbies, Shale::Type::String, collection: true
|
46
|
+
attribute :address, Address
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
- `default: -> { 'value' }` - add a default value to attribute (it must be a proc that returns value)
|
51
|
+
- `collection: true` - indicated that a attribute is a collection
|
52
|
+
|
53
|
+
### Creating objects
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
person = Person.new(
|
57
|
+
first_name: 'John',
|
58
|
+
last_name: 'Doe',
|
59
|
+
age: 50,
|
60
|
+
hobbies: ['Singing', 'Dancing'],
|
61
|
+
address: Address.new(city: 'London', street: 'Oxford Street', zip: 'E1 6AN'),
|
62
|
+
)
|
63
|
+
```
|
64
|
+
|
65
|
+
### Converting JSON to object
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
person = Person.from_json(<<~DATA)
|
69
|
+
{
|
70
|
+
"first_name": "John",
|
71
|
+
"last_name": "Doe",
|
72
|
+
"age": 50,
|
73
|
+
"married": false,
|
74
|
+
"hobbies": ["Singing", "Dancing"],
|
75
|
+
"address": {
|
76
|
+
"city": "London",
|
77
|
+
"street": "Oxford Street",
|
78
|
+
"zip": "E1 6AN"
|
79
|
+
}
|
80
|
+
}
|
81
|
+
DATA
|
82
|
+
|
83
|
+
# =>
|
84
|
+
#
|
85
|
+
# #<Person:0x00007f9bc3086d60
|
86
|
+
# @address=
|
87
|
+
# #<Address:0x00007f9bc3086748
|
88
|
+
# @city="London",
|
89
|
+
# @street="Oxford Street",
|
90
|
+
# @zip="E1 6AN">,
|
91
|
+
# @age=50,
|
92
|
+
# @first_name="John",
|
93
|
+
# @hobbies=["Singing", "Dancing"],
|
94
|
+
# @last_name="Doe",
|
95
|
+
# @married=false>
|
96
|
+
```
|
97
|
+
|
98
|
+
### Converting object to JSON
|
99
|
+
|
100
|
+
```ruby
|
101
|
+
person.to_json
|
102
|
+
# =>
|
103
|
+
#
|
104
|
+
# {
|
105
|
+
# "first_name": "John",
|
106
|
+
# "last_name": "Doe",
|
107
|
+
# "age": 50,
|
108
|
+
# "married": false,
|
109
|
+
# "hobbies": ["Singing", "Dancing"],
|
110
|
+
# "address": {
|
111
|
+
# "city": "London",
|
112
|
+
# "street": "Oxford Street",
|
113
|
+
# "zip": "E1 6AN"
|
114
|
+
# }
|
115
|
+
# }
|
116
|
+
```
|
117
|
+
|
118
|
+
### Converting YAML to object
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
person = Person.from_yaml(<<~DATA)
|
122
|
+
first_name: John
|
123
|
+
last_name: Doe
|
124
|
+
age: 50
|
125
|
+
married: false
|
126
|
+
hobbies:
|
127
|
+
- Singing
|
128
|
+
- Dancing
|
129
|
+
address:
|
130
|
+
city: London
|
131
|
+
street: Oxford Street
|
132
|
+
zip: E1 6AN
|
133
|
+
DATA
|
134
|
+
```
|
135
|
+
|
136
|
+
### Converting object to YAML
|
137
|
+
|
138
|
+
```ruby
|
139
|
+
person.to_yaml
|
140
|
+
# =>
|
141
|
+
#
|
142
|
+
# ---
|
143
|
+
# first_name: John
|
144
|
+
# last_name: Doe
|
145
|
+
# age: 50
|
146
|
+
# married: false
|
147
|
+
# hobbies:
|
148
|
+
# - Singing
|
149
|
+
# - Dancing
|
150
|
+
# address:
|
151
|
+
# city: London
|
152
|
+
# street: Oxford Street
|
153
|
+
# zip: E1 6AN
|
154
|
+
```
|
155
|
+
|
156
|
+
### Converting Hash to object
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
person = Person.from_hash(
|
160
|
+
'first_name' => 'John',
|
161
|
+
'last_name' => 'Doe',
|
162
|
+
'age' => 50,
|
163
|
+
'married' => false,
|
164
|
+
'hobbies' => ['Singing', 'Dancing'],
|
165
|
+
'address' => {
|
166
|
+
'city'=>'London',
|
167
|
+
'street'=>'Oxford Street',
|
168
|
+
'zip'=>'E1 6AN'
|
169
|
+
},
|
170
|
+
)
|
171
|
+
```
|
172
|
+
|
173
|
+
### Converting object to Hash
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
person.to_hash
|
177
|
+
# =>
|
178
|
+
#
|
179
|
+
# {
|
180
|
+
# "first_name"=>"John",
|
181
|
+
# "last_name"=>"Doe",
|
182
|
+
# "age"=>50,
|
183
|
+
# "married"=>false,
|
184
|
+
# "hobbies"=>["Singing", "Dancing"],
|
185
|
+
# "address"=>{"city"=>"London", "street"=>"Oxford Street", "zip"=>"E1 6AN"}
|
186
|
+
# }
|
187
|
+
```
|
188
|
+
|
189
|
+
### Converting XML to object
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
person = Person.from_xml(<<~DATA)
|
193
|
+
<person>
|
194
|
+
<first_name>John</first_name>
|
195
|
+
<last_name>Doe</last_name>
|
196
|
+
<age>50</age>
|
197
|
+
<married>false</married>
|
198
|
+
<hobbies>Singing</hobbies>
|
199
|
+
<hobbies>Dancing</hobbies>
|
200
|
+
<address>
|
201
|
+
<city>London</city>
|
202
|
+
<street>Oxford Street</street>
|
203
|
+
<zip>E1 6AN</zip>
|
204
|
+
</address>
|
205
|
+
</person>
|
206
|
+
DATA
|
207
|
+
```
|
208
|
+
|
209
|
+
### Converting object to XML
|
210
|
+
|
211
|
+
```ruby
|
212
|
+
person.to_xml
|
213
|
+
# =>
|
214
|
+
#
|
215
|
+
# <person>
|
216
|
+
# <first_name>John</first_name>
|
217
|
+
# <last_name>Doe</last_name>
|
218
|
+
# <age>50</age>
|
219
|
+
# <married>false</married>
|
220
|
+
# <hobbies>Singing</hobbies>
|
221
|
+
# <hobbies>Dancing</hobbies>
|
222
|
+
# <address>
|
223
|
+
# <city>London</city>
|
224
|
+
# <street>Oxford Street</street>
|
225
|
+
# <zip>E1 6AN</zip>
|
226
|
+
# </address>
|
227
|
+
# </person>
|
228
|
+
```
|
229
|
+
|
230
|
+
### Mapping JSON keys to object attributes
|
231
|
+
|
232
|
+
By default keys are named the same as attributes. To use custom key names use:
|
233
|
+
|
234
|
+
```ruby
|
235
|
+
class Person < Shale::Mapper
|
236
|
+
attribute :first_name, Shale::Type::String
|
237
|
+
attribute :last_name, Shale::Type::String
|
238
|
+
|
239
|
+
json do
|
240
|
+
map 'firstName', to: :first_name
|
241
|
+
map 'lastName', to: :last_name
|
242
|
+
end
|
243
|
+
end
|
244
|
+
```
|
245
|
+
|
246
|
+
### Mapping YAML keys to object attributes
|
247
|
+
|
248
|
+
```ruby
|
249
|
+
class Person < Shale::Mapper
|
250
|
+
attribute :first_name, Shale::Type::String
|
251
|
+
attribute :last_name, Shale::Type::String
|
252
|
+
|
253
|
+
yaml do
|
254
|
+
map 'firstName', to: :first_name
|
255
|
+
map 'lastName', to: :last_name
|
256
|
+
end
|
257
|
+
end
|
258
|
+
```
|
259
|
+
|
260
|
+
### Mapping Hash keys to object attributes
|
261
|
+
|
262
|
+
```ruby
|
263
|
+
class Person < Shale::Mapper
|
264
|
+
attribute :first_name, Shale::Type::String
|
265
|
+
attribute :last_name, Shale::Type::String
|
266
|
+
|
267
|
+
hash do
|
268
|
+
map 'firstName', to: :first_name
|
269
|
+
map 'lastName', to: :last_name
|
270
|
+
end
|
271
|
+
end
|
272
|
+
```
|
273
|
+
|
274
|
+
### Mapping XML elements and attributes to object attributes
|
275
|
+
|
276
|
+
XML is more complcated format than JSON or YAML. To map elements, attributes and content use:
|
277
|
+
|
278
|
+
```ruby
|
279
|
+
class Address < Shale::Mapper
|
280
|
+
attribute :street, Shale::Type::String
|
281
|
+
attribute :city, Shale::Type::String
|
282
|
+
attribute :zip, Shale::Type::String
|
283
|
+
|
284
|
+
xml do
|
285
|
+
map_content to: :street
|
286
|
+
map_element 'City', to: :city
|
287
|
+
map_element 'ZIP', to: :zip
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
class Person < Shale::Mapper
|
292
|
+
attribute :first_name, Shale::Type::String
|
293
|
+
attribute :last_name, Shale::Type::String
|
294
|
+
attribute :age, Shale::Type::Date
|
295
|
+
attribute :hobbies, Shale::Type::String, collection: true
|
296
|
+
attribute :address, Address
|
297
|
+
|
298
|
+
xml do
|
299
|
+
root 'Person'
|
300
|
+
|
301
|
+
map_attribute 'age', to: :age
|
302
|
+
|
303
|
+
map_element 'FirstName', to: :first_name
|
304
|
+
map_element 'LastName', to: :last_name
|
305
|
+
map_element 'Hobby', to: :hobbies
|
306
|
+
map_element 'Address', to: :address
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
person = Person.from_xml(<<~DATA)
|
311
|
+
<Person age="50">
|
312
|
+
<FirstName>John</FirstName>
|
313
|
+
<LastName>Doe</LastName>
|
314
|
+
<Hobby>Singing</Hobby>
|
315
|
+
<Hobby>Dancing</Hobby>
|
316
|
+
<Address>
|
317
|
+
Oxford Street
|
318
|
+
<City>London</City>
|
319
|
+
<ZIP>E1 6AN</ZIP>
|
320
|
+
</Address>
|
321
|
+
</person>
|
322
|
+
DATA
|
323
|
+
```
|
324
|
+
|
325
|
+
- `root` - name of the root element
|
326
|
+
- `map_element` - map content of element to attribute
|
327
|
+
- `map_attribute` - map element's attribute to attribute
|
328
|
+
- `map_content` - map first text node to attribute
|
329
|
+
|
330
|
+
### Supported types
|
331
|
+
|
332
|
+
Shale supports these types out of the box:
|
333
|
+
|
334
|
+
- `Shale::Type::Boolean`
|
335
|
+
- `Shale::Type::Date`
|
336
|
+
- `Shale::Type::Float`
|
337
|
+
- `Shale::Type::Integer`
|
338
|
+
- `Shale::Type::String`
|
339
|
+
- `Shale::Type::Time`
|
340
|
+
|
341
|
+
### Writing your own type
|
342
|
+
|
343
|
+
To add your own type extend it from `Shale::Type::Base` and implement `.cast` class method.
|
344
|
+
|
345
|
+
```ruby
|
346
|
+
require 'shale/type/base'
|
347
|
+
|
348
|
+
class MyIntegerType < Shale::Type::Base
|
349
|
+
def self.cast(value)
|
350
|
+
value.to_i
|
351
|
+
end
|
352
|
+
end
|
353
|
+
```
|
354
|
+
|
355
|
+
### Adapters
|
356
|
+
|
357
|
+
Shale uses adapters for parsing and generating documents.
|
358
|
+
By default Ruby's standard JSON parser is used for handing JSON documents, YAML for YAML and
|
359
|
+
REXML for XML.
|
360
|
+
|
361
|
+
You can change it by providing your own adapter. For JSON and YAML adapter must implement
|
362
|
+
`.load` and `.dump` class methods.
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
require 'shale'
|
366
|
+
require 'multi_json'
|
367
|
+
|
368
|
+
Shale.json_adapter = MultiJson
|
369
|
+
Shale.yaml_adapter = MyYamlAdapter
|
370
|
+
```
|
371
|
+
|
372
|
+
For XML, other than REXML, Shale provides adapters for most popular Ruby XML parsers:
|
373
|
+
|
374
|
+
```ruby
|
375
|
+
require 'shale'
|
376
|
+
|
377
|
+
require 'shale/adapter/nokogiri'
|
378
|
+
Shale.xml_adapter = Shale::Adapter::Nokogiri
|
379
|
+
|
380
|
+
# or if you want to use Ox
|
381
|
+
|
382
|
+
require 'shale/adapter/ox'
|
383
|
+
Shale.xml_adapter = Shale::Adapter::Ox
|
384
|
+
```
|
385
|
+
|
386
|
+
## Contributing
|
387
|
+
|
388
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/kgiszczak/shale.
|
389
|
+
|
390
|
+
## License
|
391
|
+
|
392
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Shale
|
6
|
+
module Adapter
|
7
|
+
# JSON adapter
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
class JSON
|
11
|
+
# Parse JSON into Hash
|
12
|
+
#
|
13
|
+
# @param [String] json JSON document
|
14
|
+
#
|
15
|
+
# @return [Hash]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def self.load(json)
|
19
|
+
::JSON.parse(json)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Serialize Hash into JSON
|
23
|
+
#
|
24
|
+
# @param [Hash] obj Hash object
|
25
|
+
#
|
26
|
+
# @return [String]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
def self.dump(obj)
|
30
|
+
::JSON.generate(obj)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
|
5
|
+
module Shale
|
6
|
+
module Adapter
|
7
|
+
# Nokogiri adapter
|
8
|
+
#
|
9
|
+
# @api public
|
10
|
+
module Nokogiri
|
11
|
+
# Parse XML into Nokogiri document
|
12
|
+
#
|
13
|
+
# @param [String] xml XML document
|
14
|
+
#
|
15
|
+
# @return [::Nokogiri::XML::Document]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def self.load(xml)
|
19
|
+
doc = ::Nokogiri::XML::Document.parse(xml) do |config|
|
20
|
+
config.noblanks
|
21
|
+
end
|
22
|
+
|
23
|
+
Node.new(doc.root)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Serialize Nokogiri document into XML
|
27
|
+
#
|
28
|
+
# @param [::Nokogiri::XML::Document] doc Nokogiri document
|
29
|
+
#
|
30
|
+
# @return [String]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
def self.dump(doc)
|
34
|
+
doc.to_xml
|
35
|
+
end
|
36
|
+
|
37
|
+
# Create Shale::Adapter::Nokogiri::Document instance
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
def self.create_document
|
41
|
+
Document.new
|
42
|
+
end
|
43
|
+
|
44
|
+
# Wrapper around Nokogiri API
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
class Document
|
48
|
+
# Return Nokogiri document
|
49
|
+
#
|
50
|
+
# @return [::Nokogiri::XML::Document]
|
51
|
+
#
|
52
|
+
# @api private
|
53
|
+
attr_reader :doc
|
54
|
+
|
55
|
+
# Initialize object
|
56
|
+
#
|
57
|
+
# @api private
|
58
|
+
def initialize
|
59
|
+
@doc = ::Nokogiri::XML::Document.new
|
60
|
+
end
|
61
|
+
|
62
|
+
# Create Nokogiri element
|
63
|
+
#
|
64
|
+
# @param [String] name Name of the XML element
|
65
|
+
#
|
66
|
+
# @return [::Nokogiri::XML::Element]
|
67
|
+
#
|
68
|
+
# @api private
|
69
|
+
def create_element(name)
|
70
|
+
::Nokogiri::XML::Element.new(name, @doc)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Add attribute to Nokogiri element
|
74
|
+
#
|
75
|
+
# @param [::Nokogiri::XML::Element] element Nokogiri element
|
76
|
+
# @param [String] name Name of the XML attribute
|
77
|
+
# @param [String] value Value of the XML attribute
|
78
|
+
#
|
79
|
+
# @api private
|
80
|
+
def add_attribute(element, name, value)
|
81
|
+
element[name] = value
|
82
|
+
end
|
83
|
+
|
84
|
+
# Add child element to Nokogiri element
|
85
|
+
#
|
86
|
+
# @param [::Nokogiri::XML::Element] element Nokogiri parent element
|
87
|
+
# @param [::Nokogiri::XML::Element] child Nokogiri child element
|
88
|
+
#
|
89
|
+
# @api private
|
90
|
+
def add_element(element, child)
|
91
|
+
element.add_child(child)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Add text node to Nokogiri element
|
95
|
+
#
|
96
|
+
# @param [::Nokogiri::XML::Element] element Nokogiri element
|
97
|
+
# @param [String] text Text to add
|
98
|
+
#
|
99
|
+
# @api private
|
100
|
+
def add_text(element, text)
|
101
|
+
element.content = text
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Wrapper around Nokogiri::XML::Node API
|
106
|
+
#
|
107
|
+
# @api private
|
108
|
+
class Node
|
109
|
+
# Initialize object with Nokogiri node
|
110
|
+
#
|
111
|
+
# @param [::Nokogiri::XML::Node] node Nokogiri node
|
112
|
+
#
|
113
|
+
# @api private
|
114
|
+
def initialize(node)
|
115
|
+
@node = node
|
116
|
+
end
|
117
|
+
|
118
|
+
# Return fully qualified name of the node in the format of
|
119
|
+
# namespace:name when the node is namespaced or just name when it's not
|
120
|
+
#
|
121
|
+
# @return [String]
|
122
|
+
#
|
123
|
+
# @example without namespace
|
124
|
+
# node.name # => Bar
|
125
|
+
#
|
126
|
+
# @example with namespace
|
127
|
+
# node.name # => foo:Bar
|
128
|
+
#
|
129
|
+
# @api private
|
130
|
+
def name
|
131
|
+
[@node.namespace&.prefix, @node.name].compact.join(':')
|
132
|
+
end
|
133
|
+
|
134
|
+
# Return all attributes associated with the node
|
135
|
+
#
|
136
|
+
# @return [Hash]
|
137
|
+
#
|
138
|
+
# @api private
|
139
|
+
def attributes
|
140
|
+
@node.attribute_nodes.each_with_object({}) do |node, hash|
|
141
|
+
name = [node.namespace&.prefix, node.name].compact.join(':')
|
142
|
+
hash[name] = node.value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Return node's element children
|
147
|
+
#
|
148
|
+
# @return [Array<Shale::Adapter::Nokogiri::Node>]
|
149
|
+
#
|
150
|
+
# @api private
|
151
|
+
def children
|
152
|
+
@node
|
153
|
+
.children
|
154
|
+
.to_a
|
155
|
+
.filter(&:element?)
|
156
|
+
.map { |e| self.class.new(e) }
|
157
|
+
end
|
158
|
+
|
159
|
+
# Return first text child of a node
|
160
|
+
#
|
161
|
+
# @return [String]
|
162
|
+
#
|
163
|
+
# @api private
|
164
|
+
def text
|
165
|
+
first = @node
|
166
|
+
.children
|
167
|
+
.to_a
|
168
|
+
.filter(&:text?)
|
169
|
+
.first
|
170
|
+
|
171
|
+
first&.text
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|