haveapi-client 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|