haveapi-client 0.3.0 → 0.4.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.
@@ -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