abiquo 0.1.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 +39 -0
- data/lib/abiquo.rb +179 -0
- data/lib/core_ext.rb +10 -0
- data/lib/to_xml.rb +22 -0
- metadata +125 -0
data/README
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
=Abiquo API Ruby client
|
2
|
+
|
3
|
+
Rest client to work with the Abiquo's API.
|
4
|
+
|
5
|
+
==Installation
|
6
|
+
|
7
|
+
gem install abiquo
|
8
|
+
|
9
|
+
==Usage
|
10
|
+
|
11
|
+
The first version of the api just uses basic authentication, so we need to create an instance with our credentials:
|
12
|
+
|
13
|
+
auth = Abiquo::Auth('Abiquo', 'username', 'password')
|
14
|
+
|
15
|
+
The entry point for the Abiquo's API is an atom document service:
|
16
|
+
|
17
|
+
api = Abiquo::Resource('http://abiquo.example.com/api', auth)
|
18
|
+
|
19
|
+
from this point we can access to every resource exposed by the api calling nested methods:
|
20
|
+
|
21
|
+
datacenters = api.datacenters
|
22
|
+
racks = api.datacenters.first.racks
|
23
|
+
|
24
|
+
or get the values of their elements:
|
25
|
+
|
26
|
+
datacenter = api.datacenters.first
|
27
|
+
datacenter.name # => The name of the datacenter
|
28
|
+
|
29
|
+
You can find further example into the specs:
|
30
|
+
|
31
|
+
http://github.com/abiquo/api_ruby_client/tree/master/spec/acceptance
|
32
|
+
|
33
|
+
Further documentation of the API can be found in our wiki:
|
34
|
+
|
35
|
+
http://abicloud.org/display/ABI16/Abiquo's+API
|
36
|
+
|
37
|
+
==Copyright
|
38
|
+
|
39
|
+
Copyright (c) 2010 Abiquo Holdings. See LICENSE for details.
|
data/lib/abiquo.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'resourceful'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'active_support'
|
4
|
+
require File.expand_path('../core_ext', __FILE__)
|
5
|
+
require File.expand_path('../to_xml', __FILE__)
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
module Abiquo
|
9
|
+
|
10
|
+
class BasicAuth < Resourceful::BasicAuthenticator
|
11
|
+
def can_handle?(request); true; end
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
module HttpAccessor
|
16
|
+
|
17
|
+
def xml
|
18
|
+
@xml ||= begin
|
19
|
+
response = http.resource(url_with_params).get
|
20
|
+
Nokogiri.parse(response.body).root
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def url_with_params
|
27
|
+
@options[:expand] = @options[:expand].join(",") if @options[:expand].is_a?(Array)
|
28
|
+
params = @options.map { |k, v| "#{k}=#{v}" }.join("&")
|
29
|
+
params = "?#{params}" unless params.empty?
|
30
|
+
@url + params
|
31
|
+
end
|
32
|
+
|
33
|
+
def http
|
34
|
+
Resourceful::HttpAccessor.new(:authenticator => @auth)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
class SingleResource
|
40
|
+
|
41
|
+
include HttpAccessor
|
42
|
+
|
43
|
+
undef id
|
44
|
+
|
45
|
+
def initialize(url, auth, xml = nil, options = {})
|
46
|
+
@url = url
|
47
|
+
@auth = auth
|
48
|
+
@xml = xml
|
49
|
+
@options = options
|
50
|
+
end
|
51
|
+
|
52
|
+
def update(attrs = {})
|
53
|
+
response = http.resource(url).put(attrs.to_xml(:root => object_type), :content_type => "application/xml")
|
54
|
+
@xml = Nokogiri.parse(response.body).root
|
55
|
+
end
|
56
|
+
|
57
|
+
def delete
|
58
|
+
http.resource(url).delete
|
59
|
+
end
|
60
|
+
|
61
|
+
def url
|
62
|
+
@url ||= xml.at_xpath("./link[@rel='edit']").try(:[], "href")
|
63
|
+
end
|
64
|
+
|
65
|
+
delegate :to_xml, :to => :xml
|
66
|
+
alias_method :inspect, :to_xml
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def object_type
|
71
|
+
@object_type ||= URI.parse(url).path.split("/")[-2].singularize
|
72
|
+
end
|
73
|
+
|
74
|
+
def method_missing(meth, *args, &blk)
|
75
|
+
attribute = xml.at_xpath("./*[name()='#{meth}' or name()='#{attribute_name(meth)}']")
|
76
|
+
return node_text(attribute) if attribute
|
77
|
+
|
78
|
+
link = xml.at_xpath("./link[@rel='#{meth}' or @rel='#{attribute_name(meth)}']")
|
79
|
+
return Resource.new(link["href"], @auth, link.at_xpath("./*"), *args) if link
|
80
|
+
|
81
|
+
if xml.namespaces.has_key?('xmlns:ns2')
|
82
|
+
@xml = Nokogiri.parse(xml.to_s.downcase).root
|
83
|
+
link = xml.at_xpath("//ns2:collection/xmlns:title[. = '#{meth}']").try(:parent)
|
84
|
+
return Resource.new(link['href'], @auth, nil, *args) if link
|
85
|
+
end
|
86
|
+
|
87
|
+
super
|
88
|
+
end
|
89
|
+
|
90
|
+
def node_text(node)
|
91
|
+
node.text
|
92
|
+
end
|
93
|
+
|
94
|
+
def attribute_name(attribute)
|
95
|
+
attribute.to_s.gsub('_', '-')
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
class ResourceCollection
|
101
|
+
|
102
|
+
include HttpAccessor
|
103
|
+
|
104
|
+
delegate :inspect, :to => :resources
|
105
|
+
|
106
|
+
def initialize(url, auth, xml = nil, options = {})
|
107
|
+
@url = url
|
108
|
+
@auth = auth
|
109
|
+
@xml = xml if options.empty?
|
110
|
+
@options = options
|
111
|
+
end
|
112
|
+
|
113
|
+
def create(attrs = {})
|
114
|
+
response = http.resource(url_with_params).post(attrs.to_xml(:root => object_type, :convert_links => true), :content_type => "application/xml")
|
115
|
+
Resource.new(nil, @auth, Nokogiri.parse(response.body).root)
|
116
|
+
end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def object_type
|
121
|
+
@object_type ||= URI.parse(@url).path.split("/").last.singularize
|
122
|
+
end
|
123
|
+
|
124
|
+
def resources
|
125
|
+
@resources ||= xml.xpath("./*").map { |subnode| Resource.new(subnode.at_xpath("./link[@rel='edit']").try(:[], "href"), @auth, subnode, @options) }
|
126
|
+
end
|
127
|
+
|
128
|
+
def method_missing(meth, *args, &blk)
|
129
|
+
resources.send(meth, *args, &blk)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Resource
|
134
|
+
|
135
|
+
include HttpAccessor
|
136
|
+
|
137
|
+
undef id
|
138
|
+
|
139
|
+
delegate :inspect, :to => :get!
|
140
|
+
|
141
|
+
def self.from_xml(xml, auth = nil)
|
142
|
+
new(nil, auth, Nokogiri.parse(xml).root)
|
143
|
+
end
|
144
|
+
|
145
|
+
def initialize(url, auth, xml = nil, options = {})
|
146
|
+
@url = url
|
147
|
+
@auth = auth
|
148
|
+
@xml = xml
|
149
|
+
@options = options
|
150
|
+
end
|
151
|
+
|
152
|
+
def method_missing(meth, *args, &blk)
|
153
|
+
@resource_object ||= resource_class(meth).new(@url, @auth, @xml, @options)
|
154
|
+
@resource_object.send(meth, *args, &blk)
|
155
|
+
end
|
156
|
+
|
157
|
+
def resource_class(meth)
|
158
|
+
@resource_class ||= if (Array.instance_methods + ["create"] - ["delete", "id"]).include?(meth.to_s)
|
159
|
+
ResourceCollection
|
160
|
+
else
|
161
|
+
SingleResource
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def get!
|
166
|
+
klass = SingleResource
|
167
|
+
if (!xml.children.empty? && xml.name.singularize == xml.children.first.name)
|
168
|
+
klass = ResourceCollection
|
169
|
+
end
|
170
|
+
@resource_object = klass.new(@url, @auth, @xml, @options)
|
171
|
+
end
|
172
|
+
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.Resource(url, auth, params = {})
|
176
|
+
Resource.new(url, auth, nil, params)
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
data/lib/core_ext.rb
ADDED
data/lib/to_xml.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Abiquo
|
2
|
+
|
3
|
+
module ToXml
|
4
|
+
|
5
|
+
def to_xml_with_links(options = {})
|
6
|
+
if options[:convert_links] && options[:builder]
|
7
|
+
options[:builder].tag!(:link, :rel => options[:root]) do
|
8
|
+
to_xml_without_links(options)
|
9
|
+
end
|
10
|
+
else
|
11
|
+
to_xml_without_links(options)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class Array
|
20
|
+
include Abiquo::ToXml
|
21
|
+
alias_method_chain :to_xml, :links
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,125 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: abiquo
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
version: 0.1.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Abiquo
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-07-28 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: resourceful
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
type: :runtime
|
31
|
+
version_requirements: *id001
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: nokogiri
|
34
|
+
prerelease: false
|
35
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
segments:
|
40
|
+
- 0
|
41
|
+
version: "0"
|
42
|
+
type: :runtime
|
43
|
+
version_requirements: *id002
|
44
|
+
- !ruby/object:Gem::Dependency
|
45
|
+
name: activesupport
|
46
|
+
prerelease: false
|
47
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
segments:
|
52
|
+
- 0
|
53
|
+
version: "0"
|
54
|
+
type: :runtime
|
55
|
+
version_requirements: *id003
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: steak
|
58
|
+
prerelease: false
|
59
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
type: :development
|
67
|
+
version_requirements: *id004
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: webmock
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
segments:
|
76
|
+
- 0
|
77
|
+
version: "0"
|
78
|
+
type: :development
|
79
|
+
version_requirements: *id005
|
80
|
+
description:
|
81
|
+
email: support@abiquo.com
|
82
|
+
executables: []
|
83
|
+
|
84
|
+
extensions: []
|
85
|
+
|
86
|
+
extra_rdoc_files:
|
87
|
+
- README
|
88
|
+
files:
|
89
|
+
- lib/to_xml.rb
|
90
|
+
- lib/core_ext.rb
|
91
|
+
- lib/abiquo.rb
|
92
|
+
- README
|
93
|
+
has_rdoc: true
|
94
|
+
homepage: http://wiki.github.com/abiquo/abiquo/
|
95
|
+
licenses: []
|
96
|
+
|
97
|
+
post_install_message:
|
98
|
+
rdoc_options:
|
99
|
+
- --main
|
100
|
+
- README
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
segments:
|
108
|
+
- 0
|
109
|
+
version: "0"
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
segments:
|
115
|
+
- 0
|
116
|
+
version: "0"
|
117
|
+
requirements: []
|
118
|
+
|
119
|
+
rubyforge_project:
|
120
|
+
rubygems_version: 1.3.6
|
121
|
+
signing_key:
|
122
|
+
specification_version: 3
|
123
|
+
summary: Abiquo API client
|
124
|
+
test_files: []
|
125
|
+
|