abiquo 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README +39 -0
  2. data/lib/abiquo.rb +179 -0
  3. data/lib/core_ext.rb +10 -0
  4. data/lib/to_xml.rb +22 -0
  5. 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
@@ -0,0 +1,10 @@
1
+ module Resourceful
2
+ class Resource
3
+ def host_with_port
4
+ add = Addressable::URI.parse(uri)
5
+ !add.port.blank? && add.port != 80 ? [add.host, add.port].join(':') : add.host
6
+ end
7
+
8
+ alias_method_chain :host, :port
9
+ end
10
+ end
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
+