scaffold_parser 0.1.0 → 0.2.0

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