shale 0.3.0 → 0.5.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 +31 -0
- data/README.md +200 -21
- data/exe/shaleb +108 -36
- data/lib/shale/adapter/nokogiri/document.rb +87 -0
- data/lib/shale/adapter/nokogiri/node.rb +100 -0
- data/lib/shale/adapter/nokogiri.rb +11 -151
- data/lib/shale/adapter/ox/document.rb +80 -0
- data/lib/shale/adapter/ox/node.rb +88 -0
- data/lib/shale/adapter/ox.rb +9 -134
- data/lib/shale/adapter/rexml/document.rb +88 -0
- data/lib/shale/adapter/rexml/node.rb +99 -0
- data/lib/shale/adapter/rexml.rb +9 -150
- data/lib/shale/attribute.rb +6 -0
- data/lib/shale/error.rb +39 -0
- data/lib/shale/mapper.rb +8 -6
- data/lib/shale/schema/compiler/boolean.rb +21 -0
- data/lib/shale/schema/compiler/complex.rb +88 -0
- data/lib/shale/schema/compiler/date.rb +21 -0
- data/lib/shale/schema/compiler/float.rb +21 -0
- data/lib/shale/schema/compiler/integer.rb +21 -0
- data/lib/shale/schema/compiler/property.rb +70 -0
- data/lib/shale/schema/compiler/string.rb +21 -0
- data/lib/shale/schema/compiler/time.rb +21 -0
- data/lib/shale/schema/compiler/value.rb +21 -0
- data/lib/shale/schema/compiler/xml_complex.rb +50 -0
- data/lib/shale/schema/compiler/xml_property.rb +73 -0
- data/lib/shale/schema/json_compiler.rb +331 -0
- data/lib/shale/schema/{json → json_generator}/base.rb +2 -2
- data/lib/shale/schema/{json → json_generator}/boolean.rb +1 -1
- data/lib/shale/schema/{json → json_generator}/collection.rb +2 -2
- data/lib/shale/schema/{json → json_generator}/date.rb +1 -1
- data/lib/shale/schema/{json → json_generator}/float.rb +1 -1
- data/lib/shale/schema/{json → json_generator}/integer.rb +1 -1
- data/lib/shale/schema/{json → json_generator}/object.rb +5 -2
- data/lib/shale/schema/{json → json_generator}/ref.rb +1 -1
- data/lib/shale/schema/{json → json_generator}/schema.rb +7 -5
- data/lib/shale/schema/{json → json_generator}/string.rb +1 -1
- data/lib/shale/schema/{json → json_generator}/time.rb +1 -1
- data/lib/shale/schema/json_generator/value.rb +23 -0
- data/lib/shale/schema/{json.rb → json_generator.rb} +36 -36
- data/lib/shale/schema/xml_compiler.rb +919 -0
- data/lib/shale/schema/{xml → xml_generator}/attribute.rb +1 -1
- data/lib/shale/schema/{xml → xml_generator}/complex_type.rb +5 -2
- data/lib/shale/schema/{xml → xml_generator}/element.rb +1 -1
- data/lib/shale/schema/{xml → xml_generator}/import.rb +1 -1
- data/lib/shale/schema/{xml → xml_generator}/ref_attribute.rb +1 -1
- data/lib/shale/schema/{xml → xml_generator}/ref_element.rb +1 -1
- data/lib/shale/schema/{xml → xml_generator}/schema.rb +5 -5
- data/lib/shale/schema/{xml → xml_generator}/typed_attribute.rb +1 -1
- data/lib/shale/schema/{xml → xml_generator}/typed_element.rb +1 -1
- data/lib/shale/schema/{xml.rb → xml_generator.rb} +25 -26
- data/lib/shale/schema.rb +44 -5
- data/lib/shale/type/{composite.rb → complex.rb} +34 -22
- data/lib/shale/utils.rb +42 -7
- data/lib/shale/version.rb +1 -1
- data/lib/shale.rb +8 -19
- data/shale.gemspec +1 -1
- metadata +47 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: aaa3043e612d332fab30ff87ecab722e3812e7769eba982db27c38f74c205ce1
|
4
|
+
data.tar.gz: bb5cdb963bce5756c48edb37d28bddc40a816901e87b4714ea512f07f8ec4c28
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed1f1ffc88cabb5f9403ff957c02efc4565aee73b9a8a1fd2297ff13bfbb6bdf0e94664b865f8d2d812ddd66fe97d154e34982eea3318ba01c6d16b4a6922c70
|
7
|
+
data.tar.gz: d41392de36bcace9efee12a869f49beffba235ae09e09f33c6973159b96320e3f657a6edd4d634e53da430f8127e74fa1025fa2f5c75dad2e7568abe6a0794ce
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,34 @@
|
|
1
|
+
## [0.5.0] - 2022-06-28
|
2
|
+
|
3
|
+
### Added
|
4
|
+
- Allow to generate Shale model from XML Schema
|
5
|
+
|
6
|
+
### Changed
|
7
|
+
- Shale doesn't defaults to REXML anymore - XML adapter needs to be set explicitly
|
8
|
+
- Rename "JSONSchemaError" to "SchemaError"
|
9
|
+
- Rename "Composite" type to "Complex"
|
10
|
+
- Drop support for Ruby 2.6
|
11
|
+
|
12
|
+
## [0.4.0] - 2022-05-30
|
13
|
+
|
14
|
+
### Added
|
15
|
+
- Allow to add title to JSON Schema
|
16
|
+
- Map Shale::Type::Value to "anyType" XML Schema type
|
17
|
+
- Map Shale::Type::Value to "any" JSON Schema type
|
18
|
+
- Allow to generate Shale model from JSON Schema
|
19
|
+
|
20
|
+
### Changed
|
21
|
+
- Performance improvements
|
22
|
+
- Reformat README a little bit and fix typos
|
23
|
+
|
24
|
+
### Fixed
|
25
|
+
- Fix stack overflow caused by circular dependency when generating JSON and XML schemas
|
26
|
+
|
27
|
+
## [0.3.1] - 2022-04-29
|
28
|
+
|
29
|
+
### Changed
|
30
|
+
- Rename `id` -> `$id` and add info about supported JSON Schema dialect
|
31
|
+
|
1
32
|
## [0.3.0] - 2022-04-29
|
2
33
|
|
3
34
|
### Added
|
data/README.md
CHANGED
@@ -1,10 +1,23 @@
|
|
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
|
+
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
8
|
+
|
9
|
+
## Features
|
10
|
+
|
11
|
+
* Convert JSON, YAML and XML to Ruby data model
|
12
|
+
* Convert Ruby data model to JSON, YAML and XML
|
13
|
+
* Generate JSON and XML Schema from Ruby models
|
14
|
+
* Compile JSON and XML Schema into Ruby models
|
15
|
+
* Out of the box support for JSON, YAML, Nokogiri, REXML and Ox parsers
|
16
|
+
* Support for custom adapters
|
4
17
|
|
5
18
|
## Installation
|
6
19
|
|
7
|
-
Shale supports Ruby (MRI) 2.
|
20
|
+
Shale supports Ruby (MRI) 2.7+
|
8
21
|
|
9
22
|
Add this line to your application's Gemfile:
|
10
23
|
|
@@ -47,12 +60,12 @@ $ gem install shale
|
|
47
60
|
* [Writing your own type](#writing-your-own-type)
|
48
61
|
* [Adapters](#adapters)
|
49
62
|
* [Generating JSON Schema](#generating-json-schema)
|
63
|
+
* [Compiling JSON Schema into Shale model](#compiling-json-schema-into-shale-model)
|
50
64
|
* [Generating XML Schema](#generating-xml-schema)
|
65
|
+
* [Compiling XML Schema into Shale model](#compiling-xml-schema-into-shale-model)
|
51
66
|
|
52
67
|
## Usage
|
53
68
|
|
54
|
-
Documentation with interactive examples is available at [Shale website](https://www.shalerb.org)
|
55
|
-
|
56
69
|
### Simple use case
|
57
70
|
|
58
71
|
```ruby
|
@@ -126,6 +139,7 @@ DATA
|
|
126
139
|
|
127
140
|
```ruby
|
128
141
|
person.to_json
|
142
|
+
|
129
143
|
# =>
|
130
144
|
#
|
131
145
|
# {
|
@@ -164,6 +178,7 @@ DATA
|
|
164
178
|
|
165
179
|
```ruby
|
166
180
|
person.to_yaml
|
181
|
+
|
167
182
|
# =>
|
168
183
|
#
|
169
184
|
# ---
|
@@ -201,6 +216,7 @@ person = Person.from_hash(
|
|
201
216
|
|
202
217
|
```ruby
|
203
218
|
person.to_hash
|
219
|
+
|
204
220
|
# =>
|
205
221
|
#
|
206
222
|
# {
|
@@ -215,6 +231,15 @@ person.to_hash
|
|
215
231
|
|
216
232
|
### Converting XML to object
|
217
233
|
|
234
|
+
To use XML with Shale you have to set adapter you want to use.
|
235
|
+
Shale comes with adapters for REXML, Nokogiri and OX parsers.
|
236
|
+
For details see [Adapters](#adapters) section.
|
237
|
+
|
238
|
+
```ruby
|
239
|
+
require 'shale/adapter/rexml'
|
240
|
+
Shale.xml_adapter = Shale::Adapter::REXML
|
241
|
+
```
|
242
|
+
|
218
243
|
```ruby
|
219
244
|
person = Person.from_xml(<<~DATA)
|
220
245
|
<person>
|
@@ -237,6 +262,7 @@ DATA
|
|
237
262
|
|
238
263
|
```ruby
|
239
264
|
person.to_xml
|
265
|
+
|
240
266
|
# =>
|
241
267
|
#
|
242
268
|
# <person>
|
@@ -256,7 +282,7 @@ person.to_xml
|
|
256
282
|
|
257
283
|
### Mapping JSON keys to object attributes
|
258
284
|
|
259
|
-
By default keys are named the same as attributes. To use custom
|
285
|
+
By default keys are named the same as attributes. To use custom keys use:
|
260
286
|
|
261
287
|
```ruby
|
262
288
|
class Person < Shale::Mapper
|
@@ -300,7 +326,7 @@ end
|
|
300
326
|
|
301
327
|
### Mapping XML elements and attributes to object attributes
|
302
328
|
|
303
|
-
XML is more
|
329
|
+
XML is more complicated format than JSON or YAML. To map elements, attributes and content use:
|
304
330
|
|
305
331
|
```ruby
|
306
332
|
class Address < Shale::Mapper
|
@@ -356,8 +382,6 @@ DATA
|
|
356
382
|
|
357
383
|
### Using XML namespaces
|
358
384
|
|
359
|
-
:warning: **Ox doesn't support XML namespaces**
|
360
|
-
|
361
385
|
To map namespaced elements and attributes use `namespace` and `prefix` properties on
|
362
386
|
`map_element` and `map_attribute`
|
363
387
|
|
@@ -385,7 +409,7 @@ DATA
|
|
385
409
|
```
|
386
410
|
|
387
411
|
To define default namespace for all elements use `namespace` declaration
|
388
|
-
(this will define namespace
|
412
|
+
(this will define namespace on elements only, if you want to define namespace on an attribute
|
389
413
|
explicitly declare it on `map_attribute`).
|
390
414
|
|
391
415
|
```ruby
|
@@ -574,8 +598,7 @@ end
|
|
574
598
|
### Adapters
|
575
599
|
|
576
600
|
Shale uses adapters for parsing and generating documents.
|
577
|
-
By default Ruby's standard JSON
|
578
|
-
REXML for XML.
|
601
|
+
By default Ruby's standard JSON and YAML parsers are used for handling JSON and YAML documents.
|
579
602
|
|
580
603
|
You can change it by providing your own adapter. For JSON and YAML, adapter must implement
|
581
604
|
`.load` and `.dump` class methods.
|
@@ -588,12 +611,15 @@ Shale.json_adapter = MultiJson
|
|
588
611
|
Shale.yaml_adapter = MyYamlAdapter
|
589
612
|
```
|
590
613
|
|
614
|
+
To handle XML documents you have to explicitly set XML adapter.
|
591
615
|
Shale provides adapters for most popular Ruby XML parsers:
|
592
616
|
|
617
|
+
:warning: **Ox doesn't support XML namespaces**
|
618
|
+
|
593
619
|
```ruby
|
594
620
|
require 'shale'
|
595
621
|
|
596
|
-
#
|
622
|
+
# if you want to use REXML:
|
597
623
|
|
598
624
|
require 'shale/adapter/rexml'
|
599
625
|
Shale.xml_adapter = Shale::Adapter::REXML
|
@@ -611,18 +637,25 @@ Shale.xml_adapter = Shale::Adapter::Ox
|
|
611
637
|
|
612
638
|
### Generating JSON Schema
|
613
639
|
|
614
|
-
|
640
|
+
:warning: Only **[Draft 2020-12](https://json-schema.org/draft/2020-12/schema)** JSON Schema is supported
|
641
|
+
|
642
|
+
To generate JSON Schema from your Shale data model use:
|
615
643
|
|
616
644
|
```ruby
|
617
645
|
require 'shale/schema'
|
618
646
|
|
619
|
-
Shale::Schema.to_json(
|
647
|
+
Shale::Schema.to_json(
|
648
|
+
Person,
|
649
|
+
id: 'http://foo.bar/schema/person',
|
650
|
+
description: 'My description',
|
651
|
+
pretty: true
|
652
|
+
)
|
620
653
|
|
621
654
|
# =>
|
622
655
|
#
|
623
656
|
# {
|
624
657
|
# "$schema": "https://json-schema.org/draft/2020-12/schema",
|
625
|
-
# "id": "
|
658
|
+
# "$id": "http://foo.bar/schema/person",
|
626
659
|
# "description": "My description",
|
627
660
|
# "$ref": "#/$defs/Person",
|
628
661
|
# "$defs": {
|
@@ -661,7 +694,7 @@ Shale::Schema.to_json(Person, id: 'My ID', description: 'My description', pretty
|
|
661
694
|
You can also use a command line tool to do it:
|
662
695
|
|
663
696
|
```
|
664
|
-
$ shaleb -i data_model.rb -
|
697
|
+
$ shaleb -i data_model.rb -r Person -p
|
665
698
|
```
|
666
699
|
|
667
700
|
If you want to convert your own types to JSON Schema types use:
|
@@ -674,18 +707,88 @@ class MyEmailType < Shale::Type::Value
|
|
674
707
|
...
|
675
708
|
end
|
676
709
|
|
677
|
-
class MyEmailJSONType < Shale::Schema::
|
710
|
+
class MyEmailJSONType < Shale::Schema::JSONGenerator::Base
|
678
711
|
def as_type
|
679
712
|
{ 'type' => 'string', 'format' => 'email' }
|
680
713
|
end
|
681
714
|
end
|
682
715
|
|
683
|
-
Shale::Schema::
|
716
|
+
Shale::Schema::JSONGenerator.register_json_type(MyEmailType, MyEmailJSONType)
|
717
|
+
```
|
718
|
+
|
719
|
+
### Compiling JSON Schema into Shale model
|
720
|
+
|
721
|
+
:warning: Only **[Draft 2020-12](https://json-schema.org/draft/2020-12/schema)** JSON Schema is supported
|
722
|
+
|
723
|
+
To generate Shale data model from JSON Schema use:
|
724
|
+
|
725
|
+
```ruby
|
726
|
+
require 'shale/schema'
|
727
|
+
|
728
|
+
schema = <<~SCHEMA
|
729
|
+
{
|
730
|
+
"type": "object",
|
731
|
+
"properties": {
|
732
|
+
"firstName": { "type": "string" },
|
733
|
+
"lastName": { "type": "string" },
|
734
|
+
"address": {
|
735
|
+
"type": "object",
|
736
|
+
"properties": {
|
737
|
+
"street": { "type": "string" },
|
738
|
+
"city": { "type": "string" }
|
739
|
+
}
|
740
|
+
}
|
741
|
+
}
|
742
|
+
}
|
743
|
+
SCHEMA
|
744
|
+
|
745
|
+
Shale::Schema.from_json([schema], root_name: 'Person')
|
746
|
+
|
747
|
+
# =>
|
748
|
+
#
|
749
|
+
# {
|
750
|
+
# "address" => "
|
751
|
+
# require 'shale'
|
752
|
+
#
|
753
|
+
# class Address < Shale::Mapper
|
754
|
+
# attribute :street, Shale::Type::String
|
755
|
+
# attribute :city, Shale::Type::String
|
756
|
+
#
|
757
|
+
# json do
|
758
|
+
# map 'street', to: :street
|
759
|
+
# map 'city', to: :city
|
760
|
+
# end
|
761
|
+
# end
|
762
|
+
# ",
|
763
|
+
# "person" => "
|
764
|
+
# require 'shale'
|
765
|
+
#
|
766
|
+
# require_relative 'address'
|
767
|
+
#
|
768
|
+
# class Person < Shale::Mapper
|
769
|
+
# attribute :first_name, Shale::Type::String
|
770
|
+
# attribute :last_name, Shale::Type::String
|
771
|
+
# attribute :address, Address
|
772
|
+
#
|
773
|
+
# json do
|
774
|
+
# map 'firstName', to: :first_name
|
775
|
+
# map 'lastName', to: :last_name
|
776
|
+
# map 'address', to: :address
|
777
|
+
# end
|
778
|
+
# end
|
779
|
+
# "
|
780
|
+
# }
|
781
|
+
```
|
782
|
+
|
783
|
+
You can also use a command line tool to do it:
|
784
|
+
|
785
|
+
```
|
786
|
+
$ shaleb -c -i schema.json -r Person
|
684
787
|
```
|
685
788
|
|
686
789
|
### Generating XML Schema
|
687
790
|
|
688
|
-
To generate XML Schema from
|
791
|
+
To generate XML Schema from your Shale data model use:
|
689
792
|
|
690
793
|
```ruby
|
691
794
|
require 'shale/schema'
|
@@ -733,7 +836,7 @@ Shale::Schema.to_xml(Person, pretty: true)
|
|
733
836
|
You can also use a command line tool to do it:
|
734
837
|
|
735
838
|
```
|
736
|
-
$ shaleb -i data_model.rb -
|
839
|
+
$ shaleb -i data_model.rb -r Person -p -f xml
|
737
840
|
```
|
738
841
|
|
739
842
|
If you want to convert your own types to XML Schema types use:
|
@@ -746,7 +849,83 @@ class MyEmailType < Shale::Type::Value
|
|
746
849
|
...
|
747
850
|
end
|
748
851
|
|
749
|
-
Shale::Schema::
|
852
|
+
Shale::Schema::XMLGenerator.register_xml_type(MyEmailType, 'myEmailXMLType')
|
853
|
+
```
|
854
|
+
|
855
|
+
### Compiling XML Schema into Shale model
|
856
|
+
|
857
|
+
To generate Shale data model from XML Schema use:
|
858
|
+
|
859
|
+
```ruby
|
860
|
+
require 'shale/schema'
|
861
|
+
|
862
|
+
schema = <<~SCHEMA
|
863
|
+
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
|
864
|
+
<xs:element name="Person" type="Person" />
|
865
|
+
|
866
|
+
<xs:complexType name="Person">
|
867
|
+
<xs:sequence>
|
868
|
+
<xs:element name="FirstName" type="xs:string" />
|
869
|
+
<xs:element name="LastName" type="xs:string" />
|
870
|
+
<xs:element name="Address" type="Address" />
|
871
|
+
</xs:sequence>
|
872
|
+
</xs:complexType>
|
873
|
+
|
874
|
+
<xs:complexType name="Address">
|
875
|
+
<xs:sequence>
|
876
|
+
<xs:element name="Street" type="xs:string" />
|
877
|
+
<xs:element name="City" type="xs:string" />
|
878
|
+
</xs:sequence>
|
879
|
+
</xs:complexType>
|
880
|
+
</xs:schema>
|
881
|
+
SCHEMA
|
882
|
+
|
883
|
+
Shale::Schema.from_xml([schema])
|
884
|
+
|
885
|
+
# =>
|
886
|
+
#
|
887
|
+
# {
|
888
|
+
# "address" => "
|
889
|
+
# require 'shale'
|
890
|
+
#
|
891
|
+
# class Address < Shale::Mapper
|
892
|
+
# attribute :street, Shale::Type::String
|
893
|
+
# attribute :city, Shale::Type::String
|
894
|
+
#
|
895
|
+
# xml do
|
896
|
+
# root 'Address'
|
897
|
+
#
|
898
|
+
# map_element 'Street', to: :street
|
899
|
+
# map_element 'City', to: :city
|
900
|
+
# end
|
901
|
+
# end
|
902
|
+
# ",
|
903
|
+
# "person" => "
|
904
|
+
# require 'shale'
|
905
|
+
#
|
906
|
+
# require_relative 'address'
|
907
|
+
#
|
908
|
+
# class Person < Shale::Mapper
|
909
|
+
# attribute :first_name, Shale::Type::String
|
910
|
+
# attribute :last_name, Shale::Type::String
|
911
|
+
# attribute :address, Address
|
912
|
+
#
|
913
|
+
# xml do
|
914
|
+
# root 'Person'
|
915
|
+
#
|
916
|
+
# map_element 'FirstName', to: :first_name
|
917
|
+
# map_element 'LastName', to: :last_name
|
918
|
+
# map_element 'Address', to: :address
|
919
|
+
# end
|
920
|
+
# end
|
921
|
+
# "
|
922
|
+
# }
|
923
|
+
```
|
924
|
+
|
925
|
+
You can also use a command line tool to do it:
|
926
|
+
|
927
|
+
```
|
928
|
+
$ shaleb -c -f xml -i schema.xml
|
750
929
|
```
|
751
930
|
|
752
931
|
## Contributing
|
data/exe/shaleb
CHANGED
@@ -1,14 +1,32 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
+
require 'fileutils'
|
4
5
|
require 'optparse'
|
5
6
|
|
6
|
-
|
7
|
+
def require_local_or_global(path)
|
8
|
+
base_path = File.expand_path('../lib', __dir__)
|
7
9
|
|
8
|
-
if File.exist?(base_path)
|
9
|
-
|
10
|
-
else
|
11
|
-
|
10
|
+
if File.exist?(base_path)
|
11
|
+
require_relative "../lib/#{path}"
|
12
|
+
else
|
13
|
+
require path
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
require_local_or_global('shale/schema')
|
18
|
+
|
19
|
+
def load_xml_parser
|
20
|
+
require_local_or_global('shale/adapter/nokogiri')
|
21
|
+
Shale.xml_adapter = Shale::Adapter::Nokogiri
|
22
|
+
rescue LoadError
|
23
|
+
begin
|
24
|
+
require_local_or_global('shale/adapter/rexml')
|
25
|
+
Shale.xml_adapter = Shale::Adapter::REXML
|
26
|
+
rescue LoadError
|
27
|
+
puts "Can't load XML parser. Make sure Nokogiri or REXML is installed on your system!"
|
28
|
+
exit
|
29
|
+
end
|
12
30
|
end
|
13
31
|
|
14
32
|
params = {}
|
@@ -16,11 +34,17 @@ params = {}
|
|
16
34
|
ARGV << '-h' if ARGV.empty?
|
17
35
|
|
18
36
|
OptionParser.new do |opts|
|
19
|
-
opts.banner =
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
37
|
+
opts.banner = <<~BANNER
|
38
|
+
Usage: shaleb [options]
|
39
|
+
example generate schema from Shale model: shaleb -g -i data_model.rb -c MyRoot
|
40
|
+
example generate Shale model from schema: shaleb -c -i schema1.json,schema2.json -c MyRoot
|
41
|
+
BANNER
|
42
|
+
|
43
|
+
opts.on('-g', '--generate', 'generate schema from Shale model')
|
44
|
+
opts.on('-c', '--compile', 'compile schema into Shale model')
|
45
|
+
opts.on('-i INPUT', '--input', Array, 'Input file')
|
46
|
+
opts.on('-o OUTPUT', '--output', 'Output (defaults to STDOUT)')
|
47
|
+
opts.on('-r ROOT', '--root ROOT', 'Shale model class name')
|
24
48
|
opts.on('-f FORMAT', '--format FORMAT', 'Schema format: JSON (default), XML')
|
25
49
|
opts.on('-p', '--pretty', 'Pretty print generated schema')
|
26
50
|
|
@@ -30,46 +54,94 @@ OptionParser.new do |opts|
|
|
30
54
|
end
|
31
55
|
end.parse!(into: params)
|
32
56
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
57
|
+
if params[:compile]
|
58
|
+
unless params[:input]
|
59
|
+
puts 'Input file is required: shaleb -c -i schema1.json,schema2.json'
|
60
|
+
exit
|
61
|
+
end
|
39
62
|
|
40
|
-
|
41
|
-
|
42
|
-
exit
|
43
|
-
end
|
63
|
+
schemas = params[:input].map do |file|
|
64
|
+
path = File.expand_path(file, Dir.pwd)
|
44
65
|
|
45
|
-
|
66
|
+
if File.exist?(path)
|
67
|
+
File.read(path)
|
68
|
+
else
|
69
|
+
puts "File '#{path}' does not exist"
|
70
|
+
exit
|
71
|
+
end
|
72
|
+
end
|
46
73
|
|
47
|
-
|
74
|
+
if params[:format] == 'xml'
|
75
|
+
load_xml_parser
|
76
|
+
models = Shale::Schema.from_xml(schemas)
|
77
|
+
else
|
78
|
+
models = Shale::Schema.from_json(schemas, root_name: params[:root])
|
79
|
+
end
|
48
80
|
|
49
|
-
if params[:format] == 'xml'
|
50
81
|
if params[:output]
|
51
|
-
|
52
|
-
|
82
|
+
dir = File.expand_path(params[:output], Dir.pwd)
|
83
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
53
84
|
|
54
|
-
|
55
|
-
File.
|
85
|
+
models.each do |name, model|
|
86
|
+
output_path = File.join(dir, "#{name}.rb")
|
87
|
+
File.write(output_path, model)
|
56
88
|
end
|
57
89
|
else
|
58
|
-
|
59
|
-
|
60
|
-
output = schemas.map do |name, xml|
|
61
|
-
"<!-- #{name} -->\n#{xml}\n"
|
90
|
+
output = models.map do |name, model|
|
91
|
+
"# --- #{name}.rb ---\n#{model}\n"
|
62
92
|
end.join("\n")
|
63
93
|
|
64
94
|
puts output
|
65
95
|
end
|
66
96
|
else
|
67
|
-
|
97
|
+
unless params[:input]
|
98
|
+
puts 'Input file is required: shaleb -i model.rb -r MyClass'
|
99
|
+
exit
|
100
|
+
end
|
68
101
|
|
69
|
-
|
70
|
-
|
71
|
-
|
102
|
+
input_path = File.expand_path(params[:input][0], Dir.pwd)
|
103
|
+
|
104
|
+
unless File.exist?(input_path)
|
105
|
+
puts "File '#{input_path}' does not exist"
|
106
|
+
exit
|
107
|
+
end
|
108
|
+
|
109
|
+
unless params[:root]
|
110
|
+
puts 'Model class is required: shaleb -i model.rb -r MyClass'
|
111
|
+
exit
|
112
|
+
end
|
113
|
+
|
114
|
+
require input_path
|
115
|
+
|
116
|
+
klass = Object.const_get(params[:root])
|
117
|
+
|
118
|
+
if params[:format] == 'xml'
|
119
|
+
load_xml_parser
|
120
|
+
|
121
|
+
if params[:output]
|
122
|
+
base_name = File.basename(params[:output], File.extname(params[:output]))
|
123
|
+
schemas = Shale::Schema.to_xml(klass, base_name, pretty: params[:pretty])
|
124
|
+
|
125
|
+
schemas.map do |name, xml|
|
126
|
+
File.write(File.expand_path(name, Dir.pwd), xml)
|
127
|
+
end
|
128
|
+
else
|
129
|
+
schemas = Shale::Schema.to_xml(klass, pretty: params[:pretty])
|
130
|
+
|
131
|
+
output = schemas.map do |name, xml|
|
132
|
+
"<!-- #{name} -->\n#{xml}\n"
|
133
|
+
end.join("\n")
|
134
|
+
|
135
|
+
puts output
|
136
|
+
end
|
72
137
|
else
|
73
|
-
|
138
|
+
schema = Shale::Schema.to_json(klass, pretty: params[:pretty])
|
139
|
+
|
140
|
+
if params[:output]
|
141
|
+
output_path = File.expand_path(params[:output], Dir.pwd)
|
142
|
+
File.write(output_path, schema)
|
143
|
+
else
|
144
|
+
puts schema
|
145
|
+
end
|
74
146
|
end
|
75
147
|
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Shale
|
4
|
+
module Adapter
|
5
|
+
module Nokogiri
|
6
|
+
# Wrapper around Nokogiri API
|
7
|
+
#
|
8
|
+
# @api private
|
9
|
+
class Document
|
10
|
+
# Initialize object
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def initialize
|
14
|
+
@doc = ::Nokogiri::XML::Document.new
|
15
|
+
@namespaces = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
# Return Nokogiri document
|
19
|
+
#
|
20
|
+
# @return [::Nokogiri::XML::Document]
|
21
|
+
#
|
22
|
+
# @api private
|
23
|
+
def doc
|
24
|
+
if @doc.root
|
25
|
+
@namespaces.each do |prefix, namespace|
|
26
|
+
@doc.root.add_namespace(prefix, namespace)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
@doc
|
31
|
+
end
|
32
|
+
|
33
|
+
# Create Nokogiri element
|
34
|
+
#
|
35
|
+
# @param [String] name Name of the XML element
|
36
|
+
#
|
37
|
+
# @return [::Nokogiri::XML::Element]
|
38
|
+
#
|
39
|
+
# @api private
|
40
|
+
def create_element(name)
|
41
|
+
::Nokogiri::XML::Element.new(name, @doc)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Add XML namespace to document
|
45
|
+
#
|
46
|
+
# @param [String] prefix
|
47
|
+
# @param [String] namespace
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def add_namespace(prefix, namespace)
|
51
|
+
@namespaces[prefix] = namespace if prefix && namespace
|
52
|
+
end
|
53
|
+
|
54
|
+
# Add attribute to Nokogiri element
|
55
|
+
#
|
56
|
+
# @param [::Nokogiri::XML::Element] element Nokogiri element
|
57
|
+
# @param [String] name Name of the XML attribute
|
58
|
+
# @param [String] value Value of the XML attribute
|
59
|
+
#
|
60
|
+
# @api private
|
61
|
+
def add_attribute(element, name, value)
|
62
|
+
element[name] = value
|
63
|
+
end
|
64
|
+
|
65
|
+
# Add child element to Nokogiri element
|
66
|
+
#
|
67
|
+
# @param [::Nokogiri::XML::Element] element Nokogiri parent element
|
68
|
+
# @param [::Nokogiri::XML::Element] child Nokogiri child element
|
69
|
+
#
|
70
|
+
# @api private
|
71
|
+
def add_element(element, child)
|
72
|
+
element.add_child(child)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Add text node to Nokogiri element
|
76
|
+
#
|
77
|
+
# @param [::Nokogiri::XML::Element] element Nokogiri element
|
78
|
+
# @param [String] text Text to add
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
def add_text(element, text)
|
82
|
+
element.content = text
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|