greenhat 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,14 +3,18 @@ module GreenHat
3
3
  # Helper to organize page check / methods
4
4
  module Page
5
5
  # Check if paging should be skipped
6
- def self.skip?(args, data)
7
- return count_rows(data) unless args[:page]
6
+ # True / False / Not Set
7
+ def self.skip?(flags, data)
8
+ # Pass if Explicitly Set / Inverse for skip
9
+ return !flags[:page] if flags.key? :page
8
10
 
9
- args[:page].first == :false
11
+ LogBot.debug('Page', count_rows(data, flags)) if ENV['DEBUG']
12
+
13
+ count_rows(data, flags)
10
14
  end
11
15
 
12
16
  # Array/Hash and String pagination check. Don't unncessarily loop through everything
13
- def self.count_rows(data)
17
+ def self.count_rows(data, flags)
14
18
  height = TTY::Screen.height
15
19
  size = 0
16
20
 
@@ -19,7 +23,9 @@ module GreenHat
19
23
  when Hash then entry.keys.count
20
24
  when Array then entry.count
21
25
  else
22
- 1
26
+ # Each Boxed Entry is 3 Lines
27
+ flags.key?(:row_size) ? flags[:row_size] : 1
28
+
23
29
  end
24
30
 
25
31
  break if size > height
@@ -11,16 +11,22 @@ module GreenHat
11
11
  end
12
12
 
13
13
  def self.help
14
- puts "\u2500".colorize(:cyan) * 20
15
- puts "#{'Process'.colorize(:yellow)} - ps helper"
16
- puts "\u2500".colorize(:cyan) * 20
14
+ puts "\u2500".pastel(:cyan) * 22
15
+ puts "#{'Process'.pastel(:yellow)} - ps helper"
16
+ puts "\u2500".pastel(:cyan) * 22
17
17
 
18
- puts 'Command Summary'.colorize(:blue)
19
- puts ' ps'.colorize(:green)
18
+ ShellHelper.common_opts
19
+
20
+ puts 'Command Summary'.pastel(:blue)
21
+ puts ' ps'.pastel(:green)
20
22
  puts ' Raw `ps`'
21
- puts ' filter'.colorize(:green)
22
- puts ' Key/Field Filtering'
23
- puts ' - See `filter_help`'
23
+ puts
24
+ puts ' filter'.pastel(:green)
25
+ puts " Key/Field Filtering. See #{'filter_help'.pastel(:blue)}"
26
+ puts ' Examples'
27
+ puts ' filter --sort=mem --reverse'.pastel(:green)
28
+ puts ' filter --user=gitlab'.pastel(:green)
29
+
24
30
  puts
25
31
  end
26
32
 
@@ -29,25 +35,31 @@ module GreenHat
29
35
  end
30
36
 
31
37
  def self.ps(raw = {})
32
- _log_list, _1opts, args = ShellHelper.param_parse(raw)
33
- ShellHelper.file_output GreenHat::Ps.ps(args)
38
+ # Extract Args
39
+ files_list, flags, _args = Args.parse(raw)
40
+
41
+ # Collect Files
42
+ files = ShellHelper.files(files_list, GreenHat::Ps.things, flags)
43
+
44
+ # Output
45
+ ShellHelper.file_output(files, flags)
34
46
  end
35
47
 
36
48
  def self.filter(raw = {})
37
- _log_list, opts, args = ShellHelper.param_parse(raw)
49
+ # Argument Parsing
50
+ files, flags, args = Args.parse(raw)
38
51
 
39
- # AND / OR Filtering
40
- filter_type = args.or ? :any? : :all?
52
+ # Prepare Log List
53
+ file_list = ShellHelper.prepare_list(files, GreenHat::Ps.things)
41
54
 
42
- # ShellHelper.file_output
43
- results = ShellHelper.filter_start(GreenHat::Ps.ps(args).map(&:name), filter_type, args, opts)
55
+ results = ShellHelper.filter_start(file_list, flags, args)
44
56
 
45
57
  # Check Search Results
46
58
  if results.instance_of?(Hash) && results.values.flatten.empty?
47
- puts 'No results'.colorize(:red)
59
+ puts 'No results'.pastel(:red)
48
60
  else
49
61
  # This causes the key 'colorized' output to also be included
50
- ShellHelper.show(results.to_a.compact.flatten, args)
62
+ ShellHelper.show(results.to_a.compact.flatten, flags)
51
63
  end
52
64
  end
53
65
  end
@@ -45,11 +45,11 @@ module GreenHat
45
45
 
46
46
  def show
47
47
  output = [
48
- archive.friendly_name.colorize(:blue)
48
+ archive.friendly_name.pastel(:blue)
49
49
  ]
50
50
 
51
51
  # OS
52
- output << 'OS'.colorize(:light_yellow)
52
+ output << 'OS'.pastel(:bright_yellow)
53
53
  output << hostname if host
54
54
  output << distro if os_release
55
55
  output << selinux if selinux_status
@@ -62,7 +62,7 @@ module GreenHat
62
62
 
63
63
  # Memory
64
64
  if meminfo || free_m
65
- output << 'Memory'.colorize(:light_yellow)
65
+ output << 'Memory'.pastel(:bright_yellow)
66
66
  output << memory_perc if meminfo
67
67
  output << memory_free if free_m
68
68
  output << ''
@@ -75,7 +75,7 @@ module GreenHat
75
75
  end
76
76
 
77
77
  # Gitlab
78
- output << 'GitLab'.colorize(:light_yellow) if gitlab_manifest
78
+ output << 'GitLab'.pastel(:bright_yellow) if gitlab_manifest
79
79
  output << gitlab_version if gitlab_manifest
80
80
  output << gitlab_services if gitlab_status
81
81
  output << title('Errors') if production_log || api_log || application_log || sidekiq_log
@@ -95,8 +95,8 @@ module GreenHat
95
95
  color = count.zero? ? :green : :red
96
96
 
97
97
  [
98
- title(' Production', :light_red, 18),
99
- count.to_s.colorize(color)
98
+ title(' Production', :bright_red, 18),
99
+ count.to_s.pastel(color)
100
100
  ].join
101
101
  end
102
102
 
@@ -105,8 +105,8 @@ module GreenHat
105
105
  color = count.zero? ? :green : :red
106
106
 
107
107
  [
108
- title(' API', :light_red, 18),
109
- count.to_s.colorize(color)
108
+ title(' API', :bright_red, 18),
109
+ count.to_s.pastel(color)
110
110
  ].join
111
111
  end
112
112
 
@@ -115,8 +115,8 @@ module GreenHat
115
115
  color = count.zero? ? :green : :red
116
116
 
117
117
  [
118
- title(' Application', :light_red, 18),
119
- count.to_s.colorize(color)
118
+ title(' Application', :bright_red, 18),
119
+ count.to_s.pastel(color)
120
120
  ].join
121
121
  end
122
122
 
@@ -125,32 +125,16 @@ module GreenHat
125
125
  color = count.zero? ? :green : :red
126
126
 
127
127
  [
128
- title(' Sidekiq', :light_red, 18),
129
- count.to_s.colorize(color)
128
+ title(' Sidekiq', :bright_red, 18),
129
+ count.to_s.pastel(color)
130
130
  ].join
131
131
  end
132
132
 
133
133
  def gitlab_services
134
- list = gitlab_status.data.keys.sort.map do |service|
135
- color = gitlab_status.data.dig(service, 0, :status) == 'run' ? :green : :red
136
- service.colorize(color)
137
- end
138
-
139
- # Keep Alphabetical Sort
140
- groups = list.each_slice((list.size / 3.to_f).round).to_a
141
-
142
- table = TTY::Table.new do |t|
143
- loop do
144
- break if groups.all?(&:empty?)
145
-
146
- t << groups.map(&:shift)
147
- end
148
- end
149
-
150
134
  [
151
135
  title('Services'),
152
136
  "\n ",
153
- table.render(:unicode, padding: [0, 1, 0, 1], indent: 3)
137
+ GitLab.services(archive, 3)
154
138
  ].join
155
139
  rescue StandardError => e
156
140
  LogBot.fatal('GitLab Services', message: e.message, backtrace: e.backtrace.first)
@@ -173,7 +157,7 @@ module GreenHat
173
157
  def distro
174
158
  [
175
159
  title('Distro'),
176
- "[#{os_release.data.ID}] ".colorize(:light_black),
160
+ "[#{os_release.data.ID}] ".pastel(:bright_black),
177
161
  os_release.data.PRETTY_NAME
178
162
  ].join
179
163
  end
@@ -184,7 +168,7 @@ module GreenHat
184
168
 
185
169
  [
186
170
  title('SeLinux'),
187
- status.colorize(status_color),
171
+ status.pastel(status_color),
188
172
  ' (',
189
173
  selinux_status.data['Current mode'],
190
174
  ')'
@@ -204,7 +188,7 @@ module GreenHat
204
188
  [
205
189
  title('Kernel'),
206
190
  value,
207
- " (#{build})".colorize(:light_black)
191
+ " (#{build})".pastel(:bright_black)
208
192
  ].join
209
193
  end
210
194
 
@@ -231,9 +215,9 @@ module GreenHat
231
215
  [
232
216
  title('Sys Time'),
233
217
  timedatectl.data['Local time'],
234
- ' (ntp: '.colorize(:light_black),
235
- ntp_status.colorize(ntp_color),
236
- ')'.colorize(:light_black)
218
+ ' (ntp: '.pastel(:bright_black),
219
+ ntp_status.pastel(ntp_color),
220
+ ')'.pastel(:bright_black)
237
221
  ].join
238
222
  end
239
223
 
@@ -258,14 +242,14 @@ module GreenHat
258
242
  [
259
243
  interval,
260
244
  ' (',
261
- "#{value}%".colorize(color),
245
+ "#{value}%".pastel(color),
262
246
  ')'
263
247
  ].join
264
248
  end
265
249
 
266
250
  [
267
251
  title('LoadAvg'),
268
- "[CPU #{cpu_count}] ".colorize(:light_white),
252
+ "[CPU #{cpu_count}] ".pastel(:bright_white),
269
253
  intervals_text.join(', ')
270
254
  ].join
271
255
  end
@@ -277,11 +261,11 @@ module GreenHat
277
261
 
278
262
  [
279
263
  title('Usage'),
280
- ' ['.colorize(:light_black),
281
- '='.colorize(:green) * (used / 2),
264
+ ' ['.pastel(:bright_black),
265
+ '='.pastel(:green) * (used / 2),
282
266
  ' ' * (50 - used / 2),
283
- ']'.colorize(:light_black),
284
- " #{100 - percent(free, total)}%".colorize(:green) # Inverse
267
+ ']'.pastel(:bright_black),
268
+ " #{100 - percent(free, total)}%".pastel(:green) # Inverse
285
269
  ].join
286
270
  end
287
271
 
@@ -317,7 +301,7 @@ module GreenHat
317
301
 
318
302
  # Preapre / Indent List
319
303
  [
320
- 'Disks(Top % Usage)'.colorize(:light_yellow),
304
+ 'Disks'.pastel(:bright_yellow) + ' (Top % Usage)'.pastel(:bright_black),
321
305
  "\n",
322
306
  disk_list.each { |x| x.prepend(' ' * 4) }.join("\n")
323
307
  ].join
@@ -332,7 +316,7 @@ module GreenHat
332
316
 
333
317
  # Helper to Make Cyan Titles
334
318
  def title(name, color = :cyan, ljust = 12)
335
- " #{name}:".ljust(ljust).colorize(color)
319
+ " #{name}:".ljust(ljust).pastel(color)
336
320
  end
337
321
  end
338
322
  # rubocop:enable Metrics/ClassLength
@@ -2,94 +2,8 @@ module GreenHat
2
2
  # Common Helpers
3
3
  # rubocop:disable Metrics/ModuleLength
4
4
  module ShellHelper
5
- # Generic Parameter Parsing
6
- def self.param_parse(params, skip_args = [])
7
- # Turn Params into Hash
8
- opts = params.flat_map { |param| param_opt_scan(param) }
9
-
10
- # Collect and Remove Args
11
- args = params.each_with_object({}) { |param, obj| param_arg_scan(param, obj) }
12
-
13
- # Move Special Arguments
14
- opts.reject! do |opt|
15
- if param_special_opts.include? opt.field
16
- args[opt.field] = opt.value.split(',').map(&:to_sym)
17
- true
18
- end
19
- end
20
-
21
- # Remove Opts and Args
22
- params.reject! do |param|
23
- opt_field_remove?(opts, param) || arg_field_remove?(args, param)
24
- end
25
-
26
- # Update other user defaults
27
- Settings.default_log_args(args, skip_args)
28
-
29
- [params, opts, args]
30
- end
31
-
32
- def self.opt_field_remove?(opts, param)
33
- opts.any? do |opt|
34
- param.include? "--#{opt.field}#{opt.bang ? '!' : nil}=#{opt.value}"
35
- end
36
- end
37
-
38
- def self.arg_field_remove?(args, param)
39
- args.keys.any? { |field| param.include? "--#{field}" }
40
- end
41
-
42
- # Def Param Scan (Split -- values into keys)
43
- def self.param_opt_scan(param)
44
- param.scan(/--([^=]+)=(.*)/).map do |field, value|
45
- bang = false
46
- if field.include? '!'
47
- field.delete!('!')
48
- bang = true
49
- end
50
-
51
- { field: field.to_sym, value: value, bang: bang }
52
- end
53
- end
54
-
55
- # Params that should be set as Args
56
- # Comma Delimited
57
- # --slice=thing,thing2
58
- # --except=time,params
59
- # --round=2
60
-
61
- def self.param_special_opts
62
- %i[
63
- slice except stats uniq pluck round archive start end sort limit truncate page case
64
- ]
65
- end
66
-
67
- # Parameter Extraction
68
- # Special Opts: --slice, --except
69
- def self.param_arg_scan(param, obj)
70
- # TODO: Why is capture group doing two arrays
71
- param.scan(/--([^=]+)$/).flatten.each do |field|
72
- obj[field.to_sym] = param_arg_defaults(field.to_sym)
73
- end
74
-
75
- obj
76
- end
77
-
78
- # Arg Defaults
79
- def self.param_arg_defaults(field)
80
- case field
81
- when :round then [2]
82
- when :limit then [TTY::Screen.height / 2]
83
- when :truncate then [TTY::Screen.width * 4]
84
- when :page, :case then [:true]
85
- when *param_special_opts then []
86
- else
87
- true
88
- end
89
- end
90
-
91
5
  # Use File Process for Output
92
- def self.file_output(files)
6
+ def self.file_output(files, flags = {})
93
7
  results = file_process(files) do |file|
94
8
  [
95
9
  file.friendly_name,
@@ -98,7 +12,7 @@ module GreenHat
98
12
  ]
99
13
  end
100
14
 
101
- ShellHelper.show(results.flatten)
15
+ ShellHelper.show(results.flatten, flags)
102
16
  end
103
17
 
104
18
  def self.file_process(files, &block)
@@ -119,7 +33,7 @@ module GreenHat
119
33
  end
120
34
 
121
35
  # Show Data / Auto Paginate Helper
122
- def self.show(data, args = {})
36
+ def self.show(data, flags = {})
123
37
  # If Block of String
124
38
  if data.instance_of?(String)
125
39
  TTY::Pager.page data
@@ -127,23 +41,24 @@ module GreenHat
127
41
  end
128
42
 
129
43
  # If raw just print out
130
- if args.raw
44
+ if flags[:raw]
131
45
  puts data.join("\n")
132
46
  return true
133
47
  end
134
48
 
135
49
  # Check if content needs to paged, or if auto_height is off
136
- if Page.skip?(args, data)
137
- puts data.map { |entry| entry_show(args, entry) }.compact.join("\n")
50
+ if Page.skip?(flags, data)
51
+ puts data.map { |entry| entry_show(flags, entry) }.compact.join("\n")
138
52
  return true
139
53
  end
140
54
 
141
55
  # Default Pager
142
56
  TTY::Pager.page do |pager|
143
57
  data.each do |entry|
144
- output = entry_show(args, entry)
58
+ output = entry_show(flags, entry)
145
59
 
146
- next if output.blank?
60
+ # Breaks any intentional spaces
61
+ # next if output.blank?
147
62
 
148
63
  pager.write("\n#{output}") # write line to the pager
149
64
  end
@@ -151,12 +66,12 @@ module GreenHat
151
66
  end
152
67
 
153
68
  # Entry Shower / Top Level
154
- def self.entry_show(args, entry, key = nil)
69
+ def self.entry_show(flags, entry, key = nil)
155
70
  LogBot.debug('Entry Show', entry.class) if ENV['DEBUG']
156
71
  case entry
157
- when Hash then render_table(entry, args)
72
+ when Hash then render_table(entry, flags)
158
73
  when Float, Integer, Array
159
- format_table_entry(args, entry, key)
74
+ format_table_entry(flags, entry, key)
160
75
  # Ignore Special Formatting for Strings / Usually already formatted
161
76
  when String
162
77
  entry
@@ -167,39 +82,42 @@ module GreenHat
167
82
  end
168
83
 
169
84
  # Format Table Entries
170
- def self.format_table_entry(args, entry, key = nil)
85
+ def self.format_table_entry(flags, entry, key = nil)
171
86
  formatted_entry = case entry
172
87
  # Rounding
173
88
  when Float, Integer || entry.numeric?
174
- args.round ? entry.to_f.round(args.round.first.to_s.to_i).ai : entry.ai
89
+ flags.key?(:round) ? entry.to_f.round(flags.round).ai : entry.ai
175
90
 
176
91
  # General Inspecting
177
92
  when Hash then entry.ai(ruby19_syntax: true)
178
93
 
179
94
  # Arrays often contain Hashes. Dangerous Recursive?
180
95
  when Array
181
- entry.map { |x| format_table_entry(args, x) }.join("\n")
96
+ entry.map { |x| format_table_entry(flags, x) }.join("\n")
182
97
 
183
98
  when Time
184
- entry.to_s.colorize(:light_white)
99
+ entry.to_s.pastel(:bright_white)
185
100
 
186
101
  # Default String Formatting
187
102
  else
188
103
  StringColor.do(key, entry)
189
104
  end
190
105
 
191
- if args[:truncate]
192
- entry_truncate(formatted_entry, args[:truncate].join.to_i)
106
+ if flags[:truncate]
107
+ entry_truncate(formatted_entry, flags[:truncate])
193
108
  else
194
109
  formatted_entry
195
110
  end
196
111
  rescue StandardError => e
197
- binding.pry
112
+ if ENV['DEBUG']
113
+ LogBot.warn('Table Format Entry', message: e.message)
114
+ ap e.backtrace
115
+ end
198
116
  end
199
117
 
200
118
  # Print the Table in a Nice way
201
- def self.render_table(entry, args)
202
- entry = entry.map { |k, v| [k, format_table_entry(args, v, k)] }.to_h
119
+ def self.render_table(entry, flags)
120
+ entry = entry.map { |k, v| [k, format_table_entry(flags, v, k)] }.to_h
203
121
  # Pre-format Entry
204
122
 
205
123
  table = TTY::Table.new(header: entry.keys, rows: [entry], orientation: :vertical)
@@ -207,10 +125,6 @@ module GreenHat
207
125
  LogBot.debug('Rendering Entries') if ENV['DEBUG']
208
126
  table.render(:unicode, padding: [0, 1, 0, 1], multiline: true) do |renderer|
209
127
  renderer.border.style = :cyan
210
-
211
- # renderer.filter = lambda do |val, _row_index, col_index|
212
- # render_table_entry(val, col_index, args)
213
- # end
214
128
  end
215
129
 
216
130
  # LogBot.debug('Finish Render Table') if ENV['DEBUG']
@@ -223,33 +137,41 @@ module GreenHat
223
137
 
224
138
  [
225
139
  entry.ai,
226
- ('_' * (TTY::Screen.width / 3)).colorize(:cyan),
140
+ ('_' * (TTY::Screen.width / 3)).pastel(:cyan),
227
141
  "\n"
228
142
  ].join("\n")
229
143
  end
230
144
 
231
- def self.render_table_entry(val, col_index, args)
145
+ def self.render_table_entry(val, col_index, flags)
232
146
  return val.to_s unless col_index == 1
233
147
 
234
- format_table_entry(args, val)
148
+ format_table_entry(flags, val)
235
149
  end
236
150
 
237
151
  # Main Entry Point for Filtering
238
- def self.filter_start(log_list, filter_type, args, opts)
152
+ def self.filter_start(files, flags, args)
239
153
  # Convert to Things
240
- logs = ShellHelper.find_things(log_list, args).select(&:processed?)
154
+ logs = ShellHelper.find_things(files, flags).select(&:processed?)
241
155
 
242
156
  # Ignore Archive/Host Dividers
243
- if args[:combine]
157
+ if flags[:combine]
244
158
  results = logs.reject(&:blank?).map(&:data).flatten.compact
245
- ShellHelper.filter(results, filter_type, args, opts)
159
+ ShellHelper.filter(results, flags, args)
246
160
  else
247
161
  # Iterate and Preserve Archive/Host Index
248
162
  logs.each_with_object({}) do |log, obj|
249
163
  # Ignore Empty Results / No Thing
250
164
  next if log&.blank?
251
165
 
252
- obj[log.friendly_name] = ShellHelper.filter(log.data, filter_type, args, opts)
166
+ # Include Total Count in Name
167
+ results = ShellHelper.filter(log.data, flags, args)
168
+ title = [
169
+ log.friendly_name,
170
+ " #{results.count}".pastel(:bright_black)
171
+ ]
172
+
173
+ # Save unless empty
174
+ obj[title.join] = results unless results.count.zero?
253
175
 
254
176
  obj
255
177
  end
@@ -259,50 +181,55 @@ module GreenHat
259
181
  # Filter Logic
260
182
  # TODO: Simplify
261
183
  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
262
- def self.filter(data, type = :all?, args = {}, opts = {})
184
+ def self.filter(data, flags = {}, args = {})
263
185
  results = data.clone.flatten.compact
264
186
  results.select! do |row|
265
- opts.send(type) do |opt|
266
- filter_row_key(row, opt, args)
187
+ args.send(flags.logic) do |arg|
188
+ filter_row_key(row, arg, flags)
267
189
  end
268
190
  end
269
191
 
192
+ # Ensure presecense of a specific field
193
+ results = filter_exists(results, flags[:exists]) if flags.key?(:exists)
194
+
270
195
  # Time Filtering
271
- results = filter_time(results, args) if args[:start] || args[:end]
196
+ results = filter_time(results, flags) if flags.key?(:start) || flags.key?(:end)
272
197
 
273
198
  # Strip Results if Slice is defined
274
- results = filter_slice(results, args[:slice]) if args[:slice]
199
+ results = filter_slice(results, flags[:slice]) if flags.key?(:slice)
275
200
 
276
201
  # Strip Results if Except is defined
277
- results = filter_except(results, args[:except]) if args[:except]
202
+ results = filter_except(results, flags[:except]) if flags.key?(:except)
278
203
 
279
204
  # Remove Blank from either slice or except
280
205
  results.reject!(&:empty?)
281
206
 
282
207
  # Sort
283
- results.sort_by! { |x| x.slice(*args[:sort]).values } if args[:sort]
208
+ results.sort_by! { |x| x.slice(*flags[:sort]).values } if flags.key?(:sort)
284
209
 
285
210
  # JSON Formatting
286
- results = results.map { |x| Oj.dump(x) } if args[:json]
211
+ results = results.map { |x| Oj.dump(x) } if flags.key?(:json)
287
212
 
288
213
  # Show Unique Only
289
- results = filter_uniq(results, args[:uniq]) if args.key?(:uniq)
214
+ results = filter_uniq(results, flags[:uniq]) if flags.key?(:uniq)
290
215
 
291
216
  # Reverse
292
- results.reverse! if args[:reverse]
217
+ results.reverse! if flags[:reverse]
293
218
 
294
219
  # Count occurrences / Skip Results
295
- return filter_stats(results, args[:stats]) if args[:stats]
220
+ return filter_stats(results, flags[:stats]) if flags.key?(:stats)
296
221
 
297
222
  # Pluck
298
- results = filter_pluck(results, args[:pluck]) if args.key?(:pluck)
223
+ results = filter_pluck(results, flags[:pluck]) if flags.key?(:pluck)
299
224
 
300
- # Truncate
301
- # filter_truncate(results, args[:truncate].join.to_i) if args.key?(:truncate)
225
+ # Limit / Ensure Exists and Valid Number
226
+ if flags.key?(:limit) && flags[:limit]
227
+ # Old
228
+ # results[0..flags[:limit].map(&:to_s).join.to_i - 1]
229
+
230
+ # New
231
+ results.shift flags[:limit]
302
232
 
303
- # Limit
304
- if args[:limit]
305
- results[0..args[:limit].map(&:to_s).join.to_i - 1]
306
233
  else
307
234
  results
308
235
  end
@@ -312,10 +239,10 @@ module GreenHat
312
239
  # Filter Start and End Times
313
240
  # rubocop:disable Metrics/MethodLength
314
241
  # TODO: This is a bit icky, simplify/dry
315
- def self.filter_time(results, args)
316
- if args.start
242
+ def self.filter_time(results, flags)
243
+ if flags.key?(:start)
317
244
  begin
318
- time_start = Time.parse(args.start.first.to_s)
245
+ time_start = Time.parse(flags[:start])
319
246
 
320
247
  results.select! do |x|
321
248
  if x.time
@@ -325,13 +252,13 @@ module GreenHat
325
252
  end
326
253
  end
327
254
  rescue StandardError
328
- puts 'Unable to Process Start Time Filter'.colorize(:red)
255
+ puts 'Unable to Process Start Time Filter'.pastel(:red)
329
256
  end
330
257
  end
331
258
 
332
- if args.end
259
+ if flags.key?(:end)
333
260
  begin
334
- time_start = Time.parse(args.end.first.to_s)
261
+ time_start = Time.parse(flags[:end])
335
262
 
336
263
  results.select! do |x|
337
264
  if x.time
@@ -341,7 +268,7 @@ module GreenHat
341
268
  end
342
269
  end
343
270
  rescue StandardError
344
- puts 'Unable to Process End Time Filter'.colorize(:red)
271
+ puts 'Unable to Process End Time Filter'.pastel(:red)
345
272
  end
346
273
  end
347
274
 
@@ -351,14 +278,24 @@ module GreenHat
351
278
 
352
279
  def self.filter_except(results, except)
353
280
  # Avoid Empty Results
354
- if slice.empty?
355
- filter_empty_arg('slice')
281
+ if except.empty?
282
+ filter_empty_arg('except')
356
283
  return results
357
284
  end
358
285
 
359
286
  results.map { |row| row.except(*except) }
360
287
  end
361
288
 
289
+ def self.filter_exists(results, exists)
290
+ # Avoid Empty Results
291
+ if exists.empty?
292
+ filter_empty_arg('exists')
293
+ return results
294
+ end
295
+
296
+ results.select { |row| (exists - row.keys).empty? }
297
+ end
298
+
362
299
  def self.entry_truncate(entry, truncate)
363
300
  # Ignore if Truncation Off
364
301
  return entry if truncate.zero?
@@ -367,7 +304,7 @@ module GreenHat
367
304
  return entry unless entry.instance_of?(String) && entry.size > truncate
368
305
 
369
306
  # Include '...' to indicate truncation
370
- "#{entry.to_s[0..truncate]} #{'...'.colorize(:light_blue)}"
307
+ "#{entry.to_s[0..truncate]} #{'...'.pastel(:bright_blue)}"
371
308
  end
372
309
 
373
310
  def self.filter_slice(results, slice)
@@ -399,7 +336,7 @@ module GreenHat
399
336
 
400
337
  unique.map do |field|
401
338
  results.uniq { |x| x[field] }
402
- end.flatten
339
+ end.inject(:&)
403
340
  end
404
341
 
405
342
  def self.filter_stats(results, stats)
@@ -409,18 +346,45 @@ module GreenHat
409
346
  return results
410
347
  end
411
348
 
412
- stats.map do |stat|
413
- occurrences = filter_count_occurrences(results, stat)
414
- occurrences.sort_by(&:last).to_h
349
+ # Loop through Stats, Separate Hash/Tables
350
+ stats.map do |field|
351
+ occurrences = filter_count_occurrences(results, field)
352
+
353
+ # Total Occurences
354
+ total = occurrences.values.sum
355
+
356
+ # Percs
357
+ occurrences.transform_values! do |count|
358
+ [
359
+ count,
360
+ " #{percent(count, total)}%".pastel(:bright_black)
361
+ ]
362
+ end
363
+
364
+ # Sort by total occurances / New Variable for Total
365
+ output = occurrences.sort_by(&:last).to_h.transform_values!(&:join).to_a
366
+
367
+ # Append Header / Total with field name
368
+ output.unshift([field.to_s.pastel(:bright_black), total])
369
+
370
+ # Format
371
+ output.to_h
415
372
  end
416
373
  end
417
374
 
375
+ # Percent Helper
376
+ def self.percent(value, total)
377
+ ((value / total.to_f) * 100).round
378
+ end
379
+
418
380
  # Helper to Count occurrences
419
- def self.filter_count_occurrences(results, stat)
381
+ def self.filter_count_occurrences(results, field)
420
382
  results.each_with_object(Hash.new(0)) do |entry, counts|
421
- next unless entry.key? stat
422
-
423
- counts[entry[stat]] += 1
383
+ if entry.key? field
384
+ counts[entry[field]] += 1
385
+ else
386
+ counts['None'.pastel(:bright_black)] += 1
387
+ end
424
388
 
425
389
  counts
426
390
  end
@@ -428,43 +392,67 @@ module GreenHat
428
392
 
429
393
  def self.filter_empty_arg(arg)
430
394
  puts [
431
- 'Ignoring'.colorize(:light_yellow),
432
- "--#{arg}".colorize(:cyan),
433
- 'it requires an argument'.colorize(:red)
395
+ 'Ignoring'.pastel(:bright_yellow),
396
+ "--#{arg}".pastel(:cyan),
397
+ 'it requires an argument'.pastel(:red)
434
398
  ].join(' ')
435
399
  end
436
400
 
437
401
  # Break out filter row logic into separate method
438
- def self.filter_row_key(row, param, args)
439
- # Ignore Other Logic if Field isn't even included
440
- return false unless row.key? param.field
441
402
 
442
- # Sensitivity Check / Check for Match
443
- included = if args.key?(:case)
444
- row[param.field].to_s.include? param.value.to_s
403
+ def self.filter_row_key(row, arg, flags)
404
+ # Ignore Other Logic if Field isn't even included / Full Text Searching
405
+ return false unless row.key?(arg[:field]) || arg[:field] == :text
406
+
407
+ # Sensitivity Check / Check for Match / Full Text Searching
408
+ included = if arg[:field] == :text
409
+ filter_row_entry(row.to_s, arg, flags)
445
410
  else
446
- row[param.field].to_s.downcase.include? param.value.to_s.downcase
411
+ filter_row_entry(row[arg.field].to_s, arg, flags)
447
412
  end
448
413
 
449
414
  # Pivot of off include vs exclude
450
- if param.bang
415
+ if arg.bang
451
416
  !included
452
417
  else
453
418
  included
454
419
  end
455
420
  end
456
421
 
422
+ # Field Partial / Case / Exact search
423
+ def self.filter_row_entry(entry, arg, flags)
424
+ # Exact Matching / Unless doing full text search
425
+ return entry.to_s == arg.value.to_s if flags.key?(:exact) && arg.field != :text
426
+
427
+ if flags.key?(:case)
428
+ entry.include? arg.value.to_s
429
+ else
430
+ entry.downcase.include? arg.value.to_s.downcase
431
+ end
432
+ end
433
+
457
434
  # Total Count Helper
458
435
  def self.total_count(results)
459
436
  results.each do |k, v|
460
437
  puts k
461
- puts "Total: #{v.count.to_s.colorize(:blue)}"
438
+ puts "Total: #{v.count.to_s.pastel(:blue)}"
462
439
  puts
463
440
  end
464
441
  end
465
442
 
443
+ # Unified Files Interface
444
+ def self.files(file_list, base_list = nil, flags = {})
445
+ base_list ||= Thing.all
446
+
447
+ # Prepare Log List
448
+ file_list = prepare_list(file_list, base_list)
449
+
450
+ # Convert to Things
451
+ find_things(file_list, flags)
452
+ end
453
+
466
454
  # Total Log List Manipulator
467
- def self.prepare_list(log_list, base_list = nil)
455
+ def self.prepare_list(log_list, base_list = nil, _flags = {})
468
456
  base_list ||= GreenHat::ShellHelper::Log.list
469
457
 
470
458
  # Assume all
@@ -476,48 +464,62 @@ module GreenHat
476
464
  log_list
477
465
  end
478
466
 
467
+ # Fuzzy match for things
468
+ def self.thing_list
469
+ @thing_list ||= Thing.all.map(&:name)
470
+
471
+ @thing_list
472
+ end
473
+
479
474
  # Shortcut find things
480
- def self.find_things(log_list, args = {})
481
- things = log_list.uniq.flat_map do |log|
482
- Thing.where name: log
483
- end
475
+ def self.find_things(files, flags = {})
476
+ things = files.uniq.flat_map do |file|
477
+ # If Thing, Return Thing
478
+ return file if file.instance_of?(Thing)
479
+
480
+ if flags.fuzzy_file_match
481
+ Thing.all.select { |x| x.name.include? file }
482
+ else
483
+ Thing.where name: file
484
+ end
485
+ end.uniq
484
486
 
485
487
  # Host / Archive
486
- things.select! { |x| x.archive? args.archive } if args.archive
488
+ things.select! { |x| x.archive? flags.archive } if flags.key?(:archive)
487
489
 
488
490
  things
489
491
  end
490
492
 
491
493
  # Main Entry Point for Searching
492
- def self.search_start(log_list, filter_type, args, opts)
494
+ # def self.search_start(log_list, filter_type, args, opts)
495
+ def self.search_start(files, flags, args)
493
496
  # Convert to Things
494
- logs = ShellHelper.find_things(log_list, args)
497
+ logs = ShellHelper.find_things(files, flags)
495
498
 
496
499
  logs.each_with_object({}) do |log, obj|
497
500
  # Ignore Empty Results / No Thing
498
501
  next if log&.data.blank?
499
502
 
500
- obj[log.friendly_name] = ShellHelper.search(log.data, filter_type, args, opts)
503
+ obj[log.friendly_name] = ShellHelper.search(log.data, flags, args)
501
504
 
502
505
  obj
503
506
  end
504
507
  end
505
508
 
506
509
  # Generic Search Helper / String/Regex
507
- def self.search(data, type = :all?, args = {}, opts = {})
510
+ def self.search(data, flags = {}, args = {})
508
511
  results = data.clone.flatten.compact
509
-
510
512
  results.select! do |row|
511
- opts.send(type) do |opt|
512
- search_row(row, opt)
513
+ args.send(flags.logic) do |arg|
514
+ search_row(row, arg, flags)
513
515
  end
514
516
  end
515
517
 
516
518
  # Strip Results if Slice is defined
517
- results.map! { |row| row.slice(*args[:slice]) } if args[:slice]
519
+ results.map! { |row| row.slice(*flags[:slice]) } if flags[:slice]
518
520
 
519
521
  # Strip Results if Except is defined
520
- results.map! { |row| row.except(*args[:except]) } if args[:except]
522
+ results.map! { |row| row.except(*flags[:except]) } if flags[:except]
521
523
 
522
524
  # Remove Blank from either slice or except
523
525
  results.reject!(&:empty?)
@@ -526,21 +528,23 @@ module GreenHat
526
528
  end
527
529
 
528
530
  # Break out filter row logic into separate method
529
- def self.search_row(row, param)
530
- # Not Included Param
531
- included = row.to_s.include? param.value
531
+ def self.search_row(row, arg, flags)
532
+ # Sensitivity Check / Check for Match
533
+ included = filter_row_entry(row.to_s, arg, flags)
532
534
 
533
- if param.bang
535
+ # Pivot of off include vs exclude
536
+ if arg.bang
534
537
  !included
535
538
  else
536
539
  included
537
540
  end
538
541
  end
539
542
 
543
+ # TODO: Remove?
540
544
  # Color Reader Helper
541
- def self.pastel
542
- @pastel ||= Pastel.new
543
- end
545
+ # def self.pastel
546
+ # @pastel ||= Pastel.new
547
+ # end
544
548
 
545
549
  # Number Helper
546
550
  # https://gitlab.com/zedtux/human_size_to_number/-/blob/master/lib/human_size_to_number/helper.rb
@@ -579,6 +583,19 @@ module GreenHat
579
583
 
580
584
  result
581
585
  end
586
+
587
+ # General Helper for `show`
588
+ def self.common_opts
589
+ puts 'Common Options'.pastel(:blue)
590
+ puts ' --raw'.pastel(:green)
591
+ puts ' Do not use less/paging'
592
+ puts
593
+
594
+ puts ' --archive'.pastel(:green)
595
+ puts ' Limit to specific archive name (inclusive). Matching SOS tar.gz name'
596
+ puts ' Ex: --archive=dev-gitlab_20210622154626, --archive=202106,202107'
597
+ puts
598
+ end
582
599
  end
583
600
  # rubocop:enable Metrics/ModuleLength
584
601
  end