haveapi-client 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,52 @@
1
+ module HaveAPI::CLI
2
+ class Command
3
+ class << self
4
+ attr_reader :resource, :action
5
+
6
+ def cmd(resource, action = nil)
7
+ @resource = resource.is_a?(::Array) ? resource : [resource]
8
+ @resource.map! { |v| v.to_s }
9
+ @action = action && action.to_s
10
+
11
+ Cli.register_command(self)
12
+ end
13
+
14
+ def args(v = nil)
15
+ if v
16
+ @args = v
17
+
18
+ else
19
+ @args
20
+ end
21
+ end
22
+
23
+ def desc(v = nil)
24
+ if v
25
+ @desc = v
26
+
27
+ else
28
+ @desc
29
+ end
30
+ end
31
+
32
+ def handle?(resource, action)
33
+ resource == @resource && action == @action
34
+ end
35
+ end
36
+
37
+ attr_reader :global_opts
38
+
39
+ def initialize(opts, client)
40
+ @global_opts = opts
41
+ @api = client
42
+ end
43
+
44
+ def options(opts)
45
+
46
+ end
47
+
48
+ def exec(args)
49
+ raise NotImplementedError
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,221 @@
1
+ module HaveAPI::CLI
2
+ class OutputFormatter
3
+ def self.format(*args)
4
+ f = new(*args)
5
+ f.format
6
+ end
7
+
8
+ def self.print(*args)
9
+ f = new(*args)
10
+ f.print
11
+ end
12
+
13
+ def initialize(objects, cols = nil, header: true, sort: nil, layout: nil, empty: '-')
14
+ @objects = objects
15
+ @header = header
16
+ @sort = sort
17
+ @layout = layout
18
+ @empty = empty
19
+
20
+ if @layout.nil?
21
+ if many?
22
+ @layout = :columns
23
+
24
+ else
25
+ @layout = :rows
26
+ end
27
+ end
28
+
29
+ if cols
30
+ @cols = parse_cols(cols)
31
+
32
+ else
33
+ if @objects.is_a?(::Array) # A list of items
34
+ @cols ||= parse_cols(@objects.first.keys)
35
+
36
+ elsif @objects.is_a?(::Hash) # Single item
37
+ @cols ||= parse_cols(@objects.keys)
38
+
39
+ else
40
+ fail "unsupported type #{@objects.class}"
41
+ end
42
+ end
43
+ end
44
+
45
+ def format
46
+ @out = ''
47
+ generate
48
+ @out
49
+ end
50
+
51
+ def print
52
+ @out = nil
53
+ generate
54
+ end
55
+
56
+ protected
57
+ def parse_cols(cols)
58
+ ret = []
59
+
60
+ cols.each do |c|
61
+ base = {
62
+ align: 'left'
63
+ }
64
+
65
+ if c.is_a?(::String) || c.is_a?(::Symbol)
66
+ base.update({
67
+ name: c,
68
+ label: c.to_s.upcase,
69
+ })
70
+ ret << base
71
+
72
+ elsif c.is_a?(::Hash)
73
+ base.update(c)
74
+ ret << base
75
+
76
+ else
77
+ fail "unsupported column type #{c.class}"
78
+ end
79
+ end
80
+
81
+ ret
82
+ end
83
+
84
+ def generate
85
+ return if @cols.empty?
86
+ prepare
87
+
88
+ case @layout
89
+ when :columns
90
+ columns
91
+
92
+ when :rows
93
+ rows
94
+
95
+ else
96
+ fail "unsupported layout '#{@layout}'"
97
+ end
98
+ end
99
+
100
+ # Each object is printed on one line, it's parameters aligned into columns.
101
+ def columns
102
+ i = 0
103
+
104
+ formatters = @cols.map do |c|
105
+ ret = case c[:align].to_sym
106
+ when :right
107
+ "%#{col_width(i, c)}s"
108
+
109
+ else
110
+ "%-#{col_width(i, c)}s"
111
+ end
112
+
113
+ i += 1
114
+ ret
115
+ end.join(' ')
116
+
117
+ line sprintf(formatters, * @cols.map { |c| c[:label] }) if @header
118
+
119
+ @str_objects.each do |o|
120
+ line sprintf(formatters, *o)
121
+ end
122
+ end
123
+
124
+ # Each object is printed on multiple lines, one parameter per line.
125
+ def rows
126
+ w = heading_width
127
+
128
+ @str_objects.each do |o|
129
+ @cols.each_index do |i|
130
+ c = @cols[i]
131
+
132
+ if o[i].is_a?(::String) && o[i].index("\n")
133
+ lines = o[i].split("\n")
134
+ v = ([lines.first] + lines[1..-1].map { |l| (' ' * (w+3)) + l }).join("\n")
135
+
136
+ else
137
+ v = o[i]
138
+ end
139
+
140
+ line sprintf("%#{w}s: %s", c[:label], v)
141
+ end
142
+
143
+ line
144
+ end
145
+ end
146
+
147
+ def line(str = '')
148
+ if @out
149
+ @out += str + "\n"
150
+
151
+ else
152
+ puts str
153
+ end
154
+ end
155
+
156
+ def prepare
157
+ @str_objects = []
158
+
159
+ each_object do |o|
160
+ arr = []
161
+
162
+ @cols.each do |c|
163
+ v = o[ c[:name] ]
164
+ str = (c[:display] ? c[:display].call(v) : v)
165
+ str = @empty if !str || (str.is_a?(::String) && str.empty?)
166
+
167
+ arr << str
168
+ end
169
+
170
+ @str_objects << arr
171
+ end
172
+
173
+ if @sort
174
+ col_i = @cols.index { |c| c[:name] == @sort }
175
+ fail "unknown column '#{@sort}'" unless col_i
176
+
177
+ @str_objects.sort! do |a, b|
178
+ a[col_i] <=> b[col_i]
179
+ end
180
+ end
181
+
182
+ @str_objects
183
+ end
184
+
185
+ def col_width(i, c)
186
+ w = c[:label].to_s.length
187
+
188
+ @str_objects.each do |o|
189
+ len = o[i].to_s.length
190
+ w = len if len > w
191
+ end
192
+
193
+ w + 1
194
+ end
195
+
196
+ def heading_width
197
+ w = @cols.first[:label].to_s.length
198
+
199
+ @cols.each do |c|
200
+ len = c[:label].to_s.length
201
+
202
+ w = len if len > w
203
+ end
204
+
205
+ w + 1
206
+ end
207
+
208
+ def each_object
209
+ if @objects.is_a?(::Array)
210
+ @objects.each { |v| yield(v) }
211
+
212
+ else
213
+ yield(@objects)
214
+ end
215
+ end
216
+
217
+ def many?
218
+ @objects.is_a?(::Array) && @objects.size > 1
219
+ end
220
+ end
221
+ end
@@ -1,3 +1,8 @@
1
1
  require 'require_all'
2
2
  require 'date'
3
+
4
+ module HaveAPI
5
+ module Client ; end
6
+ end
7
+
3
8
  require_rel 'client'
@@ -1,131 +1,135 @@
1
- module HaveAPI
2
- module Client
3
- class Action
4
- attr_accessor :resource_path
1
+ module HaveAPI::Client
2
+ class Action
3
+ attr_accessor :resource_path
5
4
 
6
- def initialize(api, name, spec, args)
7
- @api = api
8
- @name = name
9
- @spec = spec
5
+ def initialize(api, name, spec, args)
6
+ @api = api
7
+ @name = name
8
+ @spec = spec
10
9
 
11
- apply_args(args)
12
- end
10
+ apply_args(args)
11
+ end
13
12
 
14
- def execute(*args)
15
- ret = @api.call(self, *args)
16
- @prepared_url = nil
17
- @prepared_help = nil
18
- ret
13
+ def execute(data, *_)
14
+ params = Params.new(self, data)
15
+
16
+ unless params.valid?
17
+ raise ValidationError.new(self, params.errors)
19
18
  end
20
19
 
21
- def name
22
- @name
23
- end
20
+ ret = @api.call(self, params.to_api)
21
+ @prepared_url = nil
22
+ @prepared_help = nil
23
+ ret
24
+ end
24
25
 
25
- def auth?
26
- @spec[:auth]
27
- end
26
+ def name
27
+ @name
28
+ end
28
29
 
29
- def aliases(include_name = false)
30
- if include_name
31
- [@name] + @spec[:aliases]
32
- else
33
- @spec[:aliases]
34
- end
35
- end
30
+ def auth?
31
+ @spec[:auth]
32
+ end
36
33
 
37
- def description
38
- @spec[:description]
34
+ def aliases(include_name = false)
35
+ if include_name
36
+ [@name] + @spec[:aliases]
37
+ else
38
+ @spec[:aliases]
39
39
  end
40
+ end
40
41
 
41
- def input
42
- @spec[:input]
43
- end
42
+ def description
43
+ @spec[:description]
44
+ end
44
45
 
45
- def output
46
- @spec[:output]
47
- end
46
+ def input
47
+ @spec[:input]
48
+ end
48
49
 
49
- def input_layout
50
- @spec[:input][:layout].to_sym
51
- end
50
+ def output
51
+ @spec[:output]
52
+ end
52
53
 
53
- def output_layout
54
- @spec[:output][:layout].to_sym
55
- end
54
+ def input_layout
55
+ @spec[:input][:layout].to_sym
56
+ end
56
57
 
57
- def structure
58
- @spec[:output][:format]
59
- end
58
+ def output_layout
59
+ @spec[:output][:layout].to_sym
60
+ end
60
61
 
61
- def namespace(src)
62
- @spec[src][:namespace]
63
- end
62
+ def structure
63
+ @spec[:output][:format]
64
+ end
64
65
 
65
- def examples
66
- @spec[:examples]
67
- end
66
+ def namespace(src)
67
+ @spec[src][:namespace]
68
+ end
68
69
 
69
- def input_params
70
- @spec[:input][:parameters]
71
- end
70
+ def examples
71
+ @spec[:examples]
72
+ end
72
73
 
73
- def params
74
- @spec[:output][:parameters]
75
- end
74
+ def input_params
75
+ @spec[:input][:parameters]
76
+ end
76
77
 
77
- def param_description(dir, name)
78
- @spec[dir][:parameters][name]
79
- end
78
+ def params
79
+ @spec[:output][:parameters]
80
+ end
80
81
 
81
- def url
82
- @spec[:url]
83
- end
82
+ def param_description(dir, name)
83
+ @spec[dir][:parameters][name]
84
+ end
84
85
 
85
- def help
86
- @spec[:help]
87
- end
86
+ def url
87
+ @spec[:url]
88
+ end
88
89
 
89
- # Url with resolved parameters.
90
- def prepared_url
91
- @prepared_url || @spec[:url]
92
- end
90
+ def help
91
+ @spec[:help]
92
+ end
93
93
 
94
- def prepared_help
95
- @prepared_help || @spec[:help]
96
- end
94
+ # Url with resolved parameters.
95
+ def prepared_url
96
+ @prepared_url || @spec[:url]
97
+ end
97
98
 
98
- def http_method
99
- @spec[:method]
100
- end
99
+ def prepared_help
100
+ @prepared_help || @spec[:help]
101
+ end
101
102
 
102
- def unresolved_args?
103
- prepared_url =~ /:[a-zA-Z\-_]+/
104
- end
103
+ def http_method
104
+ @spec[:method]
105
+ end
105
106
 
106
- def provide_args(*args)
107
- apply_args(args)
108
- end
107
+ def unresolved_args?
108
+ prepared_url =~ /:[a-zA-Z\-_]+/
109
+ end
109
110
 
110
- def provide_url(url, help)
111
- @prepared_url = url
112
- @prepared_help = help
113
- end
111
+ def provide_args(*args)
112
+ apply_args(args)
113
+ end
114
114
 
115
- def update_description(spec)
116
- @spec = spec
117
- end
115
+ def provide_url(url, help)
116
+ @prepared_url = url
117
+ @prepared_help = help
118
+ end
118
119
 
119
- private
120
- def apply_args(args)
121
- @prepared_url ||= @spec[:url].dup
122
- @prepared_help ||= @spec[:help].dup
120
+ def update_description(spec)
121
+ @spec = spec
122
+ end
123
+
124
+ private
125
+ def apply_args(args)
126
+ @prepared_url ||= @spec[:url].dup
127
+ @prepared_help ||= @spec[:help].dup
123
128
 
124
- args.each do |arg|
125
- @prepared_url.sub!(/:[a-zA-Z\-_]+/, arg.to_s)
126
- @prepared_help.sub!(/:[a-zA-Z\-_]+/, arg.to_s)
127
- end
128
- end
129
+ args.each do |arg|
130
+ @prepared_url.sub!(/:[a-zA-Z\-_]+/, arg.to_s)
131
+ @prepared_help.sub!(/:[a-zA-Z\-_]+/, arg.to_s)
132
+ end
129
133
  end
130
134
  end
131
135
  end