nori 1.1.5 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of nori might be problematic. Click here for more details.
- data/CHANGELOG.md +24 -12
- data/README.md +8 -9
- data/benchmark/benchmark.rb +1 -1
- data/lib/nori.rb +26 -40
- data/lib/nori/core_ext/hash.rb +1 -1
- data/lib/nori/core_ext/object.rb +1 -1
- data/lib/nori/core_ext/string.rb +1 -1
- data/lib/nori/parser/nokogiri.rb +5 -7
- data/lib/nori/parser/rexml.rb +3 -3
- data/lib/nori/string_io_file.rb +1 -1
- data/lib/nori/string_with_attributes.rb +1 -1
- data/lib/nori/version.rb +2 -2
- data/lib/nori/xml_utility_node.rb +11 -9
- data/nori.gemspec +3 -3
- data/spec/nori/api_spec.rb +108 -0
- data/spec/nori/nori_spec.rb +18 -90
- metadata +12 -13
- data/lib/nori/parser.rb +0 -51
- data/spec/nori/parser_spec.rb +0 -54
data/CHANGELOG.md
CHANGED
@@ -1,17 +1,29 @@
|
|
1
|
-
|
1
|
+
# 2.0 (2012-12-12)
|
2
2
|
|
3
|
-
|
4
|
-
problem on Ruby 1.9.3-p392.
|
3
|
+
Please make sure to read the updated README for how to use the new version.
|
5
4
|
|
6
|
-
|
5
|
+
* Change: Nori now defaults to use the Nokogiri parser.
|
7
6
|
|
8
|
-
*
|
9
|
-
|
10
|
-
|
7
|
+
* Refactoring: Changed the `Nori` module to a class. This might cause problems if you
|
8
|
+
included the `Nori` module somewhere in your application. This use case was removed
|
9
|
+
for overall simplicity.
|
10
|
+
|
11
|
+
* Refactoring: Changed the interface to remove any global state. The global configuration
|
12
|
+
is gone and replaced with simple options to be passed to `Nori.new`.
|
13
|
+
|
14
|
+
``` ruby
|
15
|
+
parser = Nori.new(strip_namespaces: true)
|
16
|
+
parser.parse(xml)
|
17
|
+
```
|
18
|
+
|
19
|
+
* Refactoring: Removed the `Nori::Parser` module methods. After refactoring the rest,
|
20
|
+
there was only a single method left for this module and that was moved to `Nori`.
|
21
|
+
|
22
|
+
* Fix: [#16](https://github.com/savonrb/nori/issues/16) strip XML passed to Nori.
|
11
23
|
|
12
24
|
## 1.1.3 (2012-07-12)
|
13
25
|
|
14
|
-
* Fix: Merged [pull request 21](https://github.com/
|
26
|
+
* Fix: Merged [pull request 21](https://github.com/savonrb/nori/pull/21) to fix an
|
15
27
|
issue with date/time/datetime regexes not matching positive time zone offsets and
|
16
28
|
datetime strings with seconds.
|
17
29
|
|
@@ -21,15 +33,15 @@
|
|
21
33
|
|
22
34
|
## 1.1.1 (2012-06-29) - yanked
|
23
35
|
|
24
|
-
* Fix: Merged [pull request 17](https://github.com/
|
36
|
+
* Fix: Merged [pull request 17](https://github.com/savonrb/nori/pull/17) for improved
|
25
37
|
xs:time/xs:date/xs:dateTime regular expression matchers.
|
26
38
|
|
27
39
|
## 1.1.0 (2012-02-17)
|
28
40
|
|
29
|
-
* Improvement: Merged [pull request 9](https://github.com/
|
41
|
+
* Improvement: Merged [pull request 9](https://github.com/savonrb/nori/pull/9) to
|
30
42
|
allow multiple configurations of Nori.
|
31
43
|
|
32
|
-
* Fix: Merged [pull request 10](https://github.com/
|
44
|
+
* Fix: Merged [pull request 10](https://github.com/savonrb/nori/pull/10) to handle
|
33
45
|
date/time parsing errors. Fixes a couple of similar error reports.
|
34
46
|
|
35
47
|
## 1.0.2 (2011-07-04)
|
@@ -88,7 +100,7 @@
|
|
88
100
|
|
89
101
|
## 0.2.1 (2011-05-15)
|
90
102
|
|
91
|
-
* Fix: Changed XML attributes converted to Hash keys to be prefixed with an @-sign.
|
103
|
+
* Fix: Changed XML attributes converted to Hash keys to be prefixed with an @-sign.
|
92
104
|
This avoids problems with attributes and child nodes having the same name.
|
93
105
|
|
94
106
|
<multiRef id="id1">
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Nori [![Build Status](https://secure.travis-ci.org/
|
1
|
+
Nori [![Build Status](https://secure.travis-ci.org/savonrb/nori.png)](http://travis-ci.org/savonrb/nori)
|
2
2
|
====
|
3
3
|
|
4
4
|
Really simple XML parsing ripped from Crack which ripped it from Merb.
|
@@ -6,7 +6,8 @@ Nori was created to bypass the stale development of Crack, improve its XML parse
|
|
6
6
|
and fix certain issues.
|
7
7
|
|
8
8
|
``` ruby
|
9
|
-
Nori.
|
9
|
+
parser = Nori.new
|
10
|
+
parser.parse("<tag>This is the contents</tag>")
|
10
11
|
# => { 'tag' => 'This is the contents' }
|
11
12
|
```
|
12
13
|
|
@@ -14,7 +15,7 @@ Nori supports pluggable parsers and ships with both REXML and Nokogiri implement
|
|
14
15
|
It defaults to REXML, but you can change it to use Nokogiri via:
|
15
16
|
|
16
17
|
``` ruby
|
17
|
-
Nori.parser
|
18
|
+
Nori.new(:parser => :nokogiri) # or :rexml
|
18
19
|
```
|
19
20
|
|
20
21
|
Make sure Nokogiri is in your LOAD_PATH when parsing XML, because Nori tries to load it
|
@@ -33,7 +34,7 @@ Besides regular typecasting, Nori features somewhat "advanced" typecasting:
|
|
33
34
|
You can disable this feature via:
|
34
35
|
|
35
36
|
``` ruby
|
36
|
-
Nori.advanced_typecasting
|
37
|
+
Nori.new(:advanced_typecasting => false)
|
37
38
|
```
|
38
39
|
|
39
40
|
|
@@ -44,7 +45,7 @@ Nori can strip the namespaces from your XML tags. This feature might raise
|
|
44
45
|
problems and is therefore disabled by default. Enable it via:
|
45
46
|
|
46
47
|
``` ruby
|
47
|
-
Nori.strip_namespaces
|
48
|
+
Nori.new(:strip_namespaces => true)
|
48
49
|
```
|
49
50
|
|
50
51
|
|
@@ -55,10 +56,8 @@ Nori lets you specify a custom formula to convert XML tags to Hash keys.
|
|
55
56
|
Let me give you an example:
|
56
57
|
|
57
58
|
``` ruby
|
58
|
-
Nori.
|
59
|
-
config.convert_tags_to { |tag| tag.snake_case.to_sym }
|
60
|
-
end
|
59
|
+
parser = Nori.new(:convert_tags_to => lambda { |tag| tag.snake_case.to_sym })
|
61
60
|
|
62
61
|
xml = '<userResponse><accountStatus>active</accountStatus></userResponse>'
|
63
|
-
|
62
|
+
parser.parse(xml) # => { :user_response => { :account_status => "active" }
|
64
63
|
```
|
data/benchmark/benchmark.rb
CHANGED
data/lib/nori.rb
CHANGED
@@ -1,59 +1,45 @@
|
|
1
1
|
require "nori/version"
|
2
2
|
require "nori/core_ext"
|
3
|
-
require "nori/parser"
|
4
3
|
require "nori/xml_utility_node"
|
5
4
|
|
6
|
-
|
7
|
-
extend self
|
5
|
+
class Nori
|
8
6
|
|
9
|
-
|
10
|
-
def parse(xml, parser = nil)
|
11
|
-
return {} if xml.blank?
|
12
|
-
Parser.parse xml, parser, self
|
13
|
-
end
|
7
|
+
PARSERS = { :rexml => "REXML", :nokogiri => "Nokogiri" }
|
14
8
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
9
|
+
def initialize(options = {})
|
10
|
+
defaults = {
|
11
|
+
:strip_namespaces => false,
|
12
|
+
:convert_tags_to => nil,
|
13
|
+
:advanced_typecasting => true,
|
14
|
+
:parser => :nokogiri
|
15
|
+
}
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
yield self
|
17
|
+
validate_options! defaults.keys, options.keys
|
18
|
+
@options = defaults.merge(options)
|
23
19
|
end
|
24
20
|
|
25
|
-
|
26
|
-
|
21
|
+
def parse(xml)
|
22
|
+
cleaned_xml = xml.strip
|
23
|
+
return {} if cleaned_xml.empty?
|
27
24
|
|
28
|
-
|
29
|
-
|
30
|
-
def advanced_typecasting?
|
31
|
-
@advanced_typecasting != false
|
25
|
+
parser = load_parser @options[:parser]
|
26
|
+
parser.parse(cleaned_xml, @options)
|
32
27
|
end
|
33
28
|
|
34
|
-
|
35
|
-
attr_writer :strip_namespaces
|
29
|
+
private
|
36
30
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@strip_namespaces
|
31
|
+
def load_parser(parser)
|
32
|
+
require "nori/parser/#{parser}"
|
33
|
+
Parser.const_get PARSERS[parser]
|
41
34
|
end
|
42
35
|
|
43
|
-
|
44
|
-
|
45
|
-
def convert_tags_to(reset = nil, &block)
|
46
|
-
@convert_tag = reset || block
|
47
|
-
end
|
48
|
-
|
49
|
-
# Transforms a given +tag+ using the specified conversion formula.
|
50
|
-
def convert_tag(tag)
|
51
|
-
@convert_tag.call(tag)
|
52
|
-
end
|
36
|
+
def validate_options!(available_options, options)
|
37
|
+
spurious_options = options - available_options
|
53
38
|
|
54
|
-
|
55
|
-
|
56
|
-
|
39
|
+
unless spurious_options.empty?
|
40
|
+
raise ArgumentError, "Spurious options: #{spurious_options.inspect}\n" \
|
41
|
+
"Available options are: #{available_options.inspect}"
|
42
|
+
end
|
57
43
|
end
|
58
44
|
|
59
45
|
end
|
data/lib/nori/core_ext/hash.rb
CHANGED
data/lib/nori/core_ext/object.rb
CHANGED
data/lib/nori/core_ext/string.rb
CHANGED
data/lib/nori/parser/nokogiri.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "nokogiri"
|
2
2
|
|
3
|
-
|
3
|
+
class Nori
|
4
4
|
module Parser
|
5
5
|
|
6
6
|
# = Nori::Parser::Nokogiri
|
@@ -9,14 +9,14 @@ module Nori
|
|
9
9
|
module Nokogiri
|
10
10
|
|
11
11
|
class Document < ::Nokogiri::XML::SAX::Document
|
12
|
-
attr_accessor :
|
12
|
+
attr_accessor :options
|
13
13
|
|
14
14
|
def stack
|
15
15
|
@stack ||= []
|
16
16
|
end
|
17
17
|
|
18
18
|
def start_element(name, attrs = [])
|
19
|
-
stack.push Nori::XMLUtilityNode.new(
|
19
|
+
stack.push Nori::XMLUtilityNode.new(options, name, Hash[*attrs.flatten])
|
20
20
|
end
|
21
21
|
|
22
22
|
def end_element(name)
|
@@ -34,11 +34,9 @@ module Nori
|
|
34
34
|
|
35
35
|
end
|
36
36
|
|
37
|
-
def self.parse(xml,
|
38
|
-
return {} if xml.strip.empty?
|
39
|
-
|
37
|
+
def self.parse(xml, options)
|
40
38
|
document = Document.new
|
41
|
-
document.
|
39
|
+
document.options = options
|
42
40
|
parser = ::Nokogiri::XML::SAX::Parser.new document
|
43
41
|
parser.parse xml
|
44
42
|
document.stack.length > 0 ? document.stack.pop.to_hash : {}
|
data/lib/nori/parser/rexml.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require "rexml/parsers/baseparser"
|
2
2
|
|
3
|
-
|
3
|
+
class Nori
|
4
4
|
module Parser
|
5
5
|
|
6
6
|
# = Nori::Parser::REXML
|
@@ -8,7 +8,7 @@ module Nori
|
|
8
8
|
# REXML pull parser.
|
9
9
|
module REXML
|
10
10
|
|
11
|
-
def self.parse(xml,
|
11
|
+
def self.parse(xml, options)
|
12
12
|
stack = []
|
13
13
|
parser = ::REXML::Parsers::BaseParser.new(xml)
|
14
14
|
|
@@ -20,7 +20,7 @@ module Nori
|
|
20
20
|
when :end_doctype, :start_doctype
|
21
21
|
# do nothing
|
22
22
|
when :start_element
|
23
|
-
stack.push Nori::XMLUtilityNode.new(
|
23
|
+
stack.push Nori::XMLUtilityNode.new(options, event[1], event[2])
|
24
24
|
when :end_element
|
25
25
|
if stack.size > 1
|
26
26
|
temp = stack.pop
|
data/lib/nori/string_io_file.rb
CHANGED
data/lib/nori/version.rb
CHANGED
@@ -1,5 +1,4 @@
|
|
1
1
|
require "rexml/text"
|
2
|
-
require "rexml/document"
|
3
2
|
require "date"
|
4
3
|
require "time"
|
5
4
|
require "yaml"
|
@@ -8,7 +7,7 @@ require "bigdecimal"
|
|
8
7
|
require "nori/string_with_attributes"
|
9
8
|
require "nori/string_io_file"
|
10
9
|
|
11
|
-
|
10
|
+
class Nori
|
12
11
|
|
13
12
|
# This is a slighly modified version of the XMLUtilityNode from
|
14
13
|
# http://merb.devjavu.com/projects/merb/ticket/95 (has.sox@gmail.com)
|
@@ -78,21 +77,23 @@ module Nori
|
|
78
77
|
self.typecasts["decimal"] = lambda { |v| v.nil? ? nil : BigDecimal(v.to_s) }
|
79
78
|
self.typecasts["double"] = lambda { |v| v.nil? ? nil : v.to_f }
|
80
79
|
self.typecasts["float"] = lambda { |v| v.nil? ? nil : v.to_f }
|
80
|
+
self.typecasts["symbol"] = lambda { |v| v.nil? ? nil : v.to_sym }
|
81
81
|
self.typecasts["string"] = lambda { |v| v.to_s }
|
82
|
+
self.typecasts["yaml"] = lambda { |v| v.nil? ? nil : YAML.load(v) }
|
82
83
|
self.typecasts["base64Binary"] = lambda { |v| v.unpack('m').first }
|
83
84
|
|
84
85
|
self.available_typecasts = self.typecasts.keys
|
85
86
|
|
86
|
-
def initialize(
|
87
|
+
def initialize(options, name, normalized_attributes = {})
|
87
88
|
# unnormalize attribute values
|
88
89
|
attributes = Hash[* normalized_attributes.map do |key, value|
|
89
90
|
[ key, unnormalize_xml_entities(value) ]
|
90
91
|
end.flatten]
|
91
92
|
|
92
|
-
@
|
93
|
-
@name
|
94
|
-
@name
|
95
|
-
@name
|
93
|
+
@options = options
|
94
|
+
@name = name.tr("-", "_")
|
95
|
+
@name = @name.split(":").last if @options[:strip_namespaces]
|
96
|
+
@name = @options[:convert_tags_to].call(@name) if @options[:convert_tags_to].respond_to? :call
|
96
97
|
|
97
98
|
# leave the type alone if we don't know what it is
|
98
99
|
@type = self.class.available_typecasts.include?(attributes["type"]) ? attributes.delete("type") : attributes["type"]
|
@@ -119,7 +120,8 @@ module Nori
|
|
119
120
|
end
|
120
121
|
|
121
122
|
def prefixed_attribute_name(attribute)
|
122
|
-
|
123
|
+
return attribute unless @options[:convert_tags_to].respond_to? :call
|
124
|
+
@options[:convert_tags_to].call(attribute)
|
123
125
|
end
|
124
126
|
|
125
127
|
def add_node(node)
|
@@ -137,7 +139,7 @@ module Nori
|
|
137
139
|
|
138
140
|
if @text
|
139
141
|
t = typecast_value unnormalize_xml_entities(inner_html)
|
140
|
-
t = advanced_typecasting(t) if t.is_a?(String) && @
|
142
|
+
t = advanced_typecasting(t) if t.is_a?(String) && @options[:advanced_typecasting]
|
141
143
|
|
142
144
|
if t.is_a?(String)
|
143
145
|
t = StringWithAttributes.new(t)
|
data/nori.gemspec
CHANGED
@@ -7,15 +7,15 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.version = Nori::VERSION
|
8
8
|
s.authors = ["Daniel Harrington", "John Nunemaker", "Wynn Netherland"]
|
9
9
|
s.email = "me@rubiii.com"
|
10
|
-
s.homepage = "
|
10
|
+
s.homepage = "https://github.com/savonrb/nori"
|
11
11
|
s.summary = "XML to Hash translator"
|
12
12
|
s.description = s.summary
|
13
13
|
|
14
14
|
s.rubyforge_project = "nori"
|
15
15
|
|
16
|
-
s.add_development_dependency "rake", "~> 0
|
16
|
+
s.add_development_dependency "rake", "~> 10.0"
|
17
17
|
s.add_development_dependency "nokogiri", ">= 1.4.0"
|
18
|
-
s.add_development_dependency "rspec", "~> 2.
|
18
|
+
s.add_development_dependency "rspec", "~> 2.12"
|
19
19
|
|
20
20
|
s.files = `git ls-files`.split("\n")
|
21
21
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Nori do
|
4
|
+
|
5
|
+
describe "PARSERS" do
|
6
|
+
it "should return a Hash of parser details" do
|
7
|
+
Nori::PARSERS.should == { :rexml => "REXML", :nokogiri => "Nokogiri" }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
context ".new" do
|
12
|
+
it "defaults to not strip any namespace identifiers" do
|
13
|
+
xml = <<-XML
|
14
|
+
<history xmlns:ns10="http://ns10.example.com">
|
15
|
+
<ns10:case>a_case</ns10:case>
|
16
|
+
</history>
|
17
|
+
XML
|
18
|
+
|
19
|
+
nori.parse(xml)["history"]["ns10:case"].should == "a_case"
|
20
|
+
end
|
21
|
+
|
22
|
+
it "defaults to not change XML tags" do
|
23
|
+
xml = '<userResponse id="1"><accountStatus>active</accountStatus></userResponse>'
|
24
|
+
nori.parse(xml).should == { "userResponse" => { "@id" => "1", "accountStatus" => "active" } }
|
25
|
+
end
|
26
|
+
|
27
|
+
it "raises when passed unknown global options" do
|
28
|
+
expect { Nori.new(:invalid => true) }.
|
29
|
+
to raise_error(ArgumentError, /Spurious options: \[:invalid\]/)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
context ".new with :strip_namespaces" do
|
34
|
+
it "strips the namespace identifiers when set to true" do
|
35
|
+
xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"></soap:Envelope>'
|
36
|
+
nori(:strip_namespaces => true).parse(xml).should have_key("Envelope")
|
37
|
+
end
|
38
|
+
|
39
|
+
it "still converts namespaced entries to array elements" do
|
40
|
+
xml = <<-XML
|
41
|
+
<history
|
42
|
+
xmlns:ns10="http://ns10.example.com"
|
43
|
+
xmlns:ns11="http://ns10.example.com">
|
44
|
+
<ns10:case><ns10:name>a_name</ns10:name></ns10:case>
|
45
|
+
<ns11:case><ns11:name>another_name</ns11:name></ns11:case>
|
46
|
+
</history>
|
47
|
+
XML
|
48
|
+
|
49
|
+
expected = [{ "name" => "a_name" }, { "name" => "another_name" }]
|
50
|
+
nori(:strip_namespaces => true).parse(xml)["history"]["case"].should == expected
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context ".new with :convert_tags_to" do
|
55
|
+
it "converts all tags by a given formula" do
|
56
|
+
xml = '<userResponse id="1"><accountStatus>active</accountStatus></userResponse>'
|
57
|
+
|
58
|
+
snakecase_symbols = lambda { |tag| tag.snakecase.to_sym }
|
59
|
+
nori = nori(:convert_tags_to => snakecase_symbols)
|
60
|
+
|
61
|
+
nori.parse(xml).should == { :user_response => { :@id => "1", :account_status => "active" } }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context "#parse" do
|
66
|
+
it "defaults to use advanced typecasting" do
|
67
|
+
hash = nori.parse("<value>true</value>")
|
68
|
+
hash["value"].should == true
|
69
|
+
end
|
70
|
+
|
71
|
+
it "defaults to use the Nokogiri parser" do
|
72
|
+
# parsers are loaded lazily by default
|
73
|
+
require "nori/parser/nokogiri"
|
74
|
+
|
75
|
+
Nori::Parser::Nokogiri.should_receive(:parse).once
|
76
|
+
nori.parse("<any>thing</any>")
|
77
|
+
end
|
78
|
+
|
79
|
+
it "strips the XML" do
|
80
|
+
xml = double("xml")
|
81
|
+
xml.should_receive(:strip).and_return("<any>thing</any>")
|
82
|
+
|
83
|
+
nori.parse(xml).should == { "any" => "thing" }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "#parse without :advanced_typecasting" do
|
88
|
+
it "can be changed to not typecast too much" do
|
89
|
+
hash = nori(:advanced_typecasting => false).parse("<value>true</value>")
|
90
|
+
hash["value"].should == "true"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context "#parse with :parser" do
|
95
|
+
it "can be configured to use the REXML parser" do
|
96
|
+
# parsers are loaded lazily by default
|
97
|
+
require "nori/parser/rexml"
|
98
|
+
|
99
|
+
Nori::Parser::REXML.should_receive(:parse).once
|
100
|
+
nori(:parser => :rexml).parse("<any>thing</any>")
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def nori(options = {})
|
105
|
+
Nori.new(options)
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
data/spec/nori/nori_spec.rb
CHANGED
@@ -2,16 +2,11 @@ require "spec_helper"
|
|
2
2
|
|
3
3
|
describe Nori do
|
4
4
|
|
5
|
-
Nori::
|
5
|
+
Nori::PARSERS.each do |parser, class_name|
|
6
6
|
context "using the :#{parser} parser" do
|
7
7
|
|
8
8
|
let(:parser) { parser }
|
9
9
|
|
10
|
-
it "should work with unnormalized characters" do
|
11
|
-
xml = '<root>&</root>'
|
12
|
-
parse(xml).should == { 'root' => "&" }
|
13
|
-
end
|
14
|
-
|
15
10
|
it "should transform a simple tag with content" do
|
16
11
|
xml = "<tag>This is the contents</tag>"
|
17
12
|
parse(xml).should == { 'tag' => 'This is the contents' }
|
@@ -92,40 +87,33 @@ describe Nori do
|
|
92
87
|
end
|
93
88
|
|
94
89
|
context "without advanced typecasting" do
|
95
|
-
around do |example|
|
96
|
-
Nori.advanced_typecasting = false
|
97
|
-
example.run
|
98
|
-
Nori.advanced_typecasting = true
|
99
|
-
end
|
100
|
-
|
101
90
|
it "should not transform 'true'" do
|
102
|
-
parse("<value>true</value>"
|
91
|
+
hash = parse("<value>true</value>", :advanced_typecasting => false)
|
92
|
+
hash["value"].should == "true"
|
103
93
|
end
|
104
94
|
|
105
95
|
it "should not transform 'false'" do
|
106
|
-
parse("<value>false</value>"
|
96
|
+
hash = parse("<value>false</value>", :advanced_typecasting => false)
|
97
|
+
hash["value"].should == "false"
|
107
98
|
end
|
108
99
|
|
109
100
|
it "should not transform Strings matching the xs:time format" do
|
110
|
-
parse("<value>09:33:55Z</value>"
|
101
|
+
hash = parse("<value>09:33:55Z</value>", :advanced_typecasting => false)
|
102
|
+
hash["value"].should == "09:33:55Z"
|
111
103
|
end
|
112
104
|
|
113
105
|
it "should not transform Strings matching the xs:date format" do
|
114
|
-
parse("<value>1955-04-18-05:00</value>"
|
106
|
+
hash = parse("<value>1955-04-18-05:00</value>", :advanced_typecasting => false)
|
107
|
+
hash["value"].should == "1955-04-18-05:00"
|
115
108
|
end
|
116
109
|
|
117
110
|
it "should not transform Strings matching the xs:dateTime format" do
|
118
|
-
parse("<value>1955-04-18T11:22:33-05:00</value>"
|
111
|
+
hash = parse("<value>1955-04-18T11:22:33-05:00</value>", :advanced_typecasting => false)
|
112
|
+
hash["value"].should == "1955-04-18T11:22:33-05:00"
|
119
113
|
end
|
120
114
|
end
|
121
115
|
|
122
116
|
context "with advanced typecasting" do
|
123
|
-
around do |example|
|
124
|
-
Nori.advanced_typecasting = true
|
125
|
-
example.run
|
126
|
-
Nori.advanced_typecasting = false
|
127
|
-
end
|
128
|
-
|
129
117
|
it "should transform 'true' to TrueClass" do
|
130
118
|
parse("<value>true</value>")["value"].should == true
|
131
119
|
end
|
@@ -294,46 +282,6 @@ describe Nori do
|
|
294
282
|
parse(xml)['tag_1'].keys.should include('@attr_1')
|
295
283
|
end
|
296
284
|
|
297
|
-
context "with strip_namespaces set to true" do
|
298
|
-
around do |example|
|
299
|
-
Nori.strip_namespaces = true
|
300
|
-
example.run
|
301
|
-
Nori.strip_namespaces = false
|
302
|
-
end
|
303
|
-
|
304
|
-
it "should strip the namespace from every tag" do
|
305
|
-
xml = '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"></soap:Envelope>'
|
306
|
-
parse(xml).should have_key("Envelope")
|
307
|
-
end
|
308
|
-
|
309
|
-
it "converts namespaced entries to array elements" do
|
310
|
-
xml = <<-XML
|
311
|
-
<history
|
312
|
-
xmlns:ns10="http://ns10.example.com"
|
313
|
-
xmlns:ns11="http://ns10.example.com">
|
314
|
-
<ns10:case><ns10:name>a_name</ns10:name></ns10:case>
|
315
|
-
<ns11:case><ns11:name>another_name</ns11:name></ns11:case>
|
316
|
-
</history>
|
317
|
-
XML
|
318
|
-
|
319
|
-
expected_case = [{ "name" => "a_name" }, { "name" => "another_name" }]
|
320
|
-
parse(xml)["history"]["case"].should == expected_case
|
321
|
-
end
|
322
|
-
end
|
323
|
-
|
324
|
-
context "with convert_tags_to set to a custom formula" do
|
325
|
-
around do |example|
|
326
|
-
Nori.convert_tags_to { |tag| tag.snakecase.to_sym }
|
327
|
-
example.run
|
328
|
-
Nori.convert_tags_to(nil)
|
329
|
-
end
|
330
|
-
|
331
|
-
it "transforms the tags to snakecase Symbols" do
|
332
|
-
xml = '<userResponse id="1"><accountStatus>active</accountStatus></userResponse>'
|
333
|
-
parse(xml).should == { :user_response => { :@id => "1", :account_status => "active" } }
|
334
|
-
end
|
335
|
-
end
|
336
|
-
|
337
285
|
it "should render nested content correctly" do
|
338
286
|
xml = "<root><tag1>Tag1 Content <em><strong>This is strong</strong></em></tag1></root>"
|
339
287
|
parse(xml)['root']['tag1'].should == "Tag1 Content <em><strong>This is strong</strong></em>"
|
@@ -400,8 +348,7 @@ describe Nori do
|
|
400
348
|
'approved' => nil,
|
401
349
|
'written_on' => nil,
|
402
350
|
'viewed_at' => nil,
|
403
|
-
|
404
|
-
'content' => { "@type" => "yaml" },
|
351
|
+
'content' => nil,
|
405
352
|
'parent_id' => nil,
|
406
353
|
'nil_true' => nil,
|
407
354
|
'namespaced' => nil
|
@@ -420,7 +367,7 @@ describe Nori do
|
|
420
367
|
<replies-close-in type="integer">2592000000</replies-close-in>
|
421
368
|
<written-on type="date">2003-07-16</written-on>
|
422
369
|
<viewed-at type="datetime">2003-07-16T09:28:00+0000</viewed-at>
|
423
|
-
<content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true</content>
|
370
|
+
<content type="yaml">--- \n1: should be an integer\n:message: Have a nice day\narray: \n- should-have-dashes: true\n should_have_underscores: true\n</content>
|
424
371
|
<author-email-address>david@loudthinking.com</author-email-address>
|
425
372
|
<parent-id></parent-id>
|
426
373
|
<ad-revenue type="decimal">1.5</ad-revenue>
|
@@ -441,13 +388,12 @@ describe Nori do
|
|
441
388
|
# Changed this line where the key is :message. The yaml specifies this as a symbol, and who am I to change what you specify
|
442
389
|
# The line in ActiveSupport is
|
443
390
|
# 'content' => { 'message' => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
|
444
|
-
'content' =>
|
391
|
+
'content' => { :message => "Have a nice day", 1 => "should be an integer", "array" => [{ "should-have-dashes" => true, "should_have_underscores" => true }] },
|
445
392
|
'author_email_address' => "david@loudthinking.com",
|
446
393
|
'parent_id' => nil,
|
447
394
|
'ad_revenue' => BigDecimal("1.50"),
|
448
395
|
'optimum_viewing_angle' => 135.0,
|
449
|
-
|
450
|
-
'resident' => "yes"
|
396
|
+
'resident' => :yes
|
451
397
|
}
|
452
398
|
|
453
399
|
parse(topic_xml)["topic"].each do |k,v|
|
@@ -667,29 +613,11 @@ describe Nori do
|
|
667
613
|
end
|
668
614
|
|
669
615
|
end
|
670
|
-
|
671
|
-
describe "using different nori" do
|
672
|
-
let(:parser) { parser }
|
673
|
-
let(:different_nori) do
|
674
|
-
module DifferentNori
|
675
|
-
extend Nori
|
676
|
-
end
|
677
|
-
DifferentNori.configure do |config|
|
678
|
-
config.convert_tags_to { |tag| tag.upcase }
|
679
|
-
end
|
680
|
-
DifferentNori
|
681
|
-
end
|
682
|
-
|
683
|
-
it "should transform with different nori" do
|
684
|
-
xml = "<SomeThing>xml</SomeThing>"
|
685
|
-
parse(xml).should == { "SomeThing" => "xml" }
|
686
|
-
different_nori.parse(xml, parser).should == { "SOMETHING" => "xml" }
|
687
|
-
end
|
688
|
-
end
|
689
616
|
end
|
690
617
|
|
691
|
-
def parse(xml)
|
692
|
-
|
618
|
+
def parse(xml, options = {})
|
619
|
+
defaults = { :parser => parser }
|
620
|
+
Nori.new(defaults.merge(options)).parse(xml)
|
693
621
|
end
|
694
622
|
|
695
623
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: nori
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2012-12-12 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rake
|
@@ -20,7 +20,7 @@ dependencies:
|
|
20
20
|
requirements:
|
21
21
|
- - ~>
|
22
22
|
- !ruby/object:Gem::Version
|
23
|
-
version: 0
|
23
|
+
version: '10.0'
|
24
24
|
type: :development
|
25
25
|
prerelease: false
|
26
26
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -28,7 +28,7 @@ dependencies:
|
|
28
28
|
requirements:
|
29
29
|
- - ~>
|
30
30
|
- !ruby/object:Gem::Version
|
31
|
-
version: 0
|
31
|
+
version: '10.0'
|
32
32
|
- !ruby/object:Gem::Dependency
|
33
33
|
name: nokogiri
|
34
34
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,7 +52,7 @@ dependencies:
|
|
52
52
|
requirements:
|
53
53
|
- - ~>
|
54
54
|
- !ruby/object:Gem::Version
|
55
|
-
version: 2.
|
55
|
+
version: '2.12'
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
58
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -60,7 +60,7 @@ dependencies:
|
|
60
60
|
requirements:
|
61
61
|
- - ~>
|
62
62
|
- !ruby/object:Gem::Version
|
63
|
-
version: 2.
|
63
|
+
version: '2.12'
|
64
64
|
description: XML to Hash translator
|
65
65
|
email: me@rubiii.com
|
66
66
|
executables: []
|
@@ -82,7 +82,6 @@ files:
|
|
82
82
|
- lib/nori/core_ext/hash.rb
|
83
83
|
- lib/nori/core_ext/object.rb
|
84
84
|
- lib/nori/core_ext/string.rb
|
85
|
-
- lib/nori/parser.rb
|
86
85
|
- lib/nori/parser/nokogiri.rb
|
87
86
|
- lib/nori/parser/rexml.rb
|
88
87
|
- lib/nori/string_io_file.rb
|
@@ -90,13 +89,13 @@ files:
|
|
90
89
|
- lib/nori/version.rb
|
91
90
|
- lib/nori/xml_utility_node.rb
|
92
91
|
- nori.gemspec
|
92
|
+
- spec/nori/api_spec.rb
|
93
93
|
- spec/nori/core_ext/hash_spec.rb
|
94
94
|
- spec/nori/core_ext/object_spec.rb
|
95
95
|
- spec/nori/core_ext/string_spec.rb
|
96
96
|
- spec/nori/nori_spec.rb
|
97
|
-
- spec/nori/parser_spec.rb
|
98
97
|
- spec/spec_helper.rb
|
99
|
-
homepage:
|
98
|
+
homepage: https://github.com/savonrb/nori
|
100
99
|
licenses: []
|
101
100
|
post_install_message:
|
102
101
|
rdoc_options: []
|
@@ -110,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
110
109
|
version: '0'
|
111
110
|
segments:
|
112
111
|
- 0
|
113
|
-
hash:
|
112
|
+
hash: 378604752016210991
|
114
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
115
114
|
none: false
|
116
115
|
requirements:
|
@@ -119,17 +118,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
119
118
|
version: '0'
|
120
119
|
segments:
|
121
120
|
- 0
|
122
|
-
hash:
|
121
|
+
hash: 378604752016210991
|
123
122
|
requirements: []
|
124
123
|
rubyforge_project: nori
|
125
|
-
rubygems_version: 1.8.
|
124
|
+
rubygems_version: 1.8.23
|
126
125
|
signing_key:
|
127
126
|
specification_version: 3
|
128
127
|
summary: XML to Hash translator
|
129
128
|
test_files:
|
129
|
+
- spec/nori/api_spec.rb
|
130
130
|
- spec/nori/core_ext/hash_spec.rb
|
131
131
|
- spec/nori/core_ext/object_spec.rb
|
132
132
|
- spec/nori/core_ext/string_spec.rb
|
133
133
|
- spec/nori/nori_spec.rb
|
134
|
-
- spec/nori/parser_spec.rb
|
135
134
|
- spec/spec_helper.rb
|
data/lib/nori/parser.rb
DELETED
@@ -1,51 +0,0 @@
|
|
1
|
-
module Nori
|
2
|
-
|
3
|
-
# = Nori::Parser
|
4
|
-
#
|
5
|
-
# Manages the parser classes. Currently supports:
|
6
|
-
#
|
7
|
-
# * REXML
|
8
|
-
# * Nokogiri
|
9
|
-
module Parser
|
10
|
-
|
11
|
-
# The default parser.
|
12
|
-
DEFAULT = :rexml
|
13
|
-
|
14
|
-
# List of available parsers.
|
15
|
-
PARSERS = { :rexml => "REXML", :nokogiri => "Nokogiri" }
|
16
|
-
|
17
|
-
# Returns the parser to use. Defaults to <tt>Nori::Parser::REXML</tt>.
|
18
|
-
def self.use
|
19
|
-
@use ||= DEFAULT
|
20
|
-
end
|
21
|
-
|
22
|
-
# Sets the +parser+ to use. Raises an +ArgumentError+ unless the +parser+ exists.
|
23
|
-
def self.use=(parser)
|
24
|
-
validate_parser! parser
|
25
|
-
@use = parser
|
26
|
-
end
|
27
|
-
|
28
|
-
# Returns the parsed +xml+ using the parser to use. Raises an +ArgumentError+
|
29
|
-
# unless the optional or default +parser+ exists.
|
30
|
-
def self.parse(xml, parser = nil, nori = Nori)
|
31
|
-
load_parser(parser).parse(xml, nori)
|
32
|
-
end
|
33
|
-
|
34
|
-
private
|
35
|
-
|
36
|
-
# Raises an +ArgumentError+ unless the +parser+ exists.
|
37
|
-
def self.validate_parser!(parser)
|
38
|
-
raise ArgumentError, "Invalid Nori parser: #{parser}" unless PARSERS[parser]
|
39
|
-
end
|
40
|
-
|
41
|
-
# Requires and returns the +parser+ to use.
|
42
|
-
def self.load_parser(parser)
|
43
|
-
parser ||= use
|
44
|
-
validate_parser! parser
|
45
|
-
|
46
|
-
require "nori/parser/#{parser}"
|
47
|
-
const_get PARSERS[parser]
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
51
|
-
end
|
data/spec/nori/parser_spec.rb
DELETED
@@ -1,54 +0,0 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe Nori::Parser do
|
4
|
-
let(:parser) { Nori::Parser }
|
5
|
-
|
6
|
-
describe "::PARSERS" do
|
7
|
-
it "should return a Hash of parser details" do
|
8
|
-
Nori::Parser::PARSERS.should == { :rexml => "REXML", :nokogiri => "Nokogiri" }
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
|
-
describe ".use" do
|
13
|
-
it "should default to REXML" do
|
14
|
-
parser.use.should == Nori::Parser::DEFAULT
|
15
|
-
end
|
16
|
-
|
17
|
-
it "should accept a parser to use" do
|
18
|
-
parser.use = :nokogiri
|
19
|
-
parser.use.should == :nokogiri
|
20
|
-
|
21
|
-
# reset to default
|
22
|
-
parser.use = Nori::Parser::DEFAULT
|
23
|
-
parser.use.should == Nori::Parser::DEFAULT
|
24
|
-
end
|
25
|
-
|
26
|
-
it "should raise an ArgumentError in case of an invalid parser" do
|
27
|
-
lambda { parser.use = :unknown }.should raise_error(ArgumentError)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
describe ".parse" do
|
32
|
-
it "should load the parser to use and parse the given xml" do
|
33
|
-
parser.parse("<some>xml</some>").should == { "some" => "xml" }
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
describe ".parse with different nori" do
|
38
|
-
let(:other_nori) do
|
39
|
-
module OtherNori
|
40
|
-
extend Nori
|
41
|
-
end
|
42
|
-
OtherNori.configure do |config|
|
43
|
-
config.convert_tags_to { |tag| tag.upcase }
|
44
|
-
end
|
45
|
-
OtherNori
|
46
|
-
end
|
47
|
-
|
48
|
-
it "should load the parser to use and parse the given xml" do
|
49
|
-
parser.parse("<SomeThing>xml</SomeThing>").should == { "SomeThing" => "xml" }
|
50
|
-
parser.parse("<SomeThing>xml</SomeThing>", nil, other_nori).should == { "SOMETHING" => "xml" }
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
end
|