k_log 0.0.20 → 0.0.27

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.
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