scaffold_parser 0.0.1 → 0.1.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
- SHA256:
3
- metadata.gz: 1394d7a717a8378ce9ae245bf6c06c6ca52a3cf35319e33b68817b5232b7e34d
4
- data.tar.gz: ab02c055478a1e5ecec8ca99e852354e78dac60b738afcf42d1b3c22d4194f8d
2
+ SHA1:
3
+ metadata.gz: 9b4ffe5b906e5b2e028aa175ec02018bef03aee4
4
+ data.tar.gz: 60e240f28ca2ad99677c27e846468d54bea1dcc8
5
5
  SHA512:
6
- metadata.gz: bfc02d05c852fa0e2729fdaa522ba79ed3c9326113efc454a9e10cefae6f914f6d3ee04e7607d8664b81567120087e992dd1b80438c6c8e0920249d1c8973ce3
7
- data.tar.gz: e9fbf75cc51ff8ad53db88f19126869bd0ea7dff62f795493e2c4283b447e1d47e982fcb51327941dc0b3138a75eecbaee5b2e2610fa967f2dede17b125af547
6
+ metadata.gz: 691f8d7819b3f41aa64aee39b49872e901a1b9ef4136b00fd8e9b6b927903eaa3c4f607bad91081da461c1de2b2baaf5bfa186c8ffd82615ae015f241beeb278
7
+ data.tar.gz: e834263aba43d562a45ae7a6ab5766c6d4d615b98b85fee16e3d5d1907af11b935cd20d06d875c7ddcf977fb8fda25560c21f4b363448d113644b0a022b3ab48
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
12
  *.gem
13
+ /Gemfile.lock
data/README.md CHANGED
@@ -1,37 +1,56 @@
1
1
  # ScaffoldParser
2
2
 
3
- Tool for fast parser scaffolding
3
+ Scaffolds XML file parser from XSD spec
4
4
 
5
- ## Installation
5
+ Content:
6
+ 1. [About](#about)
7
+ 1. [Naming](#naming)
8
+ 2. [What's missing](#whats-missing)
9
+ 2. [Api](#api)
10
+ 1. [Parsers](#parsers)
11
+ 2. [Builder](#builder)
12
+ 3. [Performance](#performance)
13
+ 4. [External links](#external-links)
14
+ 5. [License](#license)
6
15
 
7
- Add this line to your application's Gemfile:
16
+ ## About
8
17
 
9
- ```ruby
10
- gem 'scaffold_parser'
11
- ```
18
+ WIP
12
19
 
13
- And then execute:
20
+ ### Naming
14
21
 
15
- $ bundle
22
+ WIP
16
23
 
17
- Or install it yourself as:
24
+ #### Method names
18
25
 
19
- $ gem install scaffold_parser
26
+ WIP
20
27
 
21
- ## Usage
28
+ #### Class names
22
29
 
23
- TODO: Write usage instructions here
30
+ WIP
24
31
 
25
- ## Development
32
+ ### Missing parts
26
33
 
27
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
34
+ WIP
28
35
 
29
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
36
+ ## Api
30
37
 
31
- ## Contributing
38
+ ### Parsing a document
32
39
 
33
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/scaffold_parser.
40
+ WIP
41
+
42
+ ### Creating a document
43
+
44
+ WIP
45
+
46
+ ## Performance
47
+
48
+ WIP
49
+
50
+ ## External links
51
+
52
+ WIP
34
53
 
35
54
  ## License
36
55
 
37
- The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
56
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'scaffold_parser'
4
+ require 'optparse'
5
+
6
+ options = {}
7
+ path = ARGV.shift
8
+ abort 'Path to schema file as a first argument is required' unless path
9
+
10
+ OptionParser.new do |opts|
11
+ opts.banner = 'Usage: scaffold_parser <path_to_schema> [options]'
12
+
13
+ opts.on("-nNAMESPACE", "--namespace=NAMESPACE") do |v|
14
+ options[:namespace] = v
15
+ end
16
+ end.parse!
17
+
18
+ ScaffoldParser.scaffold(path, options)
@@ -1,4 +1,17 @@
1
- require 'scaffold_parser/version'
1
+ require 'scaffold_parser/builder'
2
+ require 'nokogiri'
3
+ require 'active_support/all'
4
+ require 'scaffold_parser/nokogiri_patches'
5
+ require 'scaffold_parser/file_patches'
6
+
7
+ Nokogiri::XML::Element.include ScaffoldParser::NokogiriPatches::Element
8
+ Nokogiri::XML::Document.include ScaffoldParser::NokogiriPatches::Document
9
+ File.include ScaffoldParser::FilePatches
2
10
 
3
11
  module ScaffoldParser
12
+ def self.scaffold(path, options = {})
13
+ doc = Nokogiri::XML(File.open(path))
14
+
15
+ Builder.call(doc, options)
16
+ end
4
17
  end
@@ -0,0 +1,129 @@
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
@@ -0,0 +1,13 @@
1
+ module ScaffoldParser
2
+ module FilePatches
3
+ attr_accessor :indent
4
+
5
+ def putsi(str)
6
+ if indent
7
+ puts str.prepend(' ')
8
+ else
9
+ puts str
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,217 @@
1
+ require 'active_support/all'
2
+
3
+ module ScaffoldParser
4
+ module NokogiriPatches
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'
13
+ end
14
+ end
15
+
16
+ def parent_nodes
17
+ attribute_elements.select(&:parent_type?) + extension_parent_nodes
18
+ end
19
+
20
+ def end_nodes
21
+ attribute_elements.select(&:end_type?) + extension_end_nodes
22
+ end
23
+
24
+ def list_nodes
25
+ attribute_elements.select(&:list_type?) + extension_list_nodes
26
+ end
27
+
28
+ def extension_end_nodes
29
+ extension ? extension.end_nodes : []
30
+ end
31
+
32
+ def extension_parent_nodes
33
+ extension ? extension.parent_nodes : []
34
+ end
35
+
36
+ def extension_list_nodes
37
+ extension ? extension.list_nodes : []
38
+ end
39
+
40
+ 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
46
+ end
47
+
48
+ def parent_type?
49
+ if custom_type?
50
+ type_def&.parent_type?
51
+ else
52
+ (complex_type? || complex_types.any?) && !list_type?
53
+ end
54
+ end
55
+
56
+ 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
62
+ end
63
+
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
72
+ end
73
+
74
+ def list_element_at
75
+ if list_element
76
+ [at_xpath('xs:complexType').to_location, list_element.to_location]
77
+ end
78
+ end
79
+
80
+ def to_class_name
81
+ if self['type']
82
+ self['type'].camelize
83
+ else
84
+ to_name.camelize
85
+ end
86
+ end
87
+
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
99
+ end
100
+ end
101
+
102
+ def to_method_name
103
+ to_name.underscore
104
+ end
105
+
106
+ def to_location
107
+ to_name
108
+ end
109
+
110
+ def custom_type?
111
+ self['type'].present? && !xs_type?
112
+ end
113
+
114
+ def type_def
115
+ find_type(self['type'])
116
+ end
117
+
118
+ def list_element
119
+ eles = xpath('xs:complexType/xs:sequence/xs:element')
120
+
121
+ if eles.size == 1
122
+ eles.first
123
+ end
124
+ end
125
+
126
+ def extension
127
+ elem = at_xpath('xs:complexType/xs:complexContent/xs:extension')
128
+
129
+ if elem
130
+ find_type(elem['base'])
131
+ end
132
+ end
133
+
134
+ def xs_type?
135
+ self['type'].present? && self['type'].start_with?('xs:')
136
+ end
137
+
138
+ private
139
+
140
+ def to_name
141
+ if self['name']
142
+ self['name']
143
+ else
144
+ at_xpath('..')['name']
145
+ end
146
+ end
147
+
148
+ def complex_types
149
+ xpath('xs:complexType')
150
+ end
151
+
152
+ def no_type?
153
+ self['type'].blank?
154
+ end
155
+
156
+ def complex_type?
157
+ name == 'complexType'
158
+ end
159
+
160
+ def simple_type?
161
+ name == 'simpleType'
162
+ end
163
+
164
+ 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
175
+
176
+ def includes
177
+ original_path = ENV['XSD_PATH'] || './'
178
+
179
+ collect_includes([], [], self) + [self]
180
+ end
181
+
182
+ def collect_includes(collection, names, doc)
183
+ original_path = ENV['XSD_PATH'] || './'
184
+
185
+ new_names = doc.xpath('//xs:include').map { |incl| incl['schemaLocation'] }
186
+
187
+ (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))
195
+
196
+ collection << new_doc
197
+ names << name
198
+
199
+ collect_includes(collection, names, new_doc)
200
+ end
201
+ end
202
+
203
+ collection
204
+ end
205
+ end
206
+
207
+ module Document
208
+ def parent_nodes
209
+ xpath('xs:schema/xs:complexType|xs:schema/xs:element')
210
+ end
211
+
212
+ def list_nodes
213
+ []
214
+ end
215
+ end
216
+ end
217
+ end
@@ -1,10 +1,9 @@
1
1
  lib = File.expand_path('../lib', __FILE__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'scaffold_parser/version'
4
3
 
5
4
  Gem::Specification.new do |spec|
6
5
  spec.name = 'scaffold_parser'
7
- spec.version = ScaffoldParser::VERSION
6
+ spec.version = '0.1.0'
8
7
  spec.authors = ['Premysl Donat']
9
8
  spec.email = ['pdonat@seznam.cz']
10
9
 
@@ -16,11 +15,14 @@ Gem::Specification.new do |spec|
16
15
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
16
  f.match(%r{^(test|spec|features)/})
18
17
  end
19
- spec.bindir = 'exe'
20
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.bindir = 'bin'
19
+ spec.executables = ['scaffold_parser']
21
20
  spec.require_paths = ['lib']
22
21
 
23
22
  spec.add_development_dependency 'bundler', '~> 1.16'
24
23
  spec.add_development_dependency 'rake', '~> 10.0'
25
24
  spec.add_development_dependency 'rspec', '~> 3.0'
25
+ spec.add_development_dependency 'pry'
26
+ spec.add_development_dependency 'nokogiri'
27
+ spec.add_development_dependency 'activesupport'
26
28
  end
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.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Premysl Donat
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2018-01-07 00:00:00.000000000 Z
11
+ date: 2018-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,10 +52,53 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: activesupport
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
55
97
  description: ''
56
98
  email:
57
99
  - pdonat@seznam.cz
58
- executables: []
100
+ executables:
101
+ - scaffold_parser
59
102
  extensions: []
60
103
  extra_rdoc_files: []
61
104
  files:
@@ -67,9 +110,12 @@ files:
67
110
  - README.md
68
111
  - Rakefile
69
112
  - bin/console
113
+ - bin/scaffold_parser
70
114
  - bin/setup
71
115
  - lib/scaffold_parser.rb
72
- - lib/scaffold_parser/version.rb
116
+ - lib/scaffold_parser/builder.rb
117
+ - lib/scaffold_parser/file_patches.rb
118
+ - lib/scaffold_parser/nokogiri_patches.rb
73
119
  - scaffold_parser.gemspec
74
120
  homepage: https://github.com/Masa331/scaffold_parser
75
121
  licenses:
@@ -91,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
91
137
  version: '0'
92
138
  requirements: []
93
139
  rubyforge_project:
94
- rubygems_version: 2.7.3
140
+ rubygems_version: 2.6.14
95
141
  signing_key:
96
142
  specification_version: 4
97
143
  summary: Tool for fast parser scaffolding
@@ -1,3 +0,0 @@
1
- module ScaffoldParser
2
- VERSION = '0.0.1'
3
- end