shale 0.2.2 → 0.4.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 +33 -0
- data/README.md +366 -8
- data/exe/shaleb +123 -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 +7 -1
- data/lib/shale/error.rb +12 -0
- data/lib/shale/mapper.rb +17 -15
- 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_compiler/boolean.rb +21 -0
- data/lib/shale/schema/json_compiler/date.rb +21 -0
- data/lib/shale/schema/json_compiler/float.rb +21 -0
- data/lib/shale/schema/json_compiler/integer.rb +21 -0
- data/lib/shale/schema/json_compiler/object.rb +85 -0
- data/lib/shale/schema/json_compiler/property.rb +70 -0
- data/lib/shale/schema/json_compiler/string.rb +21 -0
- data/lib/shale/schema/json_compiler/time.rb +21 -0
- data/lib/shale/schema/json_compiler/utils.rb +52 -0
- data/lib/shale/schema/json_compiler/value.rb +13 -0
- data/lib/shale/schema/json_compiler.rb +333 -0
- data/lib/shale/schema/json_generator/base.rb +41 -0
- data/lib/shale/schema/json_generator/boolean.rb +23 -0
- data/lib/shale/schema/json_generator/collection.rb +39 -0
- data/lib/shale/schema/json_generator/date.rb +23 -0
- data/lib/shale/schema/json_generator/float.rb +23 -0
- data/lib/shale/schema/json_generator/integer.rb +23 -0
- data/lib/shale/schema/json_generator/object.rb +40 -0
- data/lib/shale/schema/json_generator/ref.rb +28 -0
- data/lib/shale/schema/json_generator/schema.rb +59 -0
- data/lib/shale/schema/json_generator/string.rb +23 -0
- data/lib/shale/schema/json_generator/time.rb +23 -0
- data/lib/shale/schema/json_generator/value.rb +23 -0
- data/lib/shale/schema/json_generator.rb +165 -0
- data/lib/shale/schema/xml_generator/attribute.rb +41 -0
- data/lib/shale/schema/xml_generator/complex_type.rb +70 -0
- data/lib/shale/schema/xml_generator/element.rb +55 -0
- data/lib/shale/schema/xml_generator/import.rb +46 -0
- data/lib/shale/schema/xml_generator/ref_attribute.rb +37 -0
- data/lib/shale/schema/xml_generator/ref_element.rb +39 -0
- data/lib/shale/schema/xml_generator/schema.rb +121 -0
- data/lib/shale/schema/xml_generator/typed_attribute.rb +46 -0
- data/lib/shale/schema/xml_generator/typed_element.rb +46 -0
- data/lib/shale/schema/xml_generator.rb +315 -0
- data/lib/shale/schema.rb +70 -0
- data/lib/shale/type/boolean.rb +2 -2
- data/lib/shale/type/composite.rb +78 -72
- 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 +53 -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: 26877841702bfce542b42206f519ffd1b03e2f0d3db1e70a42e37836ff0f3bff
|
4
|
+
data.tar.gz: 23fddba7256bc38b5d64309cf56833f47fd636ed0e9f6ec61f2a076764dad2bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e98bd0bbb20fbbaf4a8bdbfb5e2f83ba5d7bae78ad66a8597a1fe28c0c13d9799bc3ade785105ea7261147f55590ff530e85c31da459fe8c9171ee5b68f02977
|
7
|
+
data.tar.gz: 54be0efe36f4d330c551f1bd8994a36b360ecec3696aafe12382408a0656e50a181b91ff54ec9f39758908d15fcf51eafc4810540cd1aeb00bda7b552382bc80
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,36 @@
|
|
1
|
+
## [0.4.0] - 2022-05-30
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- Allow to add title to JSON Schema
|
5
|
+
- Map Shale::Type::Value to "anyType" XML Schema type
|
6
|
+
- Map Shale::Type::Value to "any" JSON Schema type
|
7
|
+
- Allow to generate Shale model from JSON Schema
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
- Performance improvements
|
11
|
+
- Reformat README a little bit and fix typos
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
- Fix stack overflow caused by circular dependency when generating JSON and XML schemas
|
15
|
+
|
16
|
+
## [0.3.1] - 2022-04-29
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
- Rename `id` -> `$id` and add info about supported JSON Schema dialect
|
20
|
+
|
21
|
+
## [0.3.0] - 2022-04-29
|
22
|
+
|
23
|
+
### Added
|
24
|
+
- Support for XML namespaces
|
25
|
+
- Add option to pretty print JSON and XML and to include XML declaration
|
26
|
+
- Add support for generating JSON and XML Schema
|
27
|
+
|
28
|
+
### Changed
|
29
|
+
- Various fixes to documentation
|
30
|
+
- Rename `hash` -> `hsh` (`hash` is used internally by Ruby)
|
31
|
+
- Rename `Shale::Type::Base` -> `Shale::Type::Value`
|
32
|
+
- Use ISO 8601 format for date and time in JSON, YAML and XML
|
33
|
+
|
1
34
|
## [0.2.2] - 2022-03-06
|
2
35
|
|
3
36
|
### Fixed
|
data/README.md
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
# Shale
|
2
2
|
|
3
|
-
Shale is a object mapper and serializer for JSON, YAML and XML.
|
3
|
+
Shale is a Ruby object mapper and serializer for JSON, YAML and XML.
|
4
|
+
It allows you to parse JSON, YAML and XML data and convert it into Ruby data structures,
|
5
|
+
as well as serialize data structures into JSON, YAML or XML.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
* Convert JSON, YAML and XML to Ruby data model
|
10
|
+
* Convert Ruby data model to JSON, YAML and XML
|
11
|
+
* Generate JSON and XML Schema from Ruby models
|
12
|
+
* Compile JSON Schema into Ruby models
|
13
|
+
* Out of the box support for JSON, YAML, Nokogiri, REXML and Ox parsers
|
14
|
+
* Support for custom adapters
|
4
15
|
|
5
16
|
## Installation
|
6
17
|
|
@@ -24,8 +35,36 @@ Or install it yourself as:
|
|
24
35
|
$ gem install shale
|
25
36
|
```
|
26
37
|
|
38
|
+
## Contents
|
39
|
+
|
40
|
+
* [Simple use case](#user-content-simple-use-case)
|
41
|
+
* [Creating objects](#creating-objects)
|
42
|
+
* [Converting JSON to object](#converting-json-to-object)
|
43
|
+
* [Converting object to JSON](#converting-object-to-json)
|
44
|
+
* [Converting YAML to object](#converting-yaml-to-object)
|
45
|
+
* [Converting object to YAML](#converting-object-to-yaml)
|
46
|
+
* [Converting Hash to object](#converting-hash-to-object)
|
47
|
+
* [Converting object to Hash](#converting-object-to-hash)
|
48
|
+
* [Converting XML to object](#converting-xml-to-object)
|
49
|
+
* [Converting object to XML](#converting-object-to-xml)
|
50
|
+
* [Mapping JSON keys to object attributes](#mapping-json-keys-to-object-attributes)
|
51
|
+
* [Mapping YAML keys to object attributes](#mapping-yaml-keys-to-object-attributes)
|
52
|
+
* [Mapping Hash keys to object attributes](#mapping-hash-keys-to-object-attributes)
|
53
|
+
* [Mapping XML elements and attributes to object attributes](#mapping-xml-elements-and-attributes-to-object-attributes)
|
54
|
+
* [Using XML namespaces](#using-xml-namespaces)
|
55
|
+
* [Using methods to extract and generate data](#using-methods-to-extract-and-generate-data)
|
56
|
+
* [Pretty printing and XML declaration](#pretty-printing-and-xml-declaration)
|
57
|
+
* [Supported types](#supported-types)
|
58
|
+
* [Writing your own type](#writing-your-own-type)
|
59
|
+
* [Adapters](#adapters)
|
60
|
+
* [Generating JSON Schema](#generating-json-schema)
|
61
|
+
* [Compiling JSON Schema into Shale model](#compiling-json-schema-into-shale-model)
|
62
|
+
* [Generating XML Schema](#generating-xml-schema)
|
63
|
+
|
27
64
|
## Usage
|
28
65
|
|
66
|
+
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
67
|
+
|
29
68
|
### Simple use case
|
30
69
|
|
31
70
|
```ruby
|
@@ -99,6 +138,7 @@ DATA
|
|
99
138
|
|
100
139
|
```ruby
|
101
140
|
person.to_json
|
141
|
+
|
102
142
|
# =>
|
103
143
|
#
|
104
144
|
# {
|
@@ -137,6 +177,7 @@ DATA
|
|
137
177
|
|
138
178
|
```ruby
|
139
179
|
person.to_yaml
|
180
|
+
|
140
181
|
# =>
|
141
182
|
#
|
142
183
|
# ---
|
@@ -174,6 +215,7 @@ person = Person.from_hash(
|
|
174
215
|
|
175
216
|
```ruby
|
176
217
|
person.to_hash
|
218
|
+
|
177
219
|
# =>
|
178
220
|
#
|
179
221
|
# {
|
@@ -210,6 +252,7 @@ DATA
|
|
210
252
|
|
211
253
|
```ruby
|
212
254
|
person.to_xml
|
255
|
+
|
213
256
|
# =>
|
214
257
|
#
|
215
258
|
# <person>
|
@@ -229,7 +272,7 @@ person.to_xml
|
|
229
272
|
|
230
273
|
### Mapping JSON keys to object attributes
|
231
274
|
|
232
|
-
By default keys are named the same as attributes. To use custom
|
275
|
+
By default keys are named the same as attributes. To use custom keys use:
|
233
276
|
|
234
277
|
```ruby
|
235
278
|
class Person < Shale::Mapper
|
@@ -264,7 +307,7 @@ class Person < Shale::Mapper
|
|
264
307
|
attribute :first_name, Shale::Type::String
|
265
308
|
attribute :last_name, Shale::Type::String
|
266
309
|
|
267
|
-
|
310
|
+
hsh do
|
268
311
|
map 'firstName', to: :first_name
|
269
312
|
map 'lastName', to: :last_name
|
270
313
|
end
|
@@ -273,7 +316,7 @@ end
|
|
273
316
|
|
274
317
|
### Mapping XML elements and attributes to object attributes
|
275
318
|
|
276
|
-
XML is more
|
319
|
+
XML is more complicated format than JSON or YAML. To map elements, attributes and content use:
|
277
320
|
|
278
321
|
```ruby
|
279
322
|
class Address < Shale::Mapper
|
@@ -327,6 +370,72 @@ DATA
|
|
327
370
|
- `map_attribute` - map element's attribute to attribute
|
328
371
|
- `map_content` - map first text node to attribute
|
329
372
|
|
373
|
+
### Using XML namespaces
|
374
|
+
|
375
|
+
To map namespaced elements and attributes use `namespace` and `prefix` properties on
|
376
|
+
`map_element` and `map_attribute`
|
377
|
+
|
378
|
+
```ruby
|
379
|
+
class Person < Shale::Mapper
|
380
|
+
attribute :first_name, Shale::Type::String
|
381
|
+
attribute :last_name, Shale::Type::String
|
382
|
+
attribute :age, Shale::Type::Integer
|
383
|
+
|
384
|
+
xml do
|
385
|
+
root 'person'
|
386
|
+
|
387
|
+
map_element 'first_name', to: :first_name, namespace: 'http://ns1.com', prefix: 'ns1'
|
388
|
+
map_element 'last_name', to: :last_name, namespace: 'http://ns2.com', prefix: 'ns2'
|
389
|
+
map_attribute 'age', to: :age, namespace: 'http://ns2.com', prefix: 'ns2'
|
390
|
+
end
|
391
|
+
end
|
392
|
+
|
393
|
+
person = Person.from_xml(<<~DATA)
|
394
|
+
<person xmlns:ns1="http://ns1.com" xmlns:ns2="http://ns2.com" ns2:age="50">
|
395
|
+
<ns1:first_name>John</ns1:first_name>
|
396
|
+
<ns2:last_name>Doe</ns2:last_name>
|
397
|
+
</person>
|
398
|
+
DATA
|
399
|
+
```
|
400
|
+
|
401
|
+
To define default namespace for all elements use `namespace` declaration
|
402
|
+
(this will define namespace on elements only, if you want to define namespace on an attribute
|
403
|
+
explicitly declare it on `map_attribute`).
|
404
|
+
|
405
|
+
```ruby
|
406
|
+
class Person < Shale::Mapper
|
407
|
+
attribute :first_name, Shale::Type::String
|
408
|
+
attribute :middle_name, Shale::Type::String
|
409
|
+
attribute :last_name, Shale::Type::String
|
410
|
+
attribute :age, Shale::Type::Integer
|
411
|
+
attribute :hobby, Shale::Type::String
|
412
|
+
|
413
|
+
xml do
|
414
|
+
root 'person'
|
415
|
+
namespace 'http://ns1.com', 'ns1'
|
416
|
+
|
417
|
+
map_element 'first_name', to: :first_name
|
418
|
+
|
419
|
+
# undeclare namespace on 'middle_name' element
|
420
|
+
map_element 'middle_name', to: :middle_name, namespace: nil, prefix: nil
|
421
|
+
|
422
|
+
# overwrite default namespace
|
423
|
+
map_element 'last_name', to: :last_name, namespace: 'http://ns2.com', prefix: 'ns2'
|
424
|
+
|
425
|
+
map_attribute 'age', to: :age
|
426
|
+
map_attribute 'hobby', to: :hobby, namespace: 'http://ns1.com', prefix: 'ns1'
|
427
|
+
end
|
428
|
+
end
|
429
|
+
|
430
|
+
person = Person.from_xml(<<~DATA)
|
431
|
+
<ns1:person xmlns:ns1="http://ns1.com" xmlns:ns2="http://ns2.com" age="50" ns1:hobby="running">
|
432
|
+
<ns1:first_name>John</ns1:first_name>
|
433
|
+
<middle_name>Joe</middle_name>
|
434
|
+
<ns2:last_name>Doe</ns2:last_name>
|
435
|
+
</ns1:person>
|
436
|
+
DATA
|
437
|
+
```
|
438
|
+
|
330
439
|
### Using methods to extract and generate data
|
331
440
|
|
332
441
|
If you need full controll over extracting and generating data from/to document,
|
@@ -421,6 +530,36 @@ DATA
|
|
421
530
|
# @city="London">
|
422
531
|
```
|
423
532
|
|
533
|
+
### Pretty printing and XML declaration
|
534
|
+
|
535
|
+
If you need formatted output you can pass `:pretty` parameter to `#to_json` and `#to_xml`
|
536
|
+
|
537
|
+
```ruby
|
538
|
+
person.to_json(:pretty)
|
539
|
+
|
540
|
+
# =>
|
541
|
+
#
|
542
|
+
# {
|
543
|
+
# "name": "John Doe",
|
544
|
+
# "address": {
|
545
|
+
# "city": "London"
|
546
|
+
# }
|
547
|
+
# }
|
548
|
+
```
|
549
|
+
|
550
|
+
You can also add an XML declaration by passing `:declaration` to `#to_xml`
|
551
|
+
|
552
|
+
```ruby
|
553
|
+
person.to_xml(:pretty, :declaration)
|
554
|
+
|
555
|
+
# =>
|
556
|
+
#
|
557
|
+
# <?xml version="1.0"?>
|
558
|
+
# <Person>
|
559
|
+
# <Address city="London"/>
|
560
|
+
# </Person>
|
561
|
+
```
|
562
|
+
|
424
563
|
### Supported types
|
425
564
|
|
426
565
|
Shale supports these types out of the box:
|
@@ -434,12 +573,12 @@ Shale supports these types out of the box:
|
|
434
573
|
|
435
574
|
### Writing your own type
|
436
575
|
|
437
|
-
To add your own type extend it from `Shale::Type::
|
576
|
+
To add your own type extend it from `Shale::Type::Value` and implement `.cast` class method.
|
438
577
|
|
439
578
|
```ruby
|
440
|
-
require 'shale/type/
|
579
|
+
require 'shale/type/value'
|
441
580
|
|
442
|
-
class MyIntegerType < Shale::Type::
|
581
|
+
class MyIntegerType < Shale::Type::Value
|
443
582
|
def self.cast(value)
|
444
583
|
value.to_i
|
445
584
|
end
|
@@ -463,7 +602,9 @@ Shale.json_adapter = MultiJson
|
|
463
602
|
Shale.yaml_adapter = MyYamlAdapter
|
464
603
|
```
|
465
604
|
|
466
|
-
|
605
|
+
Shale provides adapters for most popular Ruby XML parsers:
|
606
|
+
|
607
|
+
:warning: **Ox doesn't support XML namespaces**
|
467
608
|
|
468
609
|
```ruby
|
469
610
|
require 'shale'
|
@@ -484,6 +625,223 @@ require 'shale/adapter/ox'
|
|
484
625
|
Shale.xml_adapter = Shale::Adapter::Ox
|
485
626
|
```
|
486
627
|
|
628
|
+
### Generating JSON Schema
|
629
|
+
|
630
|
+
:warning: Only **[Draft 2020-12](https://json-schema.org/draft/2020-12/schema)** JSON Schema is supported
|
631
|
+
|
632
|
+
To generate JSON Schema from your Shale data model use:
|
633
|
+
|
634
|
+
```ruby
|
635
|
+
require 'shale/schema'
|
636
|
+
|
637
|
+
Shale::Schema.to_json(
|
638
|
+
Person,
|
639
|
+
id: 'http://foo.bar/schema/person',
|
640
|
+
description: 'My description',
|
641
|
+
pretty: true
|
642
|
+
)
|
643
|
+
|
644
|
+
# =>
|
645
|
+
#
|
646
|
+
# {
|
647
|
+
# "$schema": "https://json-schema.org/draft/2020-12/schema",
|
648
|
+
# "$id": "http://foo.bar/schema/person",
|
649
|
+
# "description": "My description",
|
650
|
+
# "$ref": "#/$defs/Person",
|
651
|
+
# "$defs": {
|
652
|
+
# "Address": {
|
653
|
+
# "type": [
|
654
|
+
# "object",
|
655
|
+
# "null"
|
656
|
+
# ],
|
657
|
+
# "properties": {
|
658
|
+
# "city": {
|
659
|
+
# "type": [
|
660
|
+
# "string",
|
661
|
+
# "null"
|
662
|
+
# ]
|
663
|
+
# }
|
664
|
+
# }
|
665
|
+
# },
|
666
|
+
# "Person": {
|
667
|
+
# "type": "object",
|
668
|
+
# "properties": {
|
669
|
+
# "name": {
|
670
|
+
# "type": [
|
671
|
+
# "string",
|
672
|
+
# "null"
|
673
|
+
# ]
|
674
|
+
# },
|
675
|
+
# "address": {
|
676
|
+
# "$ref": "#/$defs/Address"
|
677
|
+
# }
|
678
|
+
# }
|
679
|
+
# }
|
680
|
+
# }
|
681
|
+
# }
|
682
|
+
```
|
683
|
+
|
684
|
+
You can also use a command line tool to do it:
|
685
|
+
|
686
|
+
```
|
687
|
+
$ shaleb -i data_model.rb -r Person -p
|
688
|
+
```
|
689
|
+
|
690
|
+
If you want to convert your own types to JSON Schema types use:
|
691
|
+
|
692
|
+
```ruby
|
693
|
+
require 'shale'
|
694
|
+
require 'shale/schema'
|
695
|
+
|
696
|
+
class MyEmailType < Shale::Type::Value
|
697
|
+
...
|
698
|
+
end
|
699
|
+
|
700
|
+
class MyEmailJSONType < Shale::Schema::JSONGenerator::Base
|
701
|
+
def as_type
|
702
|
+
{ 'type' => 'string', 'format' => 'email' }
|
703
|
+
end
|
704
|
+
end
|
705
|
+
|
706
|
+
Shale::Schema::JSONGenerator.register_json_type(MyEmailType, MyEmailJSONType)
|
707
|
+
```
|
708
|
+
|
709
|
+
### Compiling JSON Schema into Shale model
|
710
|
+
|
711
|
+
:warning: Only **[Draft 2020-12](https://json-schema.org/draft/2020-12/schema)** JSON Schema is supported
|
712
|
+
|
713
|
+
To generate Shale data model from JSON Schema use:
|
714
|
+
|
715
|
+
```ruby
|
716
|
+
require 'shale/schema'
|
717
|
+
|
718
|
+
schema = <<~SCHEMA
|
719
|
+
{
|
720
|
+
"type": "object",
|
721
|
+
"properties": {
|
722
|
+
"firstName": { "type": "string" },
|
723
|
+
"lastName": { "type": "string" },
|
724
|
+
"address": {
|
725
|
+
"type": "object",
|
726
|
+
"properties": {
|
727
|
+
"street": { "type": "string" },
|
728
|
+
"city": { "type": "string" }
|
729
|
+
}
|
730
|
+
}
|
731
|
+
}
|
732
|
+
}
|
733
|
+
SCHEMA
|
734
|
+
|
735
|
+
Shale::Schema.from_json([schema], root_name: 'Person')
|
736
|
+
|
737
|
+
# =>
|
738
|
+
#
|
739
|
+
# {
|
740
|
+
# "address" => "
|
741
|
+
# require 'shale'
|
742
|
+
#
|
743
|
+
# class Address < Shale::Mapper
|
744
|
+
# attribute :street, Shale::Type::String
|
745
|
+
# attribute :city, Shale::Type::String
|
746
|
+
#
|
747
|
+
# json do
|
748
|
+
# map 'street', to: :street
|
749
|
+
# map 'city', to: :city
|
750
|
+
# end
|
751
|
+
# end
|
752
|
+
# ",
|
753
|
+
# "person" => "
|
754
|
+
# require 'shale'
|
755
|
+
#
|
756
|
+
# require_relative 'address'
|
757
|
+
#
|
758
|
+
# class Person < Shale::Mapper
|
759
|
+
# attribute :first_name, Shale::Type::String
|
760
|
+
# attribute :last_name, Shale::Type::String
|
761
|
+
# attribute :address, Address
|
762
|
+
#
|
763
|
+
# json do
|
764
|
+
# map 'firstName', to: :first_name
|
765
|
+
# map 'lastName', to: :last_name
|
766
|
+
# map 'address', to: :address
|
767
|
+
# end
|
768
|
+
# end
|
769
|
+
# "
|
770
|
+
# }
|
771
|
+
```
|
772
|
+
|
773
|
+
You can also use a command line tool to do it:
|
774
|
+
|
775
|
+
```
|
776
|
+
$ shaleb -c -i schema.json -r Person
|
777
|
+
```
|
778
|
+
|
779
|
+
### Generating XML Schema
|
780
|
+
|
781
|
+
To generate XML Schema from your Shale data model use:
|
782
|
+
|
783
|
+
```ruby
|
784
|
+
require 'shale/schema'
|
785
|
+
|
786
|
+
Shale::Schema.to_xml(Person, pretty: true)
|
787
|
+
|
788
|
+
# =>
|
789
|
+
#
|
790
|
+
# {
|
791
|
+
# 'schema0.xsd' => '
|
792
|
+
# <xs:schema
|
793
|
+
# elementFormDefault="qualified"
|
794
|
+
# attributeFormDefault="qualified"
|
795
|
+
# xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
796
|
+
# xmlns:foo="http://foo.com"
|
797
|
+
# >
|
798
|
+
# <xs:import namespace="http://foo.com" schemaLocation="schema1.xsd"/>
|
799
|
+
# <xs:element name="person" type="Person"/>
|
800
|
+
# <xs:complexType name="Person">
|
801
|
+
# <xs:sequence>
|
802
|
+
# <xs:element name="name" type="xs:string" minOccurs="0"/>
|
803
|
+
# <xs:element ref="foo:address" minOccurs="0"/>
|
804
|
+
# </xs:sequence>
|
805
|
+
# </xs:complexType>
|
806
|
+
# </xs:schema>',
|
807
|
+
#
|
808
|
+
# 'schema1.xsd' => '
|
809
|
+
# <xs:schema
|
810
|
+
# elementFormDefault="qualified"
|
811
|
+
# attributeFormDefault="qualified"
|
812
|
+
# targetNamespace="http://foo.com"
|
813
|
+
# xmlns:xs="http://www.w3.org/2001/XMLSchema"
|
814
|
+
# xmlns:foo="http://foo.com"
|
815
|
+
# >
|
816
|
+
# <xs:element name="address" type="foo:Address"/>
|
817
|
+
# <xs:complexType name="Address">
|
818
|
+
# <xs:sequence>
|
819
|
+
# <xs:element name="city" type="xs:string" minOccurs="0"/>
|
820
|
+
# </xs:sequence>
|
821
|
+
# </xs:complexType>
|
822
|
+
# </xs:schema>'
|
823
|
+
# }
|
824
|
+
```
|
825
|
+
|
826
|
+
You can also use a command line tool to do it:
|
827
|
+
|
828
|
+
```
|
829
|
+
$ shaleb -i data_model.rb -r Person -p -f xml
|
830
|
+
```
|
831
|
+
|
832
|
+
If you want to convert your own types to XML Schema types use:
|
833
|
+
|
834
|
+
```ruby
|
835
|
+
require 'shale'
|
836
|
+
require 'shale/schema'
|
837
|
+
|
838
|
+
class MyEmailType < Shale::Type::Value
|
839
|
+
...
|
840
|
+
end
|
841
|
+
|
842
|
+
Shale::Schema::XMLGenerator.register_xml_type(MyEmailType, 'myEmailXMLType')
|
843
|
+
```
|
844
|
+
|
487
845
|
## Contributing
|
488
846
|
|
489
847
|
Bug reports and pull requests are welcome on GitHub at https://github.com/kgiszczak/shale.
|
data/exe/shaleb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'fileutils'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
base_path = File.expand_path('../lib', __dir__)
|
8
|
+
|
9
|
+
if File.exist?(base_path)
|
10
|
+
require_relative '../lib/shale/schema'
|
11
|
+
else
|
12
|
+
require 'shale/schema'
|
13
|
+
end
|
14
|
+
|
15
|
+
params = {}
|
16
|
+
|
17
|
+
ARGV << '-h' if ARGV.empty?
|
18
|
+
|
19
|
+
OptionParser.new do |opts|
|
20
|
+
opts.banner = <<~BANNER
|
21
|
+
Usage: shaleb [options]
|
22
|
+
example generate schema from Shale model: shaleb -g -i data_model.rb -c MyRoot
|
23
|
+
example generate Shale model from schema: shaleb -c -i schema1.json,schema2.json -c MyRoot
|
24
|
+
BANNER
|
25
|
+
|
26
|
+
opts.on('-g', '--generate', 'generate schema from Shale model')
|
27
|
+
opts.on('-c', '--compile', 'compile schema into Shale model')
|
28
|
+
opts.on('-i INPUT', '--input', Array, 'Input file')
|
29
|
+
opts.on('-o OUTPUT', '--output', 'Output (defaults to STDOUT)')
|
30
|
+
opts.on('-r ROOT', '--root ROOT', 'Shale model class name')
|
31
|
+
opts.on('-f FORMAT', '--format FORMAT', 'Schema format: JSON (default), XML')
|
32
|
+
opts.on('-p', '--pretty', 'Pretty print generated schema')
|
33
|
+
|
34
|
+
opts.on('-v', '--version', 'Show version') do
|
35
|
+
puts "shaleb version #{Shale::VERSION}"
|
36
|
+
exit
|
37
|
+
end
|
38
|
+
end.parse!(into: params)
|
39
|
+
|
40
|
+
if params[:compile]
|
41
|
+
unless params[:input]
|
42
|
+
puts 'Input file is required: shaleb -c -i schema1.json,schema2.json'
|
43
|
+
exit
|
44
|
+
end
|
45
|
+
|
46
|
+
schemas = params[:input].map do |file|
|
47
|
+
path = File.expand_path(file, Dir.pwd)
|
48
|
+
|
49
|
+
if File.exist?(path)
|
50
|
+
File.read(path)
|
51
|
+
else
|
52
|
+
puts "File '#{path}' does not exist"
|
53
|
+
exit
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
models = Shale::Schema.from_json(schemas, root_name: params[:root])
|
58
|
+
|
59
|
+
if params[:output]
|
60
|
+
dir = File.expand_path(params[:output], Dir.pwd)
|
61
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
62
|
+
|
63
|
+
models.each do |name, model|
|
64
|
+
output_path = File.join(dir, "#{name}.rb")
|
65
|
+
File.write(output_path, model)
|
66
|
+
end
|
67
|
+
else
|
68
|
+
output = models.map do |name, model|
|
69
|
+
"# --- #{name}.rb ---\n#{model}\n"
|
70
|
+
end.join("\n")
|
71
|
+
|
72
|
+
puts output
|
73
|
+
end
|
74
|
+
else
|
75
|
+
unless params[:input]
|
76
|
+
puts 'Input file is required: shaleb -i model.rb -r MyClass'
|
77
|
+
exit
|
78
|
+
end
|
79
|
+
|
80
|
+
input_path = File.expand_path(params[:input][0], Dir.pwd)
|
81
|
+
|
82
|
+
unless File.exist?(input_path)
|
83
|
+
puts "File '#{input_path}' does not exist"
|
84
|
+
exit
|
85
|
+
end
|
86
|
+
|
87
|
+
unless params[:root]
|
88
|
+
puts 'Model class is required: shaleb -i model.rb -r MyClass'
|
89
|
+
exit
|
90
|
+
end
|
91
|
+
|
92
|
+
require input_path
|
93
|
+
|
94
|
+
klass = Object.const_get(params[:root])
|
95
|
+
|
96
|
+
if params[:format] == 'xml'
|
97
|
+
if params[:output]
|
98
|
+
base_name = File.basename(params[:output], File.extname(params[:output]))
|
99
|
+
schemas = Shale::Schema.to_xml(klass, base_name, pretty: params[:pretty])
|
100
|
+
|
101
|
+
schemas.map do |name, xml|
|
102
|
+
File.write(File.expand_path(name, Dir.pwd), xml)
|
103
|
+
end
|
104
|
+
else
|
105
|
+
schemas = Shale::Schema.to_xml(klass, pretty: params[:pretty])
|
106
|
+
|
107
|
+
output = schemas.map do |name, xml|
|
108
|
+
"<!-- #{name} -->\n#{xml}\n"
|
109
|
+
end.join("\n")
|
110
|
+
|
111
|
+
puts output
|
112
|
+
end
|
113
|
+
else
|
114
|
+
schema = Shale::Schema.to_json(klass, pretty: params[:pretty])
|
115
|
+
|
116
|
+
if params[:output]
|
117
|
+
output_path = File.expand_path(params[:output], Dir.pwd)
|
118
|
+
File.write(output_path, schema)
|
119
|
+
else
|
120
|
+
puts schema
|
121
|
+
end
|
122
|
+
end
|
123
|
+
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
|