xsd-populator 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 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
+