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.
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
+