scaffold_parser 0.0.1 → 0.1.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
- 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