greenhat 0.4.0 → 0.6.1

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.
Files changed (62) hide show
  1. checksums.yaml +4 -4
  2. data/bin/greenhat +1 -2
  3. data/lib/greenhat/accessors/disk.rb +1 -1
  4. data/lib/greenhat/accessors/gitlab.rb +4 -2
  5. data/lib/greenhat/archive.rb +7 -1
  6. data/lib/greenhat/cli.rb +24 -11
  7. data/lib/greenhat/entrypoint.rb +37 -34
  8. data/lib/greenhat/host.rb +1 -1
  9. data/lib/greenhat/logbot.rb +1 -1
  10. data/lib/greenhat/paper/flag_helper.rb +18 -0
  11. data/lib/greenhat/paper/paper_helper.rb +118 -0
  12. data/lib/greenhat/paper.rb +34 -0
  13. data/lib/greenhat/reports/builder.rb +98 -0
  14. data/lib/greenhat/reports/helpers.rb +101 -0
  15. data/lib/greenhat/reports/internal_methods.rb +156 -0
  16. data/lib/greenhat/reports/reports/errors.rb +49 -0
  17. data/lib/greenhat/reports/reports/faststats.rb +42 -0
  18. data/lib/greenhat/reports/reports/full.rb +143 -0
  19. data/lib/greenhat/reports/runner.rb +58 -0
  20. data/lib/greenhat/reports/shared.rb +37 -0
  21. data/lib/greenhat/reports/shell_helper.rb +34 -0
  22. data/lib/greenhat/reports.rb +79 -0
  23. data/lib/greenhat/settings.rb +35 -8
  24. data/lib/greenhat/shell/args.rb +9 -9
  25. data/lib/greenhat/shell/color_string.rb +1 -1
  26. data/lib/greenhat/shell/faststats.rb +24 -5
  27. data/lib/greenhat/shell/field_helper.rb +1 -1
  28. data/lib/greenhat/shell/filter_help.rb +65 -185
  29. data/lib/greenhat/shell/gitlab.rb +1 -0
  30. data/lib/greenhat/shell/log.rb +24 -30
  31. data/lib/greenhat/shell/markdown.rb +355 -352
  32. data/lib/greenhat/shell/process.rb +11 -5
  33. data/lib/greenhat/shell/query.rb +534 -0
  34. data/lib/greenhat/shell/report.rb +415 -410
  35. data/lib/greenhat/shell/reports.rb +41 -0
  36. data/lib/greenhat/shell/shell_helper.rb +95 -387
  37. data/lib/greenhat/shell.rb +22 -3
  38. data/lib/greenhat/thing/file_types.rb +30 -1
  39. data/lib/greenhat/thing/formatters/api_json.rb +4 -2
  40. data/lib/greenhat/thing/formatters/bracket_log.rb +1 -1
  41. data/lib/greenhat/thing/formatters/clean_raw.rb +1 -1
  42. data/lib/greenhat/thing/formatters/colon_split_strip.rb +2 -2
  43. data/lib/greenhat/thing/formatters/dotenv.rb +1 -1
  44. data/lib/greenhat/thing/formatters/format.rb +0 -11
  45. data/lib/greenhat/thing/formatters/free_m.rb +2 -2
  46. data/lib/greenhat/thing/formatters/json.rb +41 -17
  47. data/lib/greenhat/thing/formatters/json_shellwords.rb +3 -2
  48. data/lib/greenhat/thing/formatters/kube_json.rb +3 -2
  49. data/lib/greenhat/thing/formatters/multiline_json.rb +1 -1
  50. data/lib/greenhat/thing/formatters/nginx.rb +5 -1
  51. data/lib/greenhat/thing/formatters/runner_log.rb +70 -0
  52. data/lib/greenhat/thing/formatters/table.rb +17 -3
  53. data/lib/greenhat/thing/formatters/time_json.rb +12 -1
  54. data/lib/greenhat/thing/helpers.rb +0 -11
  55. data/lib/greenhat/thing/info_format.rb +4 -4
  56. data/lib/greenhat/thing/kind.rb +1 -1
  57. data/lib/greenhat/thing.rb +21 -25
  58. data/lib/greenhat/version.rb +1 -1
  59. data/lib/greenhat.rb +6 -8
  60. metadata +32 -4
  61. data/lib/greenhat/pry_helpers.rb +0 -51
  62. data/lib/greenhat/thing/super_log.rb +0 -102
@@ -0,0 +1,41 @@
1
+ # Reporting
2
+ require 'greenhat/reports/shared' # Methods for Shell and Builder
3
+ require 'greenhat/reports/internal_methods' # Class / Execution Only
4
+ require 'greenhat/reports/runner' # Class execution
5
+ require 'greenhat/reports/builder' # CLI Reports
6
+ require 'greenhat/reports/shell_helper'
7
+
8
+ module GreenHat
9
+ # CLI Helper
10
+ module Shell
11
+ # Logs
12
+ module Reports
13
+ # Easy Show All
14
+ def self.ls(raw = [])
15
+ filter, _flags, _args = Args.parse(raw)
16
+
17
+ list = GreenHat::ShellHelper::Reports.list
18
+ list.select! { |x| x.include? filter.first } unless filter.blank?
19
+
20
+ list.each do |x|
21
+ puts "- #{File.basename(x, '.rb').pastel(:yellow)}"
22
+ end
23
+ end
24
+
25
+ def self.default(raw = [])
26
+ list, flags, args = Args.parse(raw)
27
+
28
+ # Collect Reporst to Run
29
+ run_list = GreenHat::ShellHelper::Reports.list.select do |entry|
30
+ list.include? File.basename(entry, '.rb')
31
+ end
32
+
33
+ run_list.uniq!
34
+
35
+ run_list.each do |file|
36
+ GreenHat::ShellHelper::Reports.run(file: file, args: args, flags: flags)
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -48,23 +48,50 @@ module GreenHat
48
48
 
49
49
  # Check if content needs to paged, or if auto_height is off
50
50
  if Page.skip?(flags, data)
51
- puts data.map { |entry| entry_show(flags, entry) }.compact.join("\n")
51
+ create_ledger(data, flags).each { |entry| puts entry.render }
52
52
  return true
53
53
  end
54
54
 
55
- # Default Pager
56
- TTY::Pager.page do |pager|
57
- data.each do |entry|
58
- output = "\n#{entry_show(flags, entry)}"
55
+ # Create Jobs
56
+ # Generate List of Formatted Output
57
+ ledger = create_ledger(data, flags)
59
58
 
60
- # output += "\n" if flags[:table_style] == 'basic'
59
+ # Start Thread
60
+ # TODO: Confirm / Test or Remove
61
+ unless flags.no_paper_thread
62
+ thread = Thread.new do
63
+ pending = ledger.clone
64
+ loop do
65
+ pending = pending.reject(&:done)
66
+ break if pending.empty?
61
67
 
62
- # Breaks any intentional spaces
63
- # next if output.blank?
68
+ pending.first.async
69
+ end
70
+ end
71
+ end
64
72
 
65
- pager.write(output) # write line to the pager
73
+ TTY::Pager.page do |pager|
74
+ ledger.each do |paper|
75
+ # New Line is only needed on pager.write
76
+ pager.write "#{paper.render}\n"
66
77
  end
67
78
  end
79
+
80
+ # Terminate
81
+ thread.kill unless flags.no_paper_thread
82
+
83
+ # -----------------
84
+ end
85
+
86
+ # Collect Class Objects for output
87
+ def self.create_ledger(data, flags)
88
+ data.map do |x|
89
+ Paper.new(data: x, flags: flags)
90
+ end
91
+ end
92
+
93
+ def self.puts(text = '')
94
+ $stdout.puts text
68
95
  end
69
96
 
70
97
  # Entry Shower / Top Level
@@ -72,11 +99,13 @@ module GreenHat
72
99
  LogBot.debug('Entry Show', entry.class) if ENV['DEBUG']
73
100
  case entry
74
101
  when Hash then render_table(entry, flags)
75
- when Float, Integer, Array
102
+ when Float, Integer
76
103
  format_table_entry(flags, entry, key)
77
- # Ignore Special Formatting for Strings / Usually already formatted
104
+ # Ignore Special Formatting for Strings / Usually already formatted
78
105
  when String
79
106
  entry
107
+ when Array
108
+ render_array_table(entry, flags)
80
109
  else
81
110
  LogBot.warn('Shell Show', "Unknown #{entry.class}")
82
111
  nil
@@ -90,13 +119,12 @@ module GreenHat
90
119
  when Float, Integer || entry.numeric?
91
120
  flags.key?(:round) ? entry.to_f.round(flags.round).ai : entry.ai
92
121
 
93
- # General Inspecting
122
+ # General Inspecting
94
123
  when Hash then entry.ai(ruby19_syntax: true)
95
124
 
96
125
  # Arrays often contain Hashes. Dangerous Recursive?
97
126
  when Array
98
127
  entry.map { |x| format_table_entry(flags, x) }.join("\n")
99
-
100
128
  when Time
101
129
  entry.to_s.pastel(:bright_white)
102
130
 
@@ -118,9 +146,25 @@ module GreenHat
118
146
  end
119
147
  end
120
148
 
149
+ def self.render_array_table(entry, flags)
150
+ table_style = flags[:table_style]&.to_sym || :unicode
151
+ header, rows = entry
152
+ table = TTY::Table.new(header: header, rows: rows)
153
+
154
+ LogBot.debug('Rendering Entries') if ENV['DEBUG']
155
+ output = table.render(table_style, padding: [0, 1, 0, 1], multiline: true) do |renderer|
156
+ renderer.border.style = :cyan
157
+ end
158
+
159
+ # Line breaks for basic tables
160
+ output += "\n" if flags[:table_style] == 'basic'
161
+
162
+ output
163
+ end
164
+
121
165
  # Print the Table in a Nice way
122
166
  def self.render_table(entry, flags)
123
- entry = entry.map { |k, v| [k, format_table_entry(flags, v, k)] }.to_h
167
+ entry = entry.to_h { |k, v| [k, format_table_entry(flags, v, k)] }
124
168
  # Pre-format Entry
125
169
 
126
170
  table_style = flags[:table_style]&.to_sym || :unicode
@@ -136,9 +180,6 @@ module GreenHat
136
180
  output += "\n" if flags[:table_style] == 'basic'
137
181
 
138
182
  output
139
-
140
- # LogBot.debug('Finish Render Table') if ENV['DEBUG']
141
- # Fall Back to Amazing Inspect
142
183
  rescue StandardError => e
143
184
  if ENV['DEBUG']
144
185
  LogBot.warn('Table', message: e.message)
@@ -168,201 +209,7 @@ module GreenHat
168
209
  # Default to everything
169
210
  files = Thing.all.map(&:name) if files.empty?
170
211
 
171
- ShellHelper.filter_start(files, flags, args)
172
- end
173
-
174
- # Main Entry Point for Filtering
175
- def self.filter_start(files, flags = {}, args = {})
176
- # Convert to Things
177
- logs = ShellHelper.find_things(files, flags).select(&:processed?)
178
-
179
- # Ignore Archive/Host Dividers
180
- if flags[:combine]
181
- results = logs.reject(&:blank?).map(&:data).flatten.compact
182
- ShellHelper.filter(results, flags, args)
183
- else
184
- # Iterate and Preserve Archive/Host Index
185
- logs.each_with_object({}) do |log, obj|
186
- # Ignore Empty Results / No Thing
187
- next if log&.blank?
188
-
189
- # Include Total Count in Name
190
- results = ShellHelper.filter(log.data, flags, args)
191
- duration = calculate_duration(results)
192
-
193
- title = [
194
- log.friendly_name,
195
- " #{results.count}".pastel(:bright_black)
196
-
197
- ]
198
-
199
- # Append Duration
200
- title.push(" #{duration.pastel(:cyan, :dim)}") unless duration.blank?
201
-
202
- # Save unless empty
203
- obj[title.join] = results unless results.count.zero?
204
-
205
- obj
206
- end
207
- end
208
- end
209
-
210
- def self.calculate_duration(results)
211
- # Skip for Pluck
212
- only_with_time = results.select { |x| x.instance_of?(Hash) && x.key?(:time) }
213
-
214
- # If slice is used ignore
215
- return nil if only_with_time.empty?
216
-
217
- sorted = only_with_time.map(&:time).sort
218
- humanize_time(sorted.first, sorted.last)
219
- end
220
-
221
- # Replace TimeDifference with https://stackoverflow.com/a/4136485/1678507
222
- def self.humanize_time(time_start, time_end, increments = 2)
223
- miliseconds = (time_end - time_start) * 1000
224
-
225
- list = [[1000, :ms], [60, :s], [60, :m], [24, :h]].map do |count, name|
226
- next unless miliseconds.positive?
227
-
228
- miliseconds, n = miliseconds.divmod(count)
229
-
230
- "#{n.to_i}#{name}" unless n.to_i.zero?
231
- end
232
-
233
- list.compact.reverse[0..increments - 1].join(' ')
234
- end
235
-
236
- # Filter Logic
237
- # TODO: Simplify
238
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
239
- def self.filter(data, flags = {}, args = {})
240
- # results = data.clone.flatten.compact
241
-
242
- # Experimenting with deep clone
243
- results = Marshal.load(Marshal.dump(data))
244
- results.select! do |row|
245
- args.send(flags.logic || :all?) do |arg|
246
- filter_row_key(row, arg, flags)
247
- end
248
- end
249
-
250
- # Ensure presecense of a specific field
251
- results = filter_exists(results, flags[:exists]) if flags.key?(:exists)
252
-
253
- # Time Zone
254
- results = filter_modify_timezone(results, flags[:time_zone]) if flags.key?(:time_zone)
255
-
256
- # Time Filtering
257
- results = filter_time(results, flags) if flags.key?(:start) || flags.key?(:end)
258
-
259
- # Strip Results if Slice is defined
260
- results = filter_slice(results, flags[:slice]) if flags.key?(:slice)
261
-
262
- # Strip Results if Except is defined
263
- results = filter_except(results, flags[:except]) if flags.key?(:except)
264
-
265
- # Remove Blank from either slice or except
266
- results.reject!(&:empty?)
267
-
268
- # Sort
269
- results.sort_by! { |x| x.slice(*flags[:sort]).values } if flags.key?(:sort)
270
-
271
- # JSON Formatting
272
- results = results.map { |x| Oj.dump(x) } if flags.key?(:json)
273
-
274
- # Show Unique Only
275
- results = filter_uniq(results, flags[:uniq]) if flags.key?(:uniq)
276
-
277
- # Reverse
278
- results.reverse! if flags[:reverse]
279
-
280
- # Count occurrences / Skip Results
281
- return filter_stats(results, flags) if flags.key?(:stats)
282
-
283
- # Limit before Pluck / Flattening
284
- results = filter_limit(results, flags[:limit]) if flags.key?(:limit)
285
-
286
- # Pluck
287
- results = filter_pluck(results, flags[:pluck]) if flags.key?(:pluck)
288
-
289
- results
290
- end
291
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
292
-
293
- # Limit / Ensure Exists and Valid Number
294
- def self.filter_limit(results, limit)
295
- return results unless limit.integer? && limit.positive?
296
-
297
- results.shift limit
298
- end
299
-
300
- def self.filter_modify_timezone(results, time_zone)
301
- results.each do |x|
302
- next unless x.key? :time
303
-
304
- x[:time] = x[:time].in_time_zone time_zone
305
- end
306
-
307
- results
308
- end
309
-
310
- # Filter Start and End Times
311
- # TODO: This is a bit icky, simplify/dry
312
- def self.filter_time(results, flags)
313
- if flags.key?(:start)
314
- begin
315
- time_start = Time.parse(flags[:start])
316
-
317
- results.select! do |x|
318
- if x.time
319
- time_start < x.time
320
- else
321
- true
322
- end
323
- end
324
- rescue StandardError
325
- puts 'Unable to Process Start Time Filter'.pastel(:red)
326
- end
327
- end
328
-
329
- if flags.key?(:end)
330
- begin
331
- time_start = Time.parse(flags[:end])
332
-
333
- results.select! do |x|
334
- if x.time
335
- time_start > x.time
336
- else
337
- true
338
- end
339
- end
340
- rescue StandardError
341
- puts 'Unable to Process End Time Filter'.pastel(:red)
342
- end
343
- end
344
-
345
- results
346
- end
347
-
348
- def self.filter_except(results, except)
349
- # Avoid Empty Results
350
- if except.empty?
351
- filter_empty_arg('except')
352
- return results
353
- end
354
-
355
- results.map { |row| row.except(*except) }
356
- end
357
-
358
- def self.filter_exists(results, exists)
359
- # Avoid Empty Results
360
- if exists.empty?
361
- filter_empty_arg('exists')
362
- return results
363
- end
364
-
365
- results.select { |row| (exists - row.keys).empty? }
212
+ Query.start(files, flags, args)
366
213
  end
367
214
 
368
215
  def self.entry_truncate(entry, truncate)
@@ -376,184 +223,42 @@ module GreenHat
376
223
  "#{entry.to_s[0..truncate]} #{'...'.pastel(:bright_blue)}"
377
224
  end
378
225
 
379
- def self.filter_slice(results, slice)
380
- # Avoid Empty Results
381
- if slice.empty?
382
- filter_empty_arg('slice')
383
- return results
384
- end
385
-
386
- results.map { |row| row.slice(*slice) }
387
- end
388
-
389
- def self.filter_pluck(results, pluck)
390
- # Avoid Empty Results
391
- if pluck.empty?
392
- filter_empty_arg('pluck')
393
- return results
394
- end
395
-
396
- results.map { |x| x.slice(*pluck).values }.flatten
397
- end
398
-
399
- def self.filter_uniq(results, unique)
400
- # Avoid Empty Results
401
- if unique.empty?
402
- filter_empty_arg('uniq')
403
- return results
404
- end
405
-
406
- unique.map do |field|
407
- results.uniq { |x| x[field] }
408
- end.inject(:&)
409
- end
410
-
411
- def self.filter_stats(results, flags)
412
- stats = flags[:stats]
413
-
414
- # Avoid Empty Results
415
- if stats.empty?
416
- filter_empty_arg('stats')
417
- return results
418
- end
419
-
420
- # Loop through Stats, Separate Hash/Tables
421
- stats.map do |field|
422
- occurrences = filter_count_occurrences(results, field, flags)
423
-
424
- # Use Truncate For Long Keys
425
- occurrences.transform_keys! { |key| key.to_s[0..flags[:truncate]] } if flags[:truncate]
426
-
427
- # Total Occurences
428
- total = occurrences.values.sum
429
-
430
- # Percs
431
- occurrences.transform_values! do |count|
432
- [
433
- count,
434
- " #{percent(count, total)}%".pastel(:bright_black)
435
- ]
436
- end
437
-
438
- # Sort by total occurances / New Variable for Total
439
- output = occurrences.sort_by(&:last).to_h.transform_values!(&:join).to_a
440
-
441
- # Append Header / Total with field name
442
- output.unshift([field.to_s.pastel(:bright_black), total])
443
-
444
- # Format
445
- output.to_h
446
- end
447
- end
448
-
449
- # Percent Helper
450
- def self.percent(value, total)
451
- ((value / total.to_f) * 100).round
452
- end
453
-
454
- # Helper to Count occurrences
455
- def self.filter_count_occurrences(results, field, flags = {})
456
- results.each_with_object(Hash.new(0)) do |entry, counts|
457
- if entry.key? field
458
- # Rounding in pagination breaks stats
459
- key = if flags.key?(:round) && entry[field].numeric?
460
- entry[field].to_f.round(flags.round)
461
- else
462
- entry[field]
463
- end
464
-
465
- counts[key] += 1
466
- else
467
- counts['None'.pastel(:bright_black)] += 1
468
- end
469
-
470
- counts
471
- end
472
- end
226
+ # Total Count Helper
227
+ def self.total_count(results, flags)
228
+ # Support Combine Helper
229
+ # TODO I Believe this should be removed now
230
+ # total_count_hash(results, flags) if results.instance_of? Hash
473
231
 
474
- def self.filter_empty_arg(arg)
475
- puts [
476
- 'Ignoring'.pastel(:bright_yellow),
477
- "--#{arg}".pastel(:cyan),
478
- 'it requires an argument'.pastel(:red)
479
- ].join(' ')
232
+ total_count_array(results, flags) if results.instance_of? Array
480
233
  end
481
234
 
482
- # Break out filter row logic into separate method
483
- def self.filter_row_key(row, arg, flags)
484
- # Ignore Other Logic if Field isn't even included / Full Text Searching
485
- return false unless row.key?(arg[:field]) || arg[:field] == :text
486
-
487
- # Sensitivity Check / Check for Match / Full Text Searching
488
- search_data = arg[:field] == :text ? row : row[arg.field]
489
- match = filter_row_entry(search_data.to_s, arg, flags)
490
-
491
- # Pivot of off include vs exclude
492
- if arg.bang
493
- !match
494
- else
495
- match
235
+ # Hash Iteration
236
+ def self.total_count_hash(results, flags)
237
+ results.flat_map do |k, values|
238
+ [k, total_count_array(values, flags)]
496
239
  end
497
240
  end
498
241
 
499
- # Field Partial / Case / Exact search
500
- def self.filter_row_entry(entry, arg, flags)
501
- # Exact Matching / Unless doing full text search
502
- return entry == arg.value.to_s if flags.key?(:exact) && arg.field != :text
242
+ # List Calculation
243
+ def self.total_count_array(results, flags)
244
+ # Option to only return the count
245
+ return results.count if flags[:total] == 'simple'
503
246
 
504
- # Cast to String/Integer Helper
505
- entry, value = filter_entry_cast(entry, arg, flags)
247
+ output = {}
248
+ output[:total] = results.count
249
+ output[:duration] = Query.calculate_duration(results)
506
250
 
507
- entry.send(arg.logic, value)
508
- end
509
-
510
- # Handle casting to strings or integers
511
- def self.filter_entry_cast(entry, arg, flags)
512
- # Cast to String
513
- value = arg.value.to_s
514
-
515
- case arg.logic
516
- when :include?
517
-
518
- # Exact Case argument
519
- unless flags.key?(:case)
520
- entry.downcase!
521
- value.downcase!
522
- end
523
- when :>=, :<=
524
- entry = entry.to_i if entry.numeric?
525
- value = value.to_i if value&.numeric?
251
+ # Sort / Get first and Last
252
+ list = results.select(&:time).map(&:time)
253
+ unless list.blank?
254
+ output[:start] = list.first
255
+ output[:end] = list.last
526
256
  end
527
257
 
528
- [entry, value]
529
- end
258
+ # Hide empty results
259
+ output.reject! { |_k, v| v.blank? }
530
260
 
531
- def self.filter_entry_logic(entry, arg)
532
- entry.send(arg.logic, arg.value)
533
- end
534
-
535
- # Total Count Helper
536
- def self.total_count(results, flags)
537
- results.map do |k, v|
538
- puts k
539
- # puts "Total: #{v.count.to_s.pastel(:blue)}"
540
- # puts "Time Covered: #{calculate_duration(v).pastel(:cyan)}"
541
-
542
- output = {}
543
- output[:total] = v.count
544
- output[:duration] = calculate_duration(v)
545
-
546
- # Sort / Get first and Last
547
- list = v.select(&:time).map(&:time)
548
- unless list.blank?
549
- output[:start] = list.first
550
- output[:end] = list.last
551
- end
552
-
553
- puts render_table(output, flags)
554
-
555
- puts
556
- end
261
+ render_table(output, flags)
557
262
  end
558
263
 
559
264
  # Table Printer Helper
@@ -590,12 +295,12 @@ module GreenHat
590
295
  file_list = prepare_list(file_list, base_list)
591
296
 
592
297
  # Convert to Things
593
- find_things(file_list, flags)
298
+ find_things(file_list, flags, base_list)
594
299
  end
595
300
 
596
301
  # Total Log List Manipulator
597
- def self.prepare_list(log_list, base_list = nil, _flags = {})
598
- base_list ||= GreenHat::ShellHelper::Log.list
302
+ def self.prepare_list(log_list, base_list = nil)
303
+ base_list ||= Thing.list
599
304
 
600
305
  # Assume all
601
306
  log_list.push '*' if log_list.empty?
@@ -606,21 +311,23 @@ module GreenHat
606
311
  log_list
607
312
  end
608
313
 
609
- # Fuzzy match for things
314
+ # Fuzzy match for things / List used for processable (Hash Entries)
610
315
  def self.thing_list
611
- @thing_list ||= Thing.all.map(&:name)
316
+ @thing_list ||= Thing.list.map(&:name)
612
317
 
613
318
  @thing_list
614
319
  end
615
320
 
616
321
  # Shortcut find things
617
- def self.find_things(files, flags = {})
618
- things = files.uniq.flat_map do |file|
322
+ def self.find_things(file_list, flags = {}, base_list = nil)
323
+ base_list ||= Thing.all
324
+
325
+ things = file_list.uniq.flat_map do |file|
619
326
  # If Thing, Return Thing
620
327
  return file if file.instance_of?(Thing)
621
328
 
622
329
  if flags.fuzzy_file_match
623
- Thing.all.select { |x| x.name.include?(file) || x.type.include?(file) }
330
+ base_list.select { |x| x.name.include?(file) || x.type.include?(file) }
624
331
  else
625
332
  Thing.where name: file
626
333
  end
@@ -666,5 +373,6 @@ module GreenHat
666
373
  puts
667
374
  end
668
375
  end
376
+
669
377
  # rubocop:enable Metrics/ModuleLength
670
378
  end
@@ -7,6 +7,10 @@ module GreenHat
7
7
  # rubocop:enable Lint/Debugger
8
8
  end
9
9
 
10
+ def self.settings
11
+ Settings.configure_settings
12
+ end
13
+
10
14
  def self.web
11
15
  # Load Required Files
12
16
  require 'greenhat/web'
@@ -14,10 +18,20 @@ module GreenHat
14
18
  GreenHat::Web.toggle
15
19
  end
16
20
 
21
+ # Append Log Location so follow up commands are executed there
22
+ def self.default(raw_list = [])
23
+ Cli.move_shell Shell::Log
24
+ Log.default(raw_list)
25
+ end
26
+
17
27
  def self.df
18
28
  Disk.df
19
29
  end
20
30
 
31
+ def self.quit(_raw = {})
32
+ exit 0
33
+ end
34
+
21
35
  def self.ps(raw = {})
22
36
  Process.ps raw
23
37
  end
@@ -60,7 +74,7 @@ module GreenHat
60
74
  end
61
75
 
62
76
  def self.quiet
63
- Cli.quiet!
77
+ Cli.quiet_toggle
64
78
 
65
79
  if Cli.quiet
66
80
  puts "GreenHat Quiet Logging #{'Off'.pastel(:red)}"
@@ -93,8 +107,13 @@ module GreenHat
93
107
  end
94
108
  end
95
109
 
96
- def self.ls
97
- GreenHat::Cli.help(false)
110
+ def self.ls(raw = [])
111
+ if raw.empty?
112
+ GreenHat::Cli.help(false)
113
+ else
114
+ Cli.move_shell Shell::Log
115
+ Log.ls(raw)
116
+ end
98
117
  end
99
118
 
100
119
  def self.ll