opensrs 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +14 -0
- data/Gemfile.lock +36 -0
- data/LICENSE +1 -1
- data/README.rdoc +41 -37
- data/Rakefile +30 -40
- data/lib/opensrs.rb +3 -1
- data/lib/opensrs/server.rb +20 -5
- data/lib/opensrs/version.rb +5 -0
- data/lib/opensrs/xml_processor.rb +77 -0
- data/lib/opensrs/xml_processor/libxml.rb +60 -0
- data/lib/opensrs/xml_processor/nokogiri.rb +59 -0
- data/opensrs.gemspec +42 -21
- data/spec/opensrs/server_spec.rb +21 -0
- data/spec/opensrs/version_spec.rb +9 -0
- data/spec/opensrs/{xml_spec.rb → xml_processor/libxml_spec.rb} +16 -16
- data/spec/opensrs/xml_processor/nokogiri_spec.rb +228 -0
- data/spec/spec_helper.rb +5 -1
- metadata +104 -40
- data/VERSION +0 -1
- data/lib/opensrs/xml.rb +0 -109
- data/test/opensrs_test.rb +0 -7
- data/test/test_helper.rb +0 -10
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
git (1.2.5)
|
6
|
+
jeweler (1.6.0)
|
7
|
+
bundler (~> 1.0.0)
|
8
|
+
git (>= 1.2.5)
|
9
|
+
rake
|
10
|
+
libxml-ruby (1.0.0)
|
11
|
+
nokogiri (1.4.4)
|
12
|
+
rake (0.8.7)
|
13
|
+
rspec (2.5.0)
|
14
|
+
rspec-core (~> 2.5.0)
|
15
|
+
rspec-expectations (~> 2.5.0)
|
16
|
+
rspec-mocks (~> 2.5.0)
|
17
|
+
rspec-core (2.5.2)
|
18
|
+
rspec-expectations (2.5.0)
|
19
|
+
diff-lcs (~> 1.1.2)
|
20
|
+
rspec-mocks (2.5.0)
|
21
|
+
shoulda (2.11.3)
|
22
|
+
yard (0.6.8)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
ruby
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
bundler
|
29
|
+
git
|
30
|
+
jeweler
|
31
|
+
libxml-ruby (~> 1.0.0)
|
32
|
+
nokogiri
|
33
|
+
rake
|
34
|
+
rspec (~> 2.0)
|
35
|
+
shoulda
|
36
|
+
yard
|
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
= OpenSRS
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
This (unofficial) OpenSRS gem provides basic support to connect to, and utilize the OpenSRS API. This library has been well-tested in high-performance production
|
4
|
+
environments. More information on the API can be located here:
|
5
5
|
|
6
6
|
http://opensrs.com/resources/documentation/opensrs_xmlapi.pdf
|
7
7
|
|
@@ -9,19 +9,19 @@ http://opensrs.com/resources/documentation/opensrs_xmlapi.pdf
|
|
9
9
|
|
10
10
|
You can install this gem by doing the following:
|
11
11
|
|
12
|
-
|
12
|
+
$ gem install opensrs
|
13
13
|
|
14
14
|
You can then include it in a Ruby project, like so:
|
15
15
|
|
16
|
-
|
16
|
+
require 'opensrs'
|
17
17
|
|
18
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
21
|
|
22
22
|
For Rails 3.x, use the <tt>Gemfile</tt>:
|
23
23
|
|
24
|
-
|
24
|
+
gem "opensrs"
|
25
25
|
|
26
26
|
== Usage
|
27
27
|
|
@@ -32,38 +32,43 @@ This library provides basic functionality for interacting with the OpenSRS XML A
|
|
32
32
|
* XML encoding
|
33
33
|
* XML decoding
|
34
34
|
|
35
|
-
|
35
|
+
Currently, the library supports LibXML and Nokogiri as XML parsers. By default, it uses LibXML to parse and generate XML. If you'd like to use Nokogiri for parsing XML, then in one of your app initializers add the following line:
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
37
|
+
OpenSRS::Server.xml_processor = :nokogiri
|
38
|
+
|
39
|
+
To connect, instantiate a new <tt>OpenSRS::Server</tt> object:
|
40
|
+
|
41
|
+
server = OpenSRS::Server.new(
|
42
|
+
:server => "https://rr-n1-tor.opensrs.net:55443/",
|
43
|
+
:username => "testing",
|
44
|
+
:password => "53cr3t",
|
45
|
+
:key => "c633be3170c7fb3fb29e2f99b84be2410..."
|
46
|
+
)
|
47
|
+
|
48
|
+
NOTE: Connecting to OpenSRS requires that you add the IP(s) you're connecting from to their whitelist. Login to the testing or production servers, and add your IP(s) under Profile Management > Add IPs for Script/API Access. IP changes take about one hour to take effect.
|
49
|
+
|
50
|
+
Once you have a server connection class, you can build from this to create the methods that you need. For instance, let's say we want to grab our account balance. The OpenSRS XML API takes a couple of attributes for all commands. You can include those here:
|
51
|
+
|
52
|
+
def get_balance
|
53
|
+
server.call(
|
54
|
+
:action => "GET_BALANCE",
|
55
|
+
:object => "BALANCE"
|
42
56
|
)
|
43
|
-
|
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:
|
46
|
-
|
47
|
-
def get_balance
|
48
|
-
server.call(
|
49
|
-
:action => "GET_BALANCE",
|
50
|
-
:object => "BALANCE"
|
51
|
-
)
|
52
|
-
end
|
57
|
+
end
|
53
58
|
|
54
59
|
Sometimes you might need to include attributes for the command, such as a cookie, or the data attributes themselves. You can do this, too:
|
55
60
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
61
|
+
def create_nameserver(nameserver)
|
62
|
+
server.call(
|
63
|
+
:action => "CREATE",
|
64
|
+
:object => "NAMESERVER",
|
65
|
+
:cookie => "366828736:3210384",
|
66
|
+
:attributes => {
|
67
|
+
:name => nameserver.hostname,
|
68
|
+
:ipaddress => "212.112.123.11"
|
69
|
+
}
|
70
|
+
)
|
71
|
+
end
|
67
72
|
|
68
73
|
Responses from OpenSRS are returned in an OpenSRS::Response object, which gives you access to a multitude of things.
|
69
74
|
|
@@ -80,14 +85,13 @@ If you have any bugs or feature requests for this gem, feel free to add them in
|
|
80
85
|
http://github.com/voxxit/opensrs/issues
|
81
86
|
|
82
87
|
== Note on Patches/Pull Requests
|
83
|
-
|
88
|
+
|
84
89
|
* Fork the project.
|
85
90
|
* Make your feature addition or bug fix.
|
86
|
-
* Add tests for it. This is important so I don't break it in a
|
87
|
-
future version unintentionally.
|
91
|
+
* Add tests for it. This is important so I don't break it in a future version unintentionally.
|
88
92
|
* Commit, do not mess with rakefile, version, or history. If you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull.
|
89
93
|
* Send me a pull request. Bonus points for topic branches.
|
90
94
|
|
91
95
|
== Copyright
|
92
96
|
|
93
|
-
Copyright (c) 2010 Josh Delsman. See LICENSE for details.
|
97
|
+
Copyright (c) 2010-2011 Josh Delsman. Distributed under the MIT license. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -1,56 +1,46 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
|
4
|
+
begin
|
5
|
+
Bundler.setup(:default, :development)
|
6
|
+
rescue Bundler::BundlerError => e
|
7
|
+
$stderr.puts e.message
|
8
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
9
|
+
exit e.status_code
|
10
|
+
end
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift("lib")
|
13
|
+
|
2
14
|
require 'rake'
|
15
|
+
require 'opensrs'
|
3
16
|
|
4
17
|
begin
|
5
18
|
require 'jeweler'
|
19
|
+
|
6
20
|
Jeweler::Tasks.new do |gem|
|
7
|
-
gem.name
|
8
|
-
gem.
|
9
|
-
gem.
|
10
|
-
gem.
|
11
|
-
gem.
|
12
|
-
gem.
|
13
|
-
gem.
|
14
|
-
gem.
|
21
|
+
gem.name = "opensrs"
|
22
|
+
gem.version = OpenSRS::Version::VERSION
|
23
|
+
gem.summary = "Provides support to utilize the OpenSRS API with Ruby/Rails."
|
24
|
+
gem.description = "Provides support to utilize the OpenSRS API with Ruby/Rails."
|
25
|
+
gem.email = "josh@voxxit.com"
|
26
|
+
gem.homepage = "http://github.com/voxxit/opensrs"
|
27
|
+
gem.license = "MIT"
|
28
|
+
gem.authors = ["Josh Delsman"]
|
29
|
+
|
30
|
+
# Requirements are in Gemfile
|
15
31
|
end
|
16
32
|
rescue LoadError
|
17
33
|
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
18
34
|
end
|
19
35
|
|
20
|
-
require '
|
21
|
-
Rake::TestTask.new(:test) do |test|
|
22
|
-
test.libs << 'lib' << 'test'
|
23
|
-
test.pattern = 'test/**/*_test.rb'
|
24
|
-
test.verbose = true
|
25
|
-
end
|
36
|
+
require 'rspec/core/rake_task'
|
26
37
|
|
27
|
-
|
28
|
-
require 'rcov/rcovtask'
|
29
|
-
Rcov::RcovTask.new do |test|
|
30
|
-
test.libs << 'test'
|
31
|
-
test.pattern = 'test/**/*_test.rb'
|
32
|
-
test.verbose = true
|
33
|
-
end
|
34
|
-
rescue LoadError
|
35
|
-
task :rcov do
|
36
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
task :test => :check_dependencies
|
38
|
+
RSpec::Core::RakeTask.new(:spec)
|
41
39
|
|
42
|
-
task :default => :
|
40
|
+
task :default => :spec
|
43
41
|
|
44
|
-
require '
|
45
|
-
Rake::RDocTask.new do |rdoc|
|
46
|
-
if File.exist?('VERSION')
|
47
|
-
version = File.read('VERSION')
|
48
|
-
else
|
49
|
-
version = ""
|
50
|
-
end
|
42
|
+
require 'yard'
|
51
43
|
|
52
|
-
|
53
|
-
|
54
|
-
rdoc.rdoc_files.include('README*')
|
55
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
44
|
+
YARD::Rake::YardocTask.new do |t|
|
45
|
+
t.files = FileList['lib/**/*.rb']
|
56
46
|
end
|
data/lib/opensrs.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/opensrs/xml_processor'
|
2
|
+
require File.dirname(__FILE__) + '/opensrs/xml_processor/libxml.rb'
|
1
3
|
require File.dirname(__FILE__) + '/opensrs/server.rb'
|
2
4
|
require File.dirname(__FILE__) + '/opensrs/response.rb'
|
3
|
-
require File.dirname(__FILE__) + '/opensrs/
|
5
|
+
require File.dirname(__FILE__) + '/opensrs/version.rb'
|
4
6
|
|
5
7
|
module OpenSRS
|
6
8
|
end
|
data/lib/opensrs/server.rb
CHANGED
@@ -4,6 +4,8 @@ require "digest/md5"
|
|
4
4
|
require "openssl"
|
5
5
|
|
6
6
|
module OpenSRS
|
7
|
+
class BadResponse < StandardError; end
|
8
|
+
|
7
9
|
class Server
|
8
10
|
attr_accessor :server, :username, :password, :key
|
9
11
|
|
@@ -19,13 +21,26 @@ module OpenSRS
|
|
19
21
|
:protocol => "XCP"
|
20
22
|
}
|
21
23
|
|
22
|
-
xml =
|
23
|
-
|
24
|
-
response
|
25
|
-
parsed_response =
|
24
|
+
xml = xml_processor.build(attributes.merge!(options))
|
25
|
+
|
26
|
+
response = http.post(server.path, xml, headers(xml))
|
27
|
+
parsed_response = xml_processor.parse(response.body)
|
26
28
|
|
27
29
|
return OpenSRS::Response.new(parsed_response, xml, response.body)
|
30
|
+
rescue Net::HTTPBadResponse
|
31
|
+
raise OpenSRS::BadResponse, "Received a bad response from OpenSRS. Please check that your IP address is added to the whitelist, and try again."
|
32
|
+
end
|
33
|
+
|
34
|
+
def xml_processor
|
35
|
+
@@xml_processor
|
28
36
|
end
|
37
|
+
|
38
|
+
def self.xml_processor=(name)
|
39
|
+
require File.dirname(__FILE__) + "/xml_processor/#{name.to_s.downcase}"
|
40
|
+
@@xml_processor = OpenSRS::XmlProcessor.const_get("#{name.to_s.capitalize}")
|
41
|
+
end
|
42
|
+
|
43
|
+
OpenSRS::Server.xml_processor = :libxml
|
29
44
|
|
30
45
|
private
|
31
46
|
|
@@ -53,4 +68,4 @@ module OpenSRS
|
|
53
68
|
http
|
54
69
|
end
|
55
70
|
end
|
56
|
-
end
|
71
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module OpenSRS
|
2
|
+
|
3
|
+
class XmlProcessor
|
4
|
+
|
5
|
+
# Parses the main data block from OpenSRS and discards
|
6
|
+
# the rest of the response.
|
7
|
+
def self.parse(response)
|
8
|
+
data_block = data_block_element(response)
|
9
|
+
|
10
|
+
raise ArgumentError.new("No data found in document") if !data_block
|
11
|
+
|
12
|
+
return decode_data(data_block)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
# Encodes individual elements, and their child elements, for the root XML document.
|
18
|
+
def self.encode_data(data, container = nil)
|
19
|
+
case data.class.to_s
|
20
|
+
when "Array" then return encode_dt_array(data, container)
|
21
|
+
when "Hash" then return encode_dt_assoc(data, container)
|
22
|
+
when "String", "Numeric", "Date", "Time", "Symbol", "NilClass"
|
23
|
+
return data.to_s
|
24
|
+
else
|
25
|
+
return data.inspect
|
26
|
+
end
|
27
|
+
|
28
|
+
return nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.encode_dt_array(data, container)
|
32
|
+
dt_array = new_element(:dt_array, container)
|
33
|
+
|
34
|
+
data.each_with_index do |item, index|
|
35
|
+
item_node = new_element(:item, container)
|
36
|
+
item_node["key"] = index.to_s
|
37
|
+
item_node << encode_data(item, item_node)
|
38
|
+
|
39
|
+
dt_array << item_node
|
40
|
+
end
|
41
|
+
|
42
|
+
return dt_array
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.encode_dt_assoc(data, container)
|
46
|
+
dt_assoc = new_element(:dt_assoc, container)
|
47
|
+
|
48
|
+
data.each do |key, value|
|
49
|
+
item_node = new_element(:item, container)
|
50
|
+
item_node["key"] = key.to_s
|
51
|
+
item_node << encode_data(value, item_node)
|
52
|
+
|
53
|
+
dt_assoc << item_node
|
54
|
+
end
|
55
|
+
|
56
|
+
return dt_assoc
|
57
|
+
end
|
58
|
+
|
59
|
+
# Recursively decodes individual data elements from OpenSRS
|
60
|
+
# server response.
|
61
|
+
def self.decode_data(data)
|
62
|
+
data.each do |element|
|
63
|
+
case element.name
|
64
|
+
when "dt_array"
|
65
|
+
return decode_dt_array_data(element)
|
66
|
+
when "dt_assoc"
|
67
|
+
return decode_dt_assoc_data(element)
|
68
|
+
when "text", "item", "dt_scalar"
|
69
|
+
next if element.content.strip.empty?
|
70
|
+
return element.content.strip
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require "libxml"
|
2
|
+
|
3
|
+
module OpenSRS
|
4
|
+
class XmlProcessor::Libxml < OpenSRS::XmlProcessor
|
5
|
+
include ::LibXML::XML
|
6
|
+
|
7
|
+
# First, builds REXML elements for the inputted data. Then, it will
|
8
|
+
# go ahead and build the entire XML document to send to OpenSRS.
|
9
|
+
def self.build(data)
|
10
|
+
xml = Document.new
|
11
|
+
xml.root = envelope = Node.new("OPS_envelope")
|
12
|
+
|
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")
|
17
|
+
|
18
|
+
data_block << encode_data(data, data_block)
|
19
|
+
|
20
|
+
return xml.to_s
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
|
25
|
+
def self.data_block_element(response)
|
26
|
+
doc = Parser.string(response).parse
|
27
|
+
return doc.find("//OPS_envelope/body/data_block/*")
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.decode_dt_array_data(element)
|
31
|
+
dt_array = []
|
32
|
+
|
33
|
+
element.children.each do |item|
|
34
|
+
next if item.empty?
|
35
|
+
dt_array[item.attributes["key"].to_i] = decode_data(item)
|
36
|
+
end
|
37
|
+
|
38
|
+
return dt_array
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.decode_dt_assoc_data(element)
|
42
|
+
dt_assoc = {}
|
43
|
+
|
44
|
+
element.children.each do |item|
|
45
|
+
next if item.content.strip.empty?
|
46
|
+
dt_assoc[item.attributes["key"]] = decode_data(item)
|
47
|
+
end
|
48
|
+
|
49
|
+
return dt_assoc
|
50
|
+
end
|
51
|
+
|
52
|
+
# Accepts two parameters but uses only one; to keep the interface same as other xml parser classes
|
53
|
+
# Is that a side effect of Template pattern?
|
54
|
+
#
|
55
|
+
def self.new_element(element_name, container)
|
56
|
+
return Node.new(element_name.to_s)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|