opensrs 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -15,9 +15,13 @@ You can then include it in a Ruby project, like so:
15
15
 
16
16
  require 'opensrs'
17
17
 
18
- Alternatively, you can include it in a Rails project in the <tt>environment.rb</tt> file, like so:
18
+ Alternatively, you can include it in a Rails 2.x project in the <tt>environment.rb</tt> file, like so:
19
19
 
20
20
  config.gem "opensrs"
21
+
22
+ For Rails 3.x, use the <tt>Gemfile</tt>:
23
+
24
+ gem "opensrs"
21
25
 
22
26
  == Usage
23
27
 
@@ -31,14 +35,14 @@ This library provides basic functionality for interacting with the OpenSRS XML A
31
35
  To connect, instantiate a new OpenSRS::Server object:
32
36
 
33
37
  server = OpenSRS::Server.new(
34
- :server => "https://rr-n1-tor.opensrs.net:55443/",
38
+ :server => "https://rr-n1-tor.opensrs.net:55443/",
35
39
  :username => "testing",
36
40
  :password => "53cr3t",
37
- :key => "c633be3170c7fb3fb29e2f99b84be24107b37e206663967f8cdfarandomc17d411fc28b3dbff47d03c51c61279bd6f3c545a3210777ab93b44e6aef5e23d82"
41
+ :key => "c633be3170c7fb3fb29e2f99b84be2410..."
38
42
  )
39
43
 
40
- Once you have this, you can build from this to create the methods that you need. For instance, let's say we want to grab our balance. The
41
- OpenSRS XML API takes a couple of attributes for all commands. You can include those here:
44
+ Once you have this, you can build from this to create the methods that you need. For instance, let's say we want to grab
45
+ our account balance. The OpenSRS XML API takes a couple of attributes for all commands. You can include those here:
42
46
 
43
47
  def get_balance
44
48
  server.call(
data/Rakefile CHANGED
@@ -7,10 +7,12 @@ begin
7
7
  gem.name = "opensrs"
8
8
  gem.summary = %Q{Provides support to utilise the OpenSRS API with Ruby/Rails.}
9
9
  gem.description = %Q{Provides support to utilise the OpenSRS API with Ruby/Rails.}
10
- gem.email = "jdelsman@voxxit.com"
10
+ gem.email = "josh@voxx.it"
11
11
  gem.homepage = "http://github.com/voxxit/opensrs"
12
12
  gem.authors = ["Josh Delsman"]
13
+ gem.add_dependency "libxml-ruby"
13
14
  gem.add_development_dependency "thoughtbot-shoulda"
15
+ gem.add_development_dependency "rspec"
14
16
  end
15
17
  rescue LoadError
16
18
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
@@ -52,4 +54,4 @@ Rake::RDocTask.new do |rdoc|
52
54
  rdoc.title = "opensrs #{version}"
53
55
  rdoc.rdoc_files.include('README*')
54
56
  rdoc.rdoc_files.include('lib/**/*.rb')
55
- end
57
+ end
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.0
@@ -13,11 +13,17 @@ module OpenSRS
13
13
  # We need to return the error message unless the
14
14
  # response is successful.
15
15
  def errors
16
- "#{response["response_text"]} (Code #{response["response_code"]})" unless success?
16
+ if !success?
17
+ if response["response_text"] and response["response_code"]
18
+ "#{response["response_text"]} (Code #{response["response_code"]})"
19
+ else
20
+ "Unknown error"
21
+ end
22
+ end
17
23
  end
18
24
 
19
25
  def success?
20
- response["is_success"].to_s == "1"
26
+ response["is_success"] ? response["is_success"].to_s == "1" : true
21
27
  end
22
28
  end
23
29
  end
@@ -1,7 +1,7 @@
1
- require 'uri'
2
- require 'net/https'
3
- require 'digest/md5'
4
- require 'openssl'
1
+ require "uri"
2
+ require "net/https"
3
+ require "digest/md5"
4
+ require "openssl"
5
5
 
6
6
  module OpenSRS
7
7
  class Server
@@ -48,7 +48,7 @@ module OpenSRS
48
48
 
49
49
  def http
50
50
  http = Net::HTTP.new(server.host, server.port)
51
- http.use_ssl = (server.scheme == 'https')
51
+ http.use_ssl = (server.scheme == "https")
52
52
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE
53
53
  http
54
54
  end
data/lib/opensrs/xml.rb CHANGED
@@ -1,138 +1,117 @@
1
- require 'rexml/document'
1
+ require "libxml"
2
2
 
3
3
  module OpenSRS
4
4
  class XML
5
- include REXML
5
+ include LibXML::XML
6
6
 
7
7
  # First, builds REXML elements for the inputted data. Then, it will
8
8
  # go ahead and build the entire XML document to send to OpenSRS.
9
9
  def self.build(data)
10
- data = encode(data) unless data.kind_of?(REXML::Element)
10
+ xml = Document.new
11
+ xml.root = envelope = Node.new("OPS_envelope")
11
12
 
12
- doc = Document.new
13
- doc << XMLDecl.new("1.0", "UTF-8", "no")
14
- doc << DocType.new("OPS_envelope", "SYSTEM 'ops.dtd'")
13
+ envelope << header = Node.new("header")
14
+ envelope << body = Node.new("body")
15
+ header << Node.new("version", "0.9")
16
+ body << data_block = Node.new("data_block")
15
17
 
16
- root = Element.new("OPS_envelope", doc)
18
+ encode_data(data, data_block)
17
19
 
18
- Element.new("version", Element.new("header", root)).add_text("0.9")
19
- Element.new("data_block", Element.new("body", root)).add(data)
20
-
21
- return doc.to_s
22
- end
23
-
24
- # Encodes individual elements, and their child elements, for the root
25
- # XML document.
26
- def self.encode(data)
27
- case data
28
- when Array
29
- element = Element.new("dt_array")
30
-
31
- data.each_with_index do |v, j|
32
- item = Element.new("item")
33
- item.add_attribute("key", j.to_s)
34
- item.add_element(encode(v))
35
-
36
- element.add_element(item)
37
- end
38
-
39
- return element
40
- when Hash
41
- element = Element.new("dt_assoc")
42
-
43
- data.each do |k, v|
44
- item = Element.new("item")
45
- item.add_attribute("key", k.to_s)
46
- item.add_element(encode(v))
47
-
48
- element.add_element(item)
49
- end
50
-
51
- return element
52
- when String, Numeric, Date, Time, Symbol, NilClass
53
- element = Element.new("dt_scalar")
54
- element.text = data.to_s
55
-
56
- return element
57
- else
58
- # Give up, probably wrong!
59
- return encode(data.inspect)
60
- end
20
+ return xml.to_s
61
21
  end
62
22
 
63
23
  # Parses the main data block from OpenSRS and discards
64
24
  # the rest of the response.
65
25
  def self.parse(response)
66
- response.gsub!(/<!DOCTYPE OPS_envelope SYSTEM "ops.dtd">/, "")
67
- response = Document.new(response) unless response.is_a?(REXML::Document)
26
+ doc = Parser.string(response).parse
27
+ data_block = doc.find("//OPS_envelope/body/data_block/*")
68
28
 
69
- data_block_children = XPath.first(response, "//OPS_envelope/body/data_block/*")
29
+ raise ArgumentError.new("No data found in document") if !data_block
70
30
 
71
- raise ArgumentError.new("No OPS_envelope found in document") unless data_block_children
72
-
73
- return decode(data_block_children)
31
+ return decode_data(data_block)
74
32
  end
75
33
 
76
- # Decodes individual data elements from OpenSRS response.
77
- def self.decode(data)
78
- if data.is_a?(String)
79
- doc = Document.new(data)
80
-
81
- if doc.children.length == 1
82
- return decode(doc.children[0])
83
- else
84
- doc.children.map { |i| return decode(i) }
34
+ protected
35
+
36
+ # Recursively decodes individual data elements from OpenSRS
37
+ # server response.
38
+ def self.decode_data(data)
39
+ data.each do |element|
40
+ case element.name
41
+ when "dt_array"
42
+ arr = []
43
+
44
+ element.children.each do |item|
45
+ next if item.empty?
46
+ arr[item.attributes["key"].to_i] = decode_data(item)
47
+ end
48
+
49
+ return arr
50
+ when "dt_assoc"
51
+ hash = {}
52
+
53
+ element.children.each do |item|
54
+ next if item.empty?
55
+ hash[item.attributes["key"]] = decode_data(item)
56
+ end
57
+
58
+ return hash
59
+ when "text", "item", "dt_scalar"
60
+ next if element.empty?
61
+ return element.content.strip
85
62
  end
86
63
  end
64
+ end
65
+
66
+ # Encodes individual elements, and their child elements, for the root
67
+ # XML document.
68
+ def self.encode_data(data, container = nil)
69
+ case data.class.to_s
70
+ when "Array" then return encode_dt_array(data, container)
71
+ when "Hash" then return encode_dt_assoc(data, container)
72
+ when "String", "Numeric", "Date", "Time", "Symbol", "NilClass"
73
+ return data.to_s
74
+ else
75
+ return data.inspect
76
+ end
87
77
 
88
- # The OpenSRS server's response (and the client docs) don't always
89
- # encapsulate scalars in <dt_scalar> as you'd expect - so we have to
90
- # infer that unexpected raw text is always a scalar.
91
- return guess_scalar(data.value) if data.is_a?(REXML::Text)
92
-
93
- case data.name
94
- when "dt_array"
95
- array = []
78
+ return nil
79
+ end
96
80
 
97
- data.elements.each("item") do |item|
98
- array[item.attributes["key"].to_i] = trim_and_decode(item.children)
99
- end
81
+ def self.encode_dt_array(data, container)
82
+ dt_array = Node.new("dt_array")
100
83
 
101
- return array
102
- when "dt_assoc"
103
- hash = {}
84
+ data.each_with_index do |item, index|
85
+ item_node = Node.new("item")
86
+ item_node["key"] = index.to_s
87
+ item_node << encode_data(item, item_node)
104
88
 
105
- data.elements.each("item") do |item|
106
- hash[item.attributes["key"]] = trim_and_decode(item.children)
107
- end
89
+ dt_array << item_node
90
+ end
108
91
 
109
- return hash
110
- when /^dt_scalar(ref)?/ then return guess_scalar(data.value)
92
+ if container
93
+ container << dt_array
94
+ else
95
+ return dt_array
111
96
  end
112
97
  end
113
98
 
114
- protected
99
+ def self.encode_dt_assoc(data, container)
100
+ dt_assoc = Node.new("dt_assoc")
115
101
 
116
- # Tries to correctly parse individual text into Date, Time, or Integers.
117
- # If it fails, just displays the returned text.
118
- def self.guess_scalar(text)
119
- case text
120
- when /^\d\d\d\d-\d\d-\d\d \d\d:\d\d:\d\d$/ then Time.parse(text)
121
- when /^\d\d\d\d-\d\d-\d\d$/ then Date.parse(text)
122
- else
123
- text
102
+ data.each do |key, value|
103
+ item_node = Node.new("item")
104
+ item_node["key"] = key.to_s
105
+ item_node << encode_data(value, item_node)
106
+
107
+ dt_assoc << item_node
124
108
  end
125
- end
126
109
 
127
- # Decodes lists, such as contact lists.
128
- def self.trim_and_decode(list)
129
- return "" if list.length.zero?
130
- return decode(list[0]) if list.length == 1
131
-
132
- first_non_text = list.select { |e| !e.kind_of?(REXML::Text) }[0]
133
-
134
- return decode(first_non_text) unless first_non_text.nil?
135
- return decode(list[0])
110
+ if container
111
+ container << dt_assoc
112
+ else
113
+ return dt_assoc
114
+ end
136
115
  end
137
116
  end
138
117
  end
@@ -0,0 +1,216 @@
1
+ require 'spec_helper'
2
+ require 'date'
3
+
4
+ describe OpenSRS::XML do
5
+ describe '#encode_data' do
6
+ context "on a 3 element array" do
7
+ before(:each) do
8
+ @e = OpenSRS::XML.encode_data([1,2,3])
9
+ end
10
+
11
+ it "is a REXML::Element" do
12
+ @e.should be_an_instance_of(LibXML::XML::Node)
13
+ end
14
+
15
+ it "is a dt_array" do
16
+ @e.name.should == 'dt_array'
17
+ end
18
+
19
+ it "has 3 children all called <item>" do
20
+ @e.should have(3).children
21
+ @e.children[0].name.should == "item"
22
+ @e.children[1].name.should == "item"
23
+ @e.children[2].name.should == "item"
24
+ end
25
+
26
+ it "has children with keys 0, 1 and 2" do
27
+ @e.children[0].attributes["key"].should == "0"
28
+ @e.children[1].attributes["key"].should == "1"
29
+ @e.children[2].attributes["key"].should == "2"
30
+ end
31
+ end
32
+
33
+ context "on a hash" do
34
+ before(:each) do
35
+ @e = OpenSRS::XML.encode_data({:name => "kitteh"})
36
+ end
37
+
38
+ it "is a REXML::Element" do
39
+ @e.should be_an_instance_of(LibXML::XML::Node)
40
+ end
41
+
42
+ it "is a dt_assoc" do
43
+ @e.name.should == 'dt_assoc'
44
+ end
45
+
46
+ it "has an <item> child with the right key" do
47
+ @e.should have(1).children
48
+ @e.children[0].name.should == 'item'
49
+ @e.children[0].attributes["key"].should == 'name'
50
+ end
51
+ end
52
+
53
+ context "produces a scalar" do
54
+ it "from a string" do
55
+ OpenSRS::XML.encode_data("cheezburger").to_s.should == "cheezburger"
56
+ end
57
+
58
+ it "from a string with XML characters" do
59
+ OpenSRS::XML.encode_data("<smile>").to_s.should == "<smile>"
60
+ end
61
+
62
+ it "from an integer" do
63
+ OpenSRS::XML.encode_data(12345).to_s.should == "12345"
64
+ end
65
+
66
+ it "from a date" do
67
+ date = Date.parse("2010/02/12")
68
+ OpenSRS::XML.encode_data(date).to_s.should == "2010-02-12"
69
+ end
70
+
71
+ it "from a symbol" do
72
+ OpenSRS::XML.encode_data(:name).to_s.should == "name"
73
+ end
74
+
75
+ it "from true or false" do
76
+ OpenSRS::XML.encode_data(true).to_s.should == "true"
77
+ OpenSRS::XML.encode_data(false).to_s.should == "false"
78
+ end
79
+ end
80
+ end
81
+
82
+ describe '#parse' do
83
+ it "should handle scalar values" do
84
+ xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
85
+ <!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
86
+ <OPS_envelope>
87
+ <header>
88
+ <version>1.0</version>
89
+ </header>
90
+ <body>
91
+ <data_block>
92
+ <dt_scalar>Tom Jones</dt_scalar>
93
+ </data_block>
94
+ </body>
95
+ </OPS_envelope>}
96
+
97
+ resp = OpenSRS::XML.parse(xml)
98
+ resp.should == "Tom Jones"
99
+ end
100
+
101
+ it "should handle associate arrays with arrays of values" do
102
+ xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
103
+ <!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
104
+ <OPS_envelope>
105
+ <header>
106
+ <version>1.0</version>
107
+ </header>
108
+ <body>
109
+ <data_block>
110
+ <dt_assoc>
111
+ <item key='domain_list'>
112
+ <dt_array>
113
+ <item key='0'>ns1.example.com</item>
114
+ <item key='1'>ns2.example.com</item>
115
+ <item key='2'>ns3.example.com</item>
116
+ </dt_array>
117
+ </item>
118
+ </dt_assoc>
119
+ </data_block>
120
+ </body>
121
+ </OPS_envelope>}
122
+
123
+ resp = OpenSRS::XML.parse(xml)
124
+ resp["domain_list"].class.should == Array
125
+ resp["domain_list"][0].should == "ns1.example.com"
126
+ resp["domain_list"][1].should == "ns2.example.com"
127
+ resp["domain_list"][2].should == "ns3.example.com"
128
+ end
129
+
130
+ it "should handle associative arrays containing other associative arrays" do
131
+ xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
132
+ <!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
133
+ <OPS_envelope>
134
+ <header>
135
+ <version>1.0</version>
136
+ </header>
137
+ <body>
138
+ <data_block>
139
+ <dt_assoc>
140
+ <item key="contact_set">
141
+ <dt_assoc>
142
+ <item key='owner'>
143
+ <dt_assoc>
144
+ <item key='first_name'>Tom</item>
145
+ <item key='last_name'>Jones</item>
146
+ </dt_assoc>
147
+ </item>
148
+ <item key='tech'>
149
+ <dt_assoc>
150
+ <item key='first_name'>Anne</item>
151
+ <item key='last_name'>Smith</item>
152
+ </dt_assoc>
153
+ </item>
154
+ </dt_assoc>
155
+ </item>
156
+ </dt_assoc>
157
+ </data_block>
158
+ </body>
159
+ </OPS_envelope>}
160
+
161
+ resp = OpenSRS::XML.parse(xml)
162
+
163
+ resp["contact_set"]["owner"]["first_name"].should == "Tom"
164
+ resp["contact_set"]["owner"]["last_name"].should == "Jones"
165
+ resp["contact_set"]["tech"]["first_name"].should == "Anne"
166
+ resp["contact_set"]["tech"]["last_name"].should == "Smith"
167
+ end
168
+
169
+ context "with a balance enquiry example response" do
170
+ before(:each) do
171
+ xml = %{<?xml version='1.0' encoding='UTF-8' standalone='no' ?>
172
+ <!DOCTYPE OPS_envelope SYSTEM 'ops.dtd'>
173
+ <OPS_envelope>
174
+ <header>
175
+ <version>0.9</version>
176
+ </header>
177
+ <body>
178
+ <data_block>
179
+ <dt_assoc>
180
+ <item key="protocol">XCP</item>
181
+ <item key="action">REPLY</item>
182
+ <item key="object">BALANCE</item>
183
+ <item key="is_success">1</item>
184
+ <item key="response_code">200</item>
185
+ <item key="response_text">Command successful</item>
186
+ <item key="attributes">
187
+ <dt_assoc>
188
+ <item key="balance">8549.18</item>
189
+ <item key="hold_balance">1676.05</item>
190
+ </dt_assoc>
191
+ </item>
192
+ </dt_assoc>
193
+ </data_block>
194
+ </body>
195
+ </OPS_envelope>}
196
+
197
+ @resp = OpenSRS::XML.parse(xml)
198
+ end
199
+
200
+ it "produces a hash" do
201
+ @resp.should be_an_instance_of(Hash)
202
+ end
203
+
204
+ it "has top level keys" do
205
+ @resp["protocol"].should == "XCP"
206
+ @resp["action"].should == "REPLY"
207
+ @resp["object"].should == "BALANCE"
208
+ end
209
+
210
+ it "has second level keys" do
211
+ @resp["attributes"]["balance"].should == "8549.18"
212
+ @resp["attributes"]["hold_balance"].should == "1676.05"
213
+ end
214
+ end
215
+ end
216
+ end
@@ -0,0 +1 @@
1
+ require 'opensrs/xml.rb'
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opensrs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 0
9
+ version: 0.2.0
5
10
  platform: ruby
6
11
  authors:
7
12
  - Josh Delsman
@@ -9,21 +14,50 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-02-06 00:00:00 +00:00
17
+ date: 2011-01-26 00:00:00 -05:00
13
18
  default_executable:
14
19
  dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: libxml-ruby
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 0
30
+ version: "0"
31
+ type: :runtime
32
+ version_requirements: *id001
15
33
  - !ruby/object:Gem::Dependency
16
34
  name: thoughtbot-shoulda
35
+ prerelease: false
36
+ requirement: &id002 !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ segments:
42
+ - 0
43
+ version: "0"
17
44
  type: :development
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
45
+ version_requirements: *id002
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ prerelease: false
49
+ requirement: &id003 !ruby/object:Gem::Requirement
50
+ none: false
20
51
  requirements:
21
52
  - - ">="
22
53
  - !ruby/object:Gem::Version
54
+ segments:
55
+ - 0
23
56
  version: "0"
24
- version:
57
+ type: :development
58
+ version_requirements: *id003
25
59
  description: Provides support to utilise the OpenSRS API with Ruby/Rails.
26
- email: jdelsman@voxxit.com
60
+ email: josh@voxx.it
27
61
  executables: []
28
62
 
29
63
  extensions: []
@@ -42,6 +76,8 @@ files:
42
76
  - lib/opensrs/response.rb
43
77
  - lib/opensrs/server.rb
44
78
  - lib/opensrs/xml.rb
79
+ - spec/opensrs/xml_spec.rb
80
+ - spec/spec_helper.rb
45
81
  - test/opensrs_test.rb
46
82
  - test/test_helper.rb
47
83
  has_rdoc: true
@@ -54,24 +90,30 @@ rdoc_options:
54
90
  require_paths:
55
91
  - lib
56
92
  required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
57
94
  requirements:
58
95
  - - ">="
59
96
  - !ruby/object:Gem::Version
97
+ segments:
98
+ - 0
60
99
  version: "0"
61
- version:
62
100
  required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
63
102
  requirements:
64
103
  - - ">="
65
104
  - !ruby/object:Gem::Version
105
+ segments:
106
+ - 0
66
107
  version: "0"
67
- version:
68
108
  requirements: []
69
109
 
70
110
  rubyforge_project:
71
- rubygems_version: 1.3.5
111
+ rubygems_version: 1.3.7
72
112
  signing_key:
73
113
  specification_version: 3
74
114
  summary: Provides support to utilise the OpenSRS API with Ruby/Rails.
75
115
  test_files:
116
+ - spec/opensrs/xml_spec.rb
117
+ - spec/spec_helper.rb
76
118
  - test/opensrs_test.rb
77
119
  - test/test_helper.rb