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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a1fbb89bcd1d5fe7c9625230958c20b87d5b342c
4
- data.tar.gz: 8a73b16dcb2de6df15f5f39a7c31e7a748f01343
3
+ metadata.gz: f2eda4c1222169e40bd22cd1ec39960cefd5a38b
4
+ data.tar.gz: 41bc1605eaa36e836aacfbae731f34752c1ece56
5
5
  SHA512:
6
- metadata.gz: ddf9a33da6ed9f56877f073f620cfa40d67a619e89b84070f77dfb25074c36af12b040842bbd997cdc09668c4cdeb6245a7b3931c29f137bdbfd2110581aa8e3
7
- data.tar.gz: ffd6b497de51f6cda4af3e8f2d6597961cc686db808ce559ba26617fa3dd7599e68a95492ab30c80ce81751290ca9deb6c2a18fd2f35e1f6b62529c2a9a1962e
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.1)
5
- builder
6
- data-provider (>= 0.0.1)
7
- xsd-reader (>= 0.0.1)
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.1)
12
+ data-provider (0.2.0)
13
13
 
14
14
  PATH
15
15
  remote: /srv/xsd-reader
16
16
  specs:
17
- xsd-reader (0.1.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.include?(element.name)
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 = attributes_data_hash_for(element, provider, stack)
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, accoridng to the current strategy
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
- attributes_hash = attributes_hash_for_index(attributes_data_hash, idx)
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
- 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
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
- xml.tag!(element.name, attributes_hash) do
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
@@ -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
@@ -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 = GEM_NAME
6
- s.version = PKG_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.1'
12
- s.add_dependency 'data-provider', '~> 0.1'
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-07-14'
16
+ s.date = '2015-08-27'
20
17
  s.summary = %q{A Ruby gem to build XML data from XSD schemas}
21
- s.description = %q{A library of Ruby classes for generating XML data from XSD schemas (Data providers)}
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.1.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-07-14 00:00:00.000000000 Z
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.1'
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.1'
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.1'
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.1'
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 library of Ruby classes for generating XML data from XSD schemas (Data
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: []