xsd-populator 0.1.0

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: a1fbb89bcd1d5fe7c9625230958c20b87d5b342c
4
+ data.tar.gz: 8a73b16dcb2de6df15f5f39a7c31e7a748f01343
5
+ SHA512:
6
+ metadata.gz: ddf9a33da6ed9f56877f073f620cfa40d67a619e89b84070f77dfb25074c36af12b040842bbd997cdc09668c4cdeb6245a7b3931c29f137bdbfd2110581aa8e3
7
+ data.tar.gz: ffd6b497de51f6cda4af3e8f2d6597961cc686db808ce559ba26617fa3dd7599e68a95492ab30c80ce81751290ca9deb6c2a18fd2f35e1f6b62529c2a9a1962e
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,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'xsd-reader', :path => '/srv/xsd-reader'
6
+ gem 'data-provider', :path => '/srv/data-provider'
data/Gemfile.lock ADDED
@@ -0,0 +1,71 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ xsd-populator (0.0.1)
5
+ builder
6
+ data-provider (>= 0.0.1)
7
+ xsd-reader (>= 0.0.1)
8
+
9
+ PATH
10
+ remote: /srv/data-provider
11
+ specs:
12
+ data-provider (0.0.1)
13
+
14
+ PATH
15
+ remote: /srv/xsd-reader
16
+ specs:
17
+ xsd-reader (0.1.0)
18
+ nokogiri
19
+ rest-client
20
+
21
+ GEM
22
+ remote: https://rubygems.org/
23
+ specs:
24
+ builder (3.2.2)
25
+ byebug (5.0.0)
26
+ columnize (= 0.9.0)
27
+ columnize (0.9.0)
28
+ diff-lcs (1.2.5)
29
+ domain_name (0.5.24)
30
+ unf (>= 0.0.5, < 1.0.0)
31
+ http-cookie (1.0.2)
32
+ domain_name (~> 0.5)
33
+ mime-types (2.6.1)
34
+ mini_portile (0.6.2)
35
+ netrc (0.10.3)
36
+ nokogiri (1.6.6.2)
37
+ mini_portile (~> 0.6.0)
38
+ rest-client (1.8.0)
39
+ http-cookie (>= 1.0.2, < 2.0)
40
+ mime-types (>= 1.16, < 3.0)
41
+ netrc (~> 0.7)
42
+ rspec (3.3.0)
43
+ rspec-core (~> 3.3.0)
44
+ rspec-expectations (~> 3.3.0)
45
+ rspec-mocks (~> 3.3.0)
46
+ rspec-core (3.3.1)
47
+ rspec-support (~> 3.3.0)
48
+ rspec-expectations (3.3.0)
49
+ diff-lcs (>= 1.2.0, < 2.0)
50
+ rspec-support (~> 3.3.0)
51
+ rspec-mocks (3.3.1)
52
+ diff-lcs (>= 1.2.0, < 2.0)
53
+ rspec-support (~> 3.3.0)
54
+ rspec-support (3.3.0)
55
+ unf (0.1.4)
56
+ unf_ext
57
+ unf_ext (0.0.7.1)
58
+
59
+ PLATFORMS
60
+ ruby
61
+
62
+ DEPENDENCIES
63
+ byebug
64
+ data-provider!
65
+ nokogiri
66
+ rspec
67
+ xsd-populator!
68
+ xsd-reader!
69
+
70
+ BUNDLED WITH
71
+ 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,2 @@
1
+ # xsd-populator
2
+ A Ruby gem to produce XML data from XSD schemas
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,84 @@
1
+ require 'data_provider'
2
+
3
+ module DdexProvider
4
+
5
+ module MessageSender
6
+ include DataProvider::Base
7
+
8
+ provider '@LanguageAndScriptCode' do 'EN' end
9
+ provider 'PartyId' do 404 end
10
+
11
+ end # module MessageSender
12
+
13
+ module MessageRecipient
14
+ include DataProvider::Base
15
+
16
+ # provides multiple party ids; this will result in 3 <PartyId> nodes
17
+ # within the <MessageRecipient> node
18
+ provider 'PartyId' do
19
+ [12, 34, 56]
20
+ end
21
+
22
+ # Because the 'PartyId' provides 3 values, 3 nodes will be created
23
+ # because the ['PartyId', '@IsDPID'] provider provides only one value,
24
+ # all 3 PartyId nodes will get the same value for their IsDPID attribute
25
+ provider ['PartyId', '@IsDPID'] do
26
+ 'TRUTH'
27
+ end
28
+
29
+ end # module MessageRecipient
30
+
31
+ module SentOnBehalfOf
32
+ include DataProvider::Base
33
+
34
+ # provides multiple party ids; this will result in 3 <PartyId> nodes
35
+ # within the <SentOnBehalfOf> node
36
+ provider 'PartyId' do
37
+ [9,8,7]
38
+ end
39
+
40
+ # Because the 'PartyId' provides 3 values, 3 nodes will be created
41
+ # because the ['PartyId', '@IsDPID'] provider provides only two values for the third,
42
+ # nil value will be assumed and a warning will be logged
43
+ provider ['PartyId', '@IsDPID'] do
44
+ ['true', 'false']
45
+ end
46
+
47
+ end # module SentOnBehalfOf
48
+
49
+
50
+ module MessageHeader
51
+ include DataProvider::Base
52
+
53
+ provider '@LanguageAndScriptCode' do
54
+ 'NL'
55
+ end
56
+
57
+ provider 'MessageThreadId' do
58
+ 123.to_s
59
+ end
60
+
61
+ add_scoped MessageSender, :scope => 'MessageSender'
62
+ add_scoped SentOnBehalfOf, :scope => 'SentOnBehalfOf'
63
+ add_scoped MessageRecipient, :scope => 'MessageRecipient'
64
+
65
+ provider ['MessageAuditTrail', 'MessageAuditTrailEvent'] do
66
+ # gives three new data providers with each their own :name
67
+ ['John', 'Billy', 'Bob'].map do |name|
68
+ self.give(:name => name)
69
+ end
70
+ end
71
+
72
+ provider ['MessageAuditTrail', 'MessageAuditTrailEvent', 'MessagingPartyDescriptor', 'PartyName', 'FullName'], :requires => [:name] do
73
+ given(:name)
74
+ end
75
+
76
+ end # module MessageHeader
77
+
78
+
79
+ class MessageHeaderProvider
80
+ include DataProvider::Base
81
+ add_scoped MessageHeader, :scope => 'MessageHeader'
82
+ end # class Provider
83
+
84
+ end # module DdexProvider
@@ -0,0 +1,28 @@
1
+ require 'data_provider'
2
+
3
+ class XsdExplanationProvider
4
+ include DataProvider::Base
5
+
6
+ provider_missing do
7
+ raise 'XsdExplanationProvider needs an xsd reader' if (reader = get_data(:xsd_reader)).nil?
8
+
9
+ logger.debug "XsdExplanationProvider got provider id: #{missing_provider}"
10
+ item = reader[missing_provider]
11
+
12
+ if item.is_a?(XsdReader::Attribute)
13
+ item.type
14
+ elsif item.is_a?(XsdReader::Element) && item.child_elements?
15
+ # return self, the data provider, so the populator can continue with the child elements
16
+ self
17
+ elsif item
18
+ if item.complex_type && item.complex_type.simple_content && item.complex_type.simple_content.extension
19
+ item.complex_type.simple_content.extension.base
20
+ else
21
+ item.type
22
+ end
23
+ else
24
+ logger.warn "XsdExplanationProvider could not find XSD information for provider `#{missing_provider.inspect}` in XSD file `#{reader.options[:xsd_file]}`"
25
+ nil
26
+ end
27
+ end
28
+ end # class XsdExplanationProvider
@@ -0,0 +1,272 @@
1
+ require 'bundler/setup'
2
+ require 'builder'
3
+ require 'xsd_reader'
4
+ require 'xsd_explanation_provider'
5
+
6
+ class XsdPopulator
7
+
8
+ class ElementNotFoundException < Exception
9
+ end
10
+
11
+ attr_reader :options
12
+
13
+ def initialize(_opts = {})
14
+ configure _opts
15
+ end
16
+
17
+ def configure _opts = {}
18
+ @options = (@options || {}).merge(_opts.is_a?(Hash) ? _opts : {})
19
+ # remove some cached values
20
+ @logger = nil if _opts[:logger]
21
+ @xsd_reader = nil if _opts[:xsd_reader] || _opts[:reader]
22
+ uncache if _opts[:strategy]
23
+ end
24
+
25
+ def uncache
26
+ @populated_xml = nil
27
+ end
28
+
29
+ def logger
30
+ return @logger || options[:logger] if @logger || options[:logger]
31
+ @logger = Logger.new(STDOUT)
32
+ @logger.level = Logger::WARN
33
+ return @logger
34
+ end
35
+
36
+ def xsd_file
37
+ options[:xsd_file] || options[:xsd]
38
+ end
39
+
40
+ def xsd_reader
41
+ @xsd_reader ||= options[:xsd_reader] || options[:reader] || (xsd_file.nil? ? nil : XsdReader::XML.new(:xsd_file => xsd_file, :logger => logger))
42
+ end
43
+
44
+ alias :reader :xsd_reader
45
+
46
+ def provider
47
+ options[:provider] || options[:data_provider] || default_provider
48
+ end
49
+
50
+ def default_provider
51
+ @default_provider ||= XsdExplanationProvider.new(:data => {:xsd_reader => xsd_reader}, :logger => logger)
52
+ end
53
+
54
+ def populated_xml
55
+ @populated_xml ||= populate_xml
56
+ end
57
+
58
+ def populate_element(element_specifier = nil)
59
+ element_specifier.nil? ? populated_xml : populate_xml(element_specifier)
60
+ end
61
+
62
+ def write_file(path)
63
+ # logger.debug "XsdPopulator#write_file to: #{path}"
64
+ File.write(path, populated_xml)
65
+ end
66
+
67
+ private
68
+
69
+ def populate_xml(element_specifier = nil)
70
+ if (root_el = root_xsd_element(element_specifier)).nil?
71
+ logger.warn "Couldn't find element definition, aborting"
72
+ return nil
73
+ end
74
+
75
+ xml = Builder::XmlMarkup.new(:indent => 2)
76
+ xml.instruct!
77
+
78
+ stack = options[:relative_provider] == true ? [] : [element_specifier || options[:element]].flatten.compact
79
+ stack.pop
80
+ build_element(xml, root_el, self.provider, stack)
81
+
82
+ return xml.target!
83
+ end
84
+
85
+ def build_element(xml, element, provider = self.provider, stack = [])
86
+ # TODO; more sophisticated recursion detection;
87
+ # multiple elements of the same name should be able
88
+ # to occur insid the stack
89
+ if stack.include?(element.name)
90
+ logger.warn("XsdPopulator#build_element aborting because of potential endless recursion\n - Current element: #{element.name}\n - stack: #{stack.inspect}")
91
+ return
92
+ end
93
+
94
+ # let's log positive stuff as well
95
+ logger.debug("XsdPopulator#build_element element: #{element.name}, stack: #{stack.inspect}")
96
+
97
+ # get node content data from provider
98
+ content_data = provider.nil? ? nil : provider.try_take(stack + [element.name])
99
+ # get attributes content from the provider
100
+ attributes_data_hash = attributes_data_hash_for(element, provider, stack)
101
+
102
+ if explain_xml? && element.multiple_allowed?
103
+ xml.comment!("Multiple instances of #{element.name} allowed here")
104
+ end
105
+
106
+ # just log a warning if we got an array value for an element that is not allowed
107
+ # to occurs multiple times according to the XSD schema (but still allow data provider to generate the xml)
108
+ if content_data.is_a?(Array) && !element.multiple_allowed?
109
+ logger.warn("Got array value (provider id: #{(stack + [element.name]).inspect}) but element definition doesn't allow multiple instances")
110
+ end
111
+
112
+ # make sure it's an array
113
+ content_data = [content_data].flatten # if element.multiple_allowed? && content_data.is_a?(Array)
114
+ # NOTE: this doesn't array-values for single elements, which we don't support (would be turned into a string anway)
115
+
116
+ content_data.each_with_index do |node_content, idx|
117
+ # let's see if the provided data is good for building this node, accoridng to the current strategy
118
+ next if !build?(element, provider, stack, :content => node_content)
119
+
120
+ attributes_hash = attributes_hash_for_index(attributes_data_hash, idx)
121
+
122
+ # simple node; name, value, attributes
123
+ if !element.child_elements?
124
+ xml.tag!(element.name, node_content, attributes_hash)
125
+ next
126
+ end
127
+
128
+ # complex node
129
+ if node_content.respond_to?(:try_take)
130
+ child_provider = node_content
131
+ else
132
+ logger.warn "Got non-nil and non-provider value for element with child elements (value: #{node_content}, element: #{element.name}, stack: #{stack.inspect})" if node_content
133
+ # strategy dictates to continue; just use the current element's provider for its children
134
+ child_provider = provider
135
+ end
136
+
137
+ # create complex node
138
+ xml.tag!(element.name, attributes_hash) do
139
+ # loop over all child node definitions
140
+ element.elements.each do |child|
141
+ # this method call itself recursively for every child node definition of the current element
142
+ build_element(xml, child, child_provider, stack + [element.name])
143
+ end
144
+ end
145
+ end
146
+ end
147
+
148
+
149
+ #
150
+ # Attribute data
151
+ #
152
+
153
+ def attributes_data_hash_for(element, provider, stack)
154
+ element.attributes.inject({}) do |result, attribute|
155
+ attribute_data = provider.nil? ? nil : provider.try_take(stack + [element.name, "@#{attribute.name}"])
156
+ # attribute_data ||= attribute.type if provider.nil? # assume demo xml
157
+ if add_attribute?(attribute, provider, stack, :content => attribute_data)
158
+ result.merge(attribute.name => attribute_data)
159
+ else
160
+ result
161
+ end
162
+ end
163
+ end
164
+
165
+ def attribute_value_for_index(values, idx)
166
+ # if the provided attribute content is an array, just use the appropriate element
167
+ # if the array does not have enough element, this will default to nil
168
+ # if values is not an array, just always use its singular value
169
+ values.is_a?(Array) ? values[idx] : values
170
+ end
171
+
172
+ def attributes_hash_for_index(attribute_data_hash, idx)
173
+ # each of the attribute values can be an array as well. If not,
174
+ # we'll just use it's singular value for all instances of this node
175
+ # if there
176
+ current_attrs = attribute_data_hash.to_a.inject({}) do |result, key_value|
177
+ key = key_value[0]
178
+ value = key_value[1]
179
+ logger.warn "XsdPopulator#attributes_hash_for_index - got an array with insufficient values for attribute `#{key}`. Attribute data hash: #{attribute_data_hash}, index: #{idx}" if value.is_a?(Array) && value.length <= idx
180
+ attr_value = attribute_value_for_index(value, idx)
181
+ result.merge(key => attr_value)
182
+ end
183
+ end
184
+
185
+
186
+ #
187
+ # Root element
188
+ #
189
+ def specified_xsd_element(specifier = nil)
190
+ # nothing specified
191
+ return nil if (specifier || options[:element]).nil?
192
+ # find specified element
193
+ el = xsd_reader[[(specifier || options[:element])].flatten.compact]
194
+ raise ElementNotFoundException.new(:message => "Could not find specified root element (#{(specifier || options[:element]).inspect}).") if el.nil?
195
+ # log warning if specified element not found
196
+ logger.warn "XsdPopulator#populate_xml - Specified element (#{options[:element].inspect}) not found, reverting to default" if el.nil?
197
+ # return result
198
+ return el
199
+ end
200
+
201
+ def root_xsd_element(specifier = nil)
202
+ el = specified_xsd_element(specifier)
203
+
204
+ # no element specified? (or found)
205
+ if el
206
+ # log inform notice that we're using the explicitly specified element
207
+ logger.info "XsdPopulator#populate_xml - Starting at specified element: #{el.name}"
208
+ else
209
+ # default: just take the first defined element
210
+ el ||= xsd_reader.elements.first
211
+
212
+ # if there are multiple root-level elements in the xsd, let the user know, we're only processing the fist one
213
+ if el && xsd_reader.elements.length > 1
214
+ logger.info "XsdPopulator#populate_xml - Multiple root-level element definitions found in XSD schema, only processing the first one (#{el.name})"
215
+ end
216
+ end
217
+
218
+ return el
219
+ end
220
+
221
+ #
222
+ # Strategy
223
+ #
224
+ public
225
+
226
+ def strategy
227
+ options[:strategy] || (explain_xml? ? :complete : :smart)
228
+ end
229
+
230
+ def explain_xml?
231
+ provider == default_provider
232
+ end
233
+
234
+ def build_node_without_provider?
235
+ strategy == :complete
236
+ end
237
+
238
+ def add_simple_nodes_without_data?
239
+ strategy == :nil_to_empty || strategy == :complete
240
+ end
241
+
242
+ def build?(element, provider, stack, opts = {})
243
+ content = opts[:content] || provider.try_take([stack, element.name].flatten.compact)
244
+
245
+ # For comlex nodes we need either;
246
+ # - a data provider or
247
+ # - explicit confirmation to build without providers or
248
+ # - providers available for offspring elements
249
+ if element.child_elements?
250
+ return content.respond_to?(:try_take) || build_node_without_provider? || provider.has_providers_with_scope?(stack + [element.name])
251
+ end
252
+
253
+ # we got a non-nil value for a simple node? Go ahead
254
+ return true if content || add_simple_nodes_without_data?
255
+
256
+ return false
257
+
258
+ # !build_node_without_provider? ||
259
+ # return true if provider
260
+ end
261
+
262
+ def add_empty_attributes?
263
+ strategy == :nil_to_empty || strategy == :complete
264
+ end
265
+
266
+ def add_attribute?(attribute, provider, stack = [], opts = {})
267
+ return true if attribute.required?
268
+ content = opts[:content] || provider.try_take(stack + ["@#{attribute.name}"])
269
+ return (!content.nil?) || add_empty_attributes?
270
+ end
271
+ end # class XsdPopulator
272
+