xsd-populator 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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: []