chemicals 0.1.2
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.
- data/.gitignore +17 -0
- data/CHANGELOG.md +11 -0
- data/Gemfile +4 -0
- data/Guardfile +5 -0
- data/LICENSE +22 -0
- data/README.md +36 -0
- data/Rakefile +10 -0
- data/chemicals.gemspec +24 -0
- data/lib/chemicals.rb +5 -0
- data/lib/chemicals/.DS_Store +0 -0
- data/lib/chemicals/parser.rb +97 -0
- data/lib/chemicals/renderer.rb +72 -0
- data/lib/chemicals/template.rb +94 -0
- data/lib/chemicals/version.rb +3 -0
- data/spec/chemicals/chemicals_spec.rb +201 -0
- data/spec/chemicals/chemicals_spec_helper.rb +14 -0
- data/spec/chemicals/examples.yml +517 -0
- data/spec/chemicals/parser_spec.rb +252 -0
- data/spec/chemicals/renderer_spec.rb +201 -0
- metadata +150 -0
data/.gitignore
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
## 0.1.2
|
2
|
+
|
3
|
+
* Template option `:symbolize_keys` that allows hash keys to string or symbols. Default to `true`.
|
4
|
+
|
5
|
+
## 0.1.1
|
6
|
+
|
7
|
+
* Performance improvement: Don't parse the source again if it is already a Nokogiri document (Parser).
|
8
|
+
|
9
|
+
## 0.1.0
|
10
|
+
|
11
|
+
* Entirely removed Hashie dependency. All keys are now symbolized in the Parser (bonus: smaller memory footprint).
|
data/Gemfile
ADDED
data/Guardfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Mattias Putman
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# Chemicals
|
2
|
+
|
3
|
+
## TODO
|
4
|
+
|
5
|
+
* Add simple object conversion (ex. String to Fixnum)
|
6
|
+
* Add shorthand for parsing under same name of element/attribute:
|
7
|
+
`<person ch:as='#' />`
|
8
|
+
* Add support for constant (are not parsed/rendered).
|
9
|
+
* `as` with depth. ex: `ch:as='name/first_name'`
|
10
|
+
* More granular matching of nodes. ex Matching a simgle node within a collection
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
|
14
|
+
Add this line to your application's Gemfile:
|
15
|
+
|
16
|
+
gem 'chemicals'
|
17
|
+
|
18
|
+
And then execute:
|
19
|
+
|
20
|
+
$ bundle
|
21
|
+
|
22
|
+
Or install it yourself as:
|
23
|
+
|
24
|
+
$ gem install chemicals
|
25
|
+
|
26
|
+
## Usage
|
27
|
+
|
28
|
+
TODO: Write usage instructions here
|
29
|
+
|
30
|
+
## Contributing
|
31
|
+
|
32
|
+
1. Fork it
|
33
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
34
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
35
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
36
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/chemicals.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/chemicals/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.authors = ["Mattias Putman"]
|
6
|
+
s.email = ["mattias.putman@gmail.com"]
|
7
|
+
s.description = %q{Clever XML Parsing library}
|
8
|
+
s.summary = %q{Clever XML Parsing library}
|
9
|
+
s.homepage = ""
|
10
|
+
|
11
|
+
s.files = `git ls-files`.split($\)
|
12
|
+
s.executables = s.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
s.test_files = s.files.grep(%r{^(test|spec|features)/})
|
14
|
+
s.name = "chemicals"
|
15
|
+
s.require_paths = ["lib"]
|
16
|
+
s.version = Chemicals::VERSION
|
17
|
+
|
18
|
+
s.add_dependency 'nokogiri'
|
19
|
+
|
20
|
+
s.add_development_dependency 'rake'
|
21
|
+
s.add_development_dependency 'mocha'
|
22
|
+
s.add_development_dependency 'guard'
|
23
|
+
s.add_development_dependency 'guard-minitest'
|
24
|
+
end
|
data/lib/chemicals.rb
ADDED
Binary file
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Chemicals
|
2
|
+
class Parser
|
3
|
+
def initialize template
|
4
|
+
@template = template
|
5
|
+
end
|
6
|
+
|
7
|
+
def parse source
|
8
|
+
@namespaces = {}
|
9
|
+
# get the document in nokogiri without blanks
|
10
|
+
# or if the source is already a nokogiri node, then assume it is without blanks.
|
11
|
+
root = if source.kind_of? Nokogiri::XML::Node
|
12
|
+
source.document.root
|
13
|
+
else
|
14
|
+
Nokogiri::XML(source.to_s) { |c| c.noblanks }.root
|
15
|
+
end
|
16
|
+
# delete all default namespaces and map the new ones in @namespaces
|
17
|
+
handle_namespace root
|
18
|
+
# map to a general namespace prefix => href hash
|
19
|
+
@namespaces = Hash[(@namespaces.values + root.namespace_definitions).map { |ns|
|
20
|
+
[ns.prefix, ns.href] if ns.prefix
|
21
|
+
}]
|
22
|
+
# begin parsing with the root node
|
23
|
+
parse_node root, @template.for(root.path, @namespaces)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def parse_node source, config
|
29
|
+
return nil unless config
|
30
|
+
# parse all child nodes and attribute nodes
|
31
|
+
parsed = (source.children.to_a + source.attribute_nodes).map do |node|
|
32
|
+
# parse node with the correspondent config (using node xpath)
|
33
|
+
parse_node(node, @template.for(node.path, @namespaces)) || {}
|
34
|
+
end
|
35
|
+
# reject nil values
|
36
|
+
parsed.reject! { |key, value| !value } if parsed.kind_of? Hash
|
37
|
+
# in arrays reject empty hashes
|
38
|
+
parsed.reject! { |value| value.empty? } if parsed.kind_of? Array
|
39
|
+
# we have a few cases here
|
40
|
+
parsed = case source
|
41
|
+
when Nokogiri::XML::Text
|
42
|
+
source.content
|
43
|
+
when Nokogiri::XML::Attr
|
44
|
+
source.content
|
45
|
+
when Nokogiri::XML::Element
|
46
|
+
# an array of arrays is flattened
|
47
|
+
if parsed.kind_of?(Array) && parsed.all? { |part| part.kind_of? Array }
|
48
|
+
parsed.flatten
|
49
|
+
# everything else is merged
|
50
|
+
else
|
51
|
+
parsed.inject { |result, part|
|
52
|
+
# internal arrays are added
|
53
|
+
result.merge!(part) { |key, old_value, new_value|
|
54
|
+
# This way mixed collections come together.
|
55
|
+
old_value + new_value
|
56
|
+
}
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
# If nothing got parsed but we expect a hash then make it a hash!
|
61
|
+
if parsed.empty? && config[:mode] == :merge
|
62
|
+
parsed = {}
|
63
|
+
end
|
64
|
+
# wrap in array if collecting
|
65
|
+
parsed = [parsed] if config[:mode] == :collect
|
66
|
+
# wrap in alias
|
67
|
+
parsed = {config[:as] => parsed} if config.has_key?(:as) && !config[:as].nil?
|
68
|
+
# return parsed version and reject all nil values!
|
69
|
+
parsed
|
70
|
+
end
|
71
|
+
|
72
|
+
def handle_namespace node
|
73
|
+
if node.namespace
|
74
|
+
# a namespace without a prefix is a default namespace
|
75
|
+
if !node.namespace.prefix
|
76
|
+
# not mapped this namespace?
|
77
|
+
unless @namespaces.has_key? node.namespace.href
|
78
|
+
# define the new namespace
|
79
|
+
node.document.root.add_namespace_definition "ns#{@namespaces.size}",
|
80
|
+
node.namespace.href
|
81
|
+
# add a mapping
|
82
|
+
@namespaces[node.namespace.href] = node.document.root.namespace_definitions.find { |ns|
|
83
|
+
ns.prefix && ns.href == node.namespace.href
|
84
|
+
}
|
85
|
+
end
|
86
|
+
# change the namespace
|
87
|
+
node.namespace = @namespaces[node.namespace.href]
|
88
|
+
# namespace with prefix
|
89
|
+
else
|
90
|
+
@namespaces[node.namespace.href] ||= node.namespace
|
91
|
+
end
|
92
|
+
end
|
93
|
+
# do the same for all child elements and attributes
|
94
|
+
(node.children.to_a + node.attribute_nodes).each { |child| handle_namespace child }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
module Chemicals
|
2
|
+
class Renderer
|
3
|
+
|
4
|
+
def initialize template
|
5
|
+
@template = template
|
6
|
+
end
|
7
|
+
|
8
|
+
def render source
|
9
|
+
# begin with a new document
|
10
|
+
document = Nokogiri::XML::Document.new
|
11
|
+
# add a temp node as a namespace referebce
|
12
|
+
document.root = Nokogiri::XML::Element.new 'temp', document
|
13
|
+
@template.add_namespaces document.root
|
14
|
+
# now render the entire document as the new root
|
15
|
+
document.root = render_node document, source, @template.raw.root
|
16
|
+
# again add all required namespaces to the real root
|
17
|
+
@template.add_namespaces document.root
|
18
|
+
# return the root
|
19
|
+
document.root
|
20
|
+
end
|
21
|
+
|
22
|
+
def render_node document, source, template, collect = true
|
23
|
+
# the template is already the template to apply this source on
|
24
|
+
# do nothing if there's no source
|
25
|
+
return nil unless source && config = @template.for(template.path)
|
26
|
+
# don't render the chemicals namespace
|
27
|
+
return nil if template.namespace && template.namespace.href == 'http://piesync.com/xml/chemicals'
|
28
|
+
# unwrap if necessary
|
29
|
+
source = source[config[:as].to_sym] if config.has_key?(:as) && !config[:as].nil? && collect
|
30
|
+
# is this a collect node? Rendering a collect node is the same as rendering the
|
31
|
+
# template for each part of the source (and render them as a non-collect node)
|
32
|
+
# The result are the rendered nodes.
|
33
|
+
if collect && config[:mode] == :collect
|
34
|
+
return source.map { |part| render_node document, part, template, false } if source
|
35
|
+
end
|
36
|
+
# render all children
|
37
|
+
rendered = (template.children.to_a + template.attribute_nodes).map do |child|
|
38
|
+
render_node document, source, child, true if source
|
39
|
+
end.flatten
|
40
|
+
# reject all nil values
|
41
|
+
rendered.reject! { |value| !value }
|
42
|
+
# we again have a few cases how to render
|
43
|
+
case template
|
44
|
+
when Nokogiri::XML::Attr
|
45
|
+
return unless source
|
46
|
+
node = Nokogiri::XML::Attr.new document, template.name
|
47
|
+
node.content = source
|
48
|
+
when Nokogiri::XML::Text
|
49
|
+
node = Nokogiri::XML::Text.new source, document if source
|
50
|
+
when Nokogiri::XML::Element
|
51
|
+
# don't render an element if it has no data in its children
|
52
|
+
return unless !rendered.empty?
|
53
|
+
node = Nokogiri::XML::Element.new template.name, document
|
54
|
+
rendered.each do |child|
|
55
|
+
# manually add attributes cause otherwise resulting in a segfault
|
56
|
+
if child.kind_of? Nokogiri::XML::Attr
|
57
|
+
node.set_attribute child.name, child.value
|
58
|
+
node.attribute(child.name).namespace = child.namespace
|
59
|
+
# add the non-attibutes
|
60
|
+
else
|
61
|
+
node.add_child child
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
# Add the correct namespaces!
|
66
|
+
node.namespace = document.root.namespace_definitions.find { |ns|
|
67
|
+
ns.href == template.namespace.href
|
68
|
+
} if node && !template.namespace.nil?
|
69
|
+
node
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Chemicals
|
2
|
+
class Template
|
3
|
+
|
4
|
+
NS = 'http://piesync.com/xml/chemicals'
|
5
|
+
|
6
|
+
def initialize(template, options = {})
|
7
|
+
@template = Nokogiri::XML(template) { |c| c.noblanks }
|
8
|
+
@options = {
|
9
|
+
symbolize_keys: true
|
10
|
+
}.merge!(options)
|
11
|
+
@cache = {}
|
12
|
+
end
|
13
|
+
|
14
|
+
# returns the raw template
|
15
|
+
def raw
|
16
|
+
@template
|
17
|
+
end
|
18
|
+
|
19
|
+
# get the configuration for an xpath expression
|
20
|
+
def for path, namespaces = nil
|
21
|
+
@cache[path] ||= if namespaces
|
22
|
+
for_node @template.at(canonicalize(path), namespaces)
|
23
|
+
else
|
24
|
+
for_node @template.at(canonicalize(path))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# get the configuration for a config node
|
29
|
+
def for_node config_node
|
30
|
+
# nil if no config node
|
31
|
+
return nil unless config_node
|
32
|
+
return nil if @template.at(canonicalize(config_node.path)).nil?
|
33
|
+
# configuation is different in every case
|
34
|
+
config = case config_node
|
35
|
+
when Nokogiri::XML::Text
|
36
|
+
config_node.content == '@' ? {} : {
|
37
|
+
as: @options[:symbolize_keys] ? config_node.content.to_sym : config_node.content
|
38
|
+
}
|
39
|
+
when Nokogiri::XML::Attr
|
40
|
+
{
|
41
|
+
as: @options[:symbolize_keys] ? config_node.value.to_sym : config_node.value
|
42
|
+
}
|
43
|
+
when Nokogiri::XML::Element
|
44
|
+
as = attribute(config_node, :as)
|
45
|
+
{
|
46
|
+
as: as ? (@options[:symbolize_keys] ? as.to_sym : as) : nil,
|
47
|
+
mode: mode(config_node, as)
|
48
|
+
}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def mode config_node, as
|
53
|
+
mode = attribute(config_node, :mode)
|
54
|
+
if mode
|
55
|
+
mode.to_sym
|
56
|
+
elsif as # only merge if we parse this node.
|
57
|
+
:merge
|
58
|
+
else
|
59
|
+
nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# add the required namespaces to a node
|
64
|
+
def add_namespaces node
|
65
|
+
raw.root.namespace_definitions.each do |ns|
|
66
|
+
node.add_namespace_definition ns.prefix, ns.href if ns.href != NS
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# parse a document
|
71
|
+
def parse source
|
72
|
+
@parser ||= Parser.new self
|
73
|
+
@parser.parse source
|
74
|
+
end
|
75
|
+
|
76
|
+
# render a hash
|
77
|
+
def render source
|
78
|
+
@renderer ||= Renderer.new self
|
79
|
+
@renderer.render source
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def attribute node, name
|
85
|
+
node.attribute_with_ns(name.to_s, NS).value if node.namespaced_key?(name.to_s, NS)
|
86
|
+
end
|
87
|
+
|
88
|
+
# canonicalize an xpath expression
|
89
|
+
def canonicalize xpath
|
90
|
+
# this means removing indexes
|
91
|
+
xpath.gsub(/\[[^\]]*\]/, '')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'minitest/spec'
|
4
|
+
require 'minitest/autorun'
|
5
|
+
|
6
|
+
require 'chemicals'
|
7
|
+
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
|
12
|
+
describe Chemicals::Renderer do
|
13
|
+
describe 'rendering a text node' do
|
14
|
+
it 'should render the element value directly in the text node when the text node is aliased as @' do
|
15
|
+
template, raw = ChemicalsSpecHelper.test_example :simple_text
|
16
|
+
template.render('John Doe').to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should render the content unwrapped from its alias' do
|
20
|
+
template, raw = ChemicalsSpecHelper.test_example :simple_text_alias
|
21
|
+
template.render(name: 'John Doe').to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'rendering an attribute' do
|
26
|
+
it 'should render attributes with aliases' do
|
27
|
+
template, raw = ChemicalsSpecHelper.test_example :simple_attributes
|
28
|
+
template.render(full_name: 'John Doe').to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'rendering an element' do
|
33
|
+
it 'should unwrap contents in aliased elements' do
|
34
|
+
template, raw = ChemicalsSpecHelper.test_example :simple_element_alias
|
35
|
+
template.render(individual: 'John Doe').to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'should skip an element when no alias is provided' do
|
39
|
+
template, raw = ChemicalsSpecHelper.test_example :skip_element
|
40
|
+
template.render(individual: 'John Doe').to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe 'rendering a collection of elements' do
|
45
|
+
it 'should render each element' do
|
46
|
+
template, raw = ChemicalsSpecHelper.test_example :simple_collection
|
47
|
+
template.render(emails: [
|
48
|
+
'john.doe@gmail.com',
|
49
|
+
'john@acme.com'
|
50
|
+
]).to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should render multiple collections independently' do
|
54
|
+
template, raw = ChemicalsSpecHelper.test_example :multiple_collections
|
55
|
+
template.render(
|
56
|
+
contact: {
|
57
|
+
emails: [
|
58
|
+
'john.doe@gmail.com',
|
59
|
+
'john@acme.com'
|
60
|
+
],
|
61
|
+
phone_numbers: ['1', '2']
|
62
|
+
}).to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should be able to render collections in the presence of other elements' do
|
66
|
+
template, raw = ChemicalsSpecHelper.test_example :mixed_collection
|
67
|
+
template.render( \
|
68
|
+
contact: {
|
69
|
+
emails: [
|
70
|
+
'john.doe@gmail.com',
|
71
|
+
'john@acme.com'
|
72
|
+
],
|
73
|
+
name: 'John Doe'
|
74
|
+
}).to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'should be able to render multiple collections in the presence of other elements' do
|
78
|
+
template, raw = ChemicalsSpecHelper.test_example :mixed_collections
|
79
|
+
template.render(
|
80
|
+
contact: {
|
81
|
+
emails: [
|
82
|
+
{ address: 'john.doe@gmail.com', label: 'work' },
|
83
|
+
{ address: 'john@acme.com' }
|
84
|
+
],
|
85
|
+
phones: ['1'],
|
86
|
+
name: 'John Doe'
|
87
|
+
}).to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should be able to extreme parse multiple collections in the presence of other elements' do
|
91
|
+
template, raw = ChemicalsSpecHelper.test_example :mixed_collections_extreme
|
92
|
+
template.render(
|
93
|
+
[{
|
94
|
+
emails: [
|
95
|
+
{ address: 'john.doe@gmail.com', label: 'work' },
|
96
|
+
{ address:'john@acme.com' }
|
97
|
+
],
|
98
|
+
phones: ['1'],
|
99
|
+
name: 'John Doe'
|
100
|
+
},
|
101
|
+
{
|
102
|
+
phones: ['1', '2', '3'],
|
103
|
+
addresses: [{ country: 'Belgium', country_code: 'BE',
|
104
|
+
street: 'Désiré Van Monckhovenstraat', housenumber: '123' }]
|
105
|
+
}]).to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'should render mixed elements, collections and text nodes' do
|
110
|
+
template, raw, raw_render = ChemicalsSpecHelper.test_example :mixed_elements_text
|
111
|
+
template.render(
|
112
|
+
[{
|
113
|
+
name: { given: 'John', family: 'Doe' },
|
114
|
+
emails: ['john.doe@gmail.com', 'john@acme.com'],
|
115
|
+
phones: [
|
116
|
+
{ country: 'Belgium', number: '1' },
|
117
|
+
{ country: 'USA', number: '2' }
|
118
|
+
]
|
119
|
+
},
|
120
|
+
{
|
121
|
+
name: { given: 'Jane' },
|
122
|
+
emails: [ 'jane.doe@gmail.com' ]
|
123
|
+
}
|
124
|
+
]).to_xml.must_equal ChemicalsSpecHelper.format(raw_render)
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should render mixed elements, collections, text nodes, attributes and ignore useless nodes' do
|
128
|
+
template, raw, raw_render = ChemicalsSpecHelper.test_example :mixed_elements_text_attributes
|
129
|
+
template.render([
|
130
|
+
{
|
131
|
+
name: { given: 'John', family: 'Doe' },
|
132
|
+
emails: [
|
133
|
+
{ label: 'home', address: 'john.doe@gmail.com' },
|
134
|
+
{ address: 'john@acme.com' }
|
135
|
+
],
|
136
|
+
phones: [
|
137
|
+
{ country: 'Belgium', number: '1', system: 'sap' },
|
138
|
+
{ country: 'USA', number: '2' }
|
139
|
+
]
|
140
|
+
},
|
141
|
+
{
|
142
|
+
name: { given: 'Jane' },
|
143
|
+
emails: [{ label: 'work', address: 'jane.doe@gmail.com' }]
|
144
|
+
}
|
145
|
+
]).to_xml.must_equal ChemicalsSpecHelper.format(raw_render)
|
146
|
+
end
|
147
|
+
|
148
|
+
describe 'rendering attributes' do
|
149
|
+
it 'should render attributes with aliases' do
|
150
|
+
template, raw = ChemicalsSpecHelper.test_example :simple_attributes
|
151
|
+
template.render(full_name: 'John Doe').to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should not render attributes not mentioned in the template' do
|
156
|
+
template, raw = ChemicalsSpecHelper.test_example :ignore_attribute
|
157
|
+
template.render({}).must_equal nil
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'should mix attributes and aliased text nodes' do
|
161
|
+
template, raw = ChemicalsSpecHelper.test_example :text_attributes
|
162
|
+
template.render(age: '24', name: 'John Doe').to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
163
|
+
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should be able to render chinese characters' do
|
168
|
+
template, raw = ChemicalsSpecHelper.test_example :chinese
|
169
|
+
template.render('你好世界').to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should be able to render namespaces' do
|
173
|
+
template, raw = ChemicalsSpecHelper.test_example :namespaces
|
174
|
+
template.render(
|
175
|
+
name: { age: '24', first_name: 'John', last_name: 'Doe' }).to_xml.must_equal ChemicalsSpecHelper.format(raw)
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'should be reasonably fast' do
|
180
|
+
template, raw = ChemicalsSpecHelper.test_example :mixed_elements_text
|
181
|
+
1000.times do
|
182
|
+
template.render(
|
183
|
+
[{
|
184
|
+
name: { given: 'John', family: 'Doe' },
|
185
|
+
emails: ['john.doe@gmail.com', 'john@acme.com'],
|
186
|
+
phones: [
|
187
|
+
{ country: 'Belgium', number: '1' },
|
188
|
+
{ country: 'USA', number: '2' }
|
189
|
+
]
|
190
|
+
},
|
191
|
+
{
|
192
|
+
name: { given: 'Jane' },
|
193
|
+
emails: [ 'jane.doe@gmail.com' ]
|
194
|
+
}
|
195
|
+
])
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
__END__
|
201
|
+
|