xsd-reader 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2b0bde5f374f9fa52cf79ab8ad070a105006ded8
4
+ data.tar.gz: 3b20b79a58c606b597fbaf540b1c278030bc99c6
5
+ SHA512:
6
+ metadata.gz: 2ab56370054c6373fa72c3a2f8cfc1832804e7f325d806a0e1d7c246ffc9870abc36b2aa0fb7e055421fb6c4c3b21af32adecc95553c516867eb4f3cfec24328
7
+ data.tar.gz: f515fa9b00bd154df2f26c001dce0b8149437b21917a23cd7480966a0c1359a1a1cdab1830b54b7b9f960879ca55c6d18118836f21d82663d42313b2c225707b
data/.gitignore ADDED
@@ -0,0 +1,35 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /vendor/bundle
26
+ /lib/bundler/man/
27
+
28
+ # for a library or gem, you might want to ignore these files since the code is
29
+ # intended to run in multiple environments; otherwise, check them in:
30
+ # Gemfile.lock
31
+ .ruby-version
32
+ .ruby-gemset
33
+
34
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
35
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'byebug'
7
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ xsd-reader (0.0.1)
5
+ nokogiri
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ byebug (5.0.0)
11
+ columnize (= 0.9.0)
12
+ columnize (0.9.0)
13
+ diff-lcs (1.2.5)
14
+ mini_portile (0.6.2)
15
+ nokogiri (1.6.6.2)
16
+ mini_portile (~> 0.6.0)
17
+ rspec (3.3.0)
18
+ rspec-core (~> 3.3.0)
19
+ rspec-expectations (~> 3.3.0)
20
+ rspec-mocks (~> 3.3.0)
21
+ rspec-core (3.3.1)
22
+ rspec-support (~> 3.3.0)
23
+ rspec-expectations (3.3.0)
24
+ diff-lcs (>= 1.2.0, < 2.0)
25
+ rspec-support (~> 3.3.0)
26
+ rspec-mocks (3.3.1)
27
+ diff-lcs (>= 1.2.0, < 2.0)
28
+ rspec-support (~> 3.3.0)
29
+ rspec-support (3.3.0)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ byebug
36
+ rspec
37
+ xsd-reader!
38
+
39
+ BUNDLED WITH
40
+ 1.10.5
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Mark
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,60 @@
1
+ # XsdReader
2
+
3
+ XsdReader provides easy and flexible access to XSD information
4
+
5
+ ## Installation
6
+
7
+
8
+ Rubygems:
9
+
10
+ `
11
+ gem install xsd-reader
12
+ `
13
+
14
+ Bundler:
15
+
16
+ `
17
+ gem 'xsd-reader'
18
+ `
19
+
20
+ ## Examples
21
+
22
+ Load xsd
23
+ ```ruby
24
+ reader = XsdReader::XML.new(:xsd_file => 'ddex-ern-v36.xsd')
25
+ ```
26
+
27
+ Get elements and their child elements
28
+ ```ruby
29
+ node = reader['NewReleaseMessage']
30
+ node.elements.map(&:name) # => ['MessageHeader', 'UpdateIndicator', 'IsBackfill', 'CatalogTransfer', 'WorkList', 'CueSheetList', 'ResourceList', 'CollectionList', 'ReleaseList', 'DealList']
31
+ ```
32
+
33
+ Get attributes
34
+ ```ruby
35
+ reader['NewReleaseMessage']['MessageHeader'].attributes.map(&:name) # => ['LanguageAndScriptCode']
36
+ ```
37
+
38
+ Get type information of attribute
39
+ ```ruby
40
+ attribute = reader['NewReleaseMessage']['MessageHeader']['@LanguageAndScriptCode']
41
+ attribute.type # => 'xs:string'
42
+ attribute.type_name # => 'string'
43
+ attribute.type_namespace # => 'xs'
44
+ ```
45
+
46
+ Get element amount details
47
+ ```ruby
48
+ node = @reader['NewReleaseMessage']['ResourceList']['SoundRecording']
49
+ node.min_occurs # => 0
50
+ node.max_occurs # => :unbouded
51
+ node.multiple_allowed? # true
52
+ node.required? # false
53
+ node = @reader['NewReleaseMessage']['MessageHeader']
54
+ node.min_occurs # => nil
55
+ node.max_occurs # => nil
56
+ node.multiple_allowed? # false
57
+ node.required? # true
58
+ ```
59
+
60
+
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ # rxsd project Rakefile
2
+ #
3
+ # Copyright (C) 2010 Mohammed Morsi <movitto@yahoo.com>
4
+ # Licensed under the LGPLv3+ http://www.gnu.org/licenses/lgpl.txt
5
+
6
+ require 'rdoc/task'
7
+ require "rspec/core/rake_task"
8
+
9
+ task :default => :rspec do; end
10
+
11
+ desc "Run all specs"
12
+ RSpec::Core::RakeTask.new('rspec') do |t|
13
+ t.pattern = 'spec/**/*_spec.rb'
14
+ end
15
+
16
+ Rake::RDocTask.new do |rd|
17
+ rd.main = "README.rdoc"
18
+ rd.rdoc_dir = "doc/site/api"
19
+ rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
20
+ end
@@ -0,0 +1,9 @@
1
+ module XsdReader
2
+ class Attribute
3
+ include Shared
4
+
5
+ def required?
6
+ (node.attributes['use'] && node.attributes['use'].value == 'required') ? true : false
7
+ end
8
+ end # class Attribute
9
+ end # module XsdReader
@@ -0,0 +1,8 @@
1
+ module XsdReader
2
+
3
+ class Choice
4
+ include Shared
5
+
6
+ end # class Choice
7
+
8
+ end # module XsdReader
@@ -0,0 +1,26 @@
1
+ module XsdReader
2
+
3
+ class ComplexType
4
+ include Shared
5
+
6
+ def name
7
+ node.attributes['name'] ? node.attributes['name'].value : nil
8
+ end
9
+
10
+ def attributes
11
+ super + (simple_content ? simple_content.attributes : [])
12
+ end
13
+
14
+ def parent_element
15
+ if parent.nil? || parent.is_a?(Schema) || !parent.is_a?(Element)
16
+ parent_elements.first
17
+ end
18
+ end
19
+
20
+ def parent_elements
21
+ elements_by_type(self.name)
22
+ end
23
+
24
+ end # class ComplexType
25
+
26
+ end # module XsdReader
@@ -0,0 +1,65 @@
1
+ require 'xsd_reader/shared'
2
+
3
+ module XsdReader
4
+ class Element
5
+ include Shared
6
+
7
+ def elements(opts = {})
8
+ return super if opts[:direct] == true
9
+ all_elements
10
+ end
11
+
12
+ def attributes
13
+ super + (complex_type ? complex_type.attributes : [])
14
+ end
15
+
16
+ def complex_type
17
+ super || linked_complex_type
18
+ end
19
+
20
+ def min_occurs
21
+ node.attributes['minOccurs'] ? node.attributes['minOccurs'].value.to_i : nil
22
+ end
23
+
24
+ def max_occurs
25
+ if val = node.attributes['maxOccurs'] ? node.attributes['maxOccurs'].value : nil
26
+ val == 'unbounded' ? :unbounded : val.to_i
27
+ end
28
+ end
29
+
30
+ def multiple_allowed?
31
+ max_occurs == :unbounded || max_occurs.to_i > 0
32
+ end
33
+
34
+ def required?
35
+ min_occurs.nil? || min_occurs.to_i > 0 # TODO; consider if the element is part of a choice definition?
36
+ end
37
+
38
+ def optional?
39
+ !required?
40
+ end
41
+
42
+ def parent
43
+ node.parent
44
+ end
45
+
46
+ def family_tree(stack = [])
47
+ logger.warn('Usage of the family tree function is not recommended as it can take very long to execute and is very memory intensive')
48
+ return @_cached_family_tree if @_cached_family_tree
49
+
50
+ if stack.include?(name) # avoid endless recursive loop
51
+ # logger.debug "Element#family_tree aborting endless recursive loop at element with name: #{name} and element stack: #{stack.inspect}"
52
+ return nil
53
+ end
54
+
55
+ return "type:#{type_name}" if elements.length == 0
56
+
57
+ result = elements.inject({}) do |tree, element|
58
+ tree.merge element.name => element.family_tree(stack + [name])
59
+ end
60
+
61
+ @_cached_family_tree = result if stack == [] # only cache if this was the first one called (otherwise there will be way too many caches)
62
+ return result
63
+ end
64
+ end # class Element
65
+ end # module XsdReader
@@ -0,0 +1,5 @@
1
+ module XsdReader
2
+ class Extension
3
+ include Shared
4
+ end # class Schema
5
+ end
@@ -0,0 +1,5 @@
1
+ module XsdReader
2
+ class Schema
3
+ include Shared
4
+ end # class Schema
5
+ end
@@ -0,0 +1,8 @@
1
+ module XsdReader
2
+
3
+ class Sequence
4
+ include Shared
5
+
6
+ end # class Sequence
7
+
8
+ end # module XsdReader
@@ -0,0 +1,214 @@
1
+ require 'logger'
2
+
3
+ module XsdReader
4
+
5
+ module Shared
6
+
7
+ attr_reader :options
8
+
9
+ def initialize(_opts = {})
10
+ @options = _opts || {}
11
+ raise "#{self.class.to_s}.new expects a hash parameter" if !@options.is_a?(Hash)
12
+ end
13
+
14
+ def logger
15
+ @logger ||= options[:logger] || Logger.new(STDOUT)
16
+ end
17
+
18
+ def node
19
+ options[:node]
20
+ end
21
+
22
+ def nodes
23
+ node.search("./*")
24
+ end
25
+
26
+ def [](name)
27
+ # got an array of name? recursive search through generations
28
+ if name.is_a?(Array)
29
+ el = self
30
+ name.each{|child_name| el = el.nil? ? nil : el.elements.find{|child| child.name == child_name}}
31
+ return el
32
+ end
33
+
34
+ # starts with an @-symbol? Then we're looking for an attribute
35
+
36
+ if name =~ /^\@/
37
+ attr_name = name.gsub(/^\@/, '')
38
+ return attributes.find{|attr| attr.name == attr_name}
39
+ end
40
+
41
+ elements.find{|el| el.name == name}
42
+ end
43
+
44
+ #
45
+ # attribute properties
46
+ #
47
+ def name
48
+ node.attributes['name'].value
49
+ end
50
+
51
+ def type
52
+ node.attributes['type'] ? node.attributes['type'].value : nil
53
+ end
54
+
55
+ def type_name
56
+ type ? type.split(':').last : nil
57
+ end
58
+
59
+ def type_namespace
60
+ type ? type.split(':').first : nil
61
+ end
62
+
63
+ #
64
+ # Node to class mapping
65
+ #
66
+ def class_for(n)
67
+ class_mapping = {
68
+ 'xs:schema' => Schema,
69
+ 'xs:element' => Element,
70
+ 'xs:attribute' => Attribute,
71
+ 'xs:choice' => Choice,
72
+ 'xs:complexType' => ComplexType,
73
+ 'xs:sequence' => Sequence,
74
+ 'xs:simpleContent' => SimpleContent,
75
+ 'xs:extension' => Extension
76
+ }
77
+
78
+ return class_mapping[n.is_a?(Nokogiri::XML::Node) ? n.name : n]
79
+ end
80
+
81
+ def node_to_object(node)
82
+ fullname = [node.namespace ? node.namespace.prefix : nil, node.name].reject{|str| str.nil? || str == ''}.join(':')
83
+ klass = class_for(fullname)
84
+ # logger.debug "node_to_object, klass: #{klass.to_s}, fullname: #{fullname}"
85
+ klass.nil? ? nil : klass.new(options.merge(:node => node))
86
+ end
87
+
88
+
89
+ #
90
+ # Child objects
91
+ #
92
+
93
+ def map_children(xml_name, klass = nil)
94
+ klass ||= class_for(xml_name)
95
+ node.search("./#{xml_name}").map{|node| klass.new(options.merge(:node => node))}
96
+ end
97
+
98
+ def direct_elements
99
+ map_children("xs:element")
100
+ end
101
+
102
+ def elements
103
+ direct_elements
104
+ end
105
+
106
+ def unordered_elements
107
+ direct_elements + (complex_type ? complex_type.all_elements : []) + sequences.map(&:all_elements).flatten + choices.map(&:all_elements).flatten
108
+ end
109
+
110
+ def ordered_elements
111
+ # loop over each interpretable child xml node, and if we can convert a child node
112
+ # to an XsdReader object, let it give its compilation of all_elements
113
+ nodes.map{|node| node_to_object(node)}.compact.map do |obj|
114
+ obj.is_a?(Element) ? obj : obj.all_elements
115
+ end.flatten
116
+ end
117
+
118
+ def all_elements
119
+ ordered_elements + (linked_complex_type ? linked_complex_type.ordered_elements : [])
120
+ end
121
+
122
+ def child_elements?
123
+ elements.length > 0
124
+ end
125
+
126
+ def attributes
127
+ map_children('xs:attribute')
128
+ end
129
+
130
+ def sequences
131
+ map_children("xs:sequence",)
132
+ end
133
+
134
+ def choices
135
+ map_children("xs:choice")
136
+ end
137
+
138
+ def complex_types
139
+ map_children("xs:complexType")
140
+ end
141
+
142
+ def complex_type
143
+ complex_types.first
144
+ end
145
+
146
+ def linked_complex_type
147
+ complex_type_by_name(type) || complex_type_by_name(type_name)
148
+ end
149
+
150
+ def simple_contents
151
+ map_children("xs:simpleContent")
152
+ end
153
+
154
+ def simple_content
155
+ simple_contents.first
156
+ end
157
+
158
+ def extensions
159
+ map_children("xs:extension")
160
+ end
161
+
162
+ def extension
163
+ extensions.first
164
+ end
165
+
166
+ #
167
+ # Related objects
168
+ #
169
+
170
+ def parent
171
+ if node && node.respond_to?(:parent) && node.parent
172
+ return node_to_object(node.parent)
173
+ end
174
+
175
+ nil
176
+ end
177
+
178
+ # def ancestors
179
+ # result = [parent]
180
+
181
+ # while result.first != nil
182
+ # result.unshift (result.first.respond_to?(:parent) ? result.first.parent : nil)
183
+ # end
184
+
185
+ # result.compact
186
+ # end
187
+
188
+ def schema
189
+ p = node.parent
190
+
191
+ while p.name != 'schema' && !p.nil?
192
+ p = p.parent
193
+ end
194
+ p.nil? ? nil : node_to_object(p)
195
+ end
196
+
197
+ def complex_type_by_name(name)
198
+ ct = node.search("//xs:complexType[@name=\"#{name}\"]").first
199
+ ct.nil? ? nil : ComplexType.new(options.merge(:node => ct))
200
+ end
201
+
202
+ def elements_by_type(type_name)
203
+ els = schema.node.search("//xs:element[@type=\"#{type_name}\"]")
204
+
205
+ schema.node.search("//xs:element[@type=\"#{type_name}\"]")
206
+
207
+ while els.length == 0
208
+
209
+ end
210
+ end
211
+
212
+ end
213
+
214
+ end # module XsdReader
@@ -0,0 +1,9 @@
1
+ module XsdReader
2
+ class SimpleContent
3
+ include Shared
4
+
5
+ def attributes
6
+ super + (extension ? extension.attributes : [])
7
+ end
8
+ end # class Schema
9
+ end
@@ -0,0 +1,38 @@
1
+ require 'nokogiri'
2
+ # require 'open-uri'
3
+
4
+ module XsdReader
5
+
6
+ class XML
7
+ include Shared
8
+
9
+ def xsd_from_uri
10
+ # @xsd_from_uri ||= options[:xsd_uri].nil ? nil : open(options[:xsd_uri])
11
+ end
12
+
13
+ def xsd_from_file
14
+ @xsd_from_file ||= options[:xsd_file].nil? ? nil : File.read(options[:xsd_file])
15
+ end
16
+
17
+ def xml
18
+ @xsd_xml ||= options[:xsd_xml] || options[:xsd_data] || options[:xsd_raw] || xsd_from_file || xsd_from_uri
19
+ end
20
+
21
+ def doc
22
+ @doc ||= Nokogiri.XML(xml)
23
+ end
24
+
25
+ def schema_node
26
+ doc.root.name == 'schema' ? doc.root : nil
27
+ end
28
+
29
+ def schema
30
+ node_to_object(schema_node)
31
+ end
32
+
33
+ def elements
34
+ schema.elements
35
+ end
36
+ end # class XML
37
+
38
+ end # module XsdReader
data/lib/xsd_reader.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'xsd_reader/shared'
2
+ require 'xsd_reader/xml'
3
+ require 'xsd_reader/schema'
4
+ require 'xsd_reader/element'
5
+ require 'xsd_reader/attribute'
6
+ require 'xsd_reader/sequence'
7
+ require 'xsd_reader/choice'
8
+ require 'xsd_reader/complex_type'
9
+ require 'xsd_reader/simple_content'
10
+ require 'xsd_reader/extension'
11
+
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/spec_helper'
2
+
3
+ describe XsdReader do
4
+ before :all do
5
+ @reader ||= XsdReader::XML.new(:xsd_file => File.expand_path(File.join(File.dirname(__FILE__), 'examples', 'ddex-ern-v36.xsd')))
6
+ end
7
+
8
+ describe XsdReader::Attribute do
9
+ before :each do
10
+ @attribute = @reader['NewReleaseMessage']['@MessageSchemaVersionId']
11
+ end
12
+
13
+ it "gives a name" do
14
+ expect(@attribute.name).to eq 'MessageSchemaVersionId'
15
+ end
16
+
17
+ it "gives a type information" do
18
+ expect(@attribute.type).to eq 'xs:string'
19
+ expect(@attribute.type_name).to eq 'string'
20
+ expect(@attribute.type_namespace).to eq 'xs'
21
+ end
22
+
23
+ it "gives a boolean required indication" do
24
+ expect(@attribute.required?).to eq true
25
+ end
26
+ end
27
+
28
+ describe "[] operator" do
29
+ it "gives attribute objects through the square brackets ([]) operator" do
30
+ attribute = @reader['NewReleaseMessage']['MessageHeader']['@LanguageAndScriptCode']
31
+ expect(attribute.type).to eq 'xs:string'
32
+ expect(attribute.required?).to eq false
33
+ end
34
+ end
35
+ end # describe XsdReader