shale 0.2.2 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|