haveapi-client 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +36 -15
- data/haveapi-client.gemspec +1 -1
- data/lib/haveapi/cli/authentication/token.rb +12 -5
- data/lib/haveapi/cli/cli.rb +52 -36
- data/lib/haveapi/cli/example_formatter.rb +43 -0
- data/lib/haveapi/client.rb +1 -0
- data/lib/haveapi/client/action.rb +37 -2
- data/lib/haveapi/client/authentication/token.rb +5 -1
- data/lib/haveapi/client/client.rb +44 -7
- data/lib/haveapi/client/communicator.rb +41 -13
- data/lib/haveapi/client/resource.rb +113 -55
- data/lib/haveapi/client/resource_instance.rb +201 -0
- data/lib/haveapi/client/resource_instance_list.rb +29 -0
- data/lib/haveapi/client/response.rb +28 -2
- data/lib/haveapi/client/version.rb +1 -1
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c2c9e15337f0d495ac86049fee2e76cf545957c
|
4
|
+
data.tar.gz: 9eaf5f13ad4c6d128a55b7efb3795b2840dd64dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7fa14b35b737f100c15ae7c8091e2abb18f9f8b3286ac006e723484f2c967dbb9f0683606fc7cda477c6cbc7d34ed9aabbe9bba83200a01608c3a9b04c825589
|
7
|
+
data.tar.gz: b3ac301b6d97864c2a1ddf909a121efb23aa5ef419be24f9e7ab0a436c16085f156f23ebaff4ed394f67c12665685323c70a7e3e95e147f672a768dc589130c7
|
data/README.md
CHANGED
@@ -17,8 +17,7 @@ Or install it yourself as:
|
|
17
17
|
|
18
18
|
$ gem install haveapi-client
|
19
19
|
|
20
|
-
##
|
21
|
-
### CLI
|
20
|
+
## CLI
|
22
21
|
$ haveapi-cli -h
|
23
22
|
Usage: haveapi-cli [options] <resource> <action> [objects ids] [-- [parameters]]
|
24
23
|
-u, --api URL API URL
|
@@ -28,50 +27,72 @@ Or install it yourself as:
|
|
28
27
|
List available authentication methods
|
29
28
|
--list-resources [VERSION] List all resource in API version
|
30
29
|
--list-actions [VERSION] List all resources and actions in API version
|
30
|
+
--version VERSION Use specified API version
|
31
31
|
-r, --raw Print raw response as is
|
32
32
|
-s, --save Save credentials to config file for later use
|
33
33
|
-v, --[no-]verbose Run verbosely
|
34
|
+
--client-version Show client version
|
34
35
|
-h, --help Show this message
|
35
36
|
|
36
37
|
Using the API example from
|
37
|
-
[HaveAPI README](https://github.com/vpsfreecz/haveapi
|
38
|
+
[HaveAPI README](https://github.com/vpsfreecz/haveapi#example),
|
38
39
|
users would be listed with:
|
39
40
|
|
40
|
-
$ haveapi-cli --url https://your.api.tld --auth basic --username yourname --password yourpassword user
|
41
|
+
$ haveapi-cli --url https://your.api.tld --auth basic --username yourname --password yourpassword user list
|
41
42
|
|
42
43
|
Nested resources and object IDs:
|
43
44
|
|
44
|
-
$ haveapi-cli --url https://your.api.tld --auth basic --username yourname --password yourpassword user.invoice
|
45
|
+
$ haveapi-cli --url https://your.api.tld --auth basic --username yourname --password yourpassword user.invoice list 10
|
45
46
|
|
46
47
|
where `10` is user ID.
|
47
48
|
|
48
49
|
User credentials can be saved to a config:
|
49
50
|
|
50
|
-
$ haveapi-cli --url https://your.api.tld --auth basic --username yourname --password yourpassword --save user
|
51
|
+
$ haveapi-cli --url https://your.api.tld --auth basic --username yourname --password yourpassword --save user list
|
51
52
|
|
52
53
|
When saved, they don't have to be specified as command line options:
|
53
54
|
|
54
|
-
$ haveapi-cli --url https://your.api.tld user
|
55
|
+
$ haveapi-cli --url https://your.api.tld user list
|
55
56
|
|
56
|
-
|
57
|
+
List options specific to authentication methods:
|
58
|
+
|
59
|
+
$ haveapi-cli --url https://your.api.tld --auth basic -h
|
60
|
+
$ haveapi-cli --url https://your.api.tld --auth token -h
|
61
|
+
|
62
|
+
List action parameters with examples:
|
63
|
+
|
64
|
+
$ haveapi-cli --url https://your.api.tld user new -h
|
65
|
+
|
66
|
+
Provide action parameters (notice the ``--`` separator):
|
67
|
+
|
68
|
+
$ haveapi-cli --url https://your.api.tld user new -- --login mylogin --full-name "My Full Name" --role user
|
69
|
+
|
70
|
+
|
71
|
+
## Client library
|
57
72
|
```ruby
|
58
73
|
require 'haveapi/client'
|
59
74
|
|
60
75
|
api = HaveAPI::Client::Client.new('https://your.api.tld')
|
61
76
|
api.authenticate(:basic, user: 'yourname', password: 'yourpassword')
|
62
77
|
|
63
|
-
|
64
|
-
|
65
|
-
|
78
|
+
api.user.list.each do |user|
|
79
|
+
puts user.login
|
80
|
+
end
|
66
81
|
|
67
|
-
|
68
|
-
p
|
69
|
-
|
70
|
-
p api.user.delete(10)
|
82
|
+
user = api.user.find(10)
|
83
|
+
p user.invoice
|
84
|
+
user.destroy
|
71
85
|
|
72
86
|
p api.user.create({
|
73
87
|
login: 'mylogin',
|
74
88
|
full_name: 'Very Full Name',
|
75
89
|
role: 'user'
|
76
90
|
})
|
91
|
+
|
92
|
+
user = api.user.new
|
93
|
+
user.login = 'mylogin'
|
94
|
+
user.full_name = 'Very Full Name'
|
95
|
+
user.role = 'user'
|
96
|
+
user.save
|
97
|
+
p user.id
|
77
98
|
```
|
data/haveapi-client.gemspec
CHANGED
@@ -26,5 +26,5 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_runtime_dependency 'rest_client', '~> 1.7.3'
|
27
27
|
spec.add_runtime_dependency 'json', '~> 1.8.1'
|
28
28
|
spec.add_runtime_dependency 'highline', '~> 1.6.21'
|
29
|
-
spec.add_runtime_dependency 'table_print', '~> 1.5.
|
29
|
+
spec.add_runtime_dependency 'table_print', '~> 1.5.3'
|
30
30
|
end
|
@@ -15,9 +15,15 @@ module HaveAPI::CLI::Authentication
|
|
15
15
|
@token = t
|
16
16
|
end
|
17
17
|
|
18
|
-
opts.on('--token-
|
19
|
-
|
20
|
-
|
18
|
+
opts.on('--token-lifetime LIFETIME',
|
19
|
+
%i(fixed renewable_manual renewable_auto permanent),
|
20
|
+
'Token lifetime, defaults to renewable_auto') do |l|
|
21
|
+
@lifetime = l
|
22
|
+
end
|
23
|
+
|
24
|
+
opts.on('--token-interval SECONDS', Integer,
|
25
|
+
'How long will token be valid in seconds') do |s|
|
26
|
+
@interval = s
|
21
27
|
end
|
22
28
|
|
23
29
|
opts.on('--new-token', 'Request new token') do
|
@@ -49,7 +55,8 @@ module HaveAPI::CLI::Authentication
|
|
49
55
|
user: @user,
|
50
56
|
password: @password,
|
51
57
|
token: @token,
|
52
|
-
|
58
|
+
lifetime: @lifetime || :renewable_auto,
|
59
|
+
interval: @interval,
|
53
60
|
valid_to: @valid_to,
|
54
61
|
via: @via
|
55
62
|
})
|
@@ -58,7 +65,7 @@ module HaveAPI::CLI::Authentication
|
|
58
65
|
def save
|
59
66
|
super.update({
|
60
67
|
via: @via,
|
61
|
-
|
68
|
+
interval: @interval
|
62
69
|
})
|
63
70
|
end
|
64
71
|
end
|
data/lib/haveapi/cli/cli.rb
CHANGED
@@ -24,7 +24,7 @@ module HaveAPI
|
|
24
24
|
@config = read_config || {}
|
25
25
|
args, @opts = options
|
26
26
|
|
27
|
-
@api = HaveAPI::Client::Communicator.new(api_url)
|
27
|
+
@api = HaveAPI::Client::Communicator.new(api_url, @opts[:version])
|
28
28
|
@api.identity = $0.split('/').last
|
29
29
|
|
30
30
|
if @action
|
@@ -46,10 +46,7 @@ module HaveAPI
|
|
46
46
|
exit(true)
|
47
47
|
end
|
48
48
|
|
49
|
-
action =
|
50
|
-
|
51
|
-
action = @api.get_action(resources, action, args[2..-1])
|
52
|
-
|
49
|
+
action = @api.get_action(resources, args[1].to_sym, args[2..-1])
|
53
50
|
action.update_description(@api.describe_action(action)) if authenticate(action)
|
54
51
|
|
55
52
|
@input_params = parameters(action)
|
@@ -63,15 +60,18 @@ module HaveAPI
|
|
63
60
|
|
64
61
|
if ret[:status]
|
65
62
|
format_output(action, ret[:response])
|
63
|
+
|
66
64
|
else
|
67
65
|
warn "Action failed: #{ret[:message]}"
|
68
66
|
|
69
|
-
if ret[:errors].any?
|
67
|
+
if ret[:errors] && ret[:errors].any?
|
70
68
|
puts 'Errors:'
|
71
69
|
ret[:errors].each do |param, e|
|
72
70
|
puts "\t#{param}: #{e.join('; ')}"
|
73
71
|
end
|
74
72
|
end
|
73
|
+
|
74
|
+
print_examples(action)
|
75
75
|
end
|
76
76
|
|
77
77
|
else
|
@@ -119,6 +119,10 @@ module HaveAPI
|
|
119
119
|
@action = [:list_actions, v && v.sub(/^v/, '')]
|
120
120
|
end
|
121
121
|
|
122
|
+
opts.on('--version VERSION', 'Use specified API version') do |v|
|
123
|
+
options[:version] = v
|
124
|
+
end
|
125
|
+
|
122
126
|
opts.on('-r', '--raw', 'Print raw response as is') do
|
123
127
|
options[:raw] = true
|
124
128
|
end
|
@@ -131,6 +135,10 @@ module HaveAPI
|
|
131
135
|
options[:verbose] = v
|
132
136
|
end
|
133
137
|
|
138
|
+
opts.on('--client-version', 'Show client version') do
|
139
|
+
@action = [:show_version]
|
140
|
+
end
|
141
|
+
|
134
142
|
opts.on('-h', '--help', 'Show this message') do
|
135
143
|
options[:help] = true
|
136
144
|
end
|
@@ -178,8 +186,11 @@ module HaveAPI
|
|
178
186
|
if @opts[:help]
|
179
187
|
puts @global_opt.help
|
180
188
|
puts ''
|
189
|
+
puts 'Action description:'
|
190
|
+
puts action.description, "\n"
|
181
191
|
print 'Action parameters:'
|
182
192
|
puts @action_opt.help
|
193
|
+
print_examples(action)
|
183
194
|
exit
|
184
195
|
end
|
185
196
|
|
@@ -204,56 +215,46 @@ module HaveAPI
|
|
204
215
|
ret
|
205
216
|
end
|
206
217
|
|
207
|
-
def translate_action(action)
|
208
|
-
tr = {
|
209
|
-
list: :index,
|
210
|
-
new: :create,
|
211
|
-
change: :update
|
212
|
-
}
|
213
|
-
|
214
|
-
if tr.has_key?(action)
|
215
|
-
return tr[action]
|
216
|
-
end
|
217
|
-
|
218
|
-
action
|
219
|
-
end
|
220
|
-
|
221
218
|
def list_versions
|
222
|
-
desc = @api.
|
219
|
+
desc = @api.available_versions
|
223
220
|
|
224
221
|
desc[:versions].each do |v, _|
|
225
222
|
next if v == :default
|
226
223
|
|
227
224
|
v_int = v.to_s.to_i
|
228
225
|
|
229
|
-
puts "#{v_int == desc[:
|
226
|
+
puts "#{v_int == desc[:default] ? '*' : ' '} v#{v}"
|
230
227
|
end
|
231
228
|
end
|
232
229
|
|
233
230
|
def list_auth(v=nil)
|
234
|
-
desc = @api.describe_api
|
231
|
+
desc = @api.describe_api(v)
|
235
232
|
|
236
|
-
desc[:
|
233
|
+
desc[:authentication].each_key do |auth|
|
237
234
|
puts auth if Cli.auth_methods.has_key?(auth)
|
238
235
|
end
|
239
236
|
end
|
240
237
|
|
241
238
|
def list_resources(v=nil)
|
242
|
-
desc = @api.describe_api
|
239
|
+
desc = @api.describe_api(v)
|
243
240
|
|
244
|
-
desc[:
|
241
|
+
desc[:resources].each do |resource, children|
|
245
242
|
nested_resource(resource, children, false)
|
246
243
|
end
|
247
244
|
end
|
248
245
|
|
249
246
|
def list_actions(v=nil)
|
250
|
-
desc = @api.describe_api
|
247
|
+
desc = @api.describe_api(v)
|
251
248
|
|
252
|
-
desc[:
|
249
|
+
desc[:resources].each do |resource, children|
|
253
250
|
nested_resource(resource, children, true)
|
254
251
|
end
|
255
252
|
end
|
256
253
|
|
254
|
+
def show_version
|
255
|
+
puts HaveAPI::Client::VERSION
|
256
|
+
end
|
257
|
+
|
257
258
|
def describe_resource(path)
|
258
259
|
desc = @api.describe_resource(path)
|
259
260
|
|
@@ -295,7 +296,14 @@ module HaveAPI
|
|
295
296
|
end
|
296
297
|
end
|
297
298
|
|
298
|
-
def
|
299
|
+
def print_examples(action)
|
300
|
+
unless action.examples.empty?
|
301
|
+
puts "\nExamples:\n"
|
302
|
+
ExampleFormatter.format_examples(self, action)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def format_output(action, response, out = $>)
|
299
307
|
if @opts[:raw]
|
300
308
|
puts response
|
301
309
|
return
|
@@ -303,15 +311,23 @@ module HaveAPI
|
|
303
311
|
|
304
312
|
return if response.empty?
|
305
313
|
|
314
|
+
tp.set :io, out
|
315
|
+
|
306
316
|
namespace = action.namespace(:output).to_sym
|
307
317
|
|
308
|
-
case action.
|
309
|
-
when :
|
318
|
+
case action.output_layout.to_sym
|
319
|
+
when :object_list, :hash_list
|
310
320
|
cols = []
|
311
321
|
|
312
322
|
action.params.each do |name, p|
|
313
323
|
if p[:type] == 'Resource'
|
314
|
-
cols << {
|
324
|
+
cols << {
|
325
|
+
name => {
|
326
|
+
display_method: ->(r) {
|
327
|
+
r[name] && "#{r[name][p[:value_label].to_sym]} (##{r[name][p[:value_id].to_sym]})"
|
328
|
+
}
|
329
|
+
}
|
330
|
+
}
|
315
331
|
else
|
316
332
|
cols << name
|
317
333
|
end
|
@@ -320,19 +336,19 @@ module HaveAPI
|
|
320
336
|
tp response[namespace], *cols
|
321
337
|
|
322
338
|
|
323
|
-
when :object
|
339
|
+
when :object, :hash
|
324
340
|
response[namespace].each do |k, v|
|
325
341
|
|
326
342
|
if action.params[k][:type] == 'Resource'
|
327
|
-
|
343
|
+
out << "#{k}: #{v[action.params[k][:value_label].to_sym]}\n"
|
328
344
|
else
|
329
|
-
|
345
|
+
out << "#{k}: #{v}\n"
|
330
346
|
end
|
331
347
|
end
|
332
348
|
|
333
349
|
|
334
350
|
when :custom
|
335
|
-
pp
|
351
|
+
PP.pp(response[namespace], out)
|
336
352
|
|
337
353
|
end
|
338
354
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module HaveAPI::CLI
|
2
|
+
module ExampleFormatter
|
3
|
+
def self.format_examples(cli, action, out = $>)
|
4
|
+
action.examples.each do |example|
|
5
|
+
out << ' > ' << example[:title] << ":\n" unless example[:title].empty?
|
6
|
+
|
7
|
+
# request
|
8
|
+
out << "$ #{$0} #{action.resource_path.join('.')} #{action.name}"
|
9
|
+
|
10
|
+
params = example[:request][action.namespace(:input).to_sym]
|
11
|
+
|
12
|
+
if params
|
13
|
+
out << ' --' unless params.empty?
|
14
|
+
|
15
|
+
params.each do |k, v|
|
16
|
+
out << ' ' << example_param(k, v, action.param_description(:input, k))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
out << "\n"
|
21
|
+
|
22
|
+
# response
|
23
|
+
cli.format_output(action, example[:response], out)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.example_param(name, value, desc)
|
28
|
+
option = name.to_s.dasherize
|
29
|
+
|
30
|
+
case desc[:type]
|
31
|
+
when 'Boolean'
|
32
|
+
value ? "--#{option}" : "--no-#{option}"
|
33
|
+
|
34
|
+
else
|
35
|
+
"--#{option} #{example_value(value)}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.example_value(v)
|
40
|
+
(v.is_a?(String) && (v.empty? || v.index(' '))) ? "\"#{v}\"" : v
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/haveapi/client.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module HaveAPI
|
2
2
|
module Client
|
3
3
|
class Action
|
4
|
+
attr_accessor :resource_path
|
5
|
+
|
4
6
|
def initialize(api, name, spec, args)
|
5
7
|
@api = api
|
6
8
|
@name = name
|
@@ -24,6 +26,18 @@ module HaveAPI
|
|
24
26
|
@spec[:auth]
|
25
27
|
end
|
26
28
|
|
29
|
+
def aliases(include_name = false)
|
30
|
+
if include_name
|
31
|
+
[@name] + @spec[:aliases]
|
32
|
+
else
|
33
|
+
@spec[:aliases]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def description
|
38
|
+
@spec[:description]
|
39
|
+
end
|
40
|
+
|
27
41
|
def input
|
28
42
|
@spec[:input]
|
29
43
|
end
|
@@ -32,8 +46,12 @@ module HaveAPI
|
|
32
46
|
@spec[:output]
|
33
47
|
end
|
34
48
|
|
35
|
-
def
|
36
|
-
@spec[:
|
49
|
+
def input_layout
|
50
|
+
@spec[:input][:layout].to_sym
|
51
|
+
end
|
52
|
+
|
53
|
+
def output_layout
|
54
|
+
@spec[:output][:layout].to_sym
|
37
55
|
end
|
38
56
|
|
39
57
|
def structure
|
@@ -44,10 +62,22 @@ module HaveAPI
|
|
44
62
|
@spec[src][:namespace]
|
45
63
|
end
|
46
64
|
|
65
|
+
def examples
|
66
|
+
@spec[:examples]
|
67
|
+
end
|
68
|
+
|
69
|
+
def input_params
|
70
|
+
@spec[:input][:parameters]
|
71
|
+
end
|
72
|
+
|
47
73
|
def params
|
48
74
|
@spec[:output][:parameters]
|
49
75
|
end
|
50
76
|
|
77
|
+
def param_description(dir, name)
|
78
|
+
@spec[dir][:parameters][name]
|
79
|
+
end
|
80
|
+
|
51
81
|
def url
|
52
82
|
@spec[:url]
|
53
83
|
end
|
@@ -77,6 +107,11 @@ module HaveAPI
|
|
77
107
|
apply_args(args)
|
78
108
|
end
|
79
109
|
|
110
|
+
def provide_url(url, help)
|
111
|
+
@prepared_url = url
|
112
|
+
@prepared_help = help
|
113
|
+
end
|
114
|
+
|
80
115
|
def update_description(spec)
|
81
116
|
@spec = spec
|
82
117
|
end
|
@@ -36,7 +36,11 @@ module HaveAPI::Client::Authentication
|
|
36
36
|
protected
|
37
37
|
def request_token
|
38
38
|
a = HaveAPI::Client::Action.new(@communicator, :request, @desc[:resources][:token][:actions][:request], [])
|
39
|
-
ret = a.execute({
|
39
|
+
ret = a.execute({
|
40
|
+
login: @opts[:user],
|
41
|
+
password: @opts[:password],
|
42
|
+
lifetime: @opts[:lifetime],
|
43
|
+
interval: @opts[:interval] || 300})
|
40
44
|
|
41
45
|
raise AuthenticationFailed.new('bad username or password') unless ret[:status]
|
42
46
|
|
@@ -1,14 +1,34 @@
|
|
1
1
|
require 'pp'
|
2
2
|
|
3
|
+
# HaveAPI client interface.
|
3
4
|
class HaveAPI::Client::Client
|
4
5
|
attr_reader :resources
|
5
6
|
|
6
|
-
|
7
|
+
# Create an instance of client.
|
8
|
+
# The client by default uses the default version of the API.
|
9
|
+
# API is asked for description only when needed or by calling #setup.
|
10
|
+
# +identity+ is sent in each request to the API in User-Agent header.
|
11
|
+
def initialize(url, v = nil, identity: 'haveapi-client')
|
12
|
+
@setup = false
|
7
13
|
@version = v
|
8
14
|
@api = HaveAPI::Client::Communicator.new(url, v)
|
9
15
|
@api.identity = identity
|
16
|
+
end
|
17
|
+
|
18
|
+
# Get the description from the API now.
|
19
|
+
def setup(v = :_nil)
|
20
|
+
@version = v unless v == :_nil
|
21
|
+
setup_api
|
22
|
+
end
|
10
23
|
|
11
|
-
|
24
|
+
# Returns a list of API versions.
|
25
|
+
# The return value is a hash, e.g.:
|
26
|
+
# {
|
27
|
+
# versions: [1, 2, 3],
|
28
|
+
# default: 3
|
29
|
+
# }
|
30
|
+
def versions
|
31
|
+
@api.available_versions
|
12
32
|
end
|
13
33
|
|
14
34
|
# See Communicator#authenticate.
|
@@ -16,14 +36,29 @@ class HaveAPI::Client::Client
|
|
16
36
|
@api.authenticate(*args)
|
17
37
|
end
|
18
38
|
|
19
|
-
|
20
|
-
|
21
|
-
|
39
|
+
# Initialize the client if it is not yet initialized and call the resource
|
40
|
+
# if it exists.
|
41
|
+
def method_missing(symbol, *args)
|
42
|
+
return super(symbol, *args) if @setup
|
43
|
+
|
44
|
+
setup_api
|
45
|
+
|
46
|
+
if @resources.include?(symbol)
|
47
|
+
method(symbol).call(*args)
|
22
48
|
|
49
|
+
else
|
50
|
+
super(symbol, *args)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
# Get the description from the API and setup resource methods.
|
56
|
+
def setup_api
|
57
|
+
@description = @api.describe_api(@version)
|
23
58
|
@resources = {}
|
24
59
|
|
25
|
-
description[:
|
26
|
-
r = HaveAPI::Client::Resource.new(@api, name)
|
60
|
+
@description[:resources].each do |name, desc|
|
61
|
+
r = HaveAPI::Client::Resource.new(self, @api, name)
|
27
62
|
r.setup(desc)
|
28
63
|
|
29
64
|
define_singleton_method(name) do |*args|
|
@@ -35,5 +70,7 @@ class HaveAPI::Client::Client
|
|
35
70
|
|
36
71
|
@resources[name] = r
|
37
72
|
end
|
73
|
+
|
74
|
+
@setup = true
|
38
75
|
end
|
39
76
|
end
|
@@ -25,6 +25,7 @@ module HaveAPI
|
|
25
25
|
@rest = RestClient::Resource.new(@url)
|
26
26
|
@version = v
|
27
27
|
@identity = 'haveapi-client-ruby'
|
28
|
+
@desc = {}
|
28
29
|
end
|
29
30
|
|
30
31
|
# Authenticate user with selected +auth_method+.
|
@@ -32,7 +33,6 @@ module HaveAPI
|
|
32
33
|
# +options+ are specific for each authentication provider.
|
33
34
|
def authenticate(auth_method, options = {})
|
34
35
|
desc = describe_api(@version)
|
35
|
-
desc = desc[:versions][desc[:default_version].to_s.to_sym] unless @version
|
36
36
|
|
37
37
|
@auth = self.class.auth_methods[auth_method].new(self, desc[:authentication][auth_method], options)
|
38
38
|
@rest = @auth.resource || @rest
|
@@ -42,13 +42,18 @@ module HaveAPI
|
|
42
42
|
@auth.save
|
43
43
|
end
|
44
44
|
|
45
|
+
def available_versions
|
46
|
+
description_for(path_for, {describe: :versions})
|
47
|
+
end
|
48
|
+
|
45
49
|
def describe_api(v=nil)
|
46
|
-
|
50
|
+
return @desc[v] if @desc.has_key?(v)
|
51
|
+
|
52
|
+
@desc[v] = description_for(path_for(v), v.nil? ? {describe: :default} : {})
|
47
53
|
end
|
48
54
|
|
49
55
|
def describe_resource(path)
|
50
|
-
|
51
|
-
tmp = api[:versions][ api[:default_version].to_s.to_sym ]
|
56
|
+
tmp = describe_api(@version)
|
52
57
|
|
53
58
|
path.each do |r|
|
54
59
|
tmp = tmp[:resources][r.to_sym]
|
@@ -64,10 +69,7 @@ module HaveAPI
|
|
64
69
|
end
|
65
70
|
|
66
71
|
def get_action(resources, action, args)
|
67
|
-
|
68
|
-
@spec = @spec[:versions][@spec[:default_version].to_s.to_sym] unless @version
|
69
|
-
|
70
|
-
tmp = @spec
|
72
|
+
tmp = describe_api(@version)
|
71
73
|
|
72
74
|
resources.each do |r|
|
73
75
|
tmp = tmp[:resources][r.to_sym]
|
@@ -77,8 +79,19 @@ module HaveAPI
|
|
77
79
|
|
78
80
|
a = tmp[:actions][action]
|
79
81
|
|
82
|
+
unless a # search in aliases
|
83
|
+
tmp[:actions].each do |_, v|
|
84
|
+
if v[:aliases].include?(action.to_s)
|
85
|
+
a = v
|
86
|
+
break
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
80
91
|
if a
|
81
|
-
Action.new(self, action, a, args)
|
92
|
+
obj = Action.new(self, action, a, args)
|
93
|
+
obj.resource_path = resources
|
94
|
+
obj
|
82
95
|
else
|
83
96
|
false
|
84
97
|
end
|
@@ -87,9 +100,19 @@ module HaveAPI
|
|
87
100
|
def call(action, params, raw: false)
|
88
101
|
args = []
|
89
102
|
input_namespace = action.namespace(:input)
|
103
|
+
meta = nil
|
104
|
+
|
105
|
+
if params.is_a?(Hash) && params[:meta]
|
106
|
+
meta = params[:meta]
|
107
|
+
params.delete(:meta)
|
108
|
+
end
|
90
109
|
|
91
110
|
if %w(POST PUT).include?(action.http_method)
|
92
|
-
|
111
|
+
ns = {input_namespace => params}
|
112
|
+
ns[:_meta] = meta if meta
|
113
|
+
ns.update(@auth.request_payload)
|
114
|
+
|
115
|
+
args << ns.to_json
|
93
116
|
args << {content_type: :json, accept: :json, user_agent: @identity}.update(@auth.request_headers)
|
94
117
|
|
95
118
|
elsif %w(GET DELETE).include?(action.http_method)
|
@@ -99,6 +122,11 @@ module HaveAPI
|
|
99
122
|
get_params["#{input_namespace}[#{k}]"] = v
|
100
123
|
end
|
101
124
|
|
125
|
+
meta.each do |k, v|
|
126
|
+
get_params["_meta[#{k}]"] = v # FIXME: read _meta namespace from the description
|
127
|
+
|
128
|
+
end if meta
|
129
|
+
|
102
130
|
args << {params: get_params.update(@auth.request_url_params), accept: :json, user_agent: @identity}.update(@auth.request_headers)
|
103
131
|
end
|
104
132
|
|
@@ -142,11 +170,11 @@ module HaveAPI
|
|
142
170
|
ret
|
143
171
|
end
|
144
172
|
|
145
|
-
def description_for(path)
|
173
|
+
def description_for(path, query_params={})
|
146
174
|
parse(@rest[path].get_options({
|
147
|
-
params: @auth.request_payload.update(@auth.request_url_params),
|
175
|
+
params: @auth.request_payload.update(@auth.request_url_params).update(query_params),
|
148
176
|
user_agent: @identity
|
149
|
-
}.update(@auth.request_headers)))
|
177
|
+
}.update(@auth.request_headers)))[:response]
|
150
178
|
end
|
151
179
|
|
152
180
|
def parse(str)
|
@@ -1,78 +1,136 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
module HaveAPI::Client
|
2
|
+
# An API resource.
|
3
|
+
class Resource
|
4
|
+
attr_reader :actions, :resources
|
5
|
+
attr_accessor :prepared_args
|
6
|
+
|
7
|
+
def initialize(client, api, name)
|
8
|
+
@client = client
|
9
|
+
@api = api
|
10
|
+
@name = name
|
11
|
+
@prepared_args = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup(description)
|
15
|
+
@actions = {}
|
16
|
+
@resources = {}
|
10
17
|
|
11
|
-
|
12
|
-
|
13
|
-
|
18
|
+
description[:actions].each do |name, desc|
|
19
|
+
action = HaveAPI::Client::Action.new(@api, name, desc, [])
|
20
|
+
define_action(action)
|
21
|
+
@actions[name] = action
|
22
|
+
end
|
14
23
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
24
|
+
description[:resources].each do |name, desc|
|
25
|
+
r = HaveAPI::Client::Resource.new(@client, @api, name)
|
26
|
+
r.setup(desc)
|
27
|
+
define_resource(r)
|
28
|
+
@resources[name] = r
|
29
|
+
end
|
19
30
|
end
|
20
31
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
32
|
+
# Copy actions and resources from the +original+ resource
|
33
|
+
# and create methods for this instance.
|
34
|
+
def setup_from_clone(original)
|
35
|
+
original.actions.each_value do |action|
|
36
|
+
define_action(action)
|
37
|
+
end
|
38
|
+
|
39
|
+
original.resources.each_value do |resource|
|
40
|
+
define_resource(resource)
|
41
|
+
end
|
26
42
|
end
|
27
|
-
end
|
28
43
|
|
29
|
-
|
30
|
-
|
31
|
-
define_action(action)
|
44
|
+
def inspect
|
45
|
+
super
|
32
46
|
end
|
33
47
|
|
34
|
-
|
35
|
-
|
48
|
+
# Create a new instance of a resource. The created instance
|
49
|
+
# is not persistent until ResourceInstance#save is called.
|
50
|
+
def new
|
51
|
+
ResourceInstance.new(@client, @api, self, action: @actions[:create], persistent: false)
|
36
52
|
end
|
37
|
-
end
|
38
53
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
54
|
+
# Return resource name.
|
55
|
+
# Method is prefixed with an underscore to prevent name collision
|
56
|
+
# with ResourceInstance attributes.
|
57
|
+
def _name
|
58
|
+
@name
|
59
|
+
end
|
43
60
|
|
44
|
-
|
45
|
-
|
46
|
-
|
61
|
+
protected
|
62
|
+
# Define access/write methods for action +action+.
|
63
|
+
def define_action(action)
|
64
|
+
action.aliases(true).each do |name|
|
65
|
+
next unless define_method?(action, name)
|
47
66
|
|
48
|
-
|
49
|
-
|
50
|
-
end
|
67
|
+
define_singleton_method(name) do |*args|
|
68
|
+
all_args = @prepared_args + args
|
51
69
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
70
|
+
if action.unresolved_args?
|
71
|
+
all_args.delete_if do |arg|
|
72
|
+
break unless action.unresolved_args?
|
56
73
|
|
57
|
-
|
74
|
+
action.provide_args(arg)
|
75
|
+
true
|
76
|
+
end
|
58
77
|
|
59
|
-
|
78
|
+
if action.unresolved_args?
|
79
|
+
raise ArgumentError.new('One or more object ids missing')
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if all_args.empty?
|
84
|
+
all_args << default_action_input_params(action)
|
85
|
+
|
86
|
+
elsif all_args.last.is_a?(Hash)
|
87
|
+
last = all_args.pop
|
88
|
+
|
89
|
+
all_args << default_action_input_params(action).update(last)
|
90
|
+
end
|
91
|
+
|
92
|
+
ret = Response.new(action, action.execute(*all_args))
|
93
|
+
|
94
|
+
raise ActionFailed.new(ret) unless ret.ok?
|
95
|
+
|
96
|
+
case action.output_layout
|
97
|
+
when :object
|
98
|
+
ResourceInstance.new(@client, @api, self, action: action, response: ret)
|
99
|
+
|
100
|
+
when :object_list
|
101
|
+
ResourceInstanceList.new(@client, @api, self, action, ret)
|
102
|
+
|
103
|
+
when :hash, :hash_list
|
104
|
+
ret
|
105
|
+
|
106
|
+
else
|
107
|
+
ret
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
60
111
|
end
|
61
112
|
|
62
|
-
|
63
|
-
|
64
|
-
|
113
|
+
# Called before defining a method named +name+ that will
|
114
|
+
# invoke +action+.
|
115
|
+
def define_method?(action, name)
|
116
|
+
return false if %i(new).include?(name.to_sym)
|
117
|
+
true
|
118
|
+
end
|
65
119
|
|
66
|
-
|
120
|
+
# This method is called when an action is invoked.
|
121
|
+
# Override it to return a default hash of parameters to be sent to API.
|
122
|
+
# Used for example in ResourceInstance, which returns its instance attributes.
|
123
|
+
def default_action_input_params(action)
|
124
|
+
{}
|
67
125
|
end
|
68
|
-
end
|
69
126
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
127
|
+
def define_resource(resource)
|
128
|
+
define_singleton_method(resource._name) do |*args|
|
129
|
+
tmp = resource.dup
|
130
|
+
tmp.prepared_args = @prepared_args + args
|
131
|
+
tmp.setup_from_clone(resource)
|
132
|
+
tmp
|
133
|
+
end
|
76
134
|
end
|
77
135
|
end
|
78
136
|
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module HaveAPI::Client
|
2
|
+
# Instance of an object from the API.
|
3
|
+
# An instance of this class may be in three states:
|
4
|
+
# - resolved/persistent - the instance was created by an action that retrieved
|
5
|
+
# it from the API.
|
6
|
+
# - unresolved - this instance is an attribute of another instance that was resolved
|
7
|
+
# and will be resolved when first accessed.
|
8
|
+
# - not persistent - created by Resource.new, the object was not yet sent to the API.
|
9
|
+
class ResourceInstance < Resource
|
10
|
+
def initialize(client, api, resource, action: nil, response: nil,
|
11
|
+
resolved: false, meta: nil, persistent: true)
|
12
|
+
super(client, api, resource._name)
|
13
|
+
|
14
|
+
@action = action
|
15
|
+
@resource = resource
|
16
|
+
@resolved = resolved
|
17
|
+
@meta = meta
|
18
|
+
@persistent = persistent
|
19
|
+
@resource_instances = {}
|
20
|
+
|
21
|
+
if response
|
22
|
+
if response.is_a?(Hash)
|
23
|
+
@params = response
|
24
|
+
|
25
|
+
else
|
26
|
+
@response = response
|
27
|
+
@params = response.response
|
28
|
+
end
|
29
|
+
|
30
|
+
setup_from_clone(resource)
|
31
|
+
define_attributes
|
32
|
+
end
|
33
|
+
|
34
|
+
unless @persistent
|
35
|
+
setup_from_clone(resource)
|
36
|
+
define_implicit_attributes
|
37
|
+
define_attributes(@action.input_params)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def new
|
42
|
+
raise NoMethodError.new
|
43
|
+
end
|
44
|
+
|
45
|
+
# Invoke +create+ action if the object is not persistent,
|
46
|
+
# +update+ action if it is.
|
47
|
+
def save
|
48
|
+
if @persistent
|
49
|
+
method(:update).call
|
50
|
+
|
51
|
+
else
|
52
|
+
@action.provide_args
|
53
|
+
@response = Response.new(@action, @action.execute(attributes_for_api(@action)))
|
54
|
+
|
55
|
+
if @response.ok?
|
56
|
+
@params = @response.response
|
57
|
+
define_attributes
|
58
|
+
|
59
|
+
else
|
60
|
+
return nil
|
61
|
+
end
|
62
|
+
|
63
|
+
@persistent = true
|
64
|
+
self
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Call #save and raise ActionFailed if it fails.
|
69
|
+
def save!
|
70
|
+
raise ActionFailed.new(@response) if save.nil?
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
# Resolve the object (fetch it from the API) if it is not resolved yet.
|
75
|
+
def resolve
|
76
|
+
return self if @resolved
|
77
|
+
|
78
|
+
@action.provide_args(*@meta[:url_params])
|
79
|
+
@response = Response.new(@action, @action.execute({}))
|
80
|
+
@params = @response.response
|
81
|
+
|
82
|
+
setup_from_clone(@resource)
|
83
|
+
define_attributes
|
84
|
+
|
85
|
+
@resolved = true
|
86
|
+
self
|
87
|
+
end
|
88
|
+
|
89
|
+
# Return Response object which created this instance.
|
90
|
+
def api_response
|
91
|
+
@response
|
92
|
+
end
|
93
|
+
|
94
|
+
# Return a hash of all object attributes retrieved from the API.
|
95
|
+
def attributes
|
96
|
+
@params
|
97
|
+
end
|
98
|
+
|
99
|
+
def to_s
|
100
|
+
"<#{self.class.to_s}:#{object_id}:#{@resource._name}>"
|
101
|
+
end
|
102
|
+
|
103
|
+
protected
|
104
|
+
# Define access/writer methods for object attributes.
|
105
|
+
def define_attributes(params = nil)
|
106
|
+
(params || @action.params).each do |name, param|
|
107
|
+
case param[:type]
|
108
|
+
when 'Resource'
|
109
|
+
@resource_instances[name] = find_association(param, @params[name])
|
110
|
+
|
111
|
+
# id reader
|
112
|
+
ensure_method(:"#{name}_id") { @params[name][ param[:value_id].to_sym ] }
|
113
|
+
|
114
|
+
# id writer
|
115
|
+
ensure_method(:"#{name}_id=") { |id| @params[name][ param[:value_id].to_sym ] = id }
|
116
|
+
|
117
|
+
# value reader
|
118
|
+
ensure_method(name) do
|
119
|
+
@resource_instances[name] && @resource_instances[name].resolve
|
120
|
+
end
|
121
|
+
|
122
|
+
# value writer
|
123
|
+
ensure_method(:"#{name}=") do |obj|
|
124
|
+
@params[name][ param[:value_id].to_sym ] = obj.method(param[:value_id]).call
|
125
|
+
@params[name][ param[:value_label].to_sym ] = obj.method(param[:value_label]).call
|
126
|
+
|
127
|
+
@resource_instances[name] = obj
|
128
|
+
end
|
129
|
+
|
130
|
+
else
|
131
|
+
# reader
|
132
|
+
ensure_method(name) { @params[name] }
|
133
|
+
|
134
|
+
# writer
|
135
|
+
ensure_method(:"#{name}=") { |new_val| @params[name] = new_val }
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Define method +name+ with +block+ if it isn't defined yet.
|
141
|
+
def ensure_method(name, &block)
|
142
|
+
define_singleton_method(name, &block) unless respond_to?(name)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Define nil references to resource attributes.
|
146
|
+
# Used only for not-persistent objects.
|
147
|
+
def define_implicit_attributes
|
148
|
+
@params = {}
|
149
|
+
|
150
|
+
@action.input_params.each do |name, param|
|
151
|
+
@params[name] = {} if param[:type] == 'Resource'
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Return a hash of all attributes suitable to be sent to the API +action+.
|
156
|
+
def attributes_for_api(action)
|
157
|
+
ret = {}
|
158
|
+
|
159
|
+
return ret if action.input_layout != :object
|
160
|
+
|
161
|
+
action.input_params.each do |name, param|
|
162
|
+
case param[:type]
|
163
|
+
when 'Resource'
|
164
|
+
ret[name] = @params[name][ param[:value_id].to_sym ]
|
165
|
+
|
166
|
+
else
|
167
|
+
ret[name] = @params[name]
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
ret
|
172
|
+
end
|
173
|
+
|
174
|
+
# Find associated resource and create its unresolved instance.
|
175
|
+
def find_association(res_desc, res_val)
|
176
|
+
return nil unless res_val
|
177
|
+
|
178
|
+
tmp = @client
|
179
|
+
|
180
|
+
res_desc[:resource].each do |r|
|
181
|
+
tmp = tmp.method(r).call
|
182
|
+
end
|
183
|
+
|
184
|
+
# FIXME: read _meta namespace from description
|
185
|
+
ResourceInstance.new(
|
186
|
+
@client,
|
187
|
+
@api,
|
188
|
+
tmp,
|
189
|
+
action: tmp.actions[:show],
|
190
|
+
resolved: res_val[:_meta][:resolved],
|
191
|
+
response: res_val[:_meta][:resolved] ? res_val : nil,
|
192
|
+
meta: res_val[:_meta]
|
193
|
+
)
|
194
|
+
end
|
195
|
+
|
196
|
+
# Override Resource.default_action_input_params.
|
197
|
+
def default_action_input_params(action)
|
198
|
+
attributes_for_api(action)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module HaveAPI::Client
|
2
|
+
# A list of ResourceInstance objects.
|
3
|
+
class ResourceInstanceList < Array
|
4
|
+
def initialize(client, api, resource, action, response)
|
5
|
+
@response = response
|
6
|
+
|
7
|
+
response.response.each do |hash|
|
8
|
+
self << ResourceInstance.new(client, api, resource, action: action, response: hash)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# Return the API response that created this object.
|
13
|
+
def api_response
|
14
|
+
@response
|
15
|
+
end
|
16
|
+
|
17
|
+
def meta
|
18
|
+
@response.meta
|
19
|
+
end
|
20
|
+
|
21
|
+
# Return the total count of items.
|
22
|
+
# Note that for this method to work, the action that returns this
|
23
|
+
# object list must be invoked with +meta: {count: true}+, otherwise
|
24
|
+
# the object count is not sent.
|
25
|
+
def total_count
|
26
|
+
meta[:total_count]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -1,6 +1,9 @@
|
|
1
|
+
# Represents a response from the API.
|
1
2
|
class HaveAPI::Client::Response
|
2
3
|
attr_reader :action
|
3
4
|
|
5
|
+
# Create instance.
|
6
|
+
# +action+ being the called action and +response+ a received hash.
|
4
7
|
def initialize(action, response)
|
5
8
|
@action = action
|
6
9
|
@response = response
|
@@ -15,14 +18,22 @@ class HaveAPI::Client::Response
|
|
15
18
|
end
|
16
19
|
|
17
20
|
def response
|
18
|
-
case @action.
|
19
|
-
when :object, :
|
21
|
+
case @action.output_layout
|
22
|
+
when :object, :object_list, :hash, :hash_list
|
20
23
|
@response[:response][@action.namespace(:output).to_sym]
|
21
24
|
else
|
22
25
|
@response[:response]
|
23
26
|
end
|
24
27
|
end
|
25
28
|
|
29
|
+
def meta
|
30
|
+
@response[:response][:_meta] # FIXME: read _meta from API description
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_hash
|
34
|
+
response
|
35
|
+
end
|
36
|
+
|
26
37
|
def message
|
27
38
|
@response[:message]
|
28
39
|
end
|
@@ -30,4 +41,19 @@ class HaveAPI::Client::Response
|
|
30
41
|
def errors
|
31
42
|
@response[:errors]
|
32
43
|
end
|
44
|
+
|
45
|
+
# Access namespaced params directly.
|
46
|
+
def [](key)
|
47
|
+
return unless %i(object hash).include?(@action.layout.to_sym)
|
48
|
+
|
49
|
+
@response[:response][@action.namespace(:output).to_sym][key]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Iterate over namespaced items directly. Works for only for
|
53
|
+
# object_list or hash_list.
|
54
|
+
def each
|
55
|
+
return unless %i(list).include?(@action.layout.to_sym)
|
56
|
+
|
57
|
+
@response[:response][@action.namespace(:output).to_sym].each
|
58
|
+
end
|
33
59
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: haveapi-client
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jakub Skokan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -114,14 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - ~>
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: 1.5.
|
117
|
+
version: 1.5.3
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - ~>
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: 1.5.
|
124
|
+
version: 1.5.3
|
125
125
|
description: Ruby API and CLI for HaveAPI
|
126
126
|
email:
|
127
127
|
- jakub.skokan@vpsfree.cz
|
@@ -141,6 +141,7 @@ files:
|
|
141
141
|
- lib/haveapi/cli/authentication/basic.rb
|
142
142
|
- lib/haveapi/cli/authentication/token.rb
|
143
143
|
- lib/haveapi/cli/cli.rb
|
144
|
+
- lib/haveapi/cli/example_formatter.rb
|
144
145
|
- lib/haveapi/client.rb
|
145
146
|
- lib/haveapi/client/action.rb
|
146
147
|
- lib/haveapi/client/authentication/base.rb
|
@@ -152,6 +153,8 @@ files:
|
|
152
153
|
- lib/haveapi/client/exceptions.rb
|
153
154
|
- lib/haveapi/client/inflections.rb
|
154
155
|
- lib/haveapi/client/resource.rb
|
156
|
+
- lib/haveapi/client/resource_instance.rb
|
157
|
+
- lib/haveapi/client/resource_instance_list.rb
|
155
158
|
- lib/haveapi/client/response.rb
|
156
159
|
- lib/haveapi/client/version.rb
|
157
160
|
- lib/restclient_ext/request.rb
|
@@ -176,7 +179,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
179
|
version: '0'
|
177
180
|
requirements: []
|
178
181
|
rubyforge_project:
|
179
|
-
rubygems_version: 2.
|
182
|
+
rubygems_version: 2.2.2
|
180
183
|
signing_key:
|
181
184
|
specification_version: 4
|
182
185
|
summary: Ruby API and CLI for HaveAPI
|