shale 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +200 -21
  4. data/exe/shaleb +108 -36
  5. data/lib/shale/adapter/nokogiri/document.rb +87 -0
  6. data/lib/shale/adapter/nokogiri/node.rb +100 -0
  7. data/lib/shale/adapter/nokogiri.rb +11 -151
  8. data/lib/shale/adapter/ox/document.rb +80 -0
  9. data/lib/shale/adapter/ox/node.rb +88 -0
  10. data/lib/shale/adapter/ox.rb +9 -134
  11. data/lib/shale/adapter/rexml/document.rb +88 -0
  12. data/lib/shale/adapter/rexml/node.rb +99 -0
  13. data/lib/shale/adapter/rexml.rb +9 -150
  14. data/lib/shale/attribute.rb +6 -0
  15. data/lib/shale/error.rb +39 -0
  16. data/lib/shale/mapper.rb +8 -6
  17. data/lib/shale/schema/compiler/boolean.rb +21 -0
  18. data/lib/shale/schema/compiler/complex.rb +88 -0
  19. data/lib/shale/schema/compiler/date.rb +21 -0
  20. data/lib/shale/schema/compiler/float.rb +21 -0
  21. data/lib/shale/schema/compiler/integer.rb +21 -0
  22. data/lib/shale/schema/compiler/property.rb +70 -0
  23. data/lib/shale/schema/compiler/string.rb +21 -0
  24. data/lib/shale/schema/compiler/time.rb +21 -0
  25. data/lib/shale/schema/compiler/value.rb +21 -0
  26. data/lib/shale/schema/compiler/xml_complex.rb +50 -0
  27. data/lib/shale/schema/compiler/xml_property.rb +73 -0
  28. data/lib/shale/schema/json_compiler.rb +331 -0
  29. data/lib/shale/schema/{json → json_generator}/base.rb +2 -2
  30. data/lib/shale/schema/{json → json_generator}/boolean.rb +1 -1
  31. data/lib/shale/schema/{json → json_generator}/collection.rb +2 -2
  32. data/lib/shale/schema/{json → json_generator}/date.rb +1 -1
  33. data/lib/shale/schema/{json → json_generator}/float.rb +1 -1
  34. data/lib/shale/schema/{json → json_generator}/integer.rb +1 -1
  35. data/lib/shale/schema/{json → json_generator}/object.rb +5 -2
  36. data/lib/shale/schema/{json → json_generator}/ref.rb +1 -1
  37. data/lib/shale/schema/{json → json_generator}/schema.rb +7 -5
  38. data/lib/shale/schema/{json → json_generator}/string.rb +1 -1
  39. data/lib/shale/schema/{json → json_generator}/time.rb +1 -1
  40. data/lib/shale/schema/json_generator/value.rb +23 -0
  41. data/lib/shale/schema/{json.rb → json_generator.rb} +36 -36
  42. data/lib/shale/schema/xml_compiler.rb +919 -0
  43. data/lib/shale/schema/{xml → xml_generator}/attribute.rb +1 -1
  44. data/lib/shale/schema/{xml → xml_generator}/complex_type.rb +5 -2
  45. data/lib/shale/schema/{xml → xml_generator}/element.rb +1 -1
  46. data/lib/shale/schema/{xml → xml_generator}/import.rb +1 -1
  47. data/lib/shale/schema/{xml → xml_generator}/ref_attribute.rb +1 -1
  48. data/lib/shale/schema/{xml → xml_generator}/ref_element.rb +1 -1
  49. data/lib/shale/schema/{xml → xml_generator}/schema.rb +5 -5
  50. data/lib/shale/schema/{xml → xml_generator}/typed_attribute.rb +1 -1
  51. data/lib/shale/schema/{xml → xml_generator}/typed_element.rb +1 -1
  52. data/lib/shale/schema/{xml.rb → xml_generator.rb} +25 -26
  53. data/lib/shale/schema.rb +44 -5
  54. data/lib/shale/type/{composite.rb → complex.rb} +34 -22
  55. data/lib/shale/utils.rb +42 -7
  56. data/lib/shale/version.rb +1 -1
  57. data/lib/shale.rb +8 -19
  58. data/shale.gemspec +1 -1
  59. metadata +47 -27
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f9396e5fcba6ff4e1f1566abe356270ef9ddbd8ce9171063f011feeb36d234e7
4
- data.tar.gz: 4495b40dca3e01b6ba908dfc6b7831aea3f1e2865423de59ced69db33ba633b8
3
+ metadata.gz: aaa3043e612d332fab30ff87ecab722e3812e7769eba982db27c38f74c205ce1
4
+ data.tar.gz: bb5cdb963bce5756c48edb37d28bddc40a816901e87b4714ea512f07f8ec4c28
5
5
  SHA512:
6
- metadata.gz: 404737157189f2312b4e189d095899588495c8f80cf52f9bd5935358b2ec8bedbd98b0d7676c44641383109701d62377d3086b36342950d6fd657dbbba2d06b9
7
- data.tar.gz: '04818bdcac0290dfd96fbb0ebb1364350a444a9fc0533992d2cdf3bc3443e8afb4f00021346c66610400ddce12b4848c8bbed5c0bfb3835d40438c2b5acc1a6b'
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.6+
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 key names use:
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 complcated format than JSON or YAML. To map elements, attributes and content use:
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 only on elements, if you want to define namespace on an attribute
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 parser is used for handling JSON documents, YAML for YAML and
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
- # REXML is used by default:
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
- To generate JSON Schema from you Shale data model use:
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(Person, id: 'My ID', description: 'My description', pretty: true)
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": "My 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 -c Person -p
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::JSON::Base
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::JSON.register_json_type(MyEmailType, MyEmailJSONType)
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 you Shale data model use:
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 -c Person -p -f xml
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::XML.register_xml_type(MyEmailType, 'myEmailXMLType')
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
- base_path = File.expand_path('../lib', __dir__)
7
+ def require_local_or_global(path)
8
+ base_path = File.expand_path('../lib', __dir__)
7
9
 
8
- if File.exist?(base_path)
9
- require_relative '../lib/shale/schema'
10
- else
11
- require 'shale/schema'
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 = "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')
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
- 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
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
- unless params[:class]
41
- puts 'Model class is required'
42
- exit
43
- end
63
+ schemas = params[:input].map do |file|
64
+ path = File.expand_path(file, Dir.pwd)
44
65
 
45
- require input_path
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
- klass = Object.const_get(params[:class])
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
- base_name = File.basename(params[:output], File.extname(params[:output]))
52
- schemas = Shale::Schema.to_xml(klass, base_name, pretty: params[:pretty])
82
+ dir = File.expand_path(params[:output], Dir.pwd)
83
+ FileUtils.mkdir_p(dir) unless File.directory?(dir)
53
84
 
54
- schemas.map do |name, xml|
55
- File.write(File.expand_path(name, Dir.pwd), xml)
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
- schemas = Shale::Schema.to_xml(klass, pretty: params[:pretty])
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
- schema = Shale::Schema.to_json(klass, pretty: params[:pretty])
97
+ unless params[:input]
98
+ puts 'Input file is required: shaleb -i model.rb -r MyClass'
99
+ exit
100
+ end
68
101
 
69
- if params[:output]
70
- output_path = File.expand_path(params[:output], Dir.pwd)
71
- File.write(output_path, schema)
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
- puts schema
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