dev_suite 0.2.11 → 0.2.13
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.codeclimate.yml +49 -7
- data/Gemfile +4 -0
- data/Gemfile.lock +14 -1
- data/README.md +4 -0
- data/lib/dev_suite/method_tracer/logger.rb +50 -24
- data/lib/dev_suite/method_tracer/tracer.rb +14 -6
- data/lib/dev_suite/utils/color/colorizer.rb +1 -1
- data/lib/dev_suite/utils/color/palette/default.rb +33 -0
- data/lib/dev_suite/utils/data/path_access/data_traverser.rb +86 -0
- data/lib/dev_suite/utils/data/path_access/errors.rb +12 -0
- data/lib/dev_suite/utils/data/path_access/key_handler.rb +27 -0
- data/lib/dev_suite/utils/data/path_access/path_accessor.rb +36 -0
- data/lib/dev_suite/utils/data/path_access/path_parser.rb +35 -0
- data/lib/dev_suite/utils/data/path_access.rb +5 -151
- data/lib/dev_suite/utils/file_writer/file_writer.rb +14 -4
- data/lib/dev_suite/utils/file_writer/writer/base.rb +40 -15
- data/lib/dev_suite/utils/file_writer/writer/json.rb +1 -1
- data/lib/dev_suite/utils/file_writer/writer/text.rb +11 -9
- data/lib/dev_suite/utils/file_writer/writer/yaml.rb +1 -1
- data/lib/dev_suite/utils/file_writer/writer_manager.rb +40 -17
- data/lib/dev_suite/utils/logger/base.rb +54 -0
- data/lib/dev_suite/utils/logger/emoji.rb +17 -0
- data/lib/dev_suite/utils/logger/errors.rb +9 -0
- data/lib/dev_suite/utils/logger/formatter.rb +41 -0
- data/lib/dev_suite/utils/logger/logger.rb +29 -0
- data/lib/dev_suite/utils/logger.rb +1 -52
- data/lib/dev_suite/utils/table/renderer/simple.rb +5 -5
- data/lib/dev_suite/version.rb +1 -1
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c4f76e1bde363b53c6e0e76616ce4589f37e17af746b6e1d77019aa0d2c58d9
|
4
|
+
data.tar.gz: b93fd9c938777e4c5c366b34b761b38d5e3bfb38231b345b125f41f5655576ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8c153fb7c5fa8f73a03e1ae33cc8fbc71b825150c557dad9d0f2bc9e2a1b3e8054b7d334b02bfe51cb4e72c10d13732bbb0247be3c5235ece0c639338aa370e
|
7
|
+
data.tar.gz: f42462a700acc328deca1071c7a9cccd63c93eba44928c3510707afe4173b7ea6c478328103d47abfe30ce86d2e7430d120ecc5c3a46fa95f3e9355d5b27c377
|
data/.codeclimate.yml
CHANGED
@@ -1,12 +1,54 @@
|
|
1
1
|
version: "2"
|
2
2
|
checks:
|
3
|
-
|
3
|
+
argument-count:
|
4
4
|
enabled: true
|
5
|
-
|
5
|
+
complexity:
|
6
6
|
enabled: true
|
7
|
-
|
8
|
-
|
9
|
-
|
7
|
+
duplication:
|
8
|
+
enabled: true
|
9
|
+
file-lines:
|
10
|
+
enabled: true
|
11
|
+
method-lines:
|
12
|
+
enabled: true
|
13
|
+
return-statements:
|
14
|
+
enabled: true
|
15
|
+
similar-code:
|
16
|
+
enabled: true
|
17
|
+
npath-complexity:
|
18
|
+
enabled: true
|
19
|
+
identical-code:
|
20
|
+
enabled: true
|
21
|
+
|
22
|
+
engines:
|
23
|
+
rubocop:
|
24
|
+
enabled: true
|
25
|
+
channel: rubocop-1-48-1
|
26
|
+
config:
|
27
|
+
file: .rubocop.yml
|
28
|
+
bundler-audit:
|
29
|
+
enabled: true
|
30
|
+
channel: bundler-audit-0-9-1
|
31
|
+
duplication:
|
32
|
+
enabled: true
|
33
|
+
config:
|
34
|
+
languages:
|
35
|
+
ruby:
|
36
|
+
mass_threshold: 50
|
37
|
+
paths:
|
38
|
+
- "lib/**/*.rb"
|
39
|
+
fixme:
|
40
|
+
enabled: false
|
41
|
+
config:
|
42
|
+
strings:
|
43
|
+
- "TODO"
|
44
|
+
- "FIXME"
|
45
|
+
|
10
46
|
exclude_patterns:
|
11
|
-
- "
|
12
|
-
- "
|
47
|
+
- "spec/"
|
48
|
+
- "test/"
|
49
|
+
- "vendor/"
|
50
|
+
- "bin/"
|
51
|
+
- "log/"
|
52
|
+
- "tmp/"
|
53
|
+
- "coverage/"
|
54
|
+
- "examples/"
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
dev_suite (0.2.
|
4
|
+
dev_suite (0.2.13)
|
5
5
|
benchmark (~> 0.1)
|
6
6
|
csv (~> 3.0)
|
7
7
|
get_process_mem (~> 1.0)
|
@@ -10,11 +10,16 @@ PATH
|
|
10
10
|
GEM
|
11
11
|
remote: https://rubygems.org/
|
12
12
|
specs:
|
13
|
+
addressable (2.8.7)
|
14
|
+
public_suffix (>= 2.0.2, < 7.0)
|
13
15
|
ast (2.4.2)
|
14
16
|
benchmark (0.3.0)
|
15
17
|
bigdecimal (3.1.8)
|
16
18
|
coderay (1.1.3)
|
17
19
|
concurrent-ruby (1.3.4)
|
20
|
+
crack (1.0.0)
|
21
|
+
bigdecimal
|
22
|
+
rexml
|
18
23
|
csv (3.3.0)
|
19
24
|
diff-lcs (1.5.1)
|
20
25
|
docile (1.4.1)
|
@@ -47,6 +52,7 @@ GEM
|
|
47
52
|
get_process_mem (1.0.0)
|
48
53
|
bigdecimal (>= 2.0)
|
49
54
|
ffi (~> 1.0)
|
55
|
+
hashdiff (1.1.1)
|
50
56
|
i18n (1.14.5)
|
51
57
|
concurrent-ruby (~> 1.0)
|
52
58
|
json (2.7.2)
|
@@ -60,10 +66,12 @@ GEM
|
|
60
66
|
pry (0.14.2)
|
61
67
|
coderay (~> 1.1)
|
62
68
|
method_source (~> 1.0)
|
69
|
+
public_suffix (6.0.1)
|
63
70
|
racc (1.8.1)
|
64
71
|
rainbow (3.1.1)
|
65
72
|
rake (13.2.1)
|
66
73
|
regexp_parser (2.9.2)
|
74
|
+
rexml (3.3.8)
|
67
75
|
rspec (3.13.0)
|
68
76
|
rspec-core (~> 3.13.0)
|
69
77
|
rspec-expectations (~> 3.13.0)
|
@@ -101,6 +109,10 @@ GEM
|
|
101
109
|
simplecov_json_formatter (0.1.4)
|
102
110
|
thor (1.3.2)
|
103
111
|
unicode-display_width (2.5.0)
|
112
|
+
webmock (3.24.0)
|
113
|
+
addressable (>= 2.8.0)
|
114
|
+
crack (>= 0.3.2)
|
115
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
104
116
|
|
105
117
|
PLATFORMS
|
106
118
|
arm64-darwin-23
|
@@ -115,6 +127,7 @@ DEPENDENCIES
|
|
115
127
|
rubocop (~> 1.65)
|
116
128
|
rubocop-shopify (~> 2.15)
|
117
129
|
simplecov (~> 0.21)
|
130
|
+
webmock (~> 3.13)
|
118
131
|
|
119
132
|
BUNDLED WITH
|
120
133
|
2.5.17
|
data/README.md
CHANGED
@@ -428,6 +428,10 @@ Trace all method calls within a specific block of code, including optional loggi
|
|
428
428
|
🏁 #depth:1 < MathOperations#greet #=> "Hello, Ruby!" at (irb):12 in 0.02ms
|
429
429
|
Hello, Ruby!
|
430
430
|
```
|
431
|
+
|
432
|
+
**Tips**:
|
433
|
+
|
434
|
+
Use `max_depth` to Limit Tracing: If you have deeply nested method calls, use the `max_depth` option to limit the depth of tracing. This can help reduce the amount of log output and focus on the most relevant parts of your code. A recommended value for `max_depth` is between `2` and `5`, depending on the complexity of your code.
|
431
435
|
</details>
|
432
436
|
|
433
437
|
## Development
|
@@ -5,40 +5,66 @@ module DevSuite
|
|
5
5
|
module Logger
|
6
6
|
class << self
|
7
7
|
def log_method_call(tp, tracer)
|
8
|
-
tracer.depth += 1
|
9
|
-
|
10
|
-
method_name = Helpers.format_method_name(tp)
|
11
|
-
params_str = Helpers.format_params(tp) if tracer.show_params
|
12
|
-
indent = Helpers.calculate_indent(tracer.depth)
|
13
|
-
|
14
8
|
# Store the start time for execution time calculation
|
15
9
|
tracer.start_times[tp.method_id] = Time.now
|
16
10
|
|
17
|
-
message =
|
18
|
-
Utils::Logger.log(
|
19
|
-
message,
|
20
|
-
emoji: :start,
|
21
|
-
color: :blue,
|
22
|
-
)
|
11
|
+
message = build_call_message(tp, tracer)
|
12
|
+
Utils::Logger.log(message, emoji: :start, color: :blue)
|
23
13
|
end
|
24
14
|
|
25
15
|
def log_method_return(tp, tracer)
|
26
16
|
duration = Helpers.calculate_duration(tp, tracer.start_times)
|
27
17
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
18
|
+
message = build_return_message(tp, tracer, duration)
|
19
|
+
Utils::Logger.log(message, emoji: :finish, color: :cyan)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
# Builds the log message for method calls
|
25
|
+
#
|
26
|
+
# @param tp [TracePoint] The TracePoint object
|
27
|
+
# @param tracer [Tracer] The tracer instance
|
28
|
+
# @return [String] The formatted log message
|
29
|
+
def build_call_message(tp, tracer)
|
30
|
+
method_name = colorize_text(Helpers.format_method_name(tp), :blue)
|
31
|
+
params_str = tracer.show_params ? colorize_text(Helpers.format_params(tp), :yellow) : ""
|
32
|
+
indent = Helpers.calculate_indent(tracer.current_depth)
|
33
|
+
depth_str = colorize_text("#depth:#{tracer.current_depth}", :magenta)
|
32
34
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
35
|
+
"#{indent}#{depth_str} > #{method_name} at #{tp.path}:#{tp.lineno} #{params_str}"
|
36
|
+
end
|
37
|
+
|
38
|
+
# Builds the log message for method returns
|
39
|
+
#
|
40
|
+
# @param tp [TracePoint] The TracePoint object
|
41
|
+
# @param tracer [Tracer] The tracer instance
|
42
|
+
# @param duration [Float] The execution time
|
43
|
+
# @return [String] The formatted log message
|
44
|
+
def build_return_message(tp, tracer, duration)
|
45
|
+
method_name = colorize_text(Helpers.format_method_name(tp), :cyan)
|
46
|
+
result_str = tracer.show_results ? colorize_text(Helpers.format_result(tp), :green) : ""
|
47
|
+
exec_time_str = if tracer.show_execution_time
|
48
|
+
colorize_text(
|
49
|
+
Helpers.format_execution_time(duration),
|
50
|
+
:light_white,
|
51
|
+
)
|
52
|
+
else
|
53
|
+
""
|
54
|
+
end
|
55
|
+
indent = Helpers.calculate_indent(tracer.current_depth)
|
56
|
+
depth_str = colorize_text("#depth:#{tracer.current_depth}", :magenta)
|
57
|
+
|
58
|
+
"#{indent}#{depth_str} < #{method_name}#{result_str} at #{tp.path}:#{tp.lineno}#{exec_time_str}"
|
59
|
+
end
|
39
60
|
|
40
|
-
|
41
|
-
|
61
|
+
# Helper method to colorize text
|
62
|
+
#
|
63
|
+
# @param text [String] The text to colorize
|
64
|
+
# @param color [Symbol] The color to use
|
65
|
+
# @return [String] The colorized text
|
66
|
+
def colorize_text(text, color)
|
67
|
+
Utils::Color.colorize(text, color: color)
|
42
68
|
end
|
43
69
|
end
|
44
70
|
end
|
@@ -4,7 +4,7 @@ module DevSuite
|
|
4
4
|
module MethodTracer
|
5
5
|
class Tracer
|
6
6
|
attr_reader :show_params, :show_results, :show_execution_time, :max_depth
|
7
|
-
attr_accessor :
|
7
|
+
attr_accessor :current_depth, :trace_point, :start_times
|
8
8
|
|
9
9
|
def initialize(
|
10
10
|
show_params: false,
|
@@ -16,8 +16,8 @@ module DevSuite
|
|
16
16
|
@show_results = show_results
|
17
17
|
@show_execution_time = show_execution_time
|
18
18
|
@max_depth = max_depth
|
19
|
-
@depth = 0
|
20
19
|
@start_times = {}
|
20
|
+
@current_depth = 0
|
21
21
|
end
|
22
22
|
|
23
23
|
def trace(&block)
|
@@ -50,15 +50,23 @@ module DevSuite
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def handle_call_event(tp)
|
53
|
-
|
53
|
+
@current_depth += 1
|
54
|
+
|
55
|
+
if current_depth_within_limit?
|
56
|
+
Logger.log_method_call(tp, self)
|
57
|
+
end
|
54
58
|
end
|
55
59
|
|
56
60
|
def handle_return_event(tp)
|
57
|
-
|
61
|
+
if current_depth_within_limit?
|
62
|
+
Logger.log_method_return(tp, self)
|
63
|
+
end
|
64
|
+
|
65
|
+
@current_depth -= 1
|
58
66
|
end
|
59
67
|
|
60
|
-
def
|
61
|
-
max_depth.nil? ||
|
68
|
+
def current_depth_within_limit?
|
69
|
+
max_depth.nil? || current_depth <= max_depth
|
62
70
|
end
|
63
71
|
end
|
64
72
|
end
|
@@ -6,6 +6,8 @@ module DevSuite
|
|
6
6
|
module Palette
|
7
7
|
class Default < Base
|
8
8
|
COLORS = {
|
9
|
+
# Standard Colors
|
10
|
+
black: 30,
|
9
11
|
red: 31,
|
10
12
|
green: 32,
|
11
13
|
yellow: 33,
|
@@ -14,6 +16,37 @@ module DevSuite
|
|
14
16
|
cyan: 36,
|
15
17
|
white: 37,
|
16
18
|
default: 39,
|
19
|
+
|
20
|
+
# Light Colors
|
21
|
+
light_black: 90,
|
22
|
+
light_red: 91,
|
23
|
+
light_green: 92,
|
24
|
+
light_yellow: 93,
|
25
|
+
light_blue: 94,
|
26
|
+
light_magenta: 95,
|
27
|
+
light_cyan: 96,
|
28
|
+
light_white: 97,
|
29
|
+
|
30
|
+
# Background Colors
|
31
|
+
bg_black: 40,
|
32
|
+
bg_red: 41,
|
33
|
+
bg_green: 42,
|
34
|
+
bg_yellow: 43,
|
35
|
+
bg_blue: 44,
|
36
|
+
bg_magenta: 45,
|
37
|
+
bg_cyan: 46,
|
38
|
+
bg_white: 47,
|
39
|
+
bg_default: 49,
|
40
|
+
|
41
|
+
# Light Background Colors
|
42
|
+
bg_light_black: 100,
|
43
|
+
bg_light_red: 101,
|
44
|
+
bg_light_green: 102,
|
45
|
+
bg_light_yellow: 103,
|
46
|
+
bg_light_blue: 104,
|
47
|
+
bg_light_magenta: 105,
|
48
|
+
bg_light_cyan: 106,
|
49
|
+
bg_light_white: 107,
|
17
50
|
}.freeze
|
18
51
|
end
|
19
52
|
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "key_handler"
|
4
|
+
|
5
|
+
module DevSuite
|
6
|
+
module Utils
|
7
|
+
module Data
|
8
|
+
module PathAccess
|
9
|
+
module DataTraverser
|
10
|
+
extend self
|
11
|
+
|
12
|
+
# Traverse nested data for retrieving a value
|
13
|
+
def fetch(data, keys)
|
14
|
+
keys.reduce(data) { |current, key| traverse_data(current, key) }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Traverse nested data for setting a value
|
18
|
+
def assign(data, keys, value)
|
19
|
+
last_key = keys.pop
|
20
|
+
target = keys.reduce(data) { |current, key| traverse_or_create(current, key) }
|
21
|
+
set_final_value(target, last_key, value)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Traverse nested data for deleting a value
|
25
|
+
def remove(data, keys)
|
26
|
+
last_key = keys.pop
|
27
|
+
target = keys.reduce(data) { |current, key| traverse_data(current, key) }
|
28
|
+
delete_final_value(target, last_key)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
# Traverse through hash or array data types
|
34
|
+
def traverse_data(current, key)
|
35
|
+
KeyHandler.validate_path(current, key)
|
36
|
+
case current
|
37
|
+
when Hash
|
38
|
+
current[KeyHandler.find_key(current, key)]
|
39
|
+
when Array
|
40
|
+
current[key.to_i]
|
41
|
+
else
|
42
|
+
raise DataStructureError, "Unexpected data type at '#{key}'"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Traverse or create new hash elements when setting a value
|
47
|
+
def traverse_or_create(current, key)
|
48
|
+
KeyHandler.validate_path(current, key)
|
49
|
+
case current
|
50
|
+
when Hash
|
51
|
+
current[KeyHandler.find_key(current, key)] ||= {}
|
52
|
+
when Array
|
53
|
+
current[key.to_i] ||= {}
|
54
|
+
else
|
55
|
+
raise DataStructureError, "Unexpected data type at '#{key}'"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Set the final value in hash or array
|
60
|
+
def set_final_value(target, key, value)
|
61
|
+
case target
|
62
|
+
when Hash
|
63
|
+
target[KeyHandler.find_key(target, key)] = value
|
64
|
+
when Array
|
65
|
+
target[key] = value if key.is_a?(Integer)
|
66
|
+
else
|
67
|
+
raise DataStructureError, "Cannot set value on unsupported data type"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Delete the final value from hash or array
|
72
|
+
def delete_final_value(target, key)
|
73
|
+
case target
|
74
|
+
when Hash
|
75
|
+
target.delete(KeyHandler.find_key(target, key))
|
76
|
+
when Array
|
77
|
+
target.delete_at(key) if key.is_a?(Integer)
|
78
|
+
else
|
79
|
+
raise DataStructureError, "Cannot delete key from unsupported data type"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevSuite
|
4
|
+
module Utils
|
5
|
+
module Data
|
6
|
+
module PathAccess
|
7
|
+
module KeyHandler
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# Find an existing key in a hash, handling both symbol and string keys
|
11
|
+
def find_key(hash, key)
|
12
|
+
return key if hash.key?(key)
|
13
|
+
return key.to_s if hash.key?(key.to_s)
|
14
|
+
return key.to_sym if hash.key?(key.to_sym)
|
15
|
+
|
16
|
+
key
|
17
|
+
end
|
18
|
+
|
19
|
+
# Validate the path by checking if it's pointing to a valid data structure
|
20
|
+
def validate_path(data, key)
|
21
|
+
raise InvalidPathError, "Invalid path at '#{key}'" if data.nil?
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "path_parser"
|
4
|
+
require_relative "data_traverser"
|
5
|
+
require_relative "key_handler"
|
6
|
+
require_relative "errors"
|
7
|
+
|
8
|
+
module DevSuite
|
9
|
+
module Utils
|
10
|
+
module Data
|
11
|
+
module PathAccess
|
12
|
+
module PathAccessor
|
13
|
+
extend self
|
14
|
+
|
15
|
+
# Get value from nested data
|
16
|
+
def get(data, path)
|
17
|
+
keys = PathParser.parse(path)
|
18
|
+
DataTraverser.fetch(data, keys)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Set value in nested data
|
22
|
+
def set(data, path, value)
|
23
|
+
keys = PathParser.parse(path)
|
24
|
+
DataTraverser.assign(data, keys, value)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Delete key in nested data
|
28
|
+
def delete(data, path)
|
29
|
+
keys = PathParser.parse(path)
|
30
|
+
DataTraverser.remove(data, keys)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevSuite
|
4
|
+
module Utils
|
5
|
+
module Data
|
6
|
+
module PathAccess
|
7
|
+
module PathParser
|
8
|
+
extend self
|
9
|
+
|
10
|
+
def parse(path)
|
11
|
+
return path if path.is_a?(Array)
|
12
|
+
return parse_symbol_path(path) if path.is_a?(Symbol)
|
13
|
+
return parse_string_path(path) if path.is_a?(String)
|
14
|
+
|
15
|
+
[path]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse_symbol_path(symbol_path)
|
21
|
+
symbol_path.to_s.split(/\.|\[|\]/).reject(&:empty?).map { |part| parse_part(part) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def parse_string_path(string_path)
|
25
|
+
string_path.split(/\.|\[|\]/).reject(&:empty?).map { |part| parse_part(part) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_part(part)
|
29
|
+
part.match?(/^\d+$/) ? part.to_i : part.to_sym
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -4,167 +4,21 @@ module DevSuite
|
|
4
4
|
module Utils
|
5
5
|
module Data
|
6
6
|
module PathAccess
|
7
|
+
require_relative "path_access/path_accessor"
|
8
|
+
|
7
9
|
# Fetch value from a nested structure using a path
|
8
10
|
def get_value_by_path(data, path)
|
9
|
-
|
10
|
-
traverse_path_for_get(data, keys)
|
11
|
+
PathAccessor.get(data, path)
|
11
12
|
end
|
12
13
|
|
13
14
|
# Set value in a nested structure using a path
|
14
15
|
def set_value_by_path(data, path, value)
|
15
|
-
|
16
|
-
last_key = keys.pop
|
17
|
-
target = traverse_path_for_set(data, keys)
|
18
|
-
|
19
|
-
set_final_value(target, last_key, value)
|
16
|
+
PathAccessor.set(data, path, value)
|
20
17
|
end
|
21
18
|
|
22
19
|
# Delete a key from a nested structure using a path
|
23
20
|
def delete_key_by_path(data, path)
|
24
|
-
|
25
|
-
last_key = keys.pop
|
26
|
-
target = traverse_path_for_delete(data, keys)
|
27
|
-
|
28
|
-
delete_final_key(target, last_key)
|
29
|
-
end
|
30
|
-
|
31
|
-
private
|
32
|
-
|
33
|
-
# Parse the path into an array of keys/symbols/integers
|
34
|
-
def parse_path(path)
|
35
|
-
return path if path.is_a?(Array)
|
36
|
-
return [path] if single_symbol_path?(path)
|
37
|
-
|
38
|
-
parse_symbol_or_string_path(path)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Check if the path is a symbol without dots
|
42
|
-
def single_symbol_path?(path)
|
43
|
-
path.is_a?(Symbol) && !path.to_s.include?(".")
|
44
|
-
end
|
45
|
-
|
46
|
-
# Parse a symbol or string path into an array of keys
|
47
|
-
def parse_symbol_or_string_path(path)
|
48
|
-
if path.is_a?(Symbol)
|
49
|
-
parse_symbol_path(path)
|
50
|
-
else
|
51
|
-
parse_string_path(path)
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# Parse a symbol path that contains dots (e.g., :"test.1")
|
56
|
-
def parse_symbol_path(path)
|
57
|
-
path.to_s.split(".").map { |part| parse_part(part) }
|
58
|
-
end
|
59
|
-
|
60
|
-
# Parse a string path into keys (e.g., 'users.1.name')
|
61
|
-
def parse_string_path(path)
|
62
|
-
path.to_s.split(/\.|\[|\]/).reject(&:empty?).map { |part| parse_part(part) }
|
63
|
-
end
|
64
|
-
|
65
|
-
# Parse each part into either a symbol or integer
|
66
|
-
def parse_part(part)
|
67
|
-
part.match?(/^\d+$/) ? part.to_i : part.to_sym
|
68
|
-
end
|
69
|
-
|
70
|
-
# Helper to traverse the path for getting values
|
71
|
-
def traverse_path_for_get(data, keys)
|
72
|
-
keys.reduce(data) do |current_data, key|
|
73
|
-
check_invalid_path(current_data, key)
|
74
|
-
|
75
|
-
case current_data
|
76
|
-
when Hash
|
77
|
-
fetch_from_hash(current_data, key)
|
78
|
-
when Array
|
79
|
-
fetch_from_array(current_data, key)
|
80
|
-
else
|
81
|
-
raise KeyError, "Invalid data type at '#{key}'"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# Helper to traverse the path for setting values
|
87
|
-
def traverse_path_for_set(data, keys)
|
88
|
-
keys.reduce(data) do |current_data, key|
|
89
|
-
check_invalid_path(current_data, key)
|
90
|
-
|
91
|
-
case current_data
|
92
|
-
when Hash
|
93
|
-
current_data[find_existing_key(current_data, key)] ||= {}
|
94
|
-
when Array
|
95
|
-
current_data[key.to_i] ||= {}
|
96
|
-
else
|
97
|
-
break nil
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# Helper to check for invalid paths
|
103
|
-
def check_invalid_path(data, key)
|
104
|
-
raise KeyError, "Invalid path at '#{key}'" if data.nil?
|
105
|
-
end
|
106
|
-
|
107
|
-
# Fetch value from a hash, trying both symbol and string keys
|
108
|
-
def fetch_from_hash(hash, key)
|
109
|
-
hash[key.to_sym] || hash[key.to_s]
|
110
|
-
end
|
111
|
-
|
112
|
-
# Fetch value from an array if the key is an integer
|
113
|
-
def fetch_from_array(array, key)
|
114
|
-
key.is_a?(Integer) ? array[key] : nil
|
115
|
-
end
|
116
|
-
|
117
|
-
# Set the final value in a hash or array
|
118
|
-
def set_final_value(target, last_key, value)
|
119
|
-
case target
|
120
|
-
when Hash
|
121
|
-
target[find_existing_key(target, last_key)] = value
|
122
|
-
when Array
|
123
|
-
if last_key.is_a?(Integer)
|
124
|
-
target[last_key] = value
|
125
|
-
else
|
126
|
-
raise KeyError, "Invalid path or type at '#{last_key}'"
|
127
|
-
end
|
128
|
-
else
|
129
|
-
raise KeyError, "Invalid target type for path at '#{last_key}'"
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
# Check if the key already exists and return the key in its original type (symbol or string)
|
134
|
-
def find_existing_key(hash, key)
|
135
|
-
return key if hash.key?(key) # Key already exists in original form
|
136
|
-
return key.to_s if hash.key?(key.to_s) # Exists as a string
|
137
|
-
return key.to_sym if hash.key?(key.to_sym) # Exists as a symbol
|
138
|
-
|
139
|
-
key # Otherwise, return the key as-is (use the incoming type)
|
140
|
-
end
|
141
|
-
|
142
|
-
# Helper to traverse the path for deletion
|
143
|
-
def traverse_path_for_delete(data, keys)
|
144
|
-
keys.reduce(data) do |current_data, key|
|
145
|
-
check_invalid_path(current_data, key)
|
146
|
-
|
147
|
-
case current_data
|
148
|
-
when Hash
|
149
|
-
current_data[find_existing_key(current_data, key)]
|
150
|
-
when Array
|
151
|
-
current_data[key.to_i]
|
152
|
-
else
|
153
|
-
raise KeyError, "Invalid data type at '#{key}'"
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
# Helper to delete the final key in a hash or array
|
159
|
-
def delete_final_key(target, last_key)
|
160
|
-
case target
|
161
|
-
when Hash
|
162
|
-
target.delete(find_existing_key(target, last_key))
|
163
|
-
when Array
|
164
|
-
target.delete_at(last_key.to_i) if last_key.is_a?(Integer)
|
165
|
-
else
|
166
|
-
raise KeyError, "Cannot delete key from unsupported data type"
|
167
|
-
end
|
21
|
+
PathAccessor.delete(data, path)
|
168
22
|
end
|
169
23
|
end
|
170
24
|
end
|
@@ -10,13 +10,23 @@ module DevSuite
|
|
10
10
|
|
11
11
|
class << self
|
12
12
|
def write(path, content)
|
13
|
-
|
14
|
-
|
13
|
+
WriterManager.write(path, content)
|
14
|
+
end
|
15
|
+
|
16
|
+
def append(path, content)
|
17
|
+
WriterManager.append(path, content)
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete_lines(path, start_line, end_line = start_line)
|
21
|
+
WriterManager.delete_lines(path, start_line, end_line)
|
15
22
|
end
|
16
23
|
|
17
24
|
def update_key(path, key, value)
|
18
|
-
|
19
|
-
|
25
|
+
WriterManager.update_key(path, key, value)
|
26
|
+
end
|
27
|
+
|
28
|
+
def delete_key(path, key)
|
29
|
+
WriterManager.delete_key(path, key)
|
20
30
|
end
|
21
31
|
end
|
22
32
|
end
|
@@ -5,21 +5,51 @@ module DevSuite
|
|
5
5
|
module FileWriter
|
6
6
|
module Writer
|
7
7
|
class Base < Utils::Construct::Component::Base
|
8
|
-
|
9
|
-
|
10
|
-
|
8
|
+
attr_reader :path
|
9
|
+
|
10
|
+
def initialize(path)
|
11
|
+
super()
|
12
|
+
@path = path
|
13
|
+
end
|
14
|
+
|
15
|
+
def read
|
16
|
+
FileLoader.load(path)
|
11
17
|
end
|
12
18
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
19
|
+
def write(content, backup: false)
|
20
|
+
create_backup if backup
|
21
|
+
AtomicWriter.new(path, content).write
|
22
|
+
end
|
17
23
|
|
18
|
-
|
24
|
+
def append(content)
|
25
|
+
current_content = read
|
26
|
+
updated_content = current_content.merge(content)
|
27
|
+
write(updated_content)
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete_lines(start_line, end_line = start_line)
|
31
|
+
lines = ::File.readlines(path)
|
32
|
+
lines.slice!(start_line - 1, end_line - start_line + 1)
|
33
|
+
write(lines.join)
|
34
|
+
end
|
35
|
+
|
36
|
+
def update_key(key, value, **options)
|
37
|
+
content = read
|
19
38
|
Utils::Data.set_value_by_path(content, key, value)
|
39
|
+
write(content, **options)
|
40
|
+
end
|
41
|
+
|
42
|
+
def delete_key(key, **options)
|
43
|
+
content = read
|
44
|
+
Utils::Data.delete_key_by_path(content, key)
|
45
|
+
write(content, **options)
|
46
|
+
end
|
20
47
|
|
21
|
-
|
22
|
-
|
48
|
+
def append_array(array_key, new_elements)
|
49
|
+
data = read
|
50
|
+
data[array_key] ||= []
|
51
|
+
data[array_key].concat(new_elements)
|
52
|
+
write(data)
|
23
53
|
end
|
24
54
|
|
25
55
|
private
|
@@ -28,11 +58,6 @@ module DevSuite
|
|
28
58
|
::File.exist?(path)
|
29
59
|
end
|
30
60
|
|
31
|
-
# Load the file content using a file loader
|
32
|
-
def load_file_content(file_path)
|
33
|
-
FileLoader.load(file_path)
|
34
|
-
end
|
35
|
-
|
36
61
|
def create_backup(path)
|
37
62
|
BackupManager.new(path).create_backup if file_exists?(path)
|
38
63
|
end
|
@@ -5,20 +5,22 @@ module DevSuite
|
|
5
5
|
module FileWriter
|
6
6
|
module Writer
|
7
7
|
class Text < Base
|
8
|
-
def write(path, content, backup: false)
|
9
|
-
create_backup(path) if backup
|
10
|
-
|
11
|
-
AtomicWriter.new(path, content).write
|
12
|
-
end
|
13
|
-
|
14
8
|
# Updates a key-like pattern in a plain text file (find and replace)
|
15
|
-
def update_key(
|
16
|
-
content =
|
9
|
+
def update_key(key, value, backup: false)
|
10
|
+
content = read
|
17
11
|
|
18
12
|
# Simple pattern matching and replacement (e.g., `key: value`)
|
19
13
|
updated_content = content.gsub(/^#{Regexp.escape(key)}:.*/, "#{key}: #{value}")
|
20
14
|
|
21
|
-
write(
|
15
|
+
write(updated_content, backup: backup)
|
16
|
+
end
|
17
|
+
|
18
|
+
def append(content)
|
19
|
+
::File.open(path, "a") do |file|
|
20
|
+
file.write("\n") if ::File.size(path).nonzero?
|
21
|
+
|
22
|
+
file.write(content.strip)
|
23
|
+
end
|
22
24
|
end
|
23
25
|
end
|
24
26
|
end
|
@@ -5,7 +5,7 @@ module DevSuite
|
|
5
5
|
module FileWriter
|
6
6
|
module Writer
|
7
7
|
class Yaml < Base
|
8
|
-
def write(
|
8
|
+
def write(content, normalize: false, backup: false, yaml_options: {})
|
9
9
|
validate_content(content)
|
10
10
|
create_backup(path) if backup
|
11
11
|
|
@@ -3,27 +3,50 @@
|
|
3
3
|
module DevSuite
|
4
4
|
module Utils
|
5
5
|
module FileWriter
|
6
|
+
class UnsupportedFileTypeError < StandardError; end
|
7
|
+
|
6
8
|
class WriterManager
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
WRITERS = {
|
10
|
+
".json" => Writer::Json,
|
11
|
+
".yml" => Writer::Yaml,
|
12
|
+
".yaml" => Writer::Yaml,
|
13
|
+
".txt" => Writer::Text,
|
14
|
+
}.freeze
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
+
class << self
|
17
|
+
def write(path, content)
|
18
|
+
writer_instance(path).write(content)
|
19
|
+
end
|
20
|
+
|
21
|
+
def append(path, content)
|
22
|
+
writer_instance(path).append(content)
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete_lines(path, start_line, end_line = start_line)
|
26
|
+
writer_instance(path).delete_lines(start_line, end_line)
|
27
|
+
end
|
16
28
|
|
17
|
-
|
29
|
+
def update_key(path, key, value)
|
30
|
+
writer_instance(path).update_key(key, value)
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete_key(path, key)
|
34
|
+
writer_instance(path).delete_key(key)
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Returns the appropriate writer instance based on the file extension
|
40
|
+
def writer_instance(path)
|
41
|
+
writer_class = WRITERS[file_extension(path)]
|
42
|
+
raise UnsupportedFileTypeError, "Unsupported file type: #{file_extension(path)}" unless writer_class
|
43
|
+
|
44
|
+
writer_class.new(path)
|
45
|
+
end
|
18
46
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
Writer::Json.new
|
23
|
-
when ".yml", ".yaml"
|
24
|
-
Writer::Yaml.new
|
25
|
-
else
|
26
|
-
Writer::Text.new
|
47
|
+
# Returns the file extension from the path
|
48
|
+
def file_extension(path)
|
49
|
+
::File.extname(path).downcase
|
27
50
|
end
|
28
51
|
end
|
29
52
|
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevSuite
|
4
|
+
module Utils
|
5
|
+
module Logger
|
6
|
+
require_relative "formatter"
|
7
|
+
require_relative "emoji"
|
8
|
+
require_relative "errors"
|
9
|
+
|
10
|
+
class Base
|
11
|
+
LOG_LEVELS = [:none, :info, :warn, :error, :debug].freeze
|
12
|
+
|
13
|
+
attr_accessor :log_level
|
14
|
+
|
15
|
+
def initialize(log_level: :none)
|
16
|
+
validate_log_level(log_level)
|
17
|
+
@log_level = log_level
|
18
|
+
end
|
19
|
+
|
20
|
+
# Logs a message with optional emoji, prefix, and color.
|
21
|
+
def log(message, level: :none, emoji: nil, prefix: nil, color: nil)
|
22
|
+
validate_log_level(level)
|
23
|
+
return if skip_logging?(level)
|
24
|
+
|
25
|
+
options = {
|
26
|
+
level: level,
|
27
|
+
emoji: emoji,
|
28
|
+
prefix: prefix,
|
29
|
+
color: color,
|
30
|
+
}
|
31
|
+
|
32
|
+
formatted_message = Formatter.format(message, options)
|
33
|
+
output_log(formatted_message)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def validate_log_level(level)
|
39
|
+
return if LOG_LEVELS.include?(level)
|
40
|
+
|
41
|
+
raise InvalidLogLevelError, "Invalid log level: #{level}. Valid levels are: #{LOG_LEVELS.join(", ")}."
|
42
|
+
end
|
43
|
+
|
44
|
+
def skip_logging?(level)
|
45
|
+
LOG_LEVELS.index(level) < LOG_LEVELS.index(@log_level)
|
46
|
+
end
|
47
|
+
|
48
|
+
def output_log(formatted_message)
|
49
|
+
puts formatted_message if formatted_message
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevSuite
|
4
|
+
module Utils
|
5
|
+
module Logger
|
6
|
+
module Emoji
|
7
|
+
class << self
|
8
|
+
def resolve(emoji)
|
9
|
+
return "" unless emoji
|
10
|
+
|
11
|
+
emoji.is_a?(Symbol) ? Utils::Emoji.get(emoji) : emoji
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevSuite
|
4
|
+
module Utils
|
5
|
+
module Logger
|
6
|
+
module Formatter
|
7
|
+
LOG_DETAILS = {
|
8
|
+
none: { prefix: "", color: :white },
|
9
|
+
info: { prefix: "[INFO]", color: :green },
|
10
|
+
warn: { prefix: "[WARNING]", color: :yellow },
|
11
|
+
error: { prefix: "[ERROR]", color: :red },
|
12
|
+
debug: { prefix: "[DEBUG]", color: :blue },
|
13
|
+
}.freeze
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def format(message, options = {})
|
17
|
+
return "" if message.nil? || message.strip.empty?
|
18
|
+
|
19
|
+
details = fetch_log_details(options[:level])
|
20
|
+
prefix = options[:prefix] || details[:prefix]
|
21
|
+
color = options[:color] || details[:color]
|
22
|
+
emoji_icon = Emoji.resolve(options[:emoji])
|
23
|
+
|
24
|
+
formatted_message = build_message(prefix, emoji_icon, message)
|
25
|
+
Utils::Color.colorize(formatted_message, color: color)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def fetch_log_details(level)
|
31
|
+
LOG_DETAILS[level || :none]
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_message(prefix, emoji_icon, message)
|
35
|
+
"#{prefix} #{emoji_icon} #{message}".strip
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DevSuite
|
4
|
+
module Utils
|
5
|
+
module Logger
|
6
|
+
require_relative "base"
|
7
|
+
|
8
|
+
class << self
|
9
|
+
# Provides access to the global logger instance
|
10
|
+
#
|
11
|
+
# @return [Base] the global logger instance
|
12
|
+
def logger
|
13
|
+
@logger ||= Base.new
|
14
|
+
end
|
15
|
+
|
16
|
+
# Logs a message using the global logger instance
|
17
|
+
#
|
18
|
+
# @param message [String] The message to log.
|
19
|
+
# @param level [Symbol] The log level (:none, :info, :warn, :error, :debug).
|
20
|
+
# @param emoji [String, Symbol, nil] Optional emoji to prepend to the message.
|
21
|
+
# @param prefix [String, nil] Custom prefix for the message.
|
22
|
+
# @param color [Symbol, nil] Custom color for the message.
|
23
|
+
def log(message, level: :none, emoji: nil, prefix: nil, color: nil)
|
24
|
+
logger.log(message, level: level, emoji: emoji, prefix: prefix, color: color)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -3,58 +3,7 @@
|
|
3
3
|
module DevSuite
|
4
4
|
module Utils
|
5
5
|
module Logger
|
6
|
-
|
7
|
-
none: { prefix: "", color: :white },
|
8
|
-
info: { prefix: "[INFO]", color: :green },
|
9
|
-
warn: { prefix: "[WARNING]", color: :yellow },
|
10
|
-
error: { prefix: "[ERROR]", color: :red },
|
11
|
-
debug: { prefix: "[DEBUG]", color: :blue },
|
12
|
-
}.freeze
|
13
|
-
|
14
|
-
class << self
|
15
|
-
# Logs a message with an optional emoji and specified log level.
|
16
|
-
#
|
17
|
-
# @param message [String] The message to log.
|
18
|
-
# @param level [Symbol] The log level (:info, :warn, :error, :debug).
|
19
|
-
# @param emoji [String, Symbol] Optional emoji to prepend to the message.
|
20
|
-
def log(message, level: :none, emoji: nil, prefix: nil, color: nil)
|
21
|
-
emoji_icon = resolve_emoji(emoji)
|
22
|
-
formatted_message = format_message("#{emoji_icon} #{message}", level, prefix, color)
|
23
|
-
output_log(formatted_message)
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
|
28
|
-
# Resolves the emoji, either from a symbol or directly as a string.
|
29
|
-
#
|
30
|
-
# @param emoji [String, Symbol, nil] The emoji or its symbol key.
|
31
|
-
# @return [String] The resolved emoji or an empty string if none is provided.
|
32
|
-
def resolve_emoji(emoji)
|
33
|
-
return "" unless emoji
|
34
|
-
|
35
|
-
emoji.is_a?(Symbol) ? Emoji.get(emoji) : emoji
|
36
|
-
end
|
37
|
-
|
38
|
-
# Formats the log message with the appropriate prefix and color.
|
39
|
-
#
|
40
|
-
# @param message [String] The message to format.
|
41
|
-
# @param level [Symbol] The log level (:info, :warn, :error, :debug).
|
42
|
-
# @return [String] The formatted log message.
|
43
|
-
def format_message(message, level, custom_prefix, custom_color)
|
44
|
-
details = LOG_DETAILS[level]
|
45
|
-
prefix = custom_prefix || details[:prefix]
|
46
|
-
color = custom_color || details[:color]
|
47
|
-
|
48
|
-
Utils::Color.colorize("#{prefix} #{message}", color: color)
|
49
|
-
end
|
50
|
-
|
51
|
-
# Outputs the formatted log message to the console.
|
52
|
-
#
|
53
|
-
# @param formatted_message [String] The message to output.
|
54
|
-
def output_log(formatted_message)
|
55
|
-
puts formatted_message if formatted_message
|
56
|
-
end
|
57
|
-
end
|
6
|
+
require_relative "logger/logger"
|
58
7
|
end
|
59
8
|
end
|
60
9
|
end
|
@@ -49,7 +49,7 @@ module DevSuite
|
|
49
49
|
|
50
50
|
total_width = column_widths.sum + column_widths.size * 3 - 1
|
51
51
|
title_str = "| #{table.title.center(total_width - 2)} |"
|
52
|
-
colorize(title_str, settings.get("colors.title"))
|
52
|
+
puts colorize(title_str, settings.get("colors.title"))
|
53
53
|
end
|
54
54
|
|
55
55
|
def render_header(table, column_widths)
|
@@ -59,13 +59,13 @@ module DevSuite
|
|
59
59
|
text_align(column.name, column_widths[index])
|
60
60
|
end
|
61
61
|
header_str = "| #{header.join(" | ")} |"
|
62
|
-
colorize(header_str, settings.get("colors.column"))
|
62
|
+
puts colorize(header_str, settings.get("colors.column"))
|
63
63
|
end
|
64
64
|
|
65
65
|
def render_separator(column_widths)
|
66
66
|
separator = column_widths.map { |width| "-" * width }.join("-+-")
|
67
67
|
separator_str = "+-#{separator}-+"
|
68
|
-
colorize(separator_str, settings.get("colors.border"))
|
68
|
+
puts colorize(separator_str, settings.get("colors.border"))
|
69
69
|
end
|
70
70
|
|
71
71
|
def render_rows(table, column_widths)
|
@@ -73,7 +73,7 @@ module DevSuite
|
|
73
73
|
render_row(row, column_widths)
|
74
74
|
end
|
75
75
|
cells_str = cells.join("\n")
|
76
|
-
colorize(cells_str, settings.get("colors.row"))
|
76
|
+
puts colorize(cells_str, settings.get("colors.row"))
|
77
77
|
end
|
78
78
|
|
79
79
|
def render_row(row, column_widths)
|
@@ -81,7 +81,7 @@ module DevSuite
|
|
81
81
|
text_align(cell.to_s, column_widths[index])
|
82
82
|
end
|
83
83
|
cell_str = "| #{cell.join(" | ")} |"
|
84
|
-
colorize(cell_str, settings.get("colors.row"))
|
84
|
+
puts colorize(cell_str, settings.get("colors.row"))
|
85
85
|
end
|
86
86
|
end
|
87
87
|
end
|
data/lib/dev_suite/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dev_suite
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.13
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Huy Nguyen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-10-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: benchmark
|
@@ -258,6 +258,11 @@ files:
|
|
258
258
|
- lib/dev_suite/utils/data/base_operations.rb
|
259
259
|
- lib/dev_suite/utils/data/data.rb
|
260
260
|
- lib/dev_suite/utils/data/path_access.rb
|
261
|
+
- lib/dev_suite/utils/data/path_access/data_traverser.rb
|
262
|
+
- lib/dev_suite/utils/data/path_access/errors.rb
|
263
|
+
- lib/dev_suite/utils/data/path_access/key_handler.rb
|
264
|
+
- lib/dev_suite/utils/data/path_access/path_accessor.rb
|
265
|
+
- lib/dev_suite/utils/data/path_access/path_parser.rb
|
261
266
|
- lib/dev_suite/utils/data/search_filter.rb
|
262
267
|
- lib/dev_suite/utils/data/serialization.rb
|
263
268
|
- lib/dev_suite/utils/data/transformations.rb
|
@@ -289,6 +294,11 @@ files:
|
|
289
294
|
- lib/dev_suite/utils/file_writer/writer/yaml.rb
|
290
295
|
- lib/dev_suite/utils/file_writer/writer_manager.rb
|
291
296
|
- lib/dev_suite/utils/logger.rb
|
297
|
+
- lib/dev_suite/utils/logger/base.rb
|
298
|
+
- lib/dev_suite/utils/logger/emoji.rb
|
299
|
+
- lib/dev_suite/utils/logger/errors.rb
|
300
|
+
- lib/dev_suite/utils/logger/formatter.rb
|
301
|
+
- lib/dev_suite/utils/logger/logger.rb
|
292
302
|
- lib/dev_suite/utils/path_matcher.rb
|
293
303
|
- lib/dev_suite/utils/path_matcher/matcher.rb
|
294
304
|
- lib/dev_suite/utils/path_matcher/path_matcher.rb
|