abiquo-api 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1304f42a84ccb9a8c9f92e6b8e6b41e9f4a4c509
4
+ data.tar.gz: 0ebab054e7d0e287045ac821e171c8d2e6cbe969
5
+ SHA512:
6
+ metadata.gz: 85c7aa06dafb2cf62ca1c6f6312c2134e469919acd2233421ea5446847b2d292a7b8ff1005b3976ab566b54c59bc36f0c0580317e024e54d8910c04a4d7223e0
7
+ data.tar.gz: 8828b536738f0d636ef4ec1426b4392b4a4682e7452c72b0f2fc2b74246470e41b3908335afeef567c0bc996fd1562287023f70aeba7a907e8b090f5a8dd2a58
data/.gitignore ADDED
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ Gemfile.lock
30
+ .ruby-version
31
+ .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,166 @@
1
+ # Abiquo API client for Ruby
2
+
3
+ Basic API browsing and raw object manipulation for Ruby.
4
+
5
+ ## Installation
6
+
7
+ To get the client installed just issue:
8
+
9
+ ```gem install abiquo-api```
10
+
11
+ Or if you are using a Gemfile, add:
12
+
13
+ ```gem 'abiquo-api'```
14
+
15
+ An example usage would be:
16
+
17
+ ```ruby
18
+ require 'abiquo-api'
19
+
20
+ a = AbiquoAPI.new(:abiquo_api_url => 'https://10.60.13.40/api',
21
+ :abiquo_username => "admin",
22
+ :abiquo_password => "xabiquo")
23
+ ```
24
+
25
+ Or, if you want to force a specific API version:
26
+
27
+ ```ruby
28
+ require 'abiquo-api'
29
+
30
+ a = AbiquoAPI.new(:abiquo_api_url => 'https://10.60.13.40/api',
31
+ :abiquo_username => "admin",
32
+ :abiquo_password => "xabiquo",
33
+ :version => "2.9")
34
+ ```
35
+
36
+ Then you can start browsing the API:
37
+
38
+ ```ruby
39
+ l = AbiquoAPI::Link.new(:href => '/api/cloud/virtualdatacenters',
40
+ :type => 'application/vnd.abiquo.virtualdatacenters+json')
41
+
42
+ a.get(l)
43
+ ```
44
+
45
+ ## Client object
46
+
47
+ The client object contains 3 objects that allow API browsing.
48
+
49
+ - **user.** The user object returned by the ```/api/login``` call.
50
+ - **enterprise.** The user's enterprise link to be used in new objects.
51
+ - **properties.** A hash containing all the system properties in the system. Useful to get default values for some objects (ie. VLAN parameters in VDC creation).
52
+
53
+ ## Link object
54
+
55
+ Represents an Abiquo API Link. Issuing ```get``` on them will retrieve link destination. This allows for things like:
56
+
57
+ ```ruby
58
+ vapp = vdc.link(:virtualappliances).get.first
59
+ ```
60
+
61
+ ## Generic model object
62
+
63
+ This is used to map Abiquo API objects.
64
+
65
+ ## Examples
66
+
67
+ ### Browse the API
68
+
69
+ #### Initialize connection
70
+
71
+ ```ruby
72
+ a = AbiquoAPI.new(:abiquo_api_url => 'https://10.60.13.40/api',
73
+ :abiquo_username => "admin",
74
+ :abiquo_password => "xabiquo")
75
+ ```
76
+
77
+
78
+ #### User object
79
+
80
+ Is the User object returned by the API at login. You can browse the links provided like:
81
+
82
+ ```ruby
83
+ a.user
84
+
85
+ vm = a.user.link(:virtualmachines).get.first
86
+
87
+ vm.name
88
+ => "ABQ_6b6d9856-c05f-425e-8916-1ff7de1683e3"
89
+
90
+ vm.id
91
+ => 18
92
+ ```
93
+
94
+ ### Create a VDC using an existing one as reference
95
+
96
+ #### Initialize connection
97
+
98
+ ```ruby
99
+ a = AbiquoAPI.new(:abiquo_api_url => 'https://10.60.13.40/api',
100
+ :abiquo_username => "admin",
101
+ :abiquo_password => "xabiquo")
102
+ ```
103
+
104
+ #### Create a Link object to issue a request
105
+
106
+ ```ruby
107
+ l = AbiquoAPI::Link.new(:href => '/api/cloud/virtualdatacenters',
108
+ :type => 'application/vnd.abiquo.virtualdatacenters+json')
109
+ ```
110
+
111
+ #### Get on the link
112
+
113
+ ```ruby
114
+ v = a.get(l).first
115
+ ```
116
+
117
+ #### Create a new object
118
+
119
+ ```ruby
120
+ v1 = a.new_object(:name => "vdctest",
121
+ :hypervisorType => "VMX_04",
122
+ :vlan => v.vlan,
123
+ :links => [v.link(:location), a.enterprise])
124
+ v1.vlan.delete("links")
125
+ v1.vlan.delete("id")
126
+ v1.vlan.delete("tag")
127
+ ```
128
+
129
+ #### Create a link where to post data
130
+
131
+ ```ruby
132
+ l1 = AbiquoAPI::Link.new(:href => '/api/cloud/virtualdatacenters',
133
+ :type => 'application/vnd.abiquo.virtualdatacenter+json')
134
+ ```
135
+
136
+ #### Post data
137
+
138
+ ```ruby
139
+ v2 = a.post(l1, v1)
140
+ ```
141
+
142
+ #### Modify the created object
143
+
144
+ ```ruby
145
+ v2.name = "SomeValue"
146
+ v2 = a.put(v2.edit, v2)
147
+ ```
148
+
149
+ Or:
150
+
151
+ ```ruby
152
+ v2.name = "SomeValue"
153
+ v2.update
154
+ ```
155
+
156
+ #### Delete it
157
+
158
+ ```ruby
159
+ a.delete(v2.edit)
160
+ ```
161
+
162
+ Or:
163
+
164
+ ```ruby
165
+ v2.delete
166
+ ```
@@ -0,0 +1,22 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'abiquo-api/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "abiquo-api"
8
+ gem.version = AbiquoAPIClient::VERSION
9
+ gem.authors = ["Marc Cirauqui"]
10
+ gem.email = ["marc.cirauqui@abiquo.com"]
11
+ gem.description = %q{Simple Abiquo API client}
12
+ gem.homepage = "https://github.com/abiquo/api-ruby"
13
+ gem.summary = gem.description
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+ gem.require_paths = ["lib"]
18
+
19
+ gem.add_runtime_dependency "excon", '~> 0.43', '>= 0.43.0'
20
+ gem.add_runtime_dependency "formatador", '~> 0.2', '>= 0.2.5'
21
+ gem.add_runtime_dependency "json", '~> 1.8', '>= 1.8.0'
22
+ end
data/lib/abiquo-api.rb ADDED
@@ -0,0 +1,237 @@
1
+ require 'abiquo-api/errors.rb'
2
+ require 'abiquo-api/httpclient.rb'
3
+ require 'abiquo-api/link.rb'
4
+ require 'abiquo-api/model.rb'
5
+
6
+ ##
7
+ # Ruby Abiquo API client main class
8
+ #
9
+ class AbiquoAPI
10
+ include AbiquoAPIClient
11
+
12
+ ##
13
+ # The {AbiquoAPIClient::HTTPClient} used by
14
+ # this instance.
15
+ #
16
+ attr_reader :http_client
17
+
18
+ ##
19
+ # A hash of the enterprise to which the user
20
+ # belongs to.
21
+ #
22
+ attr_accessor :enterprise
23
+
24
+ ##
25
+ # An instance of {AbiquoAPIClient::LinkModel} representing
26
+ # the current user.
27
+ #
28
+ attr_accessor :user
29
+
30
+ ##
31
+ # The config properties for the UI.
32
+ #
33
+ attr_accessor :properties
34
+
35
+ ##
36
+ # The Abiquo API version used by this client.
37
+ #
38
+ attr_accessor :version
39
+
40
+ ##
41
+ # Initializes a new AbiquoAPI instance.
42
+ #
43
+ # Required options:
44
+ #
45
+ # :abiquo_api_url => The URL of the Abiquo API. ie. https://yourserver/api
46
+ # :abiquo_username => The username used to connect to the Abiquo API.
47
+ # :abiquo_password => The password for your user.
48
+ # :version => The Abiquo API version to include in each request.
49
+ # Defaults to whatever is returned in the /api/version resource
50
+ #
51
+ def initialize(options = {})
52
+ api_url = options[:abiquo_api_url]
53
+ api_username = options[:abiquo_username]
54
+ api_password = options[:abiquo_password]
55
+
56
+ raise "You need to set :abiquo_api_url, :abiquo_username and :abiquo_password" if api_url.nil? or api_username.nil? or api_password.nil?
57
+
58
+ @http_client = AbiquoAPIClient::HTTPClient.new(api_url,
59
+ api_username,
60
+ api_password)
61
+ api_path = URI.parse(api_url).path
62
+
63
+ loginresp = @http_client.request(
64
+ :expects => [200],
65
+ :method => 'GET',
66
+ :path => "#{api_path}/login",
67
+ :accept => 'application/vnd.abiquo.user+json'
68
+ )
69
+ @enterprise = AbiquoAPIClient::Link.new(loginresp['links'].select {|l| l['rel'] == 'enterprise'}.first)
70
+ @user = AbiquoAPIClient::LinkModel.new(loginresp.merge({:client => self}))
71
+
72
+ @properties = @http_client.request(
73
+ :expects => [200],
74
+ :method => 'GET',
75
+ :path => "#{api_path}/config/properties",
76
+ :accept => 'application/vnd.abiquo.systemproperties+json'
77
+ )
78
+
79
+ if options.has_key? :version
80
+ @version = options[:version][0..2]
81
+ else
82
+ @version = @http_client.request(
83
+ :expects => [200],
84
+ :method => 'GET',
85
+ :path => "#{api_path}/version",
86
+ :accept => 'text/plain'
87
+ ).delete("\n")[0..2]
88
+ end
89
+
90
+ self
91
+ end
92
+
93
+ ##
94
+ # Returns a new instance of the {AbiquoAPIClient::LinkModel} class.
95
+ #
96
+ # Parameters:
97
+ # A hash of attributes to set in the object.
98
+ #
99
+ def new_object(hash)
100
+ AbiquoAPIClient::LinkModel.new(hash.merge({ :client => self}))
101
+ end
102
+
103
+ ##
104
+ # Executes an HTTP GET over the {AbiquoAPIClient::Link} passed as parameter.
105
+ #
106
+ # Required parameters:
107
+ # [link] An instance
108
+ # of an {AbiquoAPIClient::Link}.
109
+ #
110
+ # Optional parameters:
111
+ # [options] A hash of key/values that will
112
+ # be sent as query.
113
+ #
114
+ # **NOTE:** The option :accept will override Accept header sent in
115
+ # the request.
116
+ #
117
+ # Returns an instance of the {AbiquoAPIClient::LinkModel} class representing
118
+ # the requested resource.
119
+ #
120
+ def get(link, options = {})
121
+ accept = options[:accept].nil? ? link.type : options.delete(:accept)
122
+
123
+ req_hash = {
124
+ :expects => [200],
125
+ :method => 'GET',
126
+ :path => link.href,
127
+ :query => options
128
+ }
129
+
130
+ req_hash[:accept] = "#{accept}; version=#{@version};" unless accept.eql? ""
131
+
132
+ resp = @http_client.request(req_hash)
133
+
134
+ if resp.is_a? Array
135
+ tmp_a = []
136
+ resp.each do |r|
137
+ tmp_r = AbiquoAPIClient::LinkModel.new(r.merge({:client => self}))
138
+ tmp_a << tmp_r
139
+ end
140
+ tmp_a
141
+ else
142
+ AbiquoAPIClient::LinkModel.new(resp.merge({ :client => self}))
143
+ end
144
+ end
145
+
146
+ ##
147
+ # Executes an HTTP POST over the {AbiquoAPIClient::Link} passed as parameter.
148
+ #
149
+ # Required parameters:
150
+ # [link] An instance of an {AbiquoAPIClient::Link}.
151
+ # [data] The data to send in the HTTP request. Usually an instance
152
+ # of the {AbiquoAPIClient::LinkModel} instance. Will be
153
+ # serialized to JSON before sending.
154
+ #
155
+ # Optional parameters:
156
+ # [options] A hash of key/values that will be sent as query.
157
+ #
158
+ # **NOTE:** The option :accept and :content options will override Accept
159
+ # and Content-Type headers sent in the request.
160
+ #
161
+ # Returns an instance of the {AbiquoAPIClient::LinkModel} class representing
162
+ # the requested resource or nil if the request returned empty.
163
+ #
164
+ def post(link, data, options = {})
165
+ ctype = options[:content].nil? ? link.type : options.delete(:content)
166
+ accept = options[:accept].nil? ? link.type : options.delete(:accept)
167
+
168
+ req_hash = {
169
+ :method => 'POST',
170
+ :path => link.href,
171
+ :body => data.to_json,
172
+ :query => options
173
+ }
174
+
175
+ req_hash[:accept] = "#{accept}; version=#{@version};" unless accept.eql? ""
176
+ req_hash[:content] = "#{ctype}; version=#{@version};" unless ctype.eql? ""
177
+
178
+ resp = @http_client.request(req_hash)
179
+ resp.nil? ? nil : AbiquoAPIClient::LinkModel.new({ :client => self}.merge(resp))
180
+ end
181
+
182
+ ##
183
+ # Executes an HTTP PUT over the {AbiquoAPIClient::Link} passed as parameter.
184
+ #
185
+ # Required parameters:
186
+ # [link] An instance of an {AbiquoAPIClient::Link}.
187
+ # [data] The data to send in the HTTP request. Usually an instance
188
+ # of the {AbiquoAPIClient::LinkModel} instance. Will be
189
+ # serialized to JSON before sending.
190
+ #
191
+ # Optional parameters:
192
+ # [options] A hash of key/values that will be sent as query.
193
+ #
194
+ # **NOTE:** The option :accept and :content options will override Accept
195
+ # and Content-Type headers sent in the request.
196
+ #
197
+ # Returns an instance of the {AbiquoAPIClient::LinkModel} class representing
198
+ # the requested resource or nil if the request returned empty.
199
+ #
200
+ def put(link, data, options = {})
201
+ ctype = options[:content].nil? ? link.type : options.delete(:content)
202
+ accept = options[:accept].nil? ? link.type : options.delete(:accept)
203
+
204
+ req_hash = {
205
+ :method => 'PUT',
206
+ :path => link.href,
207
+ :body => data.to_json,
208
+ :query => options
209
+ }
210
+
211
+ req_hash[:accept] = "#{accept}; version=#{@version};" unless accept.eql? ""
212
+ req_hash[:content] = "#{ctype}; version=#{@version};" unless ctype.eql? ""
213
+
214
+ resp = @http_client.request(req_hash)
215
+ resp.nil? ? nil : AbiquoAPIClient::LinkModel.new({ :client => self}.merge(resp))
216
+ end
217
+
218
+ ##
219
+ # Executes an HTTP DELETE over the {AbiquoAPIClient::Link} passed as parameter.
220
+ #
221
+ # Required parameters:
222
+ # [link] An instance of an {AbiquoAPIClient::Link}.
223
+ #
224
+ # Optional parameters:
225
+ # [options] A hash of key/values that will be sent as query.
226
+ #
227
+ # Returns nil
228
+ #
229
+ def delete(link, options = {})
230
+ @http_client.request(
231
+ :expects => [204],
232
+ :method => 'DELETE',
233
+ :path => link.href
234
+ )
235
+ nil
236
+ end
237
+ end
@@ -0,0 +1,41 @@
1
+ module AbiquoAPIClient
2
+ ##
3
+ # Common error exception
4
+ #
5
+ class Error < Exception; end
6
+
7
+ ##
8
+ # InvalidCredentials exception.
9
+ # Raised whenever the API responds with
10
+ # a 401 HTTP code.
11
+ #
12
+ class InvalidCredentials < AbiquoAPIClient::Error; end
13
+
14
+ ##
15
+ # Forbidden exception
16
+ # Raised whenever the API responds with
17
+ # a 403 HTTP code.
18
+ #
19
+ class Forbidden < AbiquoAPIClient::Error; end
20
+
21
+ ##
22
+ # Badrequest exception
23
+ # Raised whenever the API responds with
24
+ # a 400 or 406 HTTP code.
25
+ #
26
+ class BadRequest < AbiquoAPIClient::Error; end
27
+
28
+ ##
29
+ # NotFound exception
30
+ # Raised whenever the API responds with
31
+ # a 404 HTTP code.
32
+ #
33
+ class NotFound < AbiquoAPIClient::Error; end
34
+
35
+ ##
36
+ # UnsupportedMediaType exception
37
+ # Raised whenever the API responds with
38
+ # a 415 HTTP code.
39
+ #
40
+ class UnsupportedMediaType < AbiquoAPIClient::Error; end
41
+ end
@@ -0,0 +1,165 @@
1
+ require 'excon'
2
+ require 'uri'
3
+ require 'json'
4
+
5
+ module AbiquoAPIClient
6
+ ##
7
+ # HTTPClient class.
8
+ #
9
+ # Does the actual HTTP requests to the server.
10
+ #
11
+ class HTTPClient
12
+ ##
13
+ # Excon connection object.
14
+ #
15
+ attr_reader :connection
16
+
17
+ ##
18
+ # Cookies returned by the API. Contains the auth
19
+ # cookie that will be used after the first call.
20
+ #
21
+ attr_reader :cookies
22
+
23
+ ##
24
+ # Constructor. Recieves the parameters to establish
25
+ # a connection to the Abiquo API.
26
+ #
27
+ # Parameters:
28
+ # :abiquo_api_url:: The URL of the Abiquo API. ie. https://yourserver/api
29
+ # :abiquo_username:: The username used to connect to the Abiquo API.
30
+ # :abiquo_password:: The password for your user.
31
+ #
32
+ def initialize(api_url, api_username, api_password)
33
+ Excon.defaults[:ssl_verify_peer] = false
34
+ @connection = Excon.new(api_url,
35
+ :user => api_username,
36
+ :password => api_password )
37
+ self
38
+ end
39
+
40
+ ##
41
+ # The public method called by the client.
42
+ #
43
+ # Parameters:
44
+ # [params] A hash of options to be passed to the
45
+ # Excon connection.
46
+ #
47
+ # Returns a hash resulting of parsing the response
48
+ # body as JSON, or nil if the response is "No
49
+ # content" (HTTP code 204).
50
+ #
51
+ def request(params)
52
+ # Remove nil params
53
+ params.reject!{|k,v| v.nil?}
54
+
55
+ # Setup Accept and Content-Type headers
56
+ headers={}
57
+ headers.merge!('Accept' => params.delete(:accept)) if params.has_key?(:accept)
58
+ headers.merge!('Content-Type' => params.delete(:content)) if params.has_key?(:content)
59
+
60
+ # Set Auth cookie and delete user and password if present
61
+ @connection.data.delete(:user) unless @connection.data[:user].nil?
62
+ @connection.data.delete(:password) unless @connection.data[:password].nil?
63
+ headers.merge!(@cookies) unless @cookies.nil?
64
+
65
+ params[:headers] = headers
66
+
67
+ # Correct API path
68
+ path = URI.parse(params[:path]).path
69
+ params[:path] = path
70
+
71
+ response = issue_request(params)
72
+ return nil if response.nil?
73
+
74
+ begin
75
+ response = JSON.parse(response.body) unless response.body.empty?
76
+ rescue
77
+ response = response.body
78
+ end
79
+
80
+ # Handle pagination
81
+ if not response['links'].nil? and response['links'].select {|l| l['rel'].eql? "next" }.count > 0
82
+ items = []
83
+ items = items + response['collection'] if not response['collection'].nil?
84
+
85
+ loop do
86
+ next_url = response['links'].select {|l| l['rel'].eql? "next" }.first['href']
87
+ uri = URI.parse(next_url)
88
+ params[:path] = uri.path
89
+ params[:query] = uri.query
90
+ params[:headers] = headers
91
+ response = issue_request(params)
92
+ response = JSON.parse(response.body) unless response.body.empty?
93
+ items = items + response['collection'] if not response['collection'].nil?
94
+
95
+ break if response['links'].select {|l| l['rel'].eql? "next" }.count == 0
96
+ end
97
+
98
+ items
99
+ else
100
+ if not response['collection'].nil?
101
+ response['collection']
102
+ else
103
+ response.nil? ? nil : response
104
+ end
105
+ end
106
+ end
107
+
108
+ private
109
+
110
+ ##
111
+ # Issues the HTTP request using the Excon connection
112
+ # object.
113
+ #
114
+ # Handles API error codes.
115
+ #
116
+ def issue_request(options)
117
+ begin
118
+ resp = @connection.request(options)
119
+
120
+ # Save cookies
121
+ # Get all "Set-Cookie" headers and replace them with "Cookie" header.
122
+ @cookies = Hash[resp.headers.select{|k| k.eql? "Set-Cookie" }.map {|k,v| ["Cookie", v] }]
123
+
124
+ if resp.data[:status] == 204
125
+ nil
126
+ else
127
+ resp
128
+ end
129
+ rescue Excon::Errors::HTTPStatusError => error
130
+ case error.response.status
131
+ when 401
132
+ raise AbiquoAPIClient::InvalidCredentials, "Wrong username or password"
133
+ when 403
134
+ raise AbiquoAPIClient::Forbidden, "Not Authorized"
135
+ when 406, 400
136
+ raise AbiquoAPIClient::BadRequest, "Bad request"
137
+ when 415
138
+ raise AbiquoAPIClient::UnsupportedMediaType, "Unsupported mediatype"
139
+ when 404
140
+ begin
141
+ error_response = JSON.parse(error.response.body)
142
+
143
+ error_code = error_response['collection'][0]['code']
144
+ error_text = error_response['collection'][0]['message']
145
+
146
+ rescue
147
+ raise AbiquoAPIClient::NotFound, "Not Found; #{error_code} - #{error_text}"
148
+ end
149
+ raise AbiquoAPIClient::NotFound, "Not Found"
150
+ else
151
+ begin
152
+ error_response = JSON.parse(error.response.body)
153
+
154
+ error_code = error_response['collection'][0]['code']
155
+ error_text = error_response['collection'][0]['message']
156
+
157
+ rescue
158
+ raise AbiquoAPIClient::Error, error.response.body
159
+ end
160
+ raise AbiquoAPIClient::Error, "#{error_code} - #{error_text}"
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,94 @@
1
+ require 'formatador'
2
+
3
+ module AbiquoAPIClient
4
+ ##
5
+ # Represents a link on the Abiquo API.
6
+ #
7
+ class Link
8
+ ##
9
+ # The target URL of the link
10
+ #
11
+ attr_accessor :href
12
+ ##
13
+ # The 'rel' attribute of the link
14
+ #
15
+ attr_accessor :rel
16
+ ##
17
+ # The title of the link
18
+ #
19
+ attr_accessor :title
20
+ ##
21
+ # The media type of the link
22
+ #
23
+ attr_accessor :type
24
+
25
+ ##
26
+ # Constructor.
27
+ #
28
+ # Accepts a hash reprsenting a link, usually returned
29
+ # after parsing the JSON response.
30
+ #
31
+ # If the hash contains :client key, the value will be used
32
+ # as an {AbiquoAPI} client allowing the get method to retrieve
33
+ # the target resource.
34
+ #
35
+ def initialize(hash)
36
+ @client = hash.delete(:client) if hash.keys.include?(:client)
37
+
38
+ h = Hash[hash.map {|k, v| [k.to_sym, v ] }]
39
+
40
+ @href = h[:href].nil? ? '' : h[:href]
41
+ @rel = h[:rel].nil? ? '' : h[:rel]
42
+ @title = h[:title].nil? ? '' : h[:title]
43
+ @type = h[:type].nil? ? '' : h[:type]
44
+ end
45
+
46
+ ##
47
+ # If the :client attribute is not nil, will retrieve
48
+ # the resource that this link represents, or nil otherwise
49
+ #
50
+ def get
51
+ if @client.nil?
52
+ return nil
53
+ else
54
+ @client.get(self)
55
+ end
56
+ end
57
+
58
+ ##
59
+ # Converts an instance to its hash form, so it can be
60
+ # serialized as JSON.
61
+ #
62
+ def to_hash
63
+ h = self.href.nil? ? '' : self.href
64
+ r = self.rel.nil? ? '' : self.rel
65
+ t = self.title.nil? ? '' : self.title
66
+ y = self.type.nil? ? '' : self.type
67
+
68
+ {
69
+ "href" => h,
70
+ "type" => y,
71
+ "rel" => r,
72
+ "title" => t
73
+ }
74
+ end
75
+
76
+ ##
77
+ # Pretty print the object.
78
+ #
79
+ def inspect
80
+ Thread.current[:formatador] ||= Formatador.new
81
+ data = "#{Thread.current[:formatador].indentation}<#{self.class.name}"
82
+ Thread.current[:formatador].indent do
83
+ unless self.instance_variables.empty?
84
+ vars = self.instance_variables.clone
85
+ vars.delete(:@client)
86
+ data << " "
87
+ data << vars.map { |v| "#{v}=#{instance_variable_get(v.to_s).inspect}" }.join(", ")
88
+ end
89
+ end
90
+ data << " >"
91
+ data
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,194 @@
1
+ require 'formatador'
2
+
3
+ module AbiquoAPIClient
4
+ ##
5
+ # Represents a resource in the Abiquo API.
6
+ #
7
+ class LinkModel
8
+ ##
9
+ # Constructor
10
+ #
11
+ # Accepts a hash of key values representing the resource, plus
12
+ # an instance of the AbiquoAPI class to be used to execute
13
+ # the HTTP requests, specified as the :client attribute.
14
+ #
15
+ def initialize(attrs={})
16
+ raise "Needs a connection!" if attrs[:client].nil?
17
+ @client = attrs.delete(:client)
18
+
19
+ attributes = Hash[attrs.clone.map {|k, v| [k.to_s, v ] }]
20
+
21
+ if not attributes['links'].nil?
22
+ links = []
23
+
24
+ attributes['links'].each do |link|
25
+ link = link.to_hash if link.is_a? AbiquoAPIClient::Link
26
+ new_lnk = {}
27
+
28
+ if 'edit'.eql?(link['rel']) or 'self'.eql?(link['rel'])
29
+ # Create a URL string attribute
30
+ rel = 'url'
31
+ create_attr(rel, true)
32
+ instance_variable_set("@#{rel}", link['href'])
33
+ end
34
+
35
+ # Create new getters and setters
36
+ # Also sets value to a Link object
37
+ rel = "#{link['rel'].gsub(/\//, '_')}"
38
+ new_lnk[rel.to_sym] = Link.new(link.merge({:client => @client}))
39
+ links << new_lnk
40
+
41
+ # For every link that points to an ID
42
+ # create a getter
43
+ if link['href'].split('/').last.is_a? Integer
44
+ idrel = "#{link['rel'].gsub(/\//, '_')}_id"
45
+ create_attr(idrel, true)
46
+ instance_variable_set("@#{idrel}", link['href'].split('/').last.to_i)
47
+ end
48
+ end
49
+ attributes.delete('links')
50
+
51
+ create_attr("links")
52
+ instance_variable_set("@links", links)
53
+
54
+ # Now create getters and setters for every method
55
+ attributes.keys.each do |k|
56
+ create_attr(k)
57
+ instance_variable_set("@#{k}", attributes[k])
58
+ end
59
+ end
60
+ end
61
+
62
+ ##
63
+ # Serializes the object into a valid JSON for the Abiquo API.
64
+ #
65
+ def to_json
66
+ att = self.instance_variables.map {|v| v.to_s }
67
+ links = []
68
+ data = {}
69
+
70
+ att.delete("@url")
71
+ att.delete("@client")
72
+
73
+ self.links.each do |l|
74
+ links << l.values.first.to_hash
75
+ end
76
+ att.delete("@links")
77
+
78
+ att.each do |opt|
79
+ data[opt.delete("@")] = instance_variable_get(opt)
80
+ end
81
+ data['links'] = links
82
+ data.to_json
83
+ end
84
+
85
+ ##
86
+ # Pretty print an instance object.
87
+ #
88
+ def inspect
89
+ Thread.current[:formatador] ||= Formatador.new
90
+ data = "#{Thread.current[:formatador].indentation}<#{self.class.name}"
91
+ Thread.current[:formatador].indent do
92
+ unless self.instance_variables.empty?
93
+ vars = self.instance_variables.clone
94
+ vars.delete(:@client)
95
+ data << "\n"
96
+ data << vars.map { |v| "#{v}=#{instance_variable_get(v.to_s).inspect}" }.join(",\n#{Thread.current[:formatador].indentation}")
97
+ end
98
+ end
99
+ data << "\n#{Thread.current[:formatador].indentation}>"
100
+ data
101
+ end
102
+
103
+ ##
104
+ # Retrieves the link that has the 'rel' attribute specified
105
+ # as parameter.
106
+ #
107
+ # Parameters:
108
+ # [link_rel] The 'rel' value to look for, symbolized.
109
+ #
110
+ # Returns the first link found with the 'rel' attribute
111
+ # specified or nil if not found.
112
+ #
113
+ def link(link_rel)
114
+ self.links.select {|l| l[link_rel] }.first[link_rel]
115
+ end
116
+
117
+ ##
118
+ # Checks if the object has a link with the 'rel' attribute
119
+ # specified as parameter.
120
+ #
121
+ # Parameters:
122
+ # [link_rel] The 'rel' value to look for, symbolized.
123
+ #
124
+ # Returns the true if the object has a link with the
125
+ # specified 'rel' or false otherwhise.
126
+ #
127
+ def has_link?(link_rel)
128
+ c = self.links.select {|l| l[link_rel] }.count
129
+ c == 0 ? false : true
130
+ end
131
+
132
+ ##
133
+ # Executes an HTTP PUT over the resource in Abiquo API,
134
+ # sending the current attributes as data.
135
+ #
136
+ # Returns a new instance representing the updated resource.
137
+ #
138
+ def update
139
+ @client.put(self.link(:edit), self)
140
+ end
141
+
142
+ ##
143
+ # Executes an HTTP DELETE over the resource in Abiquo API,
144
+ # deleting the current resource.
145
+ #
146
+ # Returns nil on success.
147
+ #
148
+ def delete
149
+ @client.delete(self.link(:edit))
150
+ end
151
+
152
+ ##
153
+ # Executes an HTTP GET over the resource in Abiquo API.
154
+ #
155
+ # Returns a new instance representing resource.
156
+ #
157
+ def refresh
158
+ self.link(:edit).get
159
+ end
160
+
161
+ private
162
+
163
+ ##
164
+ # Creates a new method in the instance object.
165
+ #
166
+ # Parameters:
167
+ # [name] The name of the method to be created.
168
+ # [&block] The block of code for that method.
169
+ #
170
+ def create_method( name, &block )
171
+ self.class.send( :define_method, name, &block )
172
+ end
173
+
174
+ ##
175
+ # Creates a new attribute for the instance object.
176
+ #
177
+ # Parameters:
178
+ # [name] The name of the attribute to be created.
179
+ # [ro] Boolean that specifies if the attribute will
180
+ # read only or read write. Defaults to false (rw)
181
+ #
182
+ def create_attr( name , ro = false)
183
+ unless ro
184
+ create_method( "#{name}=".to_sym ) { |val|
185
+ instance_variable_set( "@" + name, val)
186
+ }
187
+ end
188
+
189
+ create_method( name.to_sym ) {
190
+ instance_variable_get( "@" + name )
191
+ }
192
+ end
193
+ end
194
+ end
@@ -0,0 +1,3 @@
1
+ module AbiquoAPIClient
2
+ VERSION = '0.0.1'
3
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: abiquo-api
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Marc Cirauqui
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: excon
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.43'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.43.0
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '0.43'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.43.0
33
+ - !ruby/object:Gem::Dependency
34
+ name: formatador
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '0.2'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 0.2.5
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '0.2'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 0.2.5
53
+ - !ruby/object:Gem::Dependency
54
+ name: json
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '1.8'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 1.8.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.8'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 1.8.0
73
+ description: Simple Abiquo API client
74
+ email:
75
+ - marc.cirauqui@abiquo.com
76
+ executables: []
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - ".gitignore"
81
+ - Gemfile
82
+ - README.md
83
+ - abiquo-api.gemspec
84
+ - lib/abiquo-api.rb
85
+ - lib/abiquo-api/errors.rb
86
+ - lib/abiquo-api/httpclient.rb
87
+ - lib/abiquo-api/link.rb
88
+ - lib/abiquo-api/model.rb
89
+ - lib/abiquo-api/version.rb
90
+ homepage: https://github.com/abiquo/api-ruby
91
+ licenses: []
92
+ metadata: {}
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - ">="
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: '0'
107
+ requirements: []
108
+ rubyforge_project:
109
+ rubygems_version: 2.2.2
110
+ signing_key:
111
+ specification_version: 4
112
+ summary: Simple Abiquo API client
113
+ test_files: []
114
+ has_rdoc: