scaffold_parser 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9b4ffe5b906e5b2e028aa175ec02018bef03aee4
4
- data.tar.gz: 60e240f28ca2ad99677c27e846468d54bea1dcc8
3
+ metadata.gz: 7899b91e50516ffec5a13ce5215f131b9f80c630
4
+ data.tar.gz: 3162f40dede2ddef1a450b742f0dd6caa536b8b3
5
5
  SHA512:
6
- metadata.gz: 691f8d7819b3f41aa64aee39b49872e901a1b9ef4136b00fd8e9b6b927903eaa3c4f607bad91081da461c1de2b2baaf5bfa186c8ffd82615ae015f241beeb278
7
- data.tar.gz: e834263aba43d562a45ae7a6ab5766c6d4d615b98b85fee16e3d5d1907af11b935cd20d06d875c7ddcf977fb8fda25560c21f4b363448d113644b0a022b3ab48
6
+ metadata.gz: 0bb8061b4f380d647698737b2c41c468a183855e384e32e4724fc4b9cee7a72b753fb775b3d5e9ad0e2a9d4dc7ed09e21d852247637b7e1080bb8d489ec646ac
7
+ data.tar.gz: 1e65927bacc4af7a59cbf6f9af928f00bb24833abed33afbdb2e74a75adf7316f0550b584eb62c7e03ba78d371ce787942eb7189e0bfbb915a67a2e6b7ea23af
@@ -1,8 +1,8 @@
1
- require 'scaffold_parser/builder'
2
1
  require 'nokogiri'
3
2
  require 'active_support/all'
4
3
  require 'scaffold_parser/nokogiri_patches'
5
4
  require 'scaffold_parser/file_patches'
5
+ require 'scaffold_parser/scaffolders/xsd'
6
6
 
7
7
  Nokogiri::XML::Element.include ScaffoldParser::NokogiriPatches::Element
8
8
  Nokogiri::XML::Document.include ScaffoldParser::NokogiriPatches::Document
@@ -12,6 +12,6 @@ module ScaffoldParser
12
12
  def self.scaffold(path, options = {})
13
13
  doc = Nokogiri::XML(File.open(path))
14
14
 
15
- Builder.call(doc, options)
15
+ Scaffolders::XSD.call(doc, options)
16
16
  end
17
17
  end
@@ -1,203 +1,175 @@
1
- require 'active_support/all'
2
-
3
1
  module ScaffoldParser
4
2
  module NokogiriPatches
3
+ class DefinitionNotFound < StandardError; end
4
+
5
5
  module Element
6
- def attribute_elements
7
- if name == 'complexType'
8
- xpath('xs:sequence/xs:element|xs:sequence/xs:sequence/xs:element|xs:sequence/xs:choice/xs:sequence/xs:element|xs:sequence/xs:choice/xs:element')
9
- elsif name == 'element'
10
- xpath('xs:complexType/xs:sequence/xs:element')
11
- else
12
- fail 'whatever'
6
+ class BlankExtension
7
+ def submodel_nodes
8
+ []
13
9
  end
14
- end
15
-
16
- def parent_nodes
17
- attribute_elements.select(&:parent_type?) + extension_parent_nodes
18
- end
19
10
 
20
- def end_nodes
21
- attribute_elements.select(&:end_type?) + extension_end_nodes
22
- end
11
+ def value_nodes
12
+ []
13
+ end
23
14
 
24
- def list_nodes
25
- attribute_elements.select(&:list_type?) + extension_list_nodes
15
+ def array_nodes
16
+ []
17
+ end
26
18
  end
27
19
 
28
- def extension_end_nodes
29
- extension ? extension.end_nodes : []
20
+ def submodel_nodes
21
+ attribute_elements.select(&:parent_type?) + extension.submodel_nodes
30
22
  end
31
23
 
32
- def extension_parent_nodes
33
- extension ? extension.parent_nodes : []
24
+ def value_nodes
25
+ attribute_elements.select(&:end_type?) + extension.value_nodes
34
26
  end
35
27
 
36
- def extension_list_nodes
37
- extension ? extension.list_nodes : []
28
+ def array_nodes
29
+ attribute_elements.select(&:list_type?) + extension.array_nodes
38
30
  end
39
31
 
40
32
  def end_type?
41
- if custom_type?
42
- type_def&.end_type?
43
- else
44
- xs_type? || (no_type? && complex_types.empty? && !complex_type? && !list_type?)
45
- end
33
+ (xs_type? && !list_type?) || (no_type? && complex_types.empty? && !complex_type? && !list_type?)
46
34
  end
47
35
 
48
36
  def parent_type?
49
- if custom_type?
50
- type_def&.parent_type?
37
+ (complex_type? || complex_types.any?) && !list_type?
38
+ end
39
+
40
+ def max_occurs
41
+ case self['maxOccurs']
42
+ when 'unbounded'
43
+ Float::INFINITY
44
+ when String
45
+ self['maxOccurs'].to_i
51
46
  else
52
- (complex_type? || complex_types.any?) && !list_type?
47
+ 1
53
48
  end
54
49
  end
55
50
 
56
51
  def list_type?
57
- if list_element
58
- max_occure = list_element['maxOccurs']
59
-
60
- max_occure == 'unbounded' || max_occure.to_i > 1
61
- end
52
+ simple_list? || named_list?
62
53
  end
63
54
 
64
- def list_element_klass
65
- if list_element
66
- if list_element['type']
67
- list_element['type'].camelize
68
- else
69
- list_element['name'].camelize
70
- end
71
- end
55
+ def simple_list?
56
+ max_occurs > 1
72
57
  end
73
58
 
74
- def list_element_at
75
- if list_element
76
- [at_xpath('xs:complexType').to_location, list_element.to_location]
77
- end
59
+ def named_list?
60
+ definition.list_element.present? && definition.list_element.max_occurs > 1
78
61
  end
79
62
 
80
- def to_class_name
81
- if self['type']
82
- self['type'].camelize
63
+ def list_element
64
+ if simple_list?
65
+ self
83
66
  else
84
- to_name.camelize
67
+ xpath('xs:complexType/xs:sequence/xs:element').first
85
68
  end
86
69
  end
87
70
 
88
- def to_file_name
89
- to_class_name.underscore
90
- end
91
-
92
- def to_require
93
- if parent_type?
94
- to_class_name.underscore
95
- elsif list_type?
96
- if list_element
97
- list_element.to_class_name.underscore
98
- end
71
+ def list_element_klass
72
+ if simple_list?
73
+ definition.to_name.camelize
74
+ elsif named_list?
75
+ list_element.definition.to_name.camelize
99
76
  end
100
77
  end
101
78
 
102
- def to_method_name
103
- to_name.underscore
104
- end
105
-
106
- def to_location
107
- to_name
79
+ def list_element_at
80
+ if simple_list?
81
+ [to_name]
82
+ elsif named_list?
83
+ [at_xpath('xs:complexType').to_name, list_element.to_name]
84
+ end
108
85
  end
109
86
 
110
- def custom_type?
111
- self['type'].present? && !xs_type?
87
+ def to_class_name
88
+ definition.to_name.camelize
112
89
  end
113
90
 
114
- def type_def
115
- find_type(self['type'])
91
+ def definition
92
+ if self['type'].present? && !self['type'].start_with?('xs:')
93
+ find_type(self['type'])
94
+ else
95
+ self
96
+ end
116
97
  end
117
98
 
118
- def list_element
119
- eles = xpath('xs:complexType/xs:sequence/xs:element')
99
+ def extension
100
+ complex_ext = at_xpath('xs:complexType/xs:complexContent/xs:extension')
101
+ # Can be also simple extension but i don't have to do anything with these:
102
+ # simple_ext = at_xpath('xs:complexType/xs:simpleContent/xs:extension')
120
103
 
121
- if eles.size == 1
122
- eles.first
123
- end
104
+ complex_ext ? find_type(complex_ext['base']) : BlankExtension.new
124
105
  end
125
106
 
126
- def extension
127
- elem = at_xpath('xs:complexType/xs:complexContent/xs:extension')
107
+ def extended_simple_type?
108
+ simple_ext = at_xpath('xs:complexType/xs:simpleContent/xs:extension')
128
109
 
129
- if elem
130
- find_type(elem['base'])
131
- end
110
+ simple_ext.present?
132
111
  end
133
112
 
134
113
  def xs_type?
135
- self['type'].present? && self['type'].start_with?('xs:')
114
+ (definition['type'].present? && definition['type'].start_with?('xs:')) || extended_simple_type?
136
115
  end
137
116
 
138
- private
139
-
140
- def to_name
141
- if self['name']
142
- self['name']
143
- else
144
- at_xpath('..')['name']
145
- end
117
+ def complex_type?
118
+ definition.name == 'complexType'
146
119
  end
147
120
 
148
121
  def complex_types
149
- xpath('xs:complexType')
122
+ definition.xpath('xs:complexType[not(xs:simpleContent)]')
150
123
  end
151
124
 
152
125
  def no_type?
153
- self['type'].blank?
126
+ definition['type'].blank?
154
127
  end
155
128
 
156
- def complex_type?
157
- name == 'complexType'
129
+ def to_name
130
+ self['name'] || at_xpath('..')['name']
158
131
  end
159
132
 
160
- def simple_type?
161
- name == 'simpleType'
133
+ private
134
+
135
+ def attribute_elements
136
+ xpath('xs:sequence/xs:element',
137
+ 'xs:sequence/xs:sequence/xs:element',
138
+ 'xs:sequence/xs:choice/xs:sequence/xs:element',
139
+ 'xs:sequence/xs:choice/xs:element',
140
+ 'xs:complexType/xs:sequence/xs:element',
141
+ 'xs:complexType/xs:choice/xs:element',
142
+ 'xs:complexType/xs:complexContent/xs:extension/xs:sequence/xs:element',
143
+ 'xs:complexContent/xs:extension/xs:sequence/xs:element'
144
+ )
162
145
  end
163
146
 
164
147
  def find_type(name)
165
- doc = includes.find do |doc|
166
- doc.at_xpath("//*[@name='#{name}']").present?
167
- end
168
-
169
- if doc.blank?
170
- fail "Cant find element definition for '#{name}'. Might be not enough includes?"
171
- end
172
-
173
- doc.at_xpath("//*[@name='#{name}']")
174
- end
148
+ if element = document.at_xpath("//*[@name='#{name}']")
149
+ return element
150
+ else
151
+ definition_schema = collect_includes.find do |doc|
152
+ doc.at_xpath("//*[@name='#{name}']").present?
153
+ end
175
154
 
176
- def includes
177
- original_path = ENV['XSD_PATH'] || './'
155
+ if definition_schema.blank?
156
+ fail DefinitionNotFound.new("Couldn't find type definiton for '#{name}' in any included schema.")
157
+ end
178
158
 
179
- collect_includes([], [], self) + [self]
159
+ definition_schema.at_xpath("//*[@name='#{name}']")
160
+ end
180
161
  end
181
162
 
182
- def collect_includes(collection, names, doc)
183
- original_path = ENV['XSD_PATH'] || './'
184
-
163
+ def collect_includes(doc = self, collection = [], names = [])
185
164
  new_names = doc.xpath('//xs:include').map { |incl| incl['schemaLocation'] }
186
165
 
187
166
  (new_names - names).each do |name|
188
- if names.include? name
189
- next
190
- else
191
- dir = original_path.split('/')
192
- include_path = (dir + [name]).join('/')
193
-
194
- new_doc = Nokogiri::XML(File.open(include_path))
167
+ new_doc = Nokogiri::XML(File.open(name))
195
168
 
196
- collection << new_doc
197
- names << name
169
+ collection << new_doc
170
+ names << name
198
171
 
199
- collect_includes(collection, names, new_doc)
200
- end
172
+ collect_includes(new_doc, collection, names)
201
173
  end
202
174
 
203
175
  collection
@@ -205,11 +177,11 @@ module ScaffoldParser
205
177
  end
206
178
 
207
179
  module Document
208
- def parent_nodes
180
+ def submodel_nodes
209
181
  xpath('xs:schema/xs:complexType|xs:schema/xs:element')
210
182
  end
211
183
 
212
- def list_nodes
184
+ def array_nodes
213
185
  []
214
186
  end
215
187
  end
@@ -0,0 +1,41 @@
1
+ require 'scaffold_parser/scaffolders/xsd/parser'
2
+
3
+ module ScaffoldParser
4
+ module Scaffolders
5
+ class XSD
6
+ def self.call(doc, options, already_scaffolded_subelements = [])
7
+ self.new(doc, options, already_scaffolded_subelements).call
8
+ end
9
+
10
+ def initialize(doc, options, already_scaffolded_subelements)
11
+ @doc = doc
12
+ @options = options
13
+ @already_scaffolded_subelements = already_scaffolded_subelements
14
+ end
15
+
16
+ def call
17
+ unless Dir.exists?('./tmp/')
18
+ Dir.mkdir('./tmp/')
19
+ puts './tmp/ directory created'
20
+ end
21
+
22
+ unscaffolded_subelements.each do |subelement|
23
+ @already_scaffolded_subelements << subelement.to_class_name
24
+
25
+ Parser.call(subelement.definition, @options)
26
+ self.class.call(subelement.definition, @options, @already_scaffolded_subelements)
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def unscaffolded_subelements
33
+ all = @doc.submodel_nodes.to_a + @doc.array_nodes.map(&:list_element)
34
+
35
+ all
36
+ .reject(&:xs_type?)
37
+ .reject { |node| @already_scaffolded_subelements.include?(node.to_class_name) }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,118 @@
1
+ module ScaffoldParser
2
+ module Scaffolders
3
+ class XSD
4
+ class Parser
5
+ attr_reader :node
6
+
7
+ def self.call(node, options)
8
+ self.new(node, options).call
9
+ end
10
+
11
+ def initialize(node, options)
12
+ @node = node
13
+ @options = options
14
+ end
15
+
16
+ def call
17
+ path = "./tmp/#{node.to_class_name.underscore}.rb"
18
+
19
+ File.open(path, 'wb') do |f|
20
+ f.indent = true if @options[:namespace]
21
+
22
+ f.puts "require '#{namespaced('base_element')}'"
23
+ node.submodel_nodes.map { |n| namespaced(n.to_class_name.underscore) }.uniq.each { |n| f.puts "require '#{n}'" }
24
+ node.array_nodes.reject { |l| l.list_element.xs_type? }.each { |n| f.puts "require '#{namespaced(n.list_element.to_class_name.underscore)}'" }
25
+ f.puts
26
+
27
+ f.puts "module #{@options[:namespace]}" if @options[:namespace]
28
+ f.putsi "class #{node.to_class_name}"
29
+ f.putsi " include BaseElement"
30
+
31
+ node.value_nodes.each do |method|
32
+ f.puts
33
+
34
+ method_name = method.to_name.underscore
35
+ at = method.to_name
36
+
37
+ f.putsi " def #{method_name}"
38
+ f.putsi " at :#{at}"
39
+ f.putsi " end"
40
+ end
41
+
42
+ node.submodel_nodes.each do |method|
43
+ f.puts
44
+
45
+ klass = method.to_class_name
46
+ method_name = method.to_name.underscore
47
+ at = method.to_name
48
+
49
+ f.putsi " def #{method_name}"
50
+ f.putsi " submodel_at(#{klass}, :#{at})"
51
+ f.putsi " end"
52
+ end
53
+
54
+ node.array_nodes.reject { |l| l.list_element.xs_type? }.each do |method|
55
+ f.puts
56
+
57
+ list_element_klass = method.list_element_klass
58
+ method_name = method.to_name.underscore
59
+ list_element_at = method.list_element_at.map { |e| ":#{e}" }.join(', ')
60
+
61
+ f.putsi " def #{method_name}"
62
+ f.putsi " array_of_at(#{list_element_klass}, [#{list_element_at}])"
63
+ f.putsi " end"
64
+ end
65
+
66
+ node.array_nodes.select { |l| l.list_element.xs_type? }.each do |method|
67
+ f.puts
68
+
69
+ list_element_klass = method.list_element_klass
70
+ method_name = method.to_name.underscore
71
+ list_element_at = method.list_element_at.map { |e| ":#{e}" }.join(', ')
72
+
73
+ f.putsi " def #{method_name}"
74
+ f.putsi " array_of_at(String, [#{list_element_at}])"
75
+ f.putsi " end"
76
+ end
77
+
78
+ ### to_h method
79
+ lines = []
80
+ node.value_nodes.each do |node|
81
+ lines << "#{node.to_name.underscore}: #{node.to_name.underscore},"
82
+ end
83
+ node.submodel_nodes.each do |node|
84
+ lines << "#{node.to_name.underscore}: #{node.to_name.underscore}.to_h,"
85
+ end
86
+ node.array_nodes.reject { |l| l.list_element.xs_type? }.each do |node|
87
+ lines << "#{node.to_name.underscore}: #{node.to_name.underscore}.map(&:to_h),"
88
+ end
89
+ node.array_nodes.select { |l| l.list_element.xs_type? }.each do |node|
90
+ lines << "#{node.to_name.underscore}: #{node.to_name.underscore},"
91
+ end
92
+ if lines.any?
93
+ f.puts
94
+ lines.last.chop!
95
+ first_line = lines.shift
96
+
97
+ f.putsi " def to_h"
98
+ f.putsi " { #{first_line}"
99
+ lines.each do |line|
100
+ f.putsi " #{line}"
101
+ end
102
+ f.putsi " }.delete_if { |k, v| v.nil? || v.empty? }"
103
+ f.putsi " end"
104
+ end
105
+ f.putsi "end"
106
+ f.puts "end" if @options[:namespace]
107
+ end
108
+ end
109
+
110
+ private
111
+
112
+ def namespaced(path)
113
+ [@options[:namespace]&.underscore, path].compact.join('/')
114
+ end
115
+ end
116
+ end
117
+ end
118
+ end
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = 'scaffold_parser'
6
- spec.version = '0.1.0'
6
+ spec.version = '0.2.0'
7
7
  spec.authors = ['Premysl Donat']
8
8
  spec.email = ['pdonat@seznam.cz']
9
9
 
@@ -13,7 +13,7 @@ Gem::Specification.new do |spec|
13
13
  spec.license = 'MIT'
14
14
 
15
15
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
- f.match(%r{^(test|spec|features)/})
16
+ f.match(%r{^(test|spec|features)/}) || f.end_with?('.xsd')
17
17
  end
18
18
  spec.bindir = 'bin'
19
19
  spec.executables = ['scaffold_parser']
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scaffold_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Premysl Donat
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-18 00:00:00.000000000 Z
11
+ date: 2018-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -113,9 +113,10 @@ files:
113
113
  - bin/scaffold_parser
114
114
  - bin/setup
115
115
  - lib/scaffold_parser.rb
116
- - lib/scaffold_parser/builder.rb
117
116
  - lib/scaffold_parser/file_patches.rb
118
117
  - lib/scaffold_parser/nokogiri_patches.rb
118
+ - lib/scaffold_parser/scaffolders/xsd.rb
119
+ - lib/scaffold_parser/scaffolders/xsd/parser.rb
119
120
  - scaffold_parser.gemspec
120
121
  homepage: https://github.com/Masa331/scaffold_parser
121
122
  licenses:
@@ -1,129 +0,0 @@
1
- module ScaffoldParser
2
- class Builder
3
- def self.call(doc, options, already_scaffolded_classes = [])
4
- self.new(doc, options, already_scaffolded_classes).call
5
- end
6
-
7
- def initialize(doc, options, already_scaffolded_classes)
8
- @doc = doc
9
- @options = options
10
- @already_scaffolded_classes = already_scaffolded_classes
11
- end
12
-
13
- def call
14
- unless Dir.exists?('./tmp/')
15
- Dir.mkdir('./tmp/')
16
- puts './tmp/ directory created'
17
- end
18
-
19
- parent_nodes = @doc.parent_nodes.select do |node|
20
- !@already_scaffolded_classes.include? node.to_class_name
21
- end
22
-
23
- parent_nodes.each do |parent|
24
- @already_scaffolded_classes << parent.to_class_name
25
-
26
- if parent.custom_type?
27
- type_def = parent.type_def
28
-
29
- scaffold_class(type_def)
30
- self.class.call(type_def, @options, @already_scaffolded_classes)
31
- else
32
- scaffold_class(parent)
33
- self.class.call(parent, @options, @already_scaffolded_classes)
34
- end
35
- end
36
-
37
- list_nodes = @doc.list_nodes.select do |node|
38
- !@already_scaffolded_classes.include? node.list_element.to_class_name
39
- end
40
-
41
- list_nodes.each do |list|
42
- @already_scaffolded_classes << list.list_element.to_class_name
43
-
44
- if list.list_element.custom_type?
45
- type_def = list.list_element.type_def
46
-
47
- scaffold_class(type_def)
48
- self.class.call(type_def, @options, @already_scaffolded_classes)
49
- elsif !list.list_element.xs_type?
50
- scaffold_class(list.list_element)
51
- self.class.call(list.list_element, @options, @already_scaffolded_classes)
52
- end
53
- end
54
- end
55
-
56
- private
57
-
58
- def namespaced(path)
59
- [@options[:namespace]&.underscore, path].compact.join('/')
60
- end
61
-
62
- def scaffold_class(node)
63
- path = "./tmp/#{node.to_file_name}.rb"
64
-
65
- File.open(path, 'wb') do |f|
66
- f.indent = true if @options[:namespace]
67
-
68
- f.puts "require '#{namespaced('base_element')}'"
69
- node.parent_nodes.each { |n| f.puts "require '#{namespaced(n.to_require)}'" }
70
- node.list_nodes.reject { |l| l.list_element.xs_type? }.each { |n| f.puts "require '#{namespaced(n.to_require)}'" }
71
- f.puts
72
-
73
- f.puts "module #{@options[:namespace]}" if @options[:namespace]
74
- f.putsi "class #{node.to_class_name}"
75
- f.putsi " include BaseElement"
76
-
77
- node.end_nodes.each do |method|
78
- f.puts
79
-
80
- method_name = method.to_method_name
81
- at = method.to_location
82
-
83
- f.putsi " def #{method_name}"
84
- f.putsi " at :#{at}"
85
- f.putsi " end"
86
- end
87
-
88
- node.parent_nodes.each do |method|
89
- f.puts
90
-
91
- klass = method.to_class_name
92
- method_name = method.to_method_name
93
- at = method.to_location
94
-
95
- f.putsi " def #{method_name}"
96
- f.putsi " submodel_at(#{klass}, :#{at})"
97
- f.putsi " end"
98
- end
99
-
100
- node.list_nodes.reject { |l| l.list_element.xs_type? }.each do |method|
101
- f.puts
102
-
103
- list_element_klass = method.list_element_klass
104
- method_name = method.to_method_name
105
- list_element_at = method.list_element_at.map { |e| ":#{e}" }.join(', ')
106
-
107
- f.putsi " def #{method_name}"
108
- f.putsi " array_of_at(#{list_element_klass}, [#{list_element_at}])"
109
- f.putsi " end"
110
- end
111
-
112
- node.list_nodes.select { |l| l.list_element.xs_type? }.each do |method|
113
- f.puts
114
-
115
- list_element_klass = method.list_element_klass
116
- method_name = method.to_method_name
117
- list_element_at = method.list_element_at.map { |e| ":#{e}" }.join(', ')
118
-
119
- f.putsi " def #{method_name}"
120
- f.putsi " array_of_at(String, [#{list_element_at}])"
121
- f.putsi " end"
122
- end
123
-
124
- f.putsi "end"
125
- f.puts "end" if @options[:namespace]
126
- end
127
- end
128
- end
129
- end