opensrs 0.1.1 → 0.2.0

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/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