shale 0.2.1 → 0.3.1
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 +274 -7
- data/exe/shaleb +75 -0
- data/lib/shale/adapter/json.rb +7 -2
- data/lib/shale/adapter/nokogiri.rb +48 -12
- data/lib/shale/adapter/ox.rb +28 -4
- data/lib/shale/adapter/rexml.rb +56 -13
- data/lib/shale/attribute.rb +1 -1
- data/lib/shale/error.rb +6 -0
- data/lib/shale/mapper.rb +11 -11
- data/lib/shale/mapping/descriptor/dict.rb +57 -0
- data/lib/shale/mapping/descriptor/xml.rb +43 -0
- data/lib/shale/mapping/descriptor/xml_namespace.rb +37 -0
- data/lib/shale/mapping/{key_value.rb → dict.rb} +8 -6
- data/lib/shale/mapping/validator.rb +51 -0
- data/lib/shale/mapping/xml.rb +86 -15
- data/lib/shale/schema/json/base.rb +41 -0
- data/lib/shale/schema/json/boolean.rb +23 -0
- data/lib/shale/schema/json/collection.rb +39 -0
- data/lib/shale/schema/json/date.rb +23 -0
- data/lib/shale/schema/json/float.rb +23 -0
- data/lib/shale/schema/json/integer.rb +23 -0
- data/lib/shale/schema/json/object.rb +37 -0
- data/lib/shale/schema/json/ref.rb +28 -0
- data/lib/shale/schema/json/schema.rb +57 -0
- data/lib/shale/schema/json/string.rb +23 -0
- data/lib/shale/schema/json/time.rb +23 -0
- data/lib/shale/schema/json.rb +165 -0
- data/lib/shale/schema/xml/attribute.rb +41 -0
- data/lib/shale/schema/xml/complex_type.rb +67 -0
- data/lib/shale/schema/xml/element.rb +55 -0
- data/lib/shale/schema/xml/import.rb +46 -0
- data/lib/shale/schema/xml/ref_attribute.rb +37 -0
- data/lib/shale/schema/xml/ref_element.rb +39 -0
- data/lib/shale/schema/xml/schema.rb +121 -0
- data/lib/shale/schema/xml/typed_attribute.rb +46 -0
- data/lib/shale/schema/xml/typed_element.rb +46 -0
- data/lib/shale/schema/xml.rb +316 -0
- data/lib/shale/schema.rb +47 -0
- data/lib/shale/type/boolean.rb +2 -2
- data/lib/shale/type/composite.rb +67 -56
- data/lib/shale/type/date.rb +35 -2
- data/lib/shale/type/float.rb +2 -2
- data/lib/shale/type/integer.rb +2 -2
- data/lib/shale/type/string.rb +2 -2
- data/lib/shale/type/time.rb +35 -2
- data/lib/shale/type/{base.rb → value.rb} +18 -7
- data/lib/shale/utils.rb +18 -2
- data/lib/shale/version.rb +1 -1
- data/lib/shale.rb +10 -10
- data/shale.gemspec +6 -2
- metadata +41 -13
- data/lib/shale/mapping/base.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 36678c1f4afd6e0cf2d6a9a9e4b5becebf11e76404cb50dba7dc5269b5ca42cc
|
4
|
+
data.tar.gz: 51c6c94abafe1767c579c270e3c3cb4da2ca79a2a29a9d28ea6bfe09bc4b53fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ea21dff00b6ea2663d20f9f377140250f78c29214a93f226047fe29d47680ae526652010ae268e4cf694ca1877cd8d19bfe8f0bbd767fa871fa80afaefee1c83
|
7
|
+
data.tar.gz: 414d45b7bcd2477bc276eee17c598efba40350e0234c32f783c73217bee65ae4260d07e8a7bcfc7f48e1f76c9af4852de1c1c109c5b807e99a945305e2de6286
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,27 @@
|
|
1
|
+
## [0.3.1] - 2022-04-29
|
2
|
+
|
3
|
+
### Changed
|
4
|
+
- Rename `id` -> `$id` and add info about supported JSON Schema dialect
|
5
|
+
|
6
|
+
## [0.3.0] - 2022-04-29
|
7
|
+
|
8
|
+
### Added
|
9
|
+
- Support for XML namespaces
|
10
|
+
- Add option to pretty print JSON and XML and to include XML declaration
|
11
|
+
- Add support for generating JSON and XML Schema
|
12
|
+
|
13
|
+
### Changed
|
14
|
+
- Various fixes to documentation
|
15
|
+
- Rename `hash` -> `hsh` (`hash` is used internally by Ruby)
|
16
|
+
- Rename `Shale::Type::Base` -> `Shale::Type::Value`
|
17
|
+
- Use ISO 8601 format for date and time in JSON, YAML and XML
|
18
|
+
|
19
|
+
## [0.2.2] - 2022-03-06
|
20
|
+
|
21
|
+
### Fixed
|
22
|
+
- Fix handling of blank attributes in XML format
|
23
|
+
- Fix incorrect types in README examples
|
24
|
+
|
1
25
|
## [0.2.1] - 2022-02-06
|
2
26
|
|
3
27
|
### Fixed
|
data/README.md
CHANGED
@@ -24,8 +24,35 @@ Or install it yourself as:
|
|
24
24
|
$ gem install shale
|
25
25
|
```
|
26
26
|
|
27
|
+
## Contents
|
28
|
+
|
29
|
+
* [Simple use case](#user-content-simple-use-case)
|
30
|
+
* [Creating objects](#creating-objects)
|
31
|
+
* [Converting JSON to object](#converting-json-to-object)
|
32
|
+
* [Converting object to JSON](#converting-object-to-json)
|
33
|
+
* [Converting YAML to object](#converting-yaml-to-object)
|
34
|
+
* [Converting object to YAML](#converting-object-to-yaml)
|
35
|
+
* [Converting Hash to object](#converting-hash-to-object)
|
36
|
+
* [Converting object to Hash](#converting-object-to-hash)
|
37
|
+
* [Converting XML to object](#converting-xml-to-object)
|
38
|
+
* [Converting object to XML](#converting-object-to-xml)
|
39
|
+
* [Mapping JSON keys to object attributes](#mapping-json-keys-to-object-attributes)
|
40
|
+
* [Mapping YAML keys to object attributes](#mapping-yaml-keys-to-object-attributes)
|
41
|
+
* [Mapping Hash keys to object attributes](#mapping-hash-keys-to-object-attributes)
|
42
|
+
* [Mapping XML elements and attributes to object attributes](#mapping-xml-elements-and-attributes-to-object-attributes)
|
43
|
+
* [Using XML namespaces](#using-xml-namespaces)
|
44
|
+
* [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
|
45
|
+
* [Pretty printing and XML declaration](#pretty-printing-and-xml-declaration)
|
46
|
+
* [Supported types](#supported-types)
|
47
|
+
* [Writing your own type](#writing-your-own-type)
|
48
|
+
* [Adapters](#adapters)
|
49
|
+
* [Generating JSON Schema](#generating-json-schema)
|
50
|
+
* [Generating XML Schema](#generating-xml-schema)
|
51
|
+
|
27
52
|
## Usage
|
28
53
|
|
54
|
+
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
55
|
+
|
29
56
|
### Simple use case
|
30
57
|
|
31
58
|
```ruby
|
@@ -40,7 +67,7 @@ end
|
|
40
67
|
class Person < Shale::Mapper
|
41
68
|
attribute :first_name, Shale::Type::String
|
42
69
|
attribute :last_name, Shale::Type::String
|
43
|
-
attribute :age, Shale::Type::
|
70
|
+
attribute :age, Shale::Type::Integer
|
44
71
|
attribute :married, Shale::Type::Boolean, default: -> { false }
|
45
72
|
attribute :hobbies, Shale::Type::String, collection: true
|
46
73
|
attribute :address, Address
|
@@ -264,7 +291,7 @@ class Person < Shale::Mapper
|
|
264
291
|
attribute :first_name, Shale::Type::String
|
265
292
|
attribute :last_name, Shale::Type::String
|
266
293
|
|
267
|
-
|
294
|
+
hsh do
|
268
295
|
map 'firstName', to: :first_name
|
269
296
|
map 'lastName', to: :last_name
|
270
297
|
end
|
@@ -291,7 +318,7 @@ end
|
|
291
318
|
class Person < Shale::Mapper
|
292
319
|
attribute :first_name, Shale::Type::String
|
293
320
|
attribute :last_name, Shale::Type::String
|
294
|
-
attribute :age, Shale::Type::
|
321
|
+
attribute :age, Shale::Type::Integer
|
295
322
|
attribute :hobbies, Shale::Type::String, collection: true
|
296
323
|
attribute :address, Address
|
297
324
|
|
@@ -327,6 +354,74 @@ DATA
|
|
327
354
|
- `map_attribute` - map element's attribute to attribute
|
328
355
|
- `map_content` - map first text node to attribute
|
329
356
|
|
357
|
+
### Using XML namespaces
|
358
|
+
|
359
|
+
:warning: **Ox doesn't support XML namespaces**
|
360
|
+
|
361
|
+
To map namespaced elements and attributes use `namespace` and `prefix` properties on
|
362
|
+
`map_element` and `map_attribute`
|
363
|
+
|
364
|
+
```ruby
|
365
|
+
class Person < Shale::Mapper
|
366
|
+
attribute :first_name, Shale::Type::String
|
367
|
+
attribute :last_name, Shale::Type::String
|
368
|
+
attribute :age, Shale::Type::Integer
|
369
|
+
|
370
|
+
xml do
|
371
|
+
root 'person'
|
372
|
+
|
373
|
+
map_element 'first_name', to: :first_name, namespace: 'http://ns1.com', prefix: 'ns1'
|
374
|
+
map_element 'last_name', to: :last_name, namespace: 'http://ns2.com', prefix: 'ns2'
|
375
|
+
map_attribute 'age', to: :age, namespace: 'http://ns2.com', prefix: 'ns2'
|
376
|
+
end
|
377
|
+
end
|
378
|
+
|
379
|
+
person = Person.from_xml(<<~DATA)
|
380
|
+
<person xmlns:ns1="http://ns1.com" xmlns:ns2="http://ns2.com" ns2:age="50">
|
381
|
+
<ns1:first_name>John</ns1:first_name>
|
382
|
+
<ns2:last_name>Doe</ns2:last_name>
|
383
|
+
</person>
|
384
|
+
DATA
|
385
|
+
```
|
386
|
+
|
387
|
+
To define default namespace for all elements use `namespace` declaration
|
388
|
+
(this will define namespace only on elements, if you want to define namespace on an attribute
|
389
|
+
explicitly declare it on `map_attribute`).
|
390
|
+
|
391
|
+
```ruby
|
392
|
+
class Person < Shale::Mapper
|
393
|
+
attribute :first_name, Shale::Type::String
|
394
|
+
attribute :middle_name, Shale::Type::String
|
395
|
+
attribute :last_name, Shale::Type::String
|
396
|
+
attribute :age, Shale::Type::Integer
|
397
|
+
attribute :hobby, Shale::Type::String
|
398
|
+
|
399
|
+
xml do
|
400
|
+
root 'person'
|
401
|
+
namespace 'http://ns1.com', 'ns1'
|
402
|
+
|
403
|
+
map_element 'first_name', to: :first_name
|
404
|
+
|
405
|
+
# undeclare namespace on 'middle_name' element
|
406
|
+
map_element 'middle_name', to: :middle_name, namespace: nil, prefix: nil
|
407
|
+
|
408
|
+
# overwrite default namespace
|
409
|
+
map_element 'last_name', to: :last_name, namespace: 'http://ns2.com', prefix: 'ns2'
|
410
|
+
|
411
|
+
map_attribute 'age', to: :age
|
412
|
+
map_attribute 'hobby', to: :hobby, namespace: 'http://ns1.com', prefix: 'ns1'
|
413
|
+
end
|
414
|
+
end
|
415
|
+
|
416
|
+
person = Person.from_xml(<<~DATA)
|
417
|
+
<ns1:person xmlns:ns1="http://ns1.com" xmlns:ns2="http://ns2.com" age="50" ns1:hobby="running">
|
418
|
+
<ns1:first_name>John</ns1:first_name>
|
419
|
+
<middle_name>Joe</middle_name>
|
420
|
+
<ns2:last_name>Doe</ns2:last_name>
|
421
|
+
</ns1:person>
|
422
|
+
DATA
|
423
|
+
```
|
424
|
+
|
330
425
|
### Using methods to extract and generate data
|
331
426
|
|
332
427
|
If you need full controll over extracting and generating data from/to document,
|
@@ -421,6 +516,36 @@ DATA
|
|
421
516
|
# @city="London">
|
422
517
|
```
|
423
518
|
|
519
|
+
### Pretty printing and XML declaration
|
520
|
+
|
521
|
+
If you need formatted output you can pass `:pretty` parameter to `#to_json` and `#to_xml`
|
522
|
+
|
523
|
+
```ruby
|
524
|
+
person.to_json(:pretty)
|
525
|
+
|
526
|
+
# =>
|
527
|
+
#
|
528
|
+
# {
|
529
|
+
# "name": "John Doe",
|
530
|
+
# "address": {
|
531
|
+
# "city": "London"
|
532
|
+
# }
|
533
|
+
# }
|
534
|
+
```
|
535
|
+
|
536
|
+
You can also add an XML declaration by passing `:declaration` to `#to_xml`
|
537
|
+
|
538
|
+
```ruby
|
539
|
+
person.to_xml(:pretty, :declaration)
|
540
|
+
|
541
|
+
# =>
|
542
|
+
#
|
543
|
+
# <?xml version="1.0"?>
|
544
|
+
# <Person>
|
545
|
+
# <Address city="London"/>
|
546
|
+
# </Person>
|
547
|
+
```
|
548
|
+
|
424
549
|
### Supported types
|
425
550
|
|
426
551
|
Shale supports these types out of the box:
|
@@ -434,12 +559,12 @@ Shale supports these types out of the box:
|
|
434
559
|
|
435
560
|
### Writing your own type
|
436
561
|
|
437
|
-
To add your own type extend it from `Shale::Type::
|
562
|
+
To add your own type extend it from `Shale::Type::Value` and implement `.cast` class method.
|
438
563
|
|
439
564
|
```ruby
|
440
|
-
require 'shale/type/
|
565
|
+
require 'shale/type/value'
|
441
566
|
|
442
|
-
class MyIntegerType < Shale::Type::
|
567
|
+
class MyIntegerType < Shale::Type::Value
|
443
568
|
def self.cast(value)
|
444
569
|
value.to_i
|
445
570
|
end
|
@@ -463,7 +588,7 @@ Shale.json_adapter = MultiJson
|
|
463
588
|
Shale.yaml_adapter = MyYamlAdapter
|
464
589
|
```
|
465
590
|
|
466
|
-
|
591
|
+
Shale provides adapters for most popular Ruby XML parsers:
|
467
592
|
|
468
593
|
```ruby
|
469
594
|
require 'shale'
|
@@ -484,6 +609,148 @@ require 'shale/adapter/ox'
|
|
484
609
|
Shale.xml_adapter = Shale::Adapter::Ox
|
485
610
|
```
|
486
611
|
|
612
|
+
### Generating JSON Schema
|
613
|
+
|
614
|
+
To generate JSON Schema from you Shale data model use:
|
615
|
+
|
616
|
+
:warning: Shale only supports **[Draft 2020-12](https://json-schema.org/draft/2020-12/schema)** JSON Schema
|
617
|
+
|
618
|
+
```ruby
|
619
|
+
require 'shale/schema'
|
620
|
+
|
621
|
+
Shale::Schema.to_json(Person, id: 'http://foo.bar/schema/person', description: 'My description', pretty: true)
|
622
|
+
|
623
|
+
# =>
|
624
|
+
#
|
625
|
+
# {
|
626
|
+
# "$schema": "https://json-schema.org/draft/2020-12/schema",
|
627
|
+
# "$id": "http://foo.bar/schema/person",
|
628
|
+
# "description": "My description",
|
629
|
+
# "$ref": "#/$defs/Person",
|
630
|
+
# "$defs": {
|
631
|
+
# "Address": {
|
632
|
+
# "type": [
|
633
|
+
# "object",
|
634
|
+
# "null"
|
635
|
+
# ],
|
636
|
+
# "properties": {
|
637
|
+
# "city": {
|
638
|
+
# "type": [
|
639
|
+
# "string",
|
640
|
+
# "null"
|
641
|
+
# ]
|
642
|
+
# }
|
643
|
+
# }
|
644
|
+
# },
|
645
|
+
# "Person": {
|
646
|
+
# "type": "object",
|
647
|
+
# "properties": {
|
648
|
+
# "name": {
|
649
|
+
# "type": [
|
650
|
+
# "string",
|
651
|
+
# "null"
|
652
|
+
# ]
|
653
|
+
# },
|
654
|
+
# "address": {
|
655
|
+
# "$ref": "#/$defs/Address"
|
656
|
+
# }
|
657
|
+
# }
|
658
|
+
# }
|
659
|
+
# }
|
660
|
+
# }
|
661
|
+
```
|
662
|
+
|
663
|
+
You can also use a command line tool to do it:
|
664
|
+
|
665
|
+
```
|
666
|
+
$ shaleb -i data_model.rb -c Person -p
|
667
|
+
```
|
668
|
+
|
669
|
+
If you want to convert your own types to JSON Schema types use:
|
670
|
+
|
671
|
+
```ruby
|
672
|
+
require 'shale'
|
673
|
+
require 'shale/schema'
|
674
|
+
|
675
|
+
class MyEmailType < Shale::Type::Value
|
676
|
+
...
|
677
|
+
end
|
678
|
+
|
679
|
+
class MyEmailJSONType < Shale::Schema::JSON::Base
|
680
|
+
def as_type
|
681
|
+
{ 'type' => 'string', 'format' => 'email' }
|
682
|
+
end
|
683
|
+
end
|
684
|
+
|
685
|
+
Shale::Schema::JSON.register_json_type(MyEmailType, MyEmailJSONType)
|
686
|
+
```
|
687
|
+
|
688
|
+
### Generating XML Schema
|
689
|
+
|
690
|
+
To generate XML Schema from your Shale data model use:
|
691
|
+
|
692
|
+
```ruby
|
693
|
+
require 'shale/schema'
|
694
|
+
|
695
|
+
Shale::Schema.to_xml(Person, pretty: true)
|
696
|
+
|
697
|
+
# =>
|
698
|
+
#
|
699
|
+
# {
|
700
|
+
# 'schema0.xsd' => '
|
701
|
+
# <xs:schema
|
702
|
+
# elementFormDefault="qualified"
|
703
|
+
# attributeFormDefault="qualified"
|
704
|
+
# xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
705
|
+
# xmlns:foo="http://foo.com"
|
706
|
+
# >
|
707
|
+
# <xs:import namespace="http://foo.com" schemaLocation="schema1.xsd"/>
|
708
|
+
# <xs:element name="person" type="Person"/>
|
709
|
+
# <xs:complexType name="Person">
|
710
|
+
# <xs:sequence>
|
711
|
+
# <xs:element name="name" type="xs:string" minOccurs="0"/>
|
712
|
+
# <xs:element ref="foo:address" minOccurs="0"/>
|
713
|
+
# </xs:sequence>
|
714
|
+
# </xs:complexType>
|
715
|
+
# </xs:schema>',
|
716
|
+
#
|
717
|
+
# 'schema1.xsd' => '
|
718
|
+
# <xs:schema
|
719
|
+
# elementFormDefault="qualified"
|
720
|
+
# attributeFormDefault="qualified"
|
721
|
+
# targetNamespace="http://foo.com"
|
722
|
+
# xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
723
|
+
# xmlns:foo="http://foo.com"
|
724
|
+
# >
|
725
|
+
# <xs:element name="address" type="foo:Address"/>
|
726
|
+
# <xs:complexType name="Address">
|
727
|
+
# <xs:sequence>
|
728
|
+
# <xs:element name="city" type="xs:string" minOccurs="0"/>
|
729
|
+
# </xs:sequence>
|
730
|
+
# </xs:complexType>
|
731
|
+
# </xs:schema>'
|
732
|
+
# }
|
733
|
+
```
|
734
|
+
|
735
|
+
You can also use a command line tool to do it:
|
736
|
+
|
737
|
+
```
|
738
|
+
$ shaleb -i data_model.rb -c Person -p -f xml
|
739
|
+
```
|
740
|
+
|
741
|
+
If you want to convert your own types to XML Schema types use:
|
742
|
+
|
743
|
+
```ruby
|
744
|
+
require 'shale'
|
745
|
+
require 'shale/schema'
|
746
|
+
|
747
|
+
class MyEmailType < Shale::Type::Value
|
748
|
+
...
|
749
|
+
end
|
750
|
+
|
751
|
+
Shale::Schema::XML.register_xml_type(MyEmailType, 'myEmailXMLType')
|
752
|
+
```
|
753
|
+
|
487
754
|
## Contributing
|
488
755
|
|
489
756
|
Bug reports and pull requests are welcome on GitHub at https://github.com/kgiszczak/shale.
|
data/exe/shaleb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'optparse'
|
5
|
+
|
6
|
+
base_path = File.expand_path('../lib', __dir__)
|
7
|
+
|
8
|
+
if File.exist?(base_path)
|
9
|
+
require_relative '../lib/shale/schema'
|
10
|
+
else
|
11
|
+
require 'shale/schema'
|
12
|
+
end
|
13
|
+
|
14
|
+
params = {}
|
15
|
+
|
16
|
+
ARGV << '-h' if ARGV.empty?
|
17
|
+
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.banner = "Usage: shaleb [options]\nexample: shaleb -i data_model.rb -c MyRoot"
|
20
|
+
|
21
|
+
opts.on('-i INPUT', '--input', 'Input file')
|
22
|
+
opts.on('-o OUTPUT', '--output', 'Output file (defaults to STDOUT)')
|
23
|
+
opts.on('-c CLASS', '--class CLASS', 'Shale model class name')
|
24
|
+
opts.on('-f FORMAT', '--format FORMAT', 'Schema format: JSON (default), XML')
|
25
|
+
opts.on('-p', '--pretty', 'Pretty print generated schema')
|
26
|
+
|
27
|
+
opts.on('-v', '--version', 'Show version') do
|
28
|
+
puts "shaleb version #{Shale::VERSION}"
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
end.parse!(into: params)
|
32
|
+
|
33
|
+
input_path = File.expand_path(params[:input], Dir.pwd)
|
34
|
+
|
35
|
+
unless File.exist?(input_path)
|
36
|
+
puts "File '#{input_path}' does not exist"
|
37
|
+
exit
|
38
|
+
end
|
39
|
+
|
40
|
+
unless params[:class]
|
41
|
+
puts 'Model class is required'
|
42
|
+
exit
|
43
|
+
end
|
44
|
+
|
45
|
+
require input_path
|
46
|
+
|
47
|
+
klass = Object.const_get(params[:class])
|
48
|
+
|
49
|
+
if params[:format] == 'xml'
|
50
|
+
if params[:output]
|
51
|
+
base_name = File.basename(params[:output], File.extname(params[:output]))
|
52
|
+
schemas = Shale::Schema.to_xml(klass, base_name, pretty: params[:pretty])
|
53
|
+
|
54
|
+
schemas.map do |name, xml|
|
55
|
+
File.write(File.expand_path(name, Dir.pwd), xml)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
schemas = Shale::Schema.to_xml(klass, pretty: params[:pretty])
|
59
|
+
|
60
|
+
output = schemas.map do |name, xml|
|
61
|
+
"<!-- #{name} -->\n#{xml}\n"
|
62
|
+
end.join("\n")
|
63
|
+
|
64
|
+
puts output
|
65
|
+
end
|
66
|
+
else
|
67
|
+
schema = Shale::Schema.to_json(klass, pretty: params[:pretty])
|
68
|
+
|
69
|
+
if params[:output]
|
70
|
+
output_path = File.expand_path(params[:output], Dir.pwd)
|
71
|
+
File.write(output_path, schema)
|
72
|
+
else
|
73
|
+
puts schema
|
74
|
+
end
|
75
|
+
end
|
data/lib/shale/adapter/json.rb
CHANGED
@@ -22,12 +22,17 @@ module Shale
|
|
22
22
|
# Serialize Hash into JSON
|
23
23
|
#
|
24
24
|
# @param [Hash] obj Hash object
|
25
|
+
# @param [Array<Symbol>] options
|
25
26
|
#
|
26
27
|
# @return [String]
|
27
28
|
#
|
28
29
|
# @api private
|
29
|
-
def self.dump(obj)
|
30
|
-
|
30
|
+
def self.dump(obj, *options)
|
31
|
+
if options.include?(:pretty)
|
32
|
+
::JSON.pretty_generate(obj)
|
33
|
+
else
|
34
|
+
::JSON.generate(obj)
|
35
|
+
end
|
31
36
|
end
|
32
37
|
end
|
33
38
|
end
|
@@ -26,12 +26,29 @@ module Shale
|
|
26
26
|
# Serialize Nokogiri document into XML
|
27
27
|
#
|
28
28
|
# @param [::Nokogiri::XML::Document] doc Nokogiri document
|
29
|
+
# @param [Array<Symbol>] options
|
29
30
|
#
|
30
31
|
# @return [String]
|
31
32
|
#
|
32
33
|
# @api private
|
33
|
-
def self.dump(doc)
|
34
|
-
|
34
|
+
def self.dump(doc, *options)
|
35
|
+
save_with = ::Nokogiri::XML::Node::SaveOptions::AS_XML
|
36
|
+
|
37
|
+
if options.include?(:pretty)
|
38
|
+
save_with |= ::Nokogiri::XML::Node::SaveOptions::FORMAT
|
39
|
+
end
|
40
|
+
|
41
|
+
unless options.include?(:declaration)
|
42
|
+
save_with |= ::Nokogiri::XML::Node::SaveOptions::NO_DECLARATION
|
43
|
+
end
|
44
|
+
|
45
|
+
result = doc.to_xml(save_with: save_with)
|
46
|
+
|
47
|
+
unless options.include?(:pretty)
|
48
|
+
result = result.sub(/\n/, '')
|
49
|
+
end
|
50
|
+
|
51
|
+
result
|
35
52
|
end
|
36
53
|
|
37
54
|
# Create Shale::Adapter::Nokogiri::Document instance
|
@@ -45,18 +62,27 @@ module Shale
|
|
45
62
|
#
|
46
63
|
# @api private
|
47
64
|
class Document
|
65
|
+
# Initialize object
|
66
|
+
#
|
67
|
+
# @api private
|
68
|
+
def initialize
|
69
|
+
@doc = ::Nokogiri::XML::Document.new
|
70
|
+
@namespaces = {}
|
71
|
+
end
|
72
|
+
|
48
73
|
# Return Nokogiri document
|
49
74
|
#
|
50
75
|
# @return [::Nokogiri::XML::Document]
|
51
76
|
#
|
52
77
|
# @api private
|
53
|
-
|
78
|
+
def doc
|
79
|
+
if @doc.root
|
80
|
+
@namespaces.each do |prefix, namespace|
|
81
|
+
@doc.root.add_namespace(prefix, namespace)
|
82
|
+
end
|
83
|
+
end
|
54
84
|
|
55
|
-
|
56
|
-
#
|
57
|
-
# @api private
|
58
|
-
def initialize
|
59
|
-
@doc = ::Nokogiri::XML::Document.new
|
85
|
+
@doc
|
60
86
|
end
|
61
87
|
|
62
88
|
# Create Nokogiri element
|
@@ -70,6 +96,16 @@ module Shale
|
|
70
96
|
::Nokogiri::XML::Element.new(name, @doc)
|
71
97
|
end
|
72
98
|
|
99
|
+
# Add XML namespace to document
|
100
|
+
#
|
101
|
+
# @param [String] prefix
|
102
|
+
# @param [String] namespace
|
103
|
+
#
|
104
|
+
# @api private
|
105
|
+
def add_namespace(prefix, namespace)
|
106
|
+
@namespaces[prefix] = namespace if prefix && namespace
|
107
|
+
end
|
108
|
+
|
73
109
|
# Add attribute to Nokogiri element
|
74
110
|
#
|
75
111
|
# @param [::Nokogiri::XML::Element] element Nokogiri element
|
@@ -115,7 +151,7 @@ module Shale
|
|
115
151
|
@node = node
|
116
152
|
end
|
117
153
|
|
118
|
-
# Return
|
154
|
+
# Return name of the node in the format of
|
119
155
|
# namespace:name when the node is namespaced or just name when it's not
|
120
156
|
#
|
121
157
|
# @return [String]
|
@@ -124,11 +160,11 @@ module Shale
|
|
124
160
|
# node.name # => Bar
|
125
161
|
#
|
126
162
|
# @example with namespace
|
127
|
-
# node.name # => foo:Bar
|
163
|
+
# node.name # => http://foo:Bar
|
128
164
|
#
|
129
165
|
# @api private
|
130
166
|
def name
|
131
|
-
[@node.namespace&.
|
167
|
+
[@node.namespace&.href, @node.name].compact.join(':')
|
132
168
|
end
|
133
169
|
|
134
170
|
# Return all attributes associated with the node
|
@@ -138,7 +174,7 @@ module Shale
|
|
138
174
|
# @api private
|
139
175
|
def attributes
|
140
176
|
@node.attribute_nodes.each_with_object({}) do |node, hash|
|
141
|
-
name = [node.namespace&.
|
177
|
+
name = [node.namespace&.href, node.name].compact.join(':')
|
142
178
|
hash[name] = node.value
|
143
179
|
end
|
144
180
|
end
|
data/lib/shale/adapter/ox.rb
CHANGED
@@ -22,12 +22,24 @@ module Shale
|
|
22
22
|
# Serialize Ox document into XML
|
23
23
|
#
|
24
24
|
# @param [::Ox::Document, ::Ox::Element] doc Ox document
|
25
|
+
# @param [Array<Symbol>] options
|
25
26
|
#
|
26
27
|
# @return [String]
|
27
28
|
#
|
28
29
|
# @api private
|
29
|
-
def self.dump(doc)
|
30
|
-
|
30
|
+
def self.dump(doc, *options)
|
31
|
+
opts = { indent: -1, with_xml: false }
|
32
|
+
|
33
|
+
if options.include?(:pretty)
|
34
|
+
opts[:indent] = 2
|
35
|
+
end
|
36
|
+
|
37
|
+
if options.include?(:declaration)
|
38
|
+
doc[:version] = '1.0'
|
39
|
+
opts[:with_xml] = true
|
40
|
+
end
|
41
|
+
|
42
|
+
::Ox.dump(doc, opts).sub(/\A\n/, '')
|
31
43
|
end
|
32
44
|
|
33
45
|
# Create Shale::Adapter::Ox::Document instance
|
@@ -66,6 +78,18 @@ module Shale
|
|
66
78
|
::Ox::Element.new(name)
|
67
79
|
end
|
68
80
|
|
81
|
+
# Add XML namespace to document
|
82
|
+
#
|
83
|
+
# Ox doesn't support XML namespaces so this method does nothing.
|
84
|
+
#
|
85
|
+
# @param [String] prefix
|
86
|
+
# @param [String] namespace
|
87
|
+
#
|
88
|
+
# @api private
|
89
|
+
def add_namespace(prefix, namespace)
|
90
|
+
# :noop:
|
91
|
+
end
|
92
|
+
|
69
93
|
# Add attribute to Ox element
|
70
94
|
#
|
71
95
|
# @param [::Ox::Element] element Ox element
|
@@ -111,8 +135,8 @@ module Shale
|
|
111
135
|
@node = node
|
112
136
|
end
|
113
137
|
|
114
|
-
# Return
|
115
|
-
#
|
138
|
+
# Return name of the node in the format of
|
139
|
+
# prefix:name when the node is namespaced or just name when it's not
|
116
140
|
#
|
117
141
|
# @return [String]
|
118
142
|
#
|