k_log 0.0.20 → 0.0.27

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a5ce4190206d26b326f0295241cf8c087c3c56bfc0bb11b4e8dbb6f8d401447f
4
- data.tar.gz: 3ed6be809644377525a00658e5f010b32e3979bb031e4d10037a4393c67c47d1
3
+ metadata.gz: 2035ee429ccfbb7cd0307d243e9b8bd303f44444d1b356c28ad079bc8219de8f
4
+ data.tar.gz: c7eeff9ee82d1514859052d264cfcb06216ad939f3c215c34dfee6f64ef0d21f
5
5
  SHA512:
6
- metadata.gz: 133ea34980568cb38ecf8e3b1d9efdf4aa292005b5a67ad9f8282da82200bf42611bb95dfe25bc2f1ee78b7b845cf9af4e65fbf136e0f73867cb07e092699419
7
- data.tar.gz: 3606bca839c257f596f6ca6a0fb1e1b6cda1da58c3735ce0857236523c47e8ca13c0ce656936239ea0ffb2ce1c435e62dbd6b68b9f67a3677f1239c9ff0038ca
6
+ metadata.gz: f12d2c0fae01798339647d3905fefe8e5e63049d074965c04b878e3ba4df7b8571cba637877395f8fa033d2c780d53bfc4aaa2da605af647908fdb7e29bd1e3a
7
+ data.tar.gz: 3d780ecca23bac05227ff96629c464f3ecf2ed61f908a6f28145e9b00786fae625e37d751210ccaaf03d613e1683bbfbb15cf009d35e57909ac3110488b34b0f
data/.rubocop.yml CHANGED
@@ -40,6 +40,8 @@ Layout/LineLength:
40
40
  # Ignores annotate output
41
41
  IgnoredPatterns: ['\A# \*\*']
42
42
  IgnoreCopDirectives: true
43
+ Exclude:
44
+ - "**/spec/**/*"
43
45
 
44
46
  Lint/UnusedMethodArgument:
45
47
  AllowUnusedKeywordArguments: true
@@ -61,6 +63,16 @@ Metrics/ClassLength:
61
63
  Metrics/ModuleLength:
62
64
  Exclude:
63
65
  - "**/spec/**/*"
66
+ Metrics/CyclomaticComplexity:
67
+ Exclude:
68
+ - "lib/k_log/log_structure.rb"
69
+ Metrics/PerceivedComplexity:
70
+ Exclude:
71
+ - "lib/k_log/log_structure.rb"
72
+ Metrics/AbcSize:
73
+ Exclude:
74
+ - "lib/k_log/log_structure.rb"
75
+
64
76
  Naming/MemoizedInstanceVariableName:
65
77
  Enabled: false
66
78
  Naming/VariableNumber:
data/Gemfile CHANGED
@@ -2,23 +2,18 @@
2
2
 
3
3
  source 'https://rubygems.org'
4
4
 
5
- # Specify your gem's dependencies in handlebars_helpers.gemspec
6
5
  gemspec
7
6
 
8
- # group :development do
9
- # # Currently conflicts with GitHub actions and so I remove it on push
10
- # # pry on steroids
11
- # gem 'jazz_fingers'
12
- # gem 'pry-coolline', github: 'owst/pry-coolline', branch: 'support_new_pry_config_api'
13
- # end
14
-
15
7
  group :development, :test do
8
+ gem 'dry-struct', '~> 1'
9
+
16
10
  gem 'guard-bundler'
17
11
  gem 'guard-rspec'
18
12
  gem 'guard-rubocop'
19
13
  gem 'rake', '~> 12.0'
20
14
  gem 'rake-compiler', require: false
21
15
  gem 'rspec', '~> 3.0'
16
+ gem 'rspec-collection_matchers'
22
17
  gem 'rubocop'
23
18
  gem 'rubocop-rake', require: false
24
19
  gem 'rubocop-rspec', require: false
@@ -43,6 +43,14 @@ module KLog
43
43
  green(character * size)
44
44
  end
45
45
 
46
+ def self.dynamic_heading(heading, size: 70, type: :heading)
47
+ return heading(heading, size) if type == :heading
48
+ return subheading(heading, size) if type == :subheading
49
+ return [section_heading(heading, size)] if %i[section_heading section].include?(type)
50
+
51
+ []
52
+ end
53
+
46
54
  def self.heading(heading, size = 70)
47
55
  line = line(size)
48
56
 
@@ -69,7 +77,8 @@ module KLog
69
77
  def self.section_heading(heading, size = 70)
70
78
  brace_open = green('[ ')
71
79
  brace_close = green(' ]')
72
- line = line(size - heading.length - 4, '-')
80
+ line_length = size - heading.length - 4
81
+ line = line_length.positive? ? line(line_length, '-') : ''
73
82
 
74
83
  # It is important that you set the colour after you have calculated the size
75
84
  "#{brace_open}#{heading}#{brace_close}#{green(line)}"
@@ -106,8 +115,60 @@ module KLog
106
115
  end
107
116
  # rubocop:enable Metrics/CyclomaticComplexity
108
117
 
118
+ def self.red(value)
119
+ "\033[31m#{value}\033[0m"
120
+ end
121
+
109
122
  def self.green(value)
110
123
  "\033[32m#{value}\033[0m"
111
124
  end
125
+
126
+ def self.yellow(value)
127
+ "\033[33m#{value}\033[0m"
128
+ end
129
+
130
+ def self.blue(value)
131
+ "\033[34m#{value}\033[0m"
132
+ end
133
+
134
+ def self.purple(value)
135
+ "\033[35m#{value}\033[0m"
136
+ end
137
+
138
+ def self.cyan(value)
139
+ "\033[36m#{value}\033[0m"
140
+ end
141
+
142
+ def self.grey(value)
143
+ "\033[37m#{value}\033[0m"
144
+ end
145
+
146
+ def self.bg_red(value)
147
+ "\033[41m#{value}\033[0m"
148
+ end
149
+
150
+ def self.bg_green(value)
151
+ "\033[42m#{value}\033[0m"
152
+ end
153
+
154
+ def self.bg_yellow(value)
155
+ "\033[43m#{value}\033[0m"
156
+ end
157
+
158
+ def self.bg_blue(value)
159
+ "\033[44m#{value}\033[0m"
160
+ end
161
+
162
+ def self.bg_purple(value)
163
+ "\033[45m#{value}\033[0m"
164
+ end
165
+
166
+ def self.bg_cyan(value)
167
+ "\033[46m#{value}\033[0m"
168
+ end
169
+
170
+ def self.bg_grey(value)
171
+ "\033[47m#{value}\033[0m"
172
+ end
112
173
  end
113
174
  end
@@ -5,15 +5,24 @@ require 'k_util'
5
5
  module KLog
6
6
  # Log Structure is flexible logger for working through a complex object graph
7
7
  class LogStructure
8
- attr_accessor :indent
9
- attr_accessor :heading
10
- attr_accessor :heading_type
11
- attr_accessor :line_width
12
- attr_accessor :formatter
13
- attr_accessor :skip_array
14
-
15
- attr_accessor :recursion_depth
16
- attr_accessor :key_format
8
+ attr_reader :indent
9
+ attr_reader :title
10
+ attr_reader :title_type
11
+ attr_reader :heading
12
+ attr_reader :heading_type
13
+ attr_reader :line_width
14
+ attr_reader :graph
15
+ attr_reader :formatter
16
+ attr_reader :convert_data_to
17
+
18
+ attr_reader :recursion_depth
19
+ attr_reader :key_format
20
+ attr_reader :graph_path
21
+ attr_reader :graph_node
22
+
23
+ attr_reader :lines
24
+ attr_reader :output_as
25
+ attr_reader :output_file
17
26
 
18
27
  # Log a structure
19
28
  #
@@ -26,68 +35,116 @@ module KLog
26
35
  # @option opts [String] :formatter is a complex configuration for formatting different data within the structure
27
36
  def initialize(opts)
28
37
  @indent = opts[:indent] || ' '
38
+ @title = opts[:title]
39
+ @title_type = opts[:title_type] || :heading
40
+
29
41
  @heading = opts[:heading]
30
42
  @heading_type = opts[:heading_type] || :heading
31
- @formatter = opts[:formatter] || {}
43
+ puts ':heading should be :title' if opts[:heading]
44
+ puts ':heading_type should be :title_type' if opts[:heading_type]
45
+
46
+ @formatter = opts[:formatter] || {}
47
+ @graph = parse_graph(opts[:graph] || {})
48
+ @convert_data_to = opts[:convert_data_to] || :raw # by default leave data as is
32
49
 
33
- @line_width = opts[:line_width] || 80
34
- # @skip_array = opts[:skip_array]
50
+ @line_width = opts[:line_width] || 80
51
+ @output_as = opts[:output_as] || [:console]
52
+ @output_as = [@output_as] unless @output_as.is_a?(Array)
53
+ @output_file = opts[:output_file]
35
54
 
36
55
  @recursion_depth = 0
37
56
  @key_format = nil
57
+ @graph_path = []
58
+ @lines = []
59
+
38
60
  update_indent_label
39
61
  end
40
62
 
63
+ def l
64
+ @l ||= KLog::LogUtil.new(KLog.logger)
65
+ end
66
+
41
67
  def log(data)
42
- log_heading(heading, heading_type)
68
+ log_heading(title, title_type) if title
69
+
70
+ data = convert_data(data)
43
71
 
44
- open_struct_data = KUtil.data.to_open_struct(data)
72
+ log_data(data)
45
73
 
46
- log_data(open_struct_data)
74
+ add_line(KLog::LogHelper.line(line_width))
47
75
 
48
- KLog.logger.line(line_width)
76
+ render_output
49
77
  end
50
78
 
51
- # Build a sample configuration based on the structure (move to own class)
52
- def build_sample_config(_data)
53
- # open_struct_data = KUtil.data.to_open_struct(data)
54
- {
55
- to: :do
56
- }
79
+ def content
80
+ @content ||= lines.join("\n")
57
81
  end
58
82
 
59
- private
83
+ def clean_content
84
+ # remove color escape codes
85
+ @clean_content ||= content.gsub(/\x1B\[\d*m/, '')
86
+ end
60
87
 
61
- def build_key_format(key)
62
- format_config = @formatter[key]
63
- format_config = @formatter[:_root] if format_config.nil? && @recursion_depth.zero?
64
- @key_format = KeyFormat.new(format_config)
88
+ def clean_lines
89
+ # remove color escape codes
90
+ lines.flat_map { |line| line.gsub(/\x1B\[\d*m/, '').split("\n") }
65
91
  end
66
92
 
93
+ def add_lines(lines)
94
+ @lines += lines
95
+ end
96
+
97
+ def add_line(line)
98
+ @lines << line
99
+ end
100
+
101
+ private
102
+
103
+ # format_config = @formatter[:_root] if format_config.nil? && @recursion_depth.zero?
104
+
67
105
  def log_data(data)
68
- data.each_pair do |key, value|
69
- build_key_format(key)
106
+ data.each_pair do |k, v|
107
+ key = k.is_a?(String) ? k.to_sym : k
108
+
109
+ graph_path.push(key)
110
+ @graph_node = GraphNode.for(graph, graph_path)
111
+ # l.kv 'key', "#{key.to_s.ljust(15)}#{graph_node.skip?.to_s.ljust(6)}#{@recursion_depth}"
112
+
113
+ if graph_node.skip?
114
+ # l.kv 'key', 'skipping...'
115
+ @graph_path.pop
116
+ next
117
+ end
118
+
119
+ value = graph_node.transform? ? graph_node.transform(v) : v
120
+
70
121
  case value
71
122
  when OpenStruct
72
- log_structure(key, value) unless @key_format.ignore?
123
+ # l.kv 'go', 'open struct ->'
124
+ log_structure(key, value)
125
+ # l.kv 'go', 'open struct <-'
73
126
  when Array
74
- log_array(key, value) unless @key_format.ignore?
127
+ # l.kv 'go', 'array ->'
128
+ log_array(key, value)
129
+ # l.kv 'go', 'array <-'
75
130
  else
76
- KLog.logger.kv "#{@indent_label}#{key}", value
131
+ # l.kv 'go', 'value ->'
132
+ log_heading(graph_node.heading, graph_node.heading_type) if graph_node.heading
133
+ add_line KLog::LogHelper.kv "#{@indent_label}#{key}", value
134
+ # l.kv 'go', 'value <-'
77
135
  end
136
+
137
+ # l.line
138
+ # @graph_node = graph.for_path(graph_path)
139
+ # l.line
140
+ @graph_path.pop
78
141
  end
79
142
  nil
80
143
  end
81
144
 
82
145
  def log_structure(key, value)
83
- # This is specifically for k_doc, I should use a configuration instead of this technique
84
- if value['rows'].is_a?(Array)
85
- # KLog.logger.subheading(key)
86
- # opts[:subheading] = key
87
- # opts.delete(:subheading)
88
- else
89
- KLog.logger.kv "#{@indent_label}#{key}", ''
90
- end
146
+ log_heading(graph_node.heading, graph_node.heading_type) if graph_node.heading
147
+ add_line(KLog::LogHelper.green("#{@indent_label}#{key}"))
91
148
  log_child_data(value)
92
149
  end
93
150
 
@@ -97,22 +154,34 @@ module KLog
97
154
  depth_up
98
155
  end
99
156
 
100
- # rubocop:disable Metrics/AbcSize
101
157
  def log_array(key, array)
102
- # next unless opts[:skip_array].nil?
103
- return unless array.length.positive?
158
+ # return if items.length.zero? && graph_node.skip_empty?
104
159
 
105
- log_heading(key_format.heading, key_format.heading_type)
160
+ items = array.clone
161
+ items.select! { |item| graph_node.filter(item) } if graph_node.filter?
162
+ items = items.take(graph_node.take) if graph_node.limited?
163
+ items.sort!(&graph_node.sort) if graph_node.sort?
106
164
 
107
- filter_items = key_format.take_all ? array : array.take(key_format.take)
165
+ return if items.length.zero? && graph_node.skip_empty?
108
166
 
109
- if primitive?(filter_items)
110
- KLog.logger.kv "#{indent}#{key}", filter_items.map(&:to_s).join(', ')
167
+ log_heading(graph_node.heading, graph_node.heading_type) if graph_node.heading
168
+
169
+ if primitive?(items)
170
+ add_line KLog::LogHelper.kv "#{@indent_label}#{key}", items.map(&:to_s).join(', ')
111
171
  else
112
- tp filter_items, tp_columns(filter_items)
172
+ table_print items, tp_columns(items)
113
173
  end
174
+ rescue StandardError
175
+ #
176
+ end
177
+
178
+ def table_print(items, columns)
179
+ io = TablePrintIo.new(self)
180
+
181
+ tp.set :io, io
182
+ tp items, columns
183
+ tp.clear :io
114
184
  end
115
- # rubocop:enable Metrics/AbcSize
116
185
 
117
186
  def primitive?(items)
118
187
  item = items.first
@@ -120,14 +189,12 @@ module KLog
120
189
  end
121
190
 
122
191
  def log_heading(heading, heading_type)
123
- return unless heading
124
-
125
- KLog.logger.dynamic_heading(heading, size: line_width, type: heading_type)
192
+ add_lines(KLog::LogHelper.dynamic_heading(heading, size: line_width, type: heading_type))
126
193
  end
127
194
 
128
195
  def tp_columns(items)
129
196
  # Use configured array columns
130
- return key_format.array_columns if key_format.array_columns
197
+ return graph_node.columns if graph_node.columns
131
198
 
132
199
  # Slow but complete list of keys
133
200
  # items.flat_map { |v| v.to_h.keys }.uniq
@@ -136,6 +203,7 @@ module KLog
136
203
  end
137
204
 
138
205
  def update_indent_label
206
+ # puts "indent_label: #{indent} - #{@recursion_depth} - #{(indent * @recursion_depth)}"
139
207
  @indent_label = (indent * @recursion_depth)
140
208
  end
141
209
 
@@ -153,10 +221,55 @@ module KLog
153
221
  end
154
222
 
155
223
  def depth_up
156
- update_indent_label
157
224
  @recursion_depth = recursion_depth - 1
225
+ update_indent_label
226
+ end
227
+
228
+ def render_output
229
+ puts content if output_as.include?(:console)
230
+ File.write(output_file, clean_content) if output_as.include?(:file) && output_file
158
231
  end
159
232
 
233
+ # convert_data_to: :open_struct
234
+ def convert_data(data)
235
+ return KUtil.data.to_open_struct(data) if convert_data_to == :open_struct
236
+
237
+ data
238
+ end
239
+
240
+ def parse_graph(data)
241
+ if data.is_a?(Hash)
242
+ transform_hash = data.each_with_object({}) do |(key, value), new_hash|
243
+ new_hash[key] = if key == :columns && value.is_a?(Array)
244
+ # Don't transform the table_print GEM columns definition as it must stay as a hash
245
+ value
246
+ else
247
+ parse_graph(value)
248
+ end
249
+ end
250
+
251
+ return OpenStruct.new(transform_hash.to_h)
252
+ end
253
+
254
+ return data.map { |o| parse_graph(o) } if data.is_a?(Array)
255
+ return parse_graph(data.to_h) if data.respond_to?(:to_h) # hash_convertible?(data)
256
+
257
+ # Some primitave type: String, True/False or an ObjectStruct
258
+ data
259
+ end
260
+
261
+ # def hash_convertible?(value)
262
+ # # Nil is a special case, it responds to :to_h but generally
263
+ # # you only want to convert nil to {} in specific scenarios
264
+ # return false if value.nil?
265
+
266
+ # value.is_a?(Array) ||
267
+ # value.is_a?(Hash) ||
268
+ # value.is_a?(Struct) ||
269
+ # value.is_a?(OpenStruct) ||
270
+ # value.respond_to?(:to_h)
271
+ # end
272
+
160
273
  # Format configuration for a specific key
161
274
  #
162
275
  # @example Example configuration for key: tables
@@ -165,7 +278,7 @@ module KLog
165
278
  # tables: {
166
279
  # heading: 'Database Tables',
167
280
  # take: :all,
168
- # array_columns: [
281
+ # columns: [
169
282
  # :name,
170
283
  # :force,
171
284
  # :primary_key,
@@ -178,36 +291,108 @@ module KLog
178
291
  # }
179
292
  # }
180
293
  #
181
- # format = KeyFormat.new(configuration[:tables])
182
- class KeyFormat
294
+
295
+ # Override table_print IO stream so that it writes into the structure
296
+ class TablePrintIo
297
+ def initialize(log_structure)
298
+ @log_structure = log_structure
299
+ end
300
+
301
+ def puts(line)
302
+ @log_structure.add_line(line)
303
+ end
304
+ end
305
+
306
+ class GraphNode
183
307
  attr_accessor :config
184
308
 
309
+ class << self
310
+ def null
311
+ @null ||= OpenStruct.new
312
+ end
313
+
314
+ def for(graph, graph_path)
315
+ # node_config = graph_path.inject(graph, :send) # (uses deep nesting, but fails when nil is returned) https://stackoverflow.com/questions/15862455/ruby-nested-send
316
+ # node.nil? ? null : node.send(name) || null
317
+ node_config = graph_path.reduce(graph) do |node, name|
318
+ result = node.send(name)
319
+
320
+ break null if result.nil?
321
+
322
+ result
323
+ end
324
+
325
+ new(node_config)
326
+ end
327
+ end
328
+
185
329
  def initialize(config)
186
- @config = OpenStruct.new(config)
330
+ @config = config || OpenStruct.new
187
331
  end
188
332
 
189
- def array_columns
190
- config.array_columns
333
+ # table_print compatible configuration for displaying columns for an array
334
+ def columns
335
+ config.columns
191
336
  end
192
337
 
338
+ # Optional heading for the node
193
339
  def heading
194
340
  config.heading
195
341
  end
196
342
 
343
+ # Type of heading [:heading, :subheading, :section]
197
344
  def heading_type
198
- config.heading_type || :section_heading
345
+ config.heading_type || :section
346
+ end
347
+
348
+ # Node data is to be transformed
349
+ def transform?
350
+ config&.transform.respond_to?(:call)
351
+ end
352
+
353
+ # Transform node value
354
+ def transform(value)
355
+ config.transform.call(value)
356
+ end
357
+
358
+ # Array rows are filtered
359
+ def filter?
360
+ config&.filter.respond_to?(:call)
361
+ end
362
+
363
+ # Array rows are filtered via this predicate
364
+ def filter(value)
365
+ config.filter.call(value)
199
366
  end
200
367
 
368
+ # How any array rows to take
201
369
  def take
202
370
  config.take
203
371
  end
204
372
 
205
- def take_all
206
- config.take.nil? || config.take == :all
373
+ # Array rows are limited, see take
374
+ def limited?
375
+ config.take&.is_a?(Integer)
376
+ end
377
+
378
+ # Array rows are sorted using .sort
379
+ def sort?
380
+ config&.sort.respond_to?(:call)
381
+ end
382
+
383
+ # Use array.sort?
384
+ def sort
385
+ config.sort
386
+ end
387
+
388
+ # Skip this node
389
+ def skip?
390
+ config.skip == true
207
391
  end
208
392
 
209
- def ignore?
210
- config.ignore == true
393
+ # Skip empty array node (my be useful for other nodes, but not yet)
394
+ def skip_empty?
395
+ config.skip_empty == true
211
396
  end
212
397
  end
213
398
  end
@@ -75,7 +75,7 @@ module KLog
75
75
  def dynamic_heading(heading, size: 70, type: :heading)
76
76
  KLog.logger.heading(heading, size) if type == :heading
77
77
  KLog.logger.subheading(heading, size) if type == :subheading
78
- KLog.logger.section_heading(heading, size) if type == :section_heading
78
+ KLog.logger.section_heading(heading, size) if %i[section_heading section].include?(type)
79
79
  end
80
80
 
81
81
  def heading(heading, size = 70)
@@ -198,8 +198,9 @@ module KLog
198
198
  def exception(exception)
199
199
  line
200
200
 
201
- @logger.info(exception.message)
202
- @logger.info(exception.backtrace.join("\n"))
201
+ @logger.info(KLog::LogHelper.bg_red(exception.message))
202
+
203
+ @logger.info(KLog::LogHelper.yellow(exception.backtrace.map { |row| row.start_with?(Dir.pwd) ? KLog::LogHelper.yellow(row) : KLog::LogHelper.red(row) }.join("\n")))
203
204
 
204
205
  line
205
206
  end
data/lib/k_log/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module KLog
4
- VERSION = '0.0.20'
4
+ VERSION = '0.0.27'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: k_log
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.20
4
+ version: 0.0.27
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Cruwys
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-08-08 00:00:00.000000000 Z
11
+ date: 2021-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: k_util