cf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,433 @@
1
+ require "thor"
2
+ require "interact"
3
+ require "json/pure"
4
+
5
+ require "conduit"
6
+
7
+ require "cf/constants"
8
+ require "cf/cli/dots"
9
+
10
+
11
+ module CF
12
+ module Interactive
13
+ include ::Interactive::Rewindable
14
+ include Dots
15
+
16
+ class InteractiveDefault
17
+ def initialize(query)
18
+ @query = query
19
+ end
20
+
21
+ def to_s
22
+ "(interaction)"
23
+ end
24
+
25
+ def to_proc
26
+ @query
27
+ end
28
+ end
29
+
30
+ def force?
31
+ false
32
+ end
33
+
34
+ def ask(question, options = {})
35
+ if force? and options.key?(:default)
36
+ options[:default]
37
+ else
38
+ super
39
+ end
40
+ end
41
+
42
+ def list_choices(choices, options)
43
+ choices.each_with_index do |o, i|
44
+ puts "#{c(i + 1, :green)}: #{o}"
45
+ end
46
+ end
47
+
48
+ def input_state(options)
49
+ CFState.new(options)
50
+ end
51
+
52
+ def prompt(question, options)
53
+ value =
54
+ case options[:default]
55
+ when true
56
+ "y"
57
+ when false
58
+ "n"
59
+ when nil
60
+ ""
61
+ else
62
+ options[:default].to_s
63
+ end
64
+
65
+ print "#{question}"
66
+ print c("> ", :blue)
67
+
68
+ unless value.empty?
69
+ print "#{c(value, :black) + "\b" * value.size}"
70
+ end
71
+ end
72
+
73
+ def handler(which, state)
74
+ ans = state.answer
75
+ pos = state.position
76
+
77
+ if state.default?
78
+ if which.is_a?(Array) and which[0] == :key
79
+ # initial non-movement keypress clears default answer
80
+ clear_input(state)
81
+ else
82
+ # wipe away any coloring
83
+ redraw_input(state)
84
+ end
85
+
86
+ state.clear_default!
87
+ end
88
+
89
+ super
90
+
91
+ print "\n" if which == :enter
92
+ end
93
+
94
+ class CFState < Interactive::InputState
95
+ def initialize(options = {}, answer = nil, position = 0)
96
+ @options = options
97
+
98
+ if answer
99
+ @answer = answer
100
+ elsif options[:default]
101
+ case options[:default]
102
+ when true
103
+ @answer = "y"
104
+ when false
105
+ @answer = "n"
106
+ else
107
+ @answer = options[:default].to_s
108
+ end
109
+
110
+ @default = true
111
+ else
112
+ @answer = ""
113
+ end
114
+
115
+ @position = position
116
+ @done = false
117
+ end
118
+
119
+ def clear_default!
120
+ @default = false
121
+ end
122
+
123
+ def default?
124
+ @default
125
+ end
126
+ end
127
+ end
128
+
129
+ class Command < Thor
130
+ include Interactive
131
+
132
+ def self.flag(name, options = {}, &query)
133
+ if query
134
+ options[:default] ||= InteractiveDefault.new(query)
135
+ end
136
+
137
+ method_option(name, options)
138
+ end
139
+
140
+ def self.callbacks_for(name)
141
+ cs = callbacks[name]
142
+ if superclass.respond_to? :callbacks
143
+ cs.merge superclass.callbacks_for(name)
144
+ else
145
+ cs
146
+ end
147
+ end
148
+
149
+ def self.callbacks
150
+ @callbacks ||= Hash.new do |h, name|
151
+ h[name] = Hash.new do |h, task|
152
+ h[task] = []
153
+ end
154
+ end
155
+ end
156
+
157
+ def self.add_callback(name, task, callback)
158
+ callbacks[name][task] << callback
159
+ end
160
+
161
+ def self.before(task, &callback)
162
+ add_callback(:before, task, callback)
163
+ end
164
+
165
+ def self.after(task, &callback)
166
+ add_callback(:after, task, callback)
167
+ end
168
+
169
+ def self.ensuring(task, &callback)
170
+ add_callback(:ensuring, task, callback)
171
+ end
172
+
173
+ def self.around(task, &callback)
174
+ add_callback(:around, task, callback)
175
+ end
176
+
177
+ private
178
+
179
+ def callbacks_for(what)
180
+ self.class.callbacks_for(what)
181
+ end
182
+
183
+ def passed_value(flag)
184
+ if (val = options[flag]) && \
185
+ !val.is_a?(CF::Interactive::InteractiveDefault)
186
+ val
187
+ end
188
+ end
189
+
190
+ def input(name, *args)
191
+ @inputs ||= {}
192
+ return @inputs[name] if @inputs.key?(name)
193
+
194
+ if options[name].respond_to? :to_proc
195
+ @inputs[name] = instance_exec(*args, &options[name])
196
+ else
197
+ @inputs[name] = options[name]
198
+ end
199
+ end
200
+
201
+ def forget(name)
202
+ @inputs.delete name
203
+ end
204
+
205
+ def script?
206
+ if options.key?("script")
207
+ options["script"]
208
+ else
209
+ !$stdout.tty?
210
+ end
211
+ end
212
+
213
+ def force?
214
+ if options.key?("force")
215
+ options["force"]
216
+ else
217
+ script?
218
+ end
219
+ end
220
+
221
+ def simple_output?
222
+ if options.key?("simple_output")
223
+ options["simple_output"]
224
+ else
225
+ script?
226
+ end
227
+ end
228
+
229
+ def color?
230
+ if options.key?("color")
231
+ options["color"]
232
+ else
233
+ !simple_output?
234
+ end
235
+ end
236
+
237
+ def err(msg, exit_status = 1)
238
+ if script?
239
+ $stderr.puts(msg)
240
+ else
241
+ puts c(msg, :red)
242
+ end
243
+
244
+ $exit_status = 1
245
+ end
246
+
247
+ def invoke_task(task, args)
248
+ callbacks_for(:before)[task.name.to_sym].each do |c|
249
+ c.call
250
+ end
251
+
252
+ action = proc do |*new_args|
253
+ if new_args.empty?
254
+ task.run(self, args)
255
+ elsif new_args.first.is_a? Array
256
+ task.run(self, new_args.first)
257
+ else
258
+ task.run(self, new_args)
259
+ end
260
+ end
261
+
262
+ callbacks_for(:around)[task.name.to_sym].each do |a|
263
+ before = action
264
+ action = proc do |passed_args|
265
+ # with more than one wrapper, when the outer wrapper passes args to
266
+ # inner wrapper, which calls the next inner with no args, it should
267
+ # get the args passed to it by outer
268
+ args = passed_args if passed_args
269
+ instance_exec(before, args, &a)
270
+ end
271
+ end
272
+
273
+ res = instance_exec(args, &action)
274
+
275
+ callbacks_for(:after)[task.name.to_sym].each do |c|
276
+ c.call
277
+ end
278
+
279
+ res
280
+ rescue Interrupt
281
+ $exit_status = 130
282
+ rescue Thor::Error
283
+ raise
284
+ rescue Exception => e
285
+ msg = e.class.name
286
+ msg << ": #{e}" unless e.to_s.empty?
287
+ err msg
288
+
289
+ ensure_config_dir
290
+
291
+ File.open(File.expand_path(CF::CRASH_FILE), "w") do |f|
292
+ f.print "Time of crash:\n "
293
+ f.puts Time.now
294
+ f.puts ""
295
+ f.puts msg
296
+ f.puts ""
297
+
298
+ e.backtrace.each do |loc|
299
+ if loc =~ /\/gems\//
300
+ f.puts loc.sub(/.*\/gems\//, "")
301
+ else
302
+ f.puts loc.sub(File.expand_path("../../../..", __FILE__) + "/", "")
303
+ end
304
+ end
305
+ end
306
+ ensure
307
+ callbacks_for(:ensuring)[task.name.to_sym].each do |c|
308
+ c.call
309
+ end
310
+ end
311
+ public :invoke_task
312
+
313
+ def sane_target_url(url)
314
+ unless url =~ /^https?:\/\//
315
+ url = "http://#{url}"
316
+ end
317
+
318
+ url.gsub(/\/$/, "")
319
+ end
320
+
321
+ def target_file
322
+ one_of(CF::TARGET_FILE, CF::OLD_TARGET_FILE)
323
+ end
324
+
325
+ def tokens_file
326
+ one_of(CF::TOKENS_FILE, CF::OLD_TOKENS_FILE)
327
+ end
328
+
329
+ def one_of(*paths)
330
+ first = nil
331
+ paths.each do |p|
332
+ exp = File.expand_path(p)
333
+ first ||= exp
334
+ return exp if File.exist? exp
335
+ end
336
+
337
+ first
338
+ end
339
+
340
+ def client_target
341
+ File.read(target_file).chomp
342
+ end
343
+
344
+ def ensure_config_dir
345
+ config = File.expand_path(CF::CONFIG_DIR)
346
+ Dir.mkdir(config) unless File.exist? config
347
+ end
348
+
349
+ def set_target(url)
350
+ ensure_config_dir
351
+
352
+ File.open(File.expand_path(CF::TARGET_FILE), "w") do |f|
353
+ f.write(sane_target_url(url))
354
+ end
355
+
356
+ @client = nil
357
+ end
358
+
359
+ def tokens
360
+ JSON.parse(File.read(tokens_file))
361
+ end
362
+
363
+ def client_token
364
+ tokens[client_target]
365
+ end
366
+
367
+ def save_tokens(ts)
368
+ ensure_config_dir
369
+
370
+ File.open(File.expand_path(CF::TOKENS_FILE), "w") do |f|
371
+ f.write ts.to_json
372
+ end
373
+ end
374
+
375
+ def save_token(token)
376
+ ts = tokens
377
+ ts[client_target] = token
378
+ save_tokens(ts)
379
+ end
380
+
381
+ def remove_token
382
+ ts = tokens
383
+ ts.delete client_target
384
+ save_tokens(ts)
385
+ end
386
+
387
+ def client
388
+ @client ||= Conduit::Client.new(client_target, client_token)
389
+ end
390
+
391
+ def usage(used, limit)
392
+ "#{b(human_size(used))} of #{b(human_size(limit, 0))}"
393
+ end
394
+
395
+ def percentage(num, low = 50, mid = 70)
396
+ color =
397
+ if num <= low
398
+ :green
399
+ elsif num <= mid
400
+ :yellow
401
+ else
402
+ :red
403
+ end
404
+
405
+ c(format("%.1f\%", num), color)
406
+ end
407
+
408
+ def megabytes(str)
409
+ if str =~ /T$/i
410
+ str.to_i * 1024 * 1024
411
+ elsif str =~ /G$/i
412
+ str.to_i * 1024
413
+ elsif str =~ /M$/i
414
+ str.to_i
415
+ elsif str =~ /K$/i
416
+ str.to_i / 1024
417
+ end
418
+ end
419
+
420
+ def human_size(num, precision = 1)
421
+ sizes = ["G", "M", "K"]
422
+ sizes.each.with_index do |suf, i|
423
+ pow = sizes.size - i
424
+ unit = 1024 ** pow
425
+ if num >= unit
426
+ return format("%.#{precision}f%s", num / unit, suf)
427
+ end
428
+ end
429
+
430
+ format("%.#{precision}fB", num)
431
+ end
432
+ end
433
+ end
@@ -0,0 +1,130 @@
1
+ module CF
2
+ module Dots
3
+ DOT_COUNT = 3
4
+ DOT_TICK = 0.15
5
+
6
+ class Skipper
7
+ def initialize(&ret)
8
+ @return = ret
9
+ end
10
+
11
+ def skip(&callback)
12
+ @return.call("SKIPPED", :yellow, callback)
13
+ end
14
+
15
+ def give_up(&callback)
16
+ @return.call("GAVE UP", :red, callback)
17
+ end
18
+
19
+ def fail(&callback)
20
+ @return.call("FAILED", :red, callback)
21
+ end
22
+ end
23
+
24
+ def with_progress(message)
25
+ unless simple_output?
26
+ print message
27
+ dots!
28
+ end
29
+
30
+ skipper = Skipper.new do |status, color, callback|
31
+ unless simple_output?
32
+ stop_dots!
33
+ puts "... #{c(status, color)}"
34
+ end
35
+
36
+ return callback && callback.call
37
+ end
38
+
39
+ begin
40
+ res = yield skipper
41
+ unless simple_output?
42
+ stop_dots!
43
+ puts "... #{c("OK", :green)}"
44
+ end
45
+ res
46
+ rescue
47
+ unless simple_output?
48
+ stop_dots!
49
+ puts "... #{c("FAILED", :red)}"
50
+ end
51
+
52
+ raise
53
+ end
54
+ end
55
+
56
+ def color?
57
+ $stdout.tty?
58
+ end
59
+
60
+ COLOR_CODES = {
61
+ :black => 0,
62
+ :red => 1,
63
+ :green => 2,
64
+ :yellow => 3,
65
+ :blue => 4,
66
+ :magenta => 5,
67
+ :cyan => 6,
68
+ :white => 7
69
+ }
70
+
71
+ # colored text
72
+ def c(str, color, bright = true)
73
+ return str unless color?
74
+ "\e[#{bright ? 9 : 3}#{COLOR_CODES[color]}m#{str}\e[0m"
75
+ end
76
+ module_function :c
77
+
78
+ # bold text
79
+ def b(str)
80
+ return str unless color?
81
+ "\e[1m#{str}\e[0m"
82
+ end
83
+ module_function :b
84
+
85
+ def dots!
86
+ @dots ||=
87
+ Thread.new do
88
+ before_sync = $stdout.sync
89
+
90
+ $stdout.sync = true
91
+
92
+ printed = false
93
+ i = 1
94
+ until @stop_dots
95
+ if printed
96
+ print "\b" * DOT_COUNT
97
+ end
98
+
99
+ print ("." * i).ljust(DOT_COUNT)
100
+ printed = true
101
+
102
+ if i == DOT_COUNT
103
+ i = 0
104
+ else
105
+ i += 1
106
+ end
107
+
108
+ sleep DOT_TICK
109
+ end
110
+
111
+ if printed
112
+ print "\b" * DOT_COUNT
113
+ print " " * DOT_COUNT
114
+ print "\b" * DOT_COUNT
115
+ end
116
+
117
+ $stdout.sync = before_sync
118
+ @stop_dots = nil
119
+ end
120
+ end
121
+
122
+ def stop_dots!
123
+ return unless @dots
124
+ return if @stop_dots
125
+ @stop_dots = true
126
+ @dots.join
127
+ @dots = nil
128
+ end
129
+ end
130
+ end