abiquo-api 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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: