greenhat 0.2.0 → 0.3.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.
@@ -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