nori-ng-1.6 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.travis.yml +13 -0
- data/CHANGELOG.md +196 -0
- data/Gemfile +2 -0
- data/LICENSE +20 -0
- data/README.md +69 -0
- data/Rakefile +12 -0
- data/benchmark/benchmark.rb +19 -0
- data/benchmark/soap_response.xml +266 -0
- data/lib/nori/core_ext/hash.rb +75 -0
- data/lib/nori/core_ext/object.rb +13 -0
- data/lib/nori/core_ext/string.rb +21 -0
- data/lib/nori/core_ext.rb +3 -0
- data/lib/nori/parser/nokogiri.rb +47 -0
- data/lib/nori/parser/rexml.rb +51 -0
- data/lib/nori/string_io_file.rb +7 -0
- data/lib/nori/string_with_attributes.rb +7 -0
- data/lib/nori/version.rb +5 -0
- data/lib/nori/xml_utility_node.rb +253 -0
- data/lib/nori.rb +72 -0
- data/nori.gemspec +25 -0
- data/spec/nori/api_spec.rb +169 -0
- data/spec/nori/core_ext/hash_spec.rb +60 -0
- data/spec/nori/core_ext/object_spec.rb +19 -0
- data/spec/nori/core_ext/string_spec.rb +33 -0
- data/spec/nori/nori_spec.rb +630 -0
- data/spec/spec_helper.rb +2 -0
- metadata +113 -0
@@ -0,0 +1,169 @@
|
|
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 '#find' do
|
66
|
+
before do
|
67
|
+
upcase = lambda { |tag| tag.upcase }
|
68
|
+
@nori = nori(:convert_tags_to => upcase)
|
69
|
+
|
70
|
+
xml = '<userResponse id="1"><accountStatus>active</accountStatus></userResponse>'
|
71
|
+
@hash = @nori.parse(xml)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'returns the Hash when the path is empty' do
|
75
|
+
result = @nori.find(@hash)
|
76
|
+
expect(result).to eq("USERRESPONSE" => { "ACCOUNTSTATUS" => "active", "@ID" => "1" })
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'returns the result for a single key' do
|
80
|
+
result = @nori.find(@hash, 'userResponse')
|
81
|
+
expect(result).to eq("ACCOUNTSTATUS" => "active", "@ID" => "1")
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'returns the result for nested keys' do
|
85
|
+
result = @nori.find(@hash, 'userResponse', 'accountStatus')
|
86
|
+
expect(result).to eq("active")
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'strips the namespaces from Hash keys' do
|
90
|
+
xml = '<v1:userResponse xmlns:v1="http://example.com"><v1:accountStatus>active</v1:accountStatus></v1:userResponse>'
|
91
|
+
hash = @nori.parse(xml)
|
92
|
+
|
93
|
+
result = @nori.find(hash, 'userResponse', 'accountStatus')
|
94
|
+
expect(result).to eq("active")
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "#parse" do
|
99
|
+
it "defaults to use advanced typecasting" do
|
100
|
+
hash = nori.parse("<value>true</value>")
|
101
|
+
hash["value"].should == true
|
102
|
+
end
|
103
|
+
|
104
|
+
it "defaults to use the Nokogiri parser" do
|
105
|
+
# parsers are loaded lazily by default
|
106
|
+
require "nori/parser/nokogiri"
|
107
|
+
|
108
|
+
Nori::Parser::Nokogiri.should_receive(:parse).and_return({})
|
109
|
+
nori.parse("<any>thing</any>")
|
110
|
+
end
|
111
|
+
|
112
|
+
it "strips the XML" do
|
113
|
+
xml = double("xml")
|
114
|
+
xml.should_receive(:strip).and_return("<any>thing</any>")
|
115
|
+
|
116
|
+
nori.parse(xml).should == { "any" => "thing" }
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
context "#parse without :advanced_typecasting" do
|
121
|
+
it "can be changed to not typecast too much" do
|
122
|
+
hash = nori(:advanced_typecasting => false).parse("<value>true</value>")
|
123
|
+
hash["value"].should == "true"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context "#parse with :parser" do
|
128
|
+
it "can be configured to use the REXML parser" do
|
129
|
+
# parsers are loaded lazily by default
|
130
|
+
require "nori/parser/rexml"
|
131
|
+
|
132
|
+
Nori::Parser::REXML.should_receive(:parse).and_return({})
|
133
|
+
nori(:parser => :rexml).parse("<any>thing</any>")
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "#parse without :delete_namespace_attributes" do
|
138
|
+
it "can be changed to not delete xmlns attributes" do
|
139
|
+
xml = '<userResponse xmlns="http://schema.company.com/some/path/to/namespace/v1"><accountStatus>active</accountStatus></userResponse>'
|
140
|
+
hash = nori(:delete_namespace_attributes => false).parse(xml)
|
141
|
+
hash.should == {"userResponse" => {"@xmlns" => "http://schema.company.com/some/path/to/namespace/v1", "accountStatus" => "active"}}
|
142
|
+
end
|
143
|
+
|
144
|
+
it "can be changed to not delete xsi attributes" do
|
145
|
+
xml = '<userResponse xsi="abc:myType"><accountStatus>active</accountStatus></userResponse>'
|
146
|
+
hash = nori(:delete_namespace_attributes => false).parse(xml)
|
147
|
+
hash.should == {"userResponse" => {"@xsi" => "abc:myType", "accountStatus" => "active"}}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
context "#parse with :delete_namespace_attributes" do
|
152
|
+
it "can be changed to delete xmlns attributes" do
|
153
|
+
xml = '<userResponse xmlns="http://schema.company.com/some/path/to/namespace/v1"><accountStatus>active</accountStatus></userResponse>'
|
154
|
+
hash = nori(:delete_namespace_attributes => true).parse(xml)
|
155
|
+
hash.should == {"userResponse" => {"accountStatus" => "active"}}
|
156
|
+
end
|
157
|
+
|
158
|
+
it "can be changed to delete xsi attributes" do
|
159
|
+
xml = '<userResponse xsi="abc:myType"><accountStatus>active</accountStatus></userResponse>'
|
160
|
+
hash = nori(:delete_namespace_attributes => true).parse(xml)
|
161
|
+
hash.should == {"userResponse" => {"accountStatus" => "active"}}
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def nori(options = {})
|
166
|
+
Nori.new(options)
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Hash do
|
4
|
+
|
5
|
+
describe "#to_params" do
|
6
|
+
|
7
|
+
{
|
8
|
+
{ "foo" => "bar", "baz" => "bat" } => "foo=bar&baz=bat",
|
9
|
+
{ "foo" => [ "bar", "baz" ] } => "foo[]=bar&foo[]=baz",
|
10
|
+
{ "foo" => [ {"bar" => "1"}, {"bar" => 2} ] } => "foo[][bar]=1&foo[][bar]=2",
|
11
|
+
{ "foo" => { "bar" => [ {"baz" => 1}, {"baz" => "2"} ] } } => "foo[bar][][baz]=1&foo[bar][][baz]=2",
|
12
|
+
{ "foo" => {"1" => "bar", "2" => "baz"} } => "foo[1]=bar&foo[2]=baz"
|
13
|
+
}.each do |hash, params|
|
14
|
+
it "should covert hash: #{hash.inspect} to params: #{params.inspect}" do
|
15
|
+
hash.to_params.split('&').sort.should == params.split('&').sort
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should not leave a trailing &" do
|
20
|
+
{
|
21
|
+
:name => 'Bob',
|
22
|
+
:address => {
|
23
|
+
:street => '111 Ruby Ave.',
|
24
|
+
:city => 'Ruby Central',
|
25
|
+
:phones => ['111-111-1111', '222-222-2222']
|
26
|
+
}
|
27
|
+
}.to_params.should_not =~ /&$/
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should URL encode unsafe characters" do
|
31
|
+
{:q => "?&\" +"}.to_params.should == "q=%3F%26%22%20%2B"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#normalize_param" do
|
36
|
+
it "should have specs"
|
37
|
+
end
|
38
|
+
|
39
|
+
describe "#to_xml_attributes" do
|
40
|
+
|
41
|
+
it "should turn the hash into xml attributes" do
|
42
|
+
attrs = { :one => "ONE", "two" => "TWO" }.to_xml_attributes
|
43
|
+
attrs.should =~ /one="ONE"/m
|
44
|
+
attrs.should =~ /two="TWO"/m
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should preserve _ in hash keys" do
|
48
|
+
attrs = {
|
49
|
+
:some_long_attribute => "with short value",
|
50
|
+
:crash => :burn,
|
51
|
+
:merb => "uses extlib"
|
52
|
+
}.to_xml_attributes
|
53
|
+
|
54
|
+
attrs.should =~ /some_long_attribute="with short value"/
|
55
|
+
attrs.should =~ /merb="uses extlib"/
|
56
|
+
attrs.should =~ /crash="burn"/
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Object do
|
4
|
+
|
5
|
+
describe "#blank?" do
|
6
|
+
[nil, false, [], {}].each do |object|
|
7
|
+
it "should return true for: #{object.inspect}" do
|
8
|
+
object.blank?.should be_true
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
[true, [nil], 1, "string", { :key => "value" }].each do |object|
|
13
|
+
it "should return false for: #{object.inspect}" do
|
14
|
+
object.blank?.should be_false
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe String do
|
4
|
+
|
5
|
+
describe "#snakecase" do
|
6
|
+
it "lowercases one word CamelCase" do
|
7
|
+
"Merb".snakecase.should == "merb"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "makes one underscore snakecase two word CamelCase" do
|
11
|
+
"MerbCore".snakecase.should == "merb_core"
|
12
|
+
end
|
13
|
+
|
14
|
+
it "handles CamelCase with more than 2 words" do
|
15
|
+
"SoYouWantContributeToMerbCore".snakecase.should == "so_you_want_contribute_to_merb_core"
|
16
|
+
end
|
17
|
+
|
18
|
+
it "handles CamelCase with more than 2 capital letter in a row" do
|
19
|
+
"CNN".snakecase.should == "cnn"
|
20
|
+
"CNNNews".snakecase.should == "cnn_news"
|
21
|
+
"HeadlineCNNNews".snakecase.should == "headline_cnn_news"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "does NOT change one word lowercase" do
|
25
|
+
"merb".snakecase.should == "merb"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "leaves snake_case as is" do
|
29
|
+
"merb_core".snakecase.should == "merb_core"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|