glennr_opensrs 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,59 @@
1
+ begin
2
+ require 'nokogiri'
3
+ rescue LoadError => e
4
+ $stderr.puts "Cannot find Nokogiri gem. Please install Nokogiri before using it as the xml processor\n\n"
5
+ raise e
6
+ end
7
+
8
+ module OpenSRS
9
+ class XmlProcessor::Nokogiri < OpenSRS::XmlProcessor
10
+
11
+ def self.build(data)
12
+ builder = ::Nokogiri::XML::Builder.new
13
+
14
+ envelope = ::Nokogiri::XML::Node.new("OPS_envelope", builder.doc)
15
+ header = ::Nokogiri::XML::Node.new("header", builder.doc)
16
+ version = ::Nokogiri::XML::Node.new("version", builder.doc)
17
+ body = ::Nokogiri::XML::Node.new("body", builder.doc)
18
+ data_block = ::Nokogiri::XML::Node.new("data_block", builder.doc)
19
+ other_data = encode_data(data, builder.doc)
20
+ builder.doc << envelope << header << version << '0.9'
21
+ envelope << body << data_block << other_data
22
+ return builder.to_xml
23
+ end
24
+
25
+ protected
26
+
27
+ def self.data_block_element(response)
28
+ doc = ::Nokogiri::XML(response)
29
+ return doc.xpath('//OPS_envelope/body/data_block/*')
30
+ end
31
+
32
+ def self.decode_dt_array_data(element)
33
+ dt_array = []
34
+
35
+ element.children.each do |item|
36
+ next if item.content.strip.empty?
37
+ dt_array[item.attributes["key"].value.to_i] = decode_data(item.children)
38
+ end
39
+
40
+ return dt_array
41
+ end
42
+
43
+ def self.decode_dt_assoc_data(element)
44
+ dt_assoc = {}
45
+
46
+ element.children.each do |item|
47
+ next if item.content.strip.empty?
48
+ dt_assoc[item.attributes["key"].value] = decode_data(item.children)
49
+ end
50
+
51
+ return dt_assoc
52
+ end
53
+
54
+ def self.new_element(element_name, container)
55
+ return ::Nokogiri::XML::Node.new(element_name.to_s, container)
56
+ end
57
+
58
+ end
59
+ end
data/opensrs.gemspec ADDED
@@ -0,0 +1,76 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "opensrs"
8
+ s.version = "0.3.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Josh Delsman", "Glenn Roberts"]
12
+ s.date = "2013-03-11"
13
+ s.description = "Provides support to utilize the OpenSRS API with Ruby/Rails."
14
+ s.email = "jdelsman@voxxit.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE",
24
+ "README.rdoc",
25
+ "Rakefile",
26
+ "lib/opensrs.rb",
27
+ "lib/opensrs/response.rb",
28
+ "lib/opensrs/server.rb",
29
+ "lib/opensrs/version.rb",
30
+ "lib/opensrs/xml_processor.rb",
31
+ "lib/opensrs/xml_processor/libxml.rb",
32
+ "lib/opensrs/xml_processor/nokogiri.rb",
33
+ "opensrs.gemspec",
34
+ "spec/opensrs/server_spec.rb",
35
+ "spec/opensrs/version_spec.rb",
36
+ "spec/opensrs/xml_processor/libxml_spec.rb",
37
+ "spec/opensrs/xml_processor/nokogiri_spec.rb",
38
+ "spec/spec_helper.rb"
39
+ ]
40
+ s.homepage = "http://github.com/voxxit/opensrs"
41
+ s.licenses = ["MIT"]
42
+ s.require_paths = ["lib"]
43
+ s.rubygems_version = "1.8.23"
44
+ s.summary = "Provides support to utilize the OpenSRS API with Ruby/Rails."
45
+
46
+ if s.respond_to? :specification_version then
47
+ s.specification_version = 3
48
+
49
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
50
+ s.add_runtime_dependency(%q<libxml-ruby>, ["~> 2.1.2"])
51
+ s.add_development_dependency(%q<nokogiri>, ["~> 1.4.6"])
52
+ s.add_development_dependency(%q<jeweler>, [">= 0"])
53
+ s.add_development_dependency(%q<git>, [">= 0"])
54
+ s.add_development_dependency(%q<rake>, [">= 0"])
55
+ s.add_development_dependency(%q<shoulda>, [">= 0"])
56
+ s.add_development_dependency(%q<rspec>, ["~> 2.0"])
57
+ else
58
+ s.add_dependency(%q<libxml-ruby>, ["~> 2.1.2"])
59
+ s.add_dependency(%q<nokogiri>, ["~> 1.4.6"])
60
+ s.add_dependency(%q<jeweler>, [">= 0"])
61
+ s.add_dependency(%q<git>, [">= 0"])
62
+ s.add_dependency(%q<rake>, [">= 0"])
63
+ s.add_dependency(%q<shoulda>, [">= 0"])
64
+ s.add_dependency(%q<rspec>, ["~> 2.0"])
65
+ end
66
+ else
67
+ s.add_dependency(%q<libxml-ruby>, ["~> 2.1.2"])
68
+ s.add_dependency(%q<nokogiri>, ["~> 1.4.6"])
69
+ s.add_dependency(%q<jeweler>, [">= 0"])
70
+ s.add_dependency(%q<git>, [">= 0"])
71
+ s.add_dependency(%q<rake>, [">= 0"])
72
+ s.add_dependency(%q<shoulda>, [">= 0"])
73
+ s.add_dependency(%q<rspec>, ["~> 2.0"])
74
+ end
75
+ end
76
+
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpenSRS::Server do
4
+ let(:server) { OpenSRS::Server.new }
5
+
6
+ describe '#new' do
7
+ it 'allows timeouts to be set' do
8
+ server = OpenSRS::Server.new({ :timeout => 90 })
9
+ server.timeout.should == 90
10
+ server.open_timeout.should be_nil
11
+ end
12
+
13
+ it 'allows open timeouts to be set' do
14
+ server = OpenSRS::Server.new({ :timeout => 90, :open_timeout => 10 })
15
+ server.timeout.should eq(90)
16
+ server.open_timeout.should eq(10)
17
+ end
18
+
19
+ it 'leaves it up to Net::HTTP if no timeouts given' do
20
+ server.timeout.should be_nil
21
+ server.open_timeout.should be_nil
22
+ end
23
+ end
24
+
25
+ describe ".call" do
26
+ let(:response) { double(:body => 'some response') }
27
+ let(:header) { {"some" => "header" } }
28
+ let(:xml) { '<some xml></some xml>' }
29
+ let(:response_xml) { xml }
30
+ let(:xml_processor) { double OpenSRS::XmlProcessor }
31
+ let(:http) { double(Net::HTTP, :use_ssl= => true, :verify_mode= => true) }
32
+
33
+ before :each do
34
+ server.stub(:headers).and_return header
35
+ xml_processor.stub(:build).and_return xml
36
+ xml_processor.stub(:parse).and_return response_xml
37
+ server.stub(:xml_processor).and_return xml_processor
38
+ http.stub(:post).and_return response
39
+ Net::HTTP.stub(:new).and_return http
40
+ end
41
+
42
+ it "builds XML request" do
43
+ xml_processor.should_receive(:build).with(:protocol => "XCP", :some => 'option')
44
+ server.call(:some => 'option')
45
+ end
46
+
47
+ it "posts to given path" do
48
+ server.server = URI.parse 'http://with-path.com/endpoint'
49
+ http.should_receive(:post).with('/endpoint', xml, header).and_return double.as_null_object
50
+ server.call
51
+ end
52
+
53
+ it "parses the response" do
54
+ xml_processor.should_receive(:parse).with(response.body)
55
+ server.call(:some => 'option')
56
+ end
57
+
58
+ it "posts to root path" do
59
+ server.server = URI.parse 'http://root-path.com/'
60
+ http.should_receive(:post).with('/', xml, header).and_return double.as_null_object
61
+ server.call
62
+ end
63
+
64
+ it "defaults path to '/'" do
65
+ server.server = URI.parse 'http://no-path.com'
66
+ http.should_receive(:post).with('/', xml, header).and_return double.as_null_object
67
+ server.call
68
+ end
69
+
70
+ it 'allows overriding of default (Net:HTTP) timeouts' do
71
+ server.timeout = 90
72
+
73
+ http.should_receive(:open_timeout=).with(90)
74
+ http.should_receive(:read_timeout=).with(90)
75
+
76
+ server.call( { :some => 'data' } )
77
+ end
78
+
79
+ it 'allows overriding of default (Net:HTTP) timeouts' do
80
+ server.timeout = 180
81
+ server.open_timeout = 30
82
+
83
+ http.should_receive(:read_timeout=).with(180)
84
+ http.should_receive(:open_timeout=).with(180)
85
+ http.should_receive(:open_timeout=).with(30)
86
+
87
+ server.call( { :some => 'data' } )
88
+ end
89
+
90
+ it 're-raises Net:HTTP timeouts' do
91
+ http.should_receive(:post).and_raise err = Timeout::Error.new('test')
92
+ expect { server.call }.to raise_exception OpenSRS::TimeoutError
93
+ end
94
+ end
95
+
96
+ describe "#test xml processor" do
97
+ context "on class initialization" do
98
+ it { server.xml_processor.should eql(OpenSRS::XmlProcessor::Libxml) }
99
+ end
100
+
101
+ context "on changing xml processor" do
102
+ before(:each) do
103
+ OpenSRS::Server.xml_processor = :nokogiri
104
+ end
105
+
106
+ it { server.xml_processor.should eql(OpenSRS::XmlProcessor::Nokogiri) }
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe OpenSRS::Version do
4
+ describe "VERSION" do
5
+ it "should return version string" do
6
+ OpenSRS::Version::VERSION.should eql("0.3.3")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,254 @@
1
+ require 'spec_helper'
2
+ require 'date'
3
+
4
+ describe OpenSRS::XmlProcessor::Libxml do
5
+ describe ".build" do
6
+ it "should create XML for a nested hash" do
7
+ attributes = {:foo => {:bar => 'baz'}}
8
+ xml = OpenSRS::XmlProcessor::Libxml.build(attributes)
9
+ xml.should eq %{<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<OPS_envelope>\n <header>\n <version>0.9</version>\n </header>\n <body>\n <data_block>\n <dt_assoc>\n <item key=\"foo\">\n <dt_assoc>\n <item key=\"bar\">baz</item>\n </dt_assoc>\n </item>\n </dt_assoc>\n </data_block>\n </body>\n</OPS_envelope>\n}
10
+ end
11
+ end
12
+
13
+ describe '.encode_data' do
14
+ context "on a 3 element array" do
15
+ before(:each) do
16
+ @e = OpenSRS::XmlProcessor::Libxml.encode_data([1,2,3])
17
+ end
18
+
19
+ it "is a REXML::Element" do
20
+ @e.should be_an_instance_of(LibXML::XML::Node)
21
+ end
22
+
23
+ it "is a dt_array" do
24
+ @e.name.should == 'dt_array'
25
+ end
26
+
27
+ it "has 3 children all called <item>" do
28
+ @e.should have(3).children
29
+ @e.children[0].name.should == "item"
30
+ @e.children[1].name.should == "item"
31
+ @e.children[2].name.should == "item"
32
+ end
33
+
34
+ it "has children with keys 0, 1 and 2" do
35
+ @e.children[0].attributes["key"].should == "0"
36
+ @e.children[1].attributes["key"].should == "1"
37
+ @e.children[2].attributes["key"].should == "2"
38
+ end
39
+ end
40
+
41
+ context "on a hash" do
42
+ before(:each) do
43
+ @e = OpenSRS::XmlProcessor::Libxml.encode_data({:name => "kitteh"})
44
+ end
45
+
46
+ it "is a REXML::Element" do
47
+ @e.should be_an_instance_of(LibXML::XML::Node)
48
+ end
49
+
50
+ it "is a dt_assoc" do
51
+ @e.name.should == 'dt_assoc'
52
+ end
53
+
54
+ it "has an <item> child with the right key" do
55
+ @e.should have(1).children
56
+ @e.children[0].name.should == 'item'
57
+ @e.children[0].attributes["key"].should == 'name'
58
+ end
59
+ end
60
+
61
+ context "on a nested hash" do
62
+ before(:each) do
63
+ @e = OpenSRS::XmlProcessor::Libxml.encode_data({:suggestion => {:maximum => "10"}})
64
+ end
65
+
66
+ it "is a REXML::Element" do
67
+ @e.should be_an_instance_of(LibXML::XML::Node)
68
+ end
69
+
70
+ it "is a dt_assoc" do
71
+ @e.name.should == 'dt_assoc'
72
+ end
73
+
74
+ it "has an <item> child with the correct children" do
75
+ @e.should have(1).children
76
+ suggestion = @e.children[0]
77
+ suggestion.name.should == 'item'
78
+ suggestion.attributes["key"].should == 'suggestion'
79
+
80
+ suggestion.should have(1).children
81
+ dt_assoc = suggestion.children[0]
82
+ dt_assoc.name.should == 'dt_assoc'
83
+
84
+ dt_assoc.should have(1).children
85
+ maximum = dt_assoc.children[0]
86
+ maximum.name.should == 'item'
87
+ maximum.attributes["key"].should == 'maximum'
88
+ end
89
+ end
90
+
91
+ context "produces a scalar" do
92
+ it "from a string" do
93
+ OpenSRS::XmlProcessor::Libxml.encode_data("cheezburger").to_s.should == "cheezburger"
94
+ end
95
+
96
+ it "from a string with XML characters" do
97
+ OpenSRS::XmlProcessor::Libxml.encode_data("<smile>").to_s.should == "<smile>"
98
+ end
99
+
100
+ it "from an integer" do
101
+ OpenSRS::XmlProcessor::Libxml.encode_data(12345).to_s.should == "12345"
102
+ end
103
+
104
+ it "from a date" do
105
+ date = Date.parse("2010/02/12")
106
+ OpenSRS::XmlProcessor::Libxml.encode_data(date).to_s.should == "2010-02-12"
107
+ end
108
+
109
+ it "from a symbol" do
110
+ OpenSRS::XmlProcessor::Libxml.encode_data(:name).to_s.should == "name"
111
+ end
112
+
113
+ it "from true or false" do
114
+ OpenSRS::XmlProcessor::Libxml.encode_data(true).to_s.should == "true"
115
+ OpenSRS::XmlProcessor::Libxml.encode_data(false).to_s.should == "false"
116
+ end
117
+ end
118
+ end
119
+
120
+ describe '.parse' do
121
+ it "should handle scalar values" do
122
+ xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
123
+ <!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
124
+ <OPS_envelope>
125
+ <header>
126
+ <version>1.0</version>
127
+ </header>
128
+ <body>
129
+ <data_block>
130
+ <dt_scalar>Tom Jones</dt_scalar>
131
+ </data_block>
132
+ </body>
133
+ </OPS_envelope>}
134
+
135
+ resp = OpenSRS::XmlProcessor::Libxml.parse(xml)
136
+ resp.should == "Tom Jones"
137
+ end
138
+
139
+ it "should handle associate arrays with arrays of values" do
140
+ xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
141
+ <!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
142
+ <OPS_envelope>
143
+ <header>
144
+ <version>1.0</version>
145
+ </header>
146
+ <body>
147
+ <data_block>
148
+ <dt_assoc>
149
+ <item key='domain_list'>
150
+ <dt_array>
151
+ <item key='0'>ns1.example.com</item>
152
+ <item key='1'>ns2.example.com</item>
153
+ <item key='2'>ns3.example.com</item>
154
+ </dt_array>
155
+ </item>
156
+ </dt_assoc>
157
+ </data_block>
158
+ </body>
159
+ </OPS_envelope>}
160
+
161
+ resp = OpenSRS::XmlProcessor::Libxml.parse(xml)
162
+ resp["domain_list"].class.should == Array
163
+ resp["domain_list"][0].should == "ns1.example.com"
164
+ resp["domain_list"][1].should == "ns2.example.com"
165
+ resp["domain_list"][2].should == "ns3.example.com"
166
+ end
167
+
168
+ it "should handle associative arrays containing other associative arrays" do
169
+ xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
170
+ <!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
171
+ <OPS_envelope>
172
+ <header>
173
+ <version>1.0</version>
174
+ </header>
175
+ <body>
176
+ <data_block>
177
+ <dt_assoc>
178
+ <item key="contact_set">
179
+ <dt_assoc>
180
+ <item key='owner'>
181
+ <dt_assoc>
182
+ <item key='first_name'>Tom</item>
183
+ <item key='last_name'>Jones</item>
184
+ </dt_assoc>
185
+ </item>
186
+ <item key='tech'>
187
+ <dt_assoc>
188
+ <item key='first_name'>Anne</item>
189
+ <item key='last_name'>Smith</item>
190
+ </dt_assoc>
191
+ </item>
192
+ </dt_assoc>
193
+ </item>
194
+ </dt_assoc>
195
+ </data_block>
196
+ </body>
197
+ </OPS_envelope>}
198
+
199
+ resp = OpenSRS::XmlProcessor::Libxml.parse(xml)
200
+
201
+ resp["contact_set"]["owner"]["first_name"].should == "Tom"
202
+ resp["contact_set"]["owner"]["last_name"].should == "Jones"
203
+ resp["contact_set"]["tech"]["first_name"].should == "Anne"
204
+ resp["contact_set"]["tech"]["last_name"].should == "Smith"
205
+ end
206
+
207
+ context "with a balance enquiry example response" do
208
+ before(:each) do
209
+ xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
210
+ <!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
211
+ <OPS_envelope>
212
+ <header>
213
+ <version>0.9</version>
214
+ </header>
215
+ <body>
216
+ <data_block>
217
+ <dt_assoc>
218
+ <item key="protocol">XCP</item>
219
+ <item key="action">REPLY</item>
220
+ <item key="object">BALANCE</item>
221
+ <item key="is_success">1</item>
222
+ <item key="response_code">200</item>
223
+ <item key="response_text">Command successful</item>
224
+ <item key="attributes">
225
+ <dt_assoc>
226
+ <item key="balance">8549.18</item>
227
+ <item key="hold_balance">1676.05</item>
228
+ </dt_assoc>
229
+ </item>
230
+ </dt_assoc>
231
+ </data_block>
232
+ </body>
233
+ </OPS_envelope>}
234
+
235
+ @resp = OpenSRS::XmlProcessor::Libxml.parse(xml)
236
+ end
237
+
238
+ it "produces a hash" do
239
+ @resp.should be_an_instance_of(Hash)
240
+ end
241
+
242
+ it "has top level keys" do
243
+ @resp["protocol"].should == "XCP"
244
+ @resp["action"].should == "REPLY"
245
+ @resp["object"].should == "BALANCE"
246
+ end
247
+
248
+ it "has second level keys" do
249
+ @resp["attributes"]["balance"].should == "8549.18"
250
+ @resp["attributes"]["hold_balance"].should == "1676.05"
251
+ end
252
+ end
253
+ end
254
+ end