xsd-populator 0.1.0 → 0.2.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 +4 -4
- data/Gemfile.lock +11 -11
- data/lib/xsd_populator.rb +66 -9
- data/spec/xsd_populator_spec.rb +119 -0
- data/xsd-populator.gemspec +6 -9
- metadata +7 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2eda4c1222169e40bd22cd1ec39960cefd5a38b
|
4
|
+
data.tar.gz: 41bc1605eaa36e836aacfbae731f34752c1ece56
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 269167d81605c0f996e2bba757b59dcf71f77e65b08c1c27f162b6d061b7945302e1052368fb71be19b20ae906b2b7507c964c7af0f3e6448f34982bc5b4f167
|
7
|
+
data.tar.gz: 7c13c2ddfb4ec11213427ab6bf7d91a91c5ac725062f13ab4c27f20f19ec99af2a4581f7c3059058808bb59f9e71ad6eb8f8d9ea5528d118e3fc54e7ec7e0d48
|
data/Gemfile.lock
CHANGED
@@ -1,22 +1,22 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
xsd-populator (0.0
|
5
|
-
builder
|
6
|
-
data-provider (
|
7
|
-
xsd-reader (
|
4
|
+
xsd-populator (0.2.0)
|
5
|
+
builder (~> 3.2)
|
6
|
+
data-provider (~> 0.2)
|
7
|
+
xsd-reader (~> 0.2)
|
8
8
|
|
9
9
|
PATH
|
10
10
|
remote: /srv/data-provider
|
11
11
|
specs:
|
12
|
-
data-provider (0.0
|
12
|
+
data-provider (0.2.0)
|
13
13
|
|
14
14
|
PATH
|
15
15
|
remote: /srv/xsd-reader
|
16
16
|
specs:
|
17
|
-
xsd-reader (0.
|
18
|
-
nokogiri
|
19
|
-
rest-client
|
17
|
+
xsd-reader (0.2.0)
|
18
|
+
nokogiri (~> 1.6)
|
19
|
+
rest-client (~> 1.8)
|
20
20
|
|
21
21
|
GEM
|
22
22
|
remote: https://rubygems.org/
|
@@ -60,10 +60,10 @@ PLATFORMS
|
|
60
60
|
ruby
|
61
61
|
|
62
62
|
DEPENDENCIES
|
63
|
-
byebug
|
63
|
+
byebug (~> 5.0)
|
64
64
|
data-provider!
|
65
|
-
nokogiri
|
66
|
-
rspec
|
65
|
+
nokogiri (~> 1.6)
|
66
|
+
rspec (~> 3.3)
|
67
67
|
xsd-populator!
|
68
68
|
xsd-reader!
|
69
69
|
|
data/lib/xsd_populator.rb
CHANGED
@@ -8,6 +8,27 @@ class XsdPopulator
|
|
8
8
|
class ElementNotFoundException < Exception
|
9
9
|
end
|
10
10
|
|
11
|
+
class Informer
|
12
|
+
attr_reader :options
|
13
|
+
|
14
|
+
def initialize(_opts = {})
|
15
|
+
@options = _opts || {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def skip?
|
19
|
+
options[:skip] == true
|
20
|
+
end
|
21
|
+
|
22
|
+
def attributes
|
23
|
+
options[:attributes] || {}
|
24
|
+
end
|
25
|
+
|
26
|
+
def namespace
|
27
|
+
options[:namespace]
|
28
|
+
end
|
29
|
+
end # class Informer
|
30
|
+
|
31
|
+
|
11
32
|
attr_reader :options
|
12
33
|
|
13
34
|
def initialize(_opts = {})
|
@@ -19,7 +40,7 @@ class XsdPopulator
|
|
19
40
|
# remove some cached values
|
20
41
|
@logger = nil if _opts[:logger]
|
21
42
|
@xsd_reader = nil if _opts[:xsd_reader] || _opts[:reader]
|
22
|
-
uncache if _opts[:strategy]
|
43
|
+
uncache if (_opts.keys & [:strategy,:element,:xsd_file,:xsd,:xsd_reader,:reader,:provider,:data_provider]).length > 0
|
23
44
|
end
|
24
45
|
|
25
46
|
def uncache
|
@@ -64,6 +85,10 @@ class XsdPopulator
|
|
64
85
|
File.write(path, populated_xml)
|
65
86
|
end
|
66
87
|
|
88
|
+
def max_recursion
|
89
|
+
options[:max_recursion] || 3
|
90
|
+
end
|
91
|
+
|
67
92
|
private
|
68
93
|
|
69
94
|
def populate_xml(element_specifier = nil)
|
@@ -82,11 +107,15 @@ class XsdPopulator
|
|
82
107
|
return xml.target!
|
83
108
|
end
|
84
109
|
|
110
|
+
def stack_recursion_count(stack = [])
|
111
|
+
stack.select{|el| el == stack.last}.length - 1
|
112
|
+
end
|
113
|
+
|
85
114
|
def build_element(xml, element, provider = self.provider, stack = [])
|
86
115
|
# TODO; more sophisticated recursion detection;
|
87
116
|
# multiple elements of the same name should be able
|
88
117
|
# to occur insid the stack
|
89
|
-
if stack
|
118
|
+
if stack_recursion_count(stack + [element.name]) > max_recursion
|
90
119
|
logger.warn("XsdPopulator#build_element aborting because of potential endless recursion\n - Current element: #{element.name}\n - stack: #{stack.inspect}")
|
91
120
|
return
|
92
121
|
end
|
@@ -97,7 +126,7 @@ class XsdPopulator
|
|
97
126
|
# get node content data from provider
|
98
127
|
content_data = provider.nil? ? nil : provider.try_take(stack + [element.name])
|
99
128
|
# get attributes content from the provider
|
100
|
-
attributes_data_hash =
|
129
|
+
attributes_data_hash = nil
|
101
130
|
|
102
131
|
if explain_xml? && element.multiple_allowed?
|
103
132
|
xml.comment!("Multiple instances of #{element.name} allowed here")
|
@@ -114,10 +143,17 @@ class XsdPopulator
|
|
114
143
|
# NOTE: this doesn't array-values for single elements, which we don't support (would be turned into a string anway)
|
115
144
|
|
116
145
|
content_data.each_with_index do |node_content, idx|
|
117
|
-
# let's see if the provided data is good for building this node,
|
146
|
+
# let's see if the provided data is good for building this node, according to the current strategy
|
118
147
|
next if !build?(element, provider, stack, :content => node_content)
|
119
148
|
|
120
|
-
|
149
|
+
# value for current element is a content provider?
|
150
|
+
if node_content.respond_to?(:try_take)
|
151
|
+
attributes_hash = attributes_for(element, node_content.respond_to?(:take) ? node_content : provider, stack)
|
152
|
+
else
|
153
|
+
attributes_data_hash ||= attributes_data_hash_for(element, provider, stack)
|
154
|
+
attributes_hash = attributes_hash_for_index(attributes_data_hash, idx)
|
155
|
+
attributes_hash = node_content.attributes.merge(attributes_hash) if node_content.is_a?(Informer) && node_content.attributes.length > 0
|
156
|
+
end
|
121
157
|
|
122
158
|
# simple node; name, value, attributes
|
123
159
|
if !element.child_elements?
|
@@ -126,16 +162,23 @@ class XsdPopulator
|
|
126
162
|
end
|
127
163
|
|
128
164
|
# complex node
|
165
|
+
child_provider = provider
|
166
|
+
|
129
167
|
if node_content.respond_to?(:try_take)
|
130
168
|
child_provider = node_content
|
131
|
-
|
132
|
-
logger.warn "Got non-nil and non-
|
169
|
+
elsif !node_content.is_a?(Informer)
|
170
|
+
logger.warn "Got non-nil, non-provider and non-infomer value for element with child elements (value: #{node_content}, element: #{element.name}, stack: #{stack.inspect})" if node_content
|
133
171
|
# strategy dictates to continue; just use the current element's provider for its children
|
134
|
-
child_provider = provider
|
135
172
|
end
|
136
173
|
|
137
174
|
# create complex node
|
138
|
-
|
175
|
+
node_name = element.name
|
176
|
+
|
177
|
+
if node_content.is_a?(Informer) && node_content.namespace.to_s.length > 0
|
178
|
+
node_name = "#{node_content.namespace}:#{node_name}"
|
179
|
+
end
|
180
|
+
|
181
|
+
xml.tag!(node_name, attributes_hash) do
|
139
182
|
# loop over all child node definitions
|
140
183
|
element.elements.each do |child|
|
141
184
|
# this method call itself recursively for every child node definition of the current element
|
@@ -182,6 +225,17 @@ class XsdPopulator
|
|
182
225
|
end
|
183
226
|
end
|
184
227
|
|
228
|
+
def attributes_for(element, provider, stack)
|
229
|
+
element.attributes.inject({}) do |result, attribute|
|
230
|
+
attribute_data = provider.nil? ? nil : provider.try_take(stack + [element.name, "@#{attribute.name}"])
|
231
|
+
# attribute_data ||= attribute.type if provider.nil? # assume demo xml
|
232
|
+
if add_attribute?(attribute, provider, stack, :content => attribute_data)
|
233
|
+
result.merge(attribute.name => attribute_data)
|
234
|
+
else
|
235
|
+
result
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
185
239
|
|
186
240
|
#
|
187
241
|
# Root element
|
@@ -242,6 +296,9 @@ class XsdPopulator
|
|
242
296
|
def build?(element, provider, stack, opts = {})
|
243
297
|
content = opts[:content] || provider.try_take([stack, element.name].flatten.compact)
|
244
298
|
|
299
|
+
# we got an Informer object that tells us explicitly to skip this node? Yes sir.
|
300
|
+
return false if content.is_a?(Informer) && content.skip?
|
301
|
+
|
245
302
|
# For comlex nodes we need either;
|
246
303
|
# - a data provider or
|
247
304
|
# - explicit confirmation to build without providers or
|
data/spec/xsd_populator_spec.rb
CHANGED
@@ -208,4 +208,123 @@ describe "XsdPopulator for partial layouts" do
|
|
208
208
|
expect(doc.at("/NewReleaseMessage/MessageHeader/SentOnBehalfOf").attributes['LanguageAndScriptCode'].value).to eq 'UK'
|
209
209
|
end
|
210
210
|
end
|
211
|
+
|
212
|
+
describe 'flexible recursion protection' do
|
213
|
+
it 'allows a couple of repetitions by default' do
|
214
|
+
xml = populator.populate_element(['NewReleaseMessage', 'ResourceList', 'SoundRecording', 'SoundRecordingDetailsByTerritory', 'TechnicalSoundRecordingDetails', 'File'])
|
215
|
+
doc = Nokogiri.XML(xml)
|
216
|
+
expect(doc.at('/File/HashSum/HashSum')).to_not eq nil
|
217
|
+
expect(doc.at('/File/HashSum/HashSum').text).to eq 'xs:string'
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
describe 'provider data for attributes' do
|
222
|
+
class FileProvider
|
223
|
+
include DataProvider::Base
|
224
|
+
|
225
|
+
# returns two datap provider, each with custom data for the TitleType attribute provider
|
226
|
+
provider ['NewReleaseMessage', 'ResourceList', 'SoundRecording', 'SoundRecordingDetailsByTerritory', 'Title'] do
|
227
|
+
[
|
228
|
+
add_data(:title_type => 'typeA'),
|
229
|
+
add_data(:title_type => 'typeB')
|
230
|
+
]
|
231
|
+
end
|
232
|
+
|
233
|
+
# the data provided by the above provider should be avialable in the provider below
|
234
|
+
provider ['NewReleaseMessage', 'ResourceList', 'SoundRecording', 'SoundRecordingDetailsByTerritory', 'Title', '@TitleType'] do
|
235
|
+
get_data(:title_type)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
it "uses provided data for an element's attribute providers" do
|
240
|
+
populator
|
241
|
+
populator.configure(:provider => FileProvider.new)
|
242
|
+
xml = populator.populate_element(['NewReleaseMessage', 'ResourceList', 'SoundRecording', 'SoundRecordingDetailsByTerritory'])
|
243
|
+
doc = Nokogiri.XML(xml)
|
244
|
+
expect(doc.search('/SoundRecordingDetailsByTerritory/Title').length).to eq 2
|
245
|
+
expect(doc.search('/SoundRecordingDetailsByTerritory/Title').map{|node| node.attributes['TitleType'].value}).to eq ['typeA', 'typeB']
|
246
|
+
end
|
247
|
+
end
|
211
248
|
end
|
249
|
+
|
250
|
+
describe XsdPopulator::Informer do
|
251
|
+
class InformProvider
|
252
|
+
include DataProvider::Base
|
253
|
+
|
254
|
+
provides(['NewReleaseMessage', 'MessageHeader'] => XsdPopulator::Informer.new(:skip => true))
|
255
|
+
provides(['NewReleaseMessage', 'MessageHeader', 'MessageId'] => 123)
|
256
|
+
end
|
257
|
+
|
258
|
+
let(:xsd_reader){
|
259
|
+
XsdReader::XML.new(:xsd_file => File.expand_path(File.join(File.dirname(__FILE__), 'examples', 'ddex-ern-v36.xsd')))
|
260
|
+
}
|
261
|
+
|
262
|
+
let(:logger){
|
263
|
+
logger = Logger.new(STDOUT)
|
264
|
+
logger.level = Logger::WARN
|
265
|
+
logger
|
266
|
+
}
|
267
|
+
|
268
|
+
let(:populator){
|
269
|
+
XsdPopulator.new({
|
270
|
+
:reader=> xsd_reader,
|
271
|
+
:logger => logger,
|
272
|
+
:provider => InformProvider.new
|
273
|
+
})
|
274
|
+
}
|
275
|
+
|
276
|
+
it "informs the populator to skip an element" do
|
277
|
+
expect(Nokogiri.XML(populator.populated_xml).at('/NewReleaseMessage/MessageHeader')).to eq nil
|
278
|
+
end
|
279
|
+
|
280
|
+
it "informs the populator to explicitly add a set of attributes to an element" do
|
281
|
+
provider_class = Class.new(Object) do
|
282
|
+
include DataProvider::Base
|
283
|
+
|
284
|
+
provider ['NewReleaseMessage', '@MessageSchemaVersionId']{ '2010/ern-main/32' }
|
285
|
+
|
286
|
+
provider ['NewReleaseMessage']{
|
287
|
+
XsdPopulator::Informer.new(:attributes => {
|
288
|
+
'xmlns:ern' => 'http://ddex.net/xml/2010/ern-main/32',
|
289
|
+
'xmlns:xsi' => 'http://www.w3.org/2001/XMLSchema-instance',
|
290
|
+
'xsi:schemaLocation' => 'http://ddex.net/xml/2010/ern-main/32 http://ddex.net/xml/2010/ern-main/32/ern-main.xsd'
|
291
|
+
})
|
292
|
+
}
|
293
|
+
|
294
|
+
provides(['NewReleaseMessage', 'MessageHeader', 'MessageId'] => 123)
|
295
|
+
end
|
296
|
+
|
297
|
+
populator = XsdPopulator.new({
|
298
|
+
:reader=> xsd_reader,
|
299
|
+
:logger => logger,
|
300
|
+
:provider => provider_class.new
|
301
|
+
})
|
302
|
+
|
303
|
+
el = Nokogiri.XML(populator.populated_xml).at('NewReleaseMessage')
|
304
|
+
expect(el.attributes['schemaLocation'].value).to eq 'http://ddex.net/xml/2010/ern-main/32 http://ddex.net/xml/2010/ern-main/32/ern-main.xsd'
|
305
|
+
expect(el.attributes['MessageSchemaVersionId'].value).to eq '2010/ern-main/32'
|
306
|
+
expect(el.attributes.keys.length).to eq 2
|
307
|
+
|
308
|
+
expect(el.namespace_definitions.map{|nsdef| [nsdef.prefix, nsdef.href]}.sort).to eq([
|
309
|
+
['ern', 'http://ddex.net/xml/2010/ern-main/32'],
|
310
|
+
['xsi', 'http://www.w3.org/2001/XMLSchema-instance']
|
311
|
+
])
|
312
|
+
end
|
313
|
+
|
314
|
+
it "informs the populator to prefix a node with a namespace" do
|
315
|
+
provider_class = Class.new(Object) do
|
316
|
+
include DataProvider::Base
|
317
|
+
|
318
|
+
provider ['NewReleaseMessage']{ XsdPopulator::Informer.new(:namespace => 'ern') }
|
319
|
+
provides(['NewReleaseMessage', 'MessageHeader', 'MessageId'] => 123)
|
320
|
+
end
|
321
|
+
|
322
|
+
populator = XsdPopulator.new({
|
323
|
+
:reader=> xsd_reader,
|
324
|
+
:logger => logger,
|
325
|
+
:provider => provider_class.new
|
326
|
+
})
|
327
|
+
|
328
|
+
expect(Nokogiri.XML(populator.populated_xml).root.name).to eq 'ern:NewReleaseMessage'
|
329
|
+
end
|
330
|
+
end
|
data/xsd-populator.gemspec
CHANGED
@@ -1,24 +1,21 @@
|
|
1
|
-
GEM_NAME="xsd-populator"
|
2
|
-
PKG_VERSION='0.1.0'
|
3
|
-
|
4
1
|
Gem::Specification.new do |s|
|
5
|
-
s.name =
|
6
|
-
s.version =
|
2
|
+
s.name = "xsd-populator"
|
3
|
+
s.version = '0.2.0'
|
7
4
|
s.files = `git ls-files`.split($/)
|
8
5
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
9
6
|
|
10
7
|
s.add_dependency 'builder', '~> 3.2'
|
11
|
-
s.add_dependency 'xsd-reader', '~> 0.
|
12
|
-
s.add_dependency 'data-provider', '~> 0.
|
8
|
+
s.add_dependency 'xsd-reader', '~> 0.2'
|
9
|
+
s.add_dependency 'data-provider', '~> 0.2'
|
13
10
|
s.add_development_dependency 'nokogiri', '~> 1.6'
|
14
11
|
s.add_development_dependency 'rspec', '~> 3.3'
|
15
12
|
s.add_development_dependency 'byebug', '~> 5.0'
|
16
13
|
|
17
14
|
s.author = "Mark van de Korput"
|
18
15
|
s.email = "dr.theman@gmail.com"
|
19
|
-
s.date = '2015-
|
16
|
+
s.date = '2015-08-27'
|
20
17
|
s.summary = %q{A Ruby gem to build XML data from XSD schemas}
|
21
|
-
s.description =
|
18
|
+
s.description = s.summary
|
22
19
|
s.homepage = %q{https://github.com/markkorput/xsd-populator}
|
23
20
|
s.license = "MIT"
|
24
21
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xsd-populator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark van de Korput
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-08-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: builder
|
@@ -30,28 +30,28 @@ dependencies:
|
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '0.
|
33
|
+
version: '0.2'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '0.
|
40
|
+
version: '0.2'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: data-provider
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0.
|
47
|
+
version: '0.2'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0.
|
54
|
+
version: '0.2'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: nokogiri
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -94,8 +94,7 @@ dependencies:
|
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
96
|
version: '5.0'
|
97
|
-
description: A
|
98
|
-
providers)
|
97
|
+
description: A Ruby gem to build XML data from XSD schemas
|
99
98
|
email: dr.theman@gmail.com
|
100
99
|
executables: []
|
101
100
|
extensions: []
|