xsd-reader 0.0.1

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 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