debugtrace 0.1.0 → 0.2.1
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 +4 -4
- data/lib/debugtrace/common.rb +1 -1
- data/lib/debugtrace/config.rb +18 -37
- data/lib/debugtrace/log_buffer.rb +12 -26
- data/lib/debugtrace/state.rb +1 -1
- data/lib/debugtrace/version.rb +2 -1
- data/lib/debugtrace.rb +157 -238
- metadata +3 -5
- data/lib/debugtrace/print_options.rb +0 -24
- data/lib/temp.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45808a8d698423fb10b4b53bf1a0152b747a85a2ae32bab2ac42aff32a5252ed
|
4
|
+
data.tar.gz: 6a0aeb4db13f194c971b974110ee03b21e8dbb2baab8840a5c9a820142d4e05c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 79b136be33532e002ab16bf327519d13567e50d49ab4d4fecfc3dfb588b207ae70cc55cf6fc89b4a51649e100dcb38f808f648326882487813ea01d9779dc735
|
7
|
+
data.tar.gz: 27e016acdd8748683c8afc0639be04b9efa2dffe593a9a8a714c849bfdcfeb86dbd7e9c1d0e133b72687636812b02730dadf2ff651fb53d6b592001b443e1cb9
|
data/lib/debugtrace/common.rb
CHANGED
data/lib/debugtrace/config.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# config.rb
|
2
|
-
# (C)
|
2
|
+
# (C) 2025 Masato Kokubo
|
3
3
|
require 'yaml'
|
4
4
|
require_relative 'common'
|
5
5
|
|
6
6
|
class Config
|
7
7
|
def initialize(config_path)
|
8
|
-
@config_path = Common
|
8
|
+
@config_path = Common.check_type('config_path', config_path, String)
|
9
9
|
if File.exist?(@config_path)
|
10
10
|
@config = YAML.load_file(@config_path)
|
11
11
|
else
|
@@ -16,9 +16,9 @@ class Config
|
|
16
16
|
@logging_destination = _get_config_value 'logging_destination' , 'STDERR'
|
17
17
|
@logging_format = _get_config_value 'logging_format' , "%2$s %1$s %4$s\n"
|
18
18
|
@logging_datetime_format = _get_config_value 'logging_datetime_format' , '%Y-%m-%d %H:%M:%S.%L%:z'
|
19
|
-
@enabled = _get_config_value 'enabled
|
19
|
+
@enabled = _get_config_value 'enabled' , true
|
20
20
|
@enter_format = _get_config_value 'enter_format' , 'Enter %1$s (%2$s:%3$d) <- %4$s (%5$s:%6$d)'
|
21
|
-
@leave_format = _get_config_value 'leave_format' , 'Leave %1$s (%2$s:%3$d) duration: %4
|
21
|
+
@leave_format = _get_config_value 'leave_format' , 'Leave %1$s (%2$s:%3$d) duration: %4$.3f ms'
|
22
22
|
@thread_boundary_format = _get_config_value 'thread_boundary_format' , '______________________________ %1$s #%2$s ______________________________'
|
23
23
|
@maximum_indents = _get_config_value 'maximum_indents' , 32
|
24
24
|
@indent_string = _get_config_value 'indent_string' , '| '
|
@@ -38,37 +38,18 @@ class Config
|
|
38
38
|
@collection_limit = _get_config_value 'collection_limit' , 128
|
39
39
|
@bytes_limit = _get_config_value 'bytes_limit' , 256
|
40
40
|
@string_limit = _get_config_value 'string_limit' , 256
|
41
|
-
@
|
41
|
+
@reflection_limit = _get_config_value 'reflection_limit' , 4
|
42
42
|
end
|
43
43
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
def
|
53
|
-
def maximum_indents ; @maximum_indents ; end
|
54
|
-
def indent_string ; @indent_string ; end
|
55
|
-
def data_indent_string ; @data_indent_string ; end
|
56
|
-
def limit_string ; @limit_string ; end
|
57
|
-
def non_output_string ; @non_output_string ; end
|
58
|
-
def cyclic_reference_string ; @cyclic_reference_string ; end
|
59
|
-
def varname_value_separator ; @varname_value_separator ; end
|
60
|
-
def key_value_separator ; @key_value_separator ; end
|
61
|
-
def print_suffix_format ; @print_suffix_format ; end
|
62
|
-
def count_format ; @count_format ; end
|
63
|
-
def minimum_output_count ; @minimum_output_count ; end
|
64
|
-
def length_format ; @length_format ; end
|
65
|
-
def minimum_output_length ; @minimum_output_length ; end
|
66
|
-
def maximum_data_output_width; @maximum_data_output_width; end
|
67
|
-
def bytes_count_in_line ; @bytes_count_in_line ; end
|
68
|
-
def collection_limit ; @collection_limit ; end
|
69
|
-
def bytes_limit ; @bytes_limit ; end
|
70
|
-
def string_limit ; @string_limit ; end
|
71
|
-
def reflection_nest_limit ; @reflection_nest_limit ; end
|
44
|
+
attr_reader :config_path, :logger_name, :logging_destination, :logging_format, :logging_datetime_format,
|
45
|
+
:enter_format, :leave_format, :thread_boundary_format, :maximum_indents,
|
46
|
+
:indent_string, :data_indent_string, :limit_string, :non_output_string,
|
47
|
+
:cyclic_reference_string, :varname_value_separator, :key_value_separator,
|
48
|
+
:print_suffix_format, :count_format, :minimum_output_count, :length_format,
|
49
|
+
:minimum_output_length, :maximum_data_output_width, :bytes_count_in_line,
|
50
|
+
:collection_limit, :bytes_limit, :string_limit, :reflection_limit
|
51
|
+
|
52
|
+
def enabled? = @enabled
|
72
53
|
|
73
54
|
private
|
74
55
|
|
@@ -77,14 +58,14 @@ class Config
|
|
77
58
|
# @param defalut_value (Object): Value to return when the value related the key is undefined
|
78
59
|
# @return Object: Value related the key
|
79
60
|
def _get_config_value(key, defalut_value)
|
80
|
-
Common
|
61
|
+
Common.check_type('key', key, String)
|
81
62
|
value = defalut_value
|
82
|
-
|
63
|
+
unless @config.nil?
|
83
64
|
value = @config[key]
|
84
|
-
if value
|
65
|
+
if value.nil?
|
85
66
|
value = defalut_value
|
86
67
|
else
|
87
|
-
Common
|
68
|
+
Common.check_type("config[#{key}]", value, defalut_value.class)
|
88
69
|
end
|
89
70
|
end
|
90
71
|
value
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# log_buffer.rb
|
2
|
-
# (C)
|
2
|
+
# (C) 2025 Masato Kokubo
|
3
3
|
require_relative 'common'
|
4
4
|
|
5
5
|
# Buffers logs.
|
@@ -11,14 +11,8 @@ class LogBuffer
|
|
11
11
|
@log = Common.check_type('log', log, String)
|
12
12
|
end
|
13
13
|
|
14
|
-
|
15
|
-
@nest_level
|
16
|
-
end
|
14
|
+
attr_reader :nest_level, :log
|
17
15
|
|
18
|
-
def log
|
19
|
-
@log
|
20
|
-
end
|
21
|
-
|
22
16
|
def to_s
|
23
17
|
"(LogBuffer.LevelAndLog){nest_level: #{@nest_level}, log: \"#{@log}\"}"
|
24
18
|
end
|
@@ -34,12 +28,12 @@ class LogBuffer
|
|
34
28
|
@lines = []
|
35
29
|
|
36
30
|
# buffer for a line of logs
|
37
|
-
@last_line =
|
31
|
+
@last_line = ''
|
38
32
|
end
|
39
33
|
|
40
34
|
# Breaks the current line.
|
41
35
|
def line_feed
|
42
|
-
@lines << LevelAndLog.new(@nest_level + @append_nest_level, @last_line.rstrip
|
36
|
+
@lines << LevelAndLog.new(@nest_level + @append_nest_level, @last_line.rstrip)
|
43
37
|
@append_nest_level = 0
|
44
38
|
@last_line = ''
|
45
39
|
end
|
@@ -63,11 +57,9 @@ class LogBuffer
|
|
63
57
|
def append(value, nest_level = 0, no_break = false)
|
64
58
|
Common.check_type('nest_level', nest_level, Integer)
|
65
59
|
Common.check_type('no_break', no_break, TrueClass)
|
66
|
-
|
60
|
+
unless value.nil?
|
67
61
|
string = value.to_s
|
68
|
-
if !no_break && length > 0 && length + string.length > @maximum_data_output_width
|
69
|
-
line_feed()
|
70
|
-
end
|
62
|
+
line_feed if !no_break && length > 0 && length + string.length > @maximum_data_output_width
|
71
63
|
@append_nest_level = nest_level
|
72
64
|
@last_line += string
|
73
65
|
end
|
@@ -83,22 +75,18 @@ class LogBuffer
|
|
83
75
|
end
|
84
76
|
|
85
77
|
# Appends lines of another LogBuffer.
|
86
|
-
# @param
|
87
|
-
# @param separator (String): The separator string to append if not
|
78
|
+
# @param
|
79
|
+
# @param separator (String): The separator string to append if not ''
|
88
80
|
# @param buff (LogBuffer): Another LogBuffer
|
89
81
|
# @returns LogBuffer: This object
|
90
82
|
def append_buffer(separator, buff)
|
91
83
|
Common.check_type('separator', separator, String)
|
92
84
|
Common.check_type('buff', buff, LogBuffer)
|
93
|
-
if separator !=
|
94
|
-
append(separator, 0, true)
|
95
|
-
end
|
85
|
+
append(separator, 0, true) if separator != ''
|
96
86
|
index = 0
|
97
87
|
for line in buff.lines
|
98
|
-
if index > 0
|
99
|
-
|
100
|
-
end
|
101
|
-
append(line.nest_level, line.log, index == 0 && separator != "")
|
88
|
+
line_feed if index > 0
|
89
|
+
append(line.log, line.nest_level, index == 0 && separator != '')
|
102
90
|
index += 1
|
103
91
|
end
|
104
92
|
self
|
@@ -117,9 +105,7 @@ class LogBuffer
|
|
117
105
|
# A list of tuple of data indentation level && log string.
|
118
106
|
def lines
|
119
107
|
lines = @lines.dup
|
120
|
-
if length > 0
|
121
|
-
lines << LevelAndLog.new(@nest_level, @last_line)
|
122
|
-
end
|
108
|
+
lines << LevelAndLog.new(@nest_level, @last_line) if length > 0
|
123
109
|
lines
|
124
110
|
end
|
125
111
|
end
|
data/lib/debugtrace/state.rb
CHANGED
data/lib/debugtrace/version.rb
CHANGED
data/lib/debugtrace.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
#
|
1
|
+
# debugtrace.rb
|
2
2
|
# (C) 2025 Masato Kokubo
|
3
|
-
require 'thread'
|
4
3
|
require 'logger'
|
5
4
|
|
6
5
|
# Require necessary files
|
@@ -9,19 +8,14 @@ require_relative 'debugtrace/common'
|
|
9
8
|
require_relative 'debugtrace/config'
|
10
9
|
require_relative 'debugtrace/log_buffer'
|
11
10
|
require_relative 'debugtrace/loggers'
|
12
|
-
require_relative 'debugtrace/print_options'
|
13
11
|
require_relative 'debugtrace/state'
|
14
12
|
|
15
13
|
module DebugTrace
|
16
|
-
# class Error < StandardError; end
|
17
|
-
@@AUTHOR = 'Masato Kokubo <masatokokubo@gmail.com>'
|
18
|
-
# @@VERSION = '1.0.0 dev 1'
|
19
|
-
|
20
14
|
# Configuration values
|
21
15
|
@@config = nil
|
22
16
|
|
23
17
|
def self.config
|
24
|
-
|
18
|
+
@@config
|
25
19
|
end
|
26
20
|
|
27
21
|
# A Mutex for thread safety
|
@@ -61,38 +55,35 @@ module DebugTrace
|
|
61
55
|
Pr._print("debugtrace: (#{@@config.config_path}) logger = #{@@config.logger_name} is unknown", STDERR)
|
62
56
|
end
|
63
57
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
58
|
+
return unless @@config.enabled?
|
59
|
+
|
60
|
+
ruby_version = RUBY_VERSION
|
61
|
+
@@logger.print("DebugTrace-rb #{DebugTrace::VERSION} on Ruby #{ruby_version}")
|
62
|
+
@@logger.print(" config file path: #{@@config.config_path}")
|
63
|
+
@@logger.print(" logger: #{@@logger}")
|
70
64
|
end
|
71
65
|
|
72
66
|
class PrintOptions
|
73
|
-
attr_reader :
|
74
|
-
:
|
67
|
+
attr_reader :reflection,
|
68
|
+
:minimum_output_count, :minimum_output_length,
|
69
|
+
:collection_limit, :bytes_limit, :string_limit, :reflection_limit
|
75
70
|
|
76
71
|
def initialize(
|
77
|
-
|
78
|
-
output_private,
|
79
|
-
output_method,
|
72
|
+
reflection,
|
80
73
|
minimum_output_count,
|
81
74
|
minimum_output_length,
|
82
75
|
collection_limit,
|
83
76
|
bytes_limit,
|
84
77
|
string_limit,
|
85
|
-
|
78
|
+
reflection_limit
|
86
79
|
)
|
87
|
-
@
|
88
|
-
@output_private = output_private
|
89
|
-
@output_method = output_method
|
80
|
+
@reflection = reflection
|
90
81
|
@minimum_output_count = minimum_output_count == -1 ? DebugTrace.config.minimum_output_count : minimum_output_count
|
91
82
|
@minimum_output_length = minimum_output_length == -1 ? DebugTrace.config.minimum_output_length : minimum_output_length
|
92
83
|
@collection_limit = collection_limit == -1 ? DebugTrace.config.collection_limit : collection_limit
|
93
84
|
@bytes_limit = bytes_limit == -1 ? DebugTrace.config.bytes_limit : bytes_limit
|
94
85
|
@string_limit = string_limit == -1 ? DebugTrace.config.string_limit : string_limit
|
95
|
-
@
|
86
|
+
@reflection_limit = reflection_limit == -1 ? DebugTrace.config.reflection_limit : reflection_limit
|
96
87
|
end
|
97
88
|
end
|
98
89
|
|
@@ -117,7 +108,7 @@ module DebugTrace
|
|
117
108
|
|
118
109
|
def self.to_string(name, value, print_options)
|
119
110
|
buff = LogBuffer.new(@@config.maximum_data_output_width)
|
120
|
-
|
111
|
+
|
121
112
|
separator = ''
|
122
113
|
unless name.empty?
|
123
114
|
buff.append(name)
|
@@ -126,38 +117,34 @@ module DebugTrace
|
|
126
117
|
|
127
118
|
case value
|
128
119
|
when nil
|
129
|
-
# None
|
130
120
|
buff.no_break_append(separator).append('nil')
|
121
|
+
when FalseClass, TrueClass, Integer, Float
|
122
|
+
buff.no_break_append(separator).append(value.to_s)
|
131
123
|
when String
|
132
|
-
# String
|
133
124
|
value_buff = to_string_str(value, print_options)
|
134
125
|
buff.append_buffer(separator, value_buff)
|
135
|
-
when
|
136
|
-
|
137
|
-
|
126
|
+
when DateTime
|
127
|
+
buff.no_break_append(separator).append(value.strftime('%Y-%m-%d %H:%M-%S.%L%:z'))
|
128
|
+
when Date
|
129
|
+
buff.no_break_append(separator).append(value.strftime('%Y-%m-%d'))
|
130
|
+
when Time
|
131
|
+
buff.no_break_append(separator).append(value.strftime('%H:%M-%S.%L%:z'))
|
138
132
|
when Array, Set, Hash
|
139
|
-
|
140
|
-
value_buff = to_string_iterable(value, print_options)
|
133
|
+
value_buff = to_string_enumerable(value, print_options)
|
141
134
|
buff.append_buffer(separator, value_buff)
|
142
135
|
else
|
143
|
-
has_str, has_repr = has_str_repr_method(value)
|
144
136
|
value_buff = LogBuffer.new(@@config.maximum_data_output_width)
|
145
|
-
if !print_options.
|
137
|
+
if !print_options.reflection && has_to_s_method?(value)
|
146
138
|
# has to_s or inspect method
|
147
|
-
|
148
|
-
|
149
|
-
value_buff.no_break_append(value.inspect)
|
150
|
-
else
|
151
|
-
value_buff.append('to_s(): ')
|
152
|
-
value_buff.no_break_append(value.to_s)
|
153
|
-
end
|
139
|
+
value_buff.append('to_s: ')
|
140
|
+
value_buff.no_break_append(value.to_s)
|
154
141
|
buff.append_buffer(separator, value_buff)
|
155
142
|
else
|
156
143
|
# use reflection
|
157
144
|
if @@reflected_objects.any? { |obj| value.equal?(obj) }
|
158
145
|
# cyclic reference
|
159
146
|
value_buff.no_break_append(@@config.cyclic_reference_string)
|
160
|
-
elsif @@reflected_objects.length > print_options.
|
147
|
+
elsif @@reflected_objects.length > print_options.reflection_limit
|
161
148
|
# over reflection level limitation
|
162
149
|
value_buff.no_break_append(@@config.limit_string)
|
163
150
|
else
|
@@ -168,7 +155,7 @@ module DebugTrace
|
|
168
155
|
buff.append_buffer(separator, value_buff)
|
169
156
|
end
|
170
157
|
end
|
171
|
-
|
158
|
+
|
172
159
|
buff
|
173
160
|
end
|
174
161
|
|
@@ -177,19 +164,19 @@ module DebugTrace
|
|
177
164
|
has_double_quote = false
|
178
165
|
single_quote_buff = LogBuffer.new(@@config.maximum_data_output_width)
|
179
166
|
double_quote_buff = LogBuffer.new(@@config.maximum_data_output_width)
|
180
|
-
|
167
|
+
|
181
168
|
if value.length >= @@config.minimum_output_length
|
182
|
-
single_quote_buff.no_break_append(
|
183
|
-
single_quote_buff.no_break_append(
|
184
|
-
single_quote_buff.no_break_append(
|
185
|
-
double_quote_buff.no_break_append(
|
186
|
-
double_quote_buff.no_break_append(
|
187
|
-
double_quote_buff.no_break_append(
|
169
|
+
single_quote_buff.no_break_append('(')
|
170
|
+
single_quote_buff.no_break_append(format(@@config.length_format, value.length))
|
171
|
+
single_quote_buff.no_break_append(')')
|
172
|
+
double_quote_buff.no_break_append('(')
|
173
|
+
double_quote_buff.no_break_append(format(@@config.length_format, value.length))
|
174
|
+
double_quote_buff.no_break_append(')')
|
188
175
|
end
|
189
|
-
|
176
|
+
|
190
177
|
single_quote_buff.no_break_append("'")
|
191
178
|
double_quote_buff.no_break_append('"')
|
192
|
-
|
179
|
+
|
193
180
|
count = 1
|
194
181
|
value.each_char do |char|
|
195
182
|
if count > print_options.string_limit
|
@@ -206,33 +193,37 @@ module DebugTrace
|
|
206
193
|
single_quote_buff.no_break_append(char)
|
207
194
|
double_quote_buff.no_break_append("\\\"")
|
208
195
|
has_double_quote = true
|
209
|
-
when
|
210
|
-
single_quote_buff.no_break_append(
|
211
|
-
double_quote_buff.no_break_append(
|
212
|
-
when
|
213
|
-
single_quote_buff.no_break_append(
|
214
|
-
double_quote_buff.no_break_append(
|
215
|
-
when
|
216
|
-
single_quote_buff.no_break_append(
|
217
|
-
double_quote_buff.no_break_append(
|
218
|
-
when
|
219
|
-
single_quote_buff.no_break_append(
|
220
|
-
double_quote_buff.no_break_append(
|
221
|
-
when ("\0".."\37").include?(char)
|
222
|
-
num_str = format('%02X', char.ord)
|
223
|
-
single_quote_buff.no_break_append("\\x" + num_str)
|
224
|
-
double_quote_buff.no_break_append("\\x" + num_str)
|
196
|
+
when "\\"
|
197
|
+
single_quote_buff.no_break_append("\\\\")
|
198
|
+
double_quote_buff.no_break_append("\\\\")
|
199
|
+
when "\n"
|
200
|
+
single_quote_buff.no_break_append("\\n")
|
201
|
+
double_quote_buff.no_break_append("\\n")
|
202
|
+
when "\r"
|
203
|
+
single_quote_buff.no_break_append("\\r")
|
204
|
+
double_quote_buff.no_break_append("\\r")
|
205
|
+
when "\t"
|
206
|
+
single_quote_buff.no_break_append("\\t")
|
207
|
+
double_quote_buff.no_break_append("\\t")
|
225
208
|
else
|
226
|
-
|
227
|
-
|
209
|
+
char_ord = char.ord
|
210
|
+
if char_ord >= 0x00 && char_ord <= 0x1F || char_ord == 0x7F
|
211
|
+
num_str = format('%02X', char_ord)
|
212
|
+
single_quote_buff.no_break_append("\\x" + num_str)
|
213
|
+
double_quote_buff.no_break_append("\\x" + num_str)
|
214
|
+
else
|
215
|
+
single_quote_buff.no_break_append(char)
|
216
|
+
double_quote_buff.no_break_append(char)
|
217
|
+
end
|
228
218
|
end
|
229
219
|
count += 1
|
230
220
|
end
|
231
|
-
|
221
|
+
|
232
222
|
double_quote_buff.no_break_append('"')
|
233
223
|
single_quote_buff.no_break_append("'")
|
234
|
-
|
224
|
+
|
235
225
|
return double_quote_buff if has_single_quote && !has_double_quote
|
226
|
+
|
236
227
|
single_quote_buff
|
237
228
|
end
|
238
229
|
|
@@ -240,7 +231,7 @@ module DebugTrace
|
|
240
231
|
bytes_length = value.length
|
241
232
|
buff = LogBuffer.new(@@config.maximum_data_output_width)
|
242
233
|
buff.no_break_append('(')
|
243
|
-
|
234
|
+
|
244
235
|
if value.is_a?(String)
|
245
236
|
buff.no_break_append('bytes')
|
246
237
|
elsif value.is_a?(Array)
|
@@ -249,7 +240,7 @@ module DebugTrace
|
|
249
240
|
|
250
241
|
if bytes_length >= @@config.minimum_output_length
|
251
242
|
buff.no_break_append(' ')
|
252
|
-
buff.no_break_append(
|
243
|
+
buff.no_break_append(format(@@config.length_format, bytes_length))
|
253
244
|
end
|
254
245
|
|
255
246
|
buff.no_break_append(') [')
|
@@ -264,20 +255,18 @@ module DebugTrace
|
|
264
255
|
chars = ''
|
265
256
|
count = 0
|
266
257
|
value.each_byte do |element|
|
267
|
-
if count != 0 && count % @@config.bytes_count_in_line == 0
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
chars = ''
|
273
|
-
end
|
258
|
+
if count != 0 && count % @@config.bytes_count_in_line == 0 && multi_lines
|
259
|
+
buff.no_break_append('| ')
|
260
|
+
buff.no_break_append(chars)
|
261
|
+
buff.line_feed
|
262
|
+
chars = ''
|
274
263
|
end
|
275
264
|
if count >= print_options.bytes_limit
|
276
265
|
buff.no_break_append(@@config.limit_string)
|
277
266
|
break
|
278
267
|
end
|
279
|
-
buff.no_break_append(
|
280
|
-
chars +=
|
268
|
+
buff.no_break_append(format('%02X ', element))
|
269
|
+
chars += element >= 0x20 && element <= 0x7E ? element.chr : '.'
|
281
270
|
count += 1
|
282
271
|
end
|
283
272
|
|
@@ -297,13 +286,13 @@ module DebugTrace
|
|
297
286
|
end
|
298
287
|
buff.no_break_append(']')
|
299
288
|
|
300
|
-
|
289
|
+
buff
|
301
290
|
end
|
302
291
|
|
303
292
|
def self.to_string_reflection(value, print_options)
|
304
293
|
buff = LogBuffer.new(@@config.maximum_data_output_width)
|
305
294
|
|
306
|
-
buff.append(
|
295
|
+
buff.append(get_type_name(value))
|
307
296
|
|
308
297
|
body_buff = to_string_reflection_body(value, print_options)
|
309
298
|
|
@@ -323,72 +312,52 @@ module DebugTrace
|
|
323
312
|
end
|
324
313
|
buff.no_break_append('}')
|
325
314
|
|
326
|
-
|
315
|
+
buff
|
327
316
|
end
|
328
317
|
|
329
318
|
def self.to_string_reflection_body(value, print_options)
|
330
319
|
buff = LogBuffer.new(@@config.maximum_data_output_width)
|
331
320
|
|
332
|
-
|
333
|
-
begin
|
334
|
-
base_members = value.methods(false).map { |m| [m, value.send(m)] }
|
335
|
-
members = base_members.select do |m|
|
336
|
-
name, _ = m
|
337
|
-
!name.start_with?('__') || !name.end_with?('__') &&
|
338
|
-
(print_options.output_method || !value.method(name).owner.nil?) &&
|
339
|
-
(print_options.output_private || !name.start_with?('_'))
|
340
|
-
end
|
341
|
-
rescue => ex
|
342
|
-
buff.append(ex.to_s)
|
343
|
-
return buff
|
344
|
-
end
|
321
|
+
variables = value.instance_variables
|
345
322
|
|
346
323
|
multi_lines = false
|
347
324
|
index = 0
|
348
|
-
|
349
|
-
if index > 0
|
350
|
-
buff.no_break_append(', ')
|
351
|
-
end
|
325
|
+
variables.each do |variable|
|
326
|
+
buff.no_break_append(', ') if index > 0
|
352
327
|
|
353
|
-
|
328
|
+
var_value = value.instance_variable_get(variable)
|
354
329
|
member_buff = LogBuffer.new(@@config.maximum_data_output_width)
|
355
|
-
member_buff.append(
|
356
|
-
member_buff.append_buffer(@@config.key_value_separator, to_string('',
|
357
|
-
if index > 0 && (multi_lines || member_buff.multi_lines?)
|
358
|
-
buff.line_feed
|
359
|
-
end
|
330
|
+
member_buff.append(variable)
|
331
|
+
member_buff.append_buffer(@@config.key_value_separator, to_string('', var_value, print_options))
|
332
|
+
buff.line_feed if index > 0 && (multi_lines || member_buff.multi_lines?)
|
360
333
|
buff.append_buffer('', member_buff)
|
361
334
|
|
362
335
|
multi_lines = member_buff.multi_lines?
|
363
336
|
index += 1
|
364
337
|
end
|
365
338
|
|
366
|
-
|
339
|
+
buff
|
367
340
|
end
|
368
341
|
|
369
|
-
def self.
|
370
|
-
open_char = '
|
371
|
-
close_char = '
|
372
|
-
|
373
|
-
if values.is_a?(
|
374
|
-
#
|
375
|
-
open_char = '
|
342
|
+
def self.to_string_enumerable(values, print_options)
|
343
|
+
open_char = '[' # Array
|
344
|
+
close_char = ']'
|
345
|
+
|
346
|
+
if values.is_a?(Hash)
|
347
|
+
# Array
|
348
|
+
open_char = '{'
|
349
|
+
close_char = '}'
|
350
|
+
elsif values.is_a?(Set)
|
351
|
+
# Sete
|
352
|
+
open_char = 'Set['
|
376
353
|
close_char = ']'
|
377
|
-
elsif values.is_a?(Tuple)
|
378
|
-
# tuple
|
379
|
-
open_char = '('
|
380
|
-
close_char = ')'
|
381
354
|
end
|
382
355
|
|
383
356
|
buff = LogBuffer.new(@@config.maximum_data_output_width)
|
384
|
-
buff.append(
|
357
|
+
buff.append(get_type_name(values, values.length))
|
385
358
|
buff.no_break_append(open_char)
|
386
359
|
|
387
|
-
body_buff =
|
388
|
-
if open_char == '(' && values.length == 1
|
389
|
-
# A tuple with 1 element
|
390
|
-
body_buff.no_break_append(',')
|
391
|
-
end
|
360
|
+
body_buff = to_string_enumerable_body(values, print_options)
|
392
361
|
|
393
362
|
multi_lines = body_buff.multi_lines? || buff.length + body_buff.length > @@config.maximum_data_output_width
|
394
363
|
|
@@ -406,48 +375,41 @@ module DebugTrace
|
|
406
375
|
|
407
376
|
buff.no_break_append(close_char)
|
408
377
|
|
409
|
-
|
378
|
+
buff
|
410
379
|
end
|
411
380
|
|
412
|
-
def self.
|
381
|
+
def self.to_string_enumerable_body(values, print_options)
|
413
382
|
buff = LogBuffer.new(@@config.maximum_data_output_width)
|
414
383
|
|
415
384
|
multi_lines = false
|
416
385
|
index = 0
|
417
386
|
|
418
387
|
values.each do |element|
|
419
|
-
if index > 0
|
420
|
-
buff.no_break_append(', ')
|
421
|
-
end
|
388
|
+
buff.no_break_append(', ') if index > 0
|
422
389
|
|
423
390
|
if index >= print_options.collection_limit
|
424
391
|
buff.append(@@config.limit_string)
|
425
392
|
break
|
426
393
|
end
|
427
394
|
|
428
|
-
element_buff =
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
end
|
395
|
+
element_buff = if values.is_a?(Hash)
|
396
|
+
# Hash
|
397
|
+
to_string_key_value(element[0], element[1], print_options)
|
398
|
+
else
|
399
|
+
# Array
|
400
|
+
to_string('', element, print_options)
|
401
|
+
end
|
436
402
|
|
437
|
-
if index > 0 && (multi_lines || element_buff.multi_lines?)
|
438
|
-
buff.line_feed
|
439
|
-
end
|
403
|
+
buff.line_feed if index > 0 && (multi_lines || element_buff.multi_lines?)
|
440
404
|
buff.append_buffer('', element_buff)
|
441
405
|
|
442
406
|
multi_lines = element_buff.multi_lines?
|
443
407
|
index += 1
|
444
408
|
end
|
445
409
|
|
446
|
-
if values.is_a?(Hash) && values.empty?
|
447
|
-
buff.no_break_append(':')
|
448
|
-
end
|
410
|
+
buff.no_break_append(':') if values.is_a?(Hash) && values.empty?
|
449
411
|
|
450
|
-
|
412
|
+
buff
|
451
413
|
end
|
452
414
|
|
453
415
|
def self.to_string_key_value(key, value, print_options)
|
@@ -455,94 +417,58 @@ module DebugTrace
|
|
455
417
|
key_buff = to_string('', key, print_options)
|
456
418
|
value_buff = to_string('', value, print_options)
|
457
419
|
buff.append_buffer('', key_buff).append_buffer(@@config.key_value_separator, value_buff)
|
458
|
-
|
420
|
+
buff
|
459
421
|
end
|
460
422
|
|
461
423
|
def self.get_type_name(value, count = -1)
|
462
|
-
|
463
|
-
type_name =
|
464
|
-
if ['Array', 'Hash', 'Set', 'Tuple'].include?(type_name)
|
465
|
-
type_name = ''
|
466
|
-
end
|
424
|
+
type_name = value.class.to_s
|
425
|
+
type_name = '' if %w[Array Hash Set].include?(type_name)
|
467
426
|
|
468
427
|
if count >= @@config.minimum_output_count
|
469
428
|
type_name += ' ' unless type_name.empty?
|
470
429
|
type_name += @@config.count_format % count
|
471
430
|
end
|
472
431
|
|
473
|
-
|
474
|
-
type_name = "(#{type_name})"
|
475
|
-
end
|
476
|
-
return type_name
|
432
|
+
type_name
|
477
433
|
end
|
478
434
|
|
479
|
-
def self.
|
480
|
-
type_name = nest == 0 ? value_type.to_s : value_type.name
|
481
|
-
if type_name.start_with?("<Class '")
|
482
|
-
type_name = type_name[8..-1]
|
483
|
-
elsif type_name.start_with?("<Enum '")
|
484
|
-
type_name = 'enum ' + type_name[7..-1]
|
485
|
-
end
|
486
|
-
if type_name.end_with?("'>")
|
487
|
-
type_name = type_name[0..-3]
|
488
|
-
end
|
489
|
-
|
490
|
-
base_names = value_type.ancestors.reject { |base| base == Object }
|
491
|
-
base_names = base_names.map { |base| get_simple_type_name(base, nest + 1) }
|
492
|
-
|
493
|
-
if base_names.any?
|
494
|
-
type_name += '(' + base_names.join(', ') + ')'
|
495
|
-
end
|
496
|
-
|
497
|
-
return type_name
|
498
|
-
end
|
499
|
-
|
500
|
-
def self.has_str_repr_method(value)
|
435
|
+
def self.has_to_s_method?(value)
|
501
436
|
begin
|
502
|
-
|
503
|
-
has_str = members.include?(:to_s)
|
504
|
-
has_repr = members.include?(:inspect)
|
505
|
-
return has_str, has_repr
|
437
|
+
value.public_method('to_s')
|
506
438
|
rescue
|
507
|
-
return false
|
439
|
+
return false
|
508
440
|
end
|
441
|
+
return true
|
509
442
|
end
|
510
443
|
|
511
|
-
# def self.get_frame_summary(limit)
|
512
|
-
# begin
|
513
|
-
# raise 'RuntimeError'
|
514
|
-
# rescue => e
|
515
|
-
# return caller_locations(limit: limit).first
|
516
|
-
# end
|
517
|
-
# return nil
|
518
|
-
# end
|
519
|
-
|
520
444
|
@@before_thread_id = nil
|
521
445
|
|
522
446
|
def self.print_start
|
447
|
+
if @@before_thread_id == nil
|
448
|
+
DebugTrace.initialize
|
449
|
+
return unless @@config.enabled?
|
450
|
+
end
|
451
|
+
|
523
452
|
thread = Thread.current
|
524
453
|
thread_id = thread.object_id
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
454
|
+
return unless thread_id != @@before_thread_id
|
455
|
+
|
456
|
+
# Thread changing
|
457
|
+
@@logger.print('')
|
458
|
+
@@logger.print(format(@@config.thread_boundary_format, thread.name, thread_id))
|
459
|
+
@@logger.print('')
|
460
|
+
@@before_thread_id = thread_id
|
532
461
|
end
|
533
462
|
|
534
463
|
@@DO_NOT_OUTPUT = 'Do not output'
|
535
464
|
|
536
|
-
def self.print(name, value = @@DO_NOT_OUTPUT,
|
537
|
-
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
return value unless @@config.enabled?
|
543
|
-
|
544
|
-
Mutex.new.synchronize do
|
465
|
+
def self.print(name, value = @@DO_NOT_OUTPUT, reflection: false,
|
466
|
+
minimum_output_count: -1, minimum_output_length: -1,
|
467
|
+
collection_limit: -1, bytes_limit: -1,
|
468
|
+
string_limit: -1, reflection_limit: -1)
|
469
|
+
@@thread_mutex.synchronize do
|
545
470
|
print_start
|
471
|
+
return value unless @@config.enabled?
|
546
472
|
|
547
473
|
state = current_state
|
548
474
|
@@reflected_objects.clear
|
@@ -556,22 +482,22 @@ module DebugTrace
|
|
556
482
|
else
|
557
483
|
# with value
|
558
484
|
print_options = PrintOptions.new(
|
559
|
-
|
485
|
+
reflection,
|
560
486
|
minimum_output_count, minimum_output_length,
|
561
|
-
collection_limit, bytes_limit,
|
487
|
+
collection_limit, bytes_limit,
|
488
|
+
string_limit, reflection_limit
|
562
489
|
)
|
563
490
|
@@last_log_buff = to_string(name, value, print_options)
|
564
491
|
end
|
565
492
|
|
566
493
|
# append print suffix
|
567
|
-
# frame_summary = get_frame_summary(3)
|
568
494
|
location = caller_locations(3, 3)[0]
|
569
|
-
name = location
|
570
|
-
filename = location
|
571
|
-
lineno = location
|
495
|
+
name = !location.nil? ? location.base_label : ''
|
496
|
+
filename = !location.nil? ? File.basename(location.absolute_path) : ''
|
497
|
+
lineno = !location.nil? ? location.lineno : 0
|
572
498
|
|
573
499
|
@@last_log_buff.no_break_append(
|
574
|
-
@@config.print_suffix_format
|
500
|
+
format(@@config.print_suffix_format, name, filename, lineno)
|
575
501
|
)
|
576
502
|
|
577
503
|
@@last_log_buff.line_feed
|
@@ -585,28 +511,25 @@ module DebugTrace
|
|
585
511
|
end
|
586
512
|
end
|
587
513
|
|
588
|
-
|
514
|
+
value
|
589
515
|
end
|
590
516
|
|
591
|
-
|
592
517
|
def self.enter
|
593
|
-
|
594
|
-
Mutex.new.synchronize do
|
518
|
+
@@thread_mutex.synchronize do
|
595
519
|
print_start
|
520
|
+
return unless @@config.enabled?
|
596
521
|
|
597
522
|
state = current_state
|
598
523
|
|
599
|
-
# frame_summary = get_frame_summary(4)
|
600
524
|
location = caller_locations(3, 3)[0]
|
601
|
-
name = location
|
602
|
-
filename = location
|
603
|
-
lineno = location
|
525
|
+
name = !location.nil? ? location.base_label : ''
|
526
|
+
filename = !location.nil? ? File.basename(location.absolute_path) : ''
|
527
|
+
lineno = !location.nil? ? location.lineno : 0
|
604
528
|
|
605
|
-
# parent_frame_summary = get_frame_summary(5)
|
606
529
|
parent_location = caller_locations(4, 4)[0]
|
607
|
-
parent_name = parent_location
|
608
|
-
parent_filename = parent_location
|
609
|
-
parent_lineno = parent_location
|
530
|
+
parent_name = !parent_location.nil? ? parent_location.base_label : ''
|
531
|
+
parent_filename = !parent_location.nil? ? File.basename(parent_location.absolute_path) : ''
|
532
|
+
parent_lineno = !parent_location.nil? ? parent_location.lineno : 0
|
610
533
|
|
611
534
|
indent_string = get_indent_string(state.nest_level, 0)
|
612
535
|
if state.nest_level < state.previous_nest_level || @@last_log_buff.multi_lines?
|
@@ -615,7 +538,7 @@ module DebugTrace
|
|
615
538
|
|
616
539
|
@@last_log_buff = LogBuffer.new(@@config.maximum_data_output_width)
|
617
540
|
@@last_log_buff.no_break_append(
|
618
|
-
@@config.enter_format
|
541
|
+
format(@@config.enter_format, name, filename, lineno, parent_name, parent_filename, parent_lineno)
|
619
542
|
)
|
620
543
|
@@last_log_buff.line_feed
|
621
544
|
@@logger.print(indent_string + @@last_log_buff.lines[0].log)
|
@@ -625,10 +548,9 @@ module DebugTrace
|
|
625
548
|
end
|
626
549
|
|
627
550
|
def self.leave
|
628
|
-
|
629
|
-
|
630
|
-
Mutex.new.synchronize do
|
551
|
+
@@thread_mutex.synchronize do
|
631
552
|
print_start
|
553
|
+
return unless @@config.enabled?
|
632
554
|
|
633
555
|
state = current_state
|
634
556
|
|
@@ -641,11 +563,11 @@ module DebugTrace
|
|
641
563
|
@@logger.print(get_indent_string(state.nest_level, 0)) # Empty Line
|
642
564
|
end
|
643
565
|
|
644
|
-
time = Time.now.utc - state.down_nest
|
566
|
+
time = (Time.now.utc - state.down_nest) * 1000 # milliseconds <- seconds
|
645
567
|
|
646
568
|
@@last_log_buff = LogBuffer.new(@@config.maximum_data_output_width)
|
647
569
|
@@last_log_buff.no_break_append(
|
648
|
-
@@config.leave_format
|
570
|
+
format(@@config.leave_format, name, filename, lineno, time)
|
649
571
|
)
|
650
572
|
@@last_log_buff.line_feed
|
651
573
|
@@logger.print(get_indent_string(state.nest_level, 0) + @@last_log_buff.lines[0].log)
|
@@ -657,13 +579,10 @@ module DebugTrace
|
|
657
579
|
buff_string = lines.map { |line| _config.data_indent_string * line[0] + line[1] }.join("\n")
|
658
580
|
|
659
581
|
state = nil
|
660
|
-
|
582
|
+
@@thread_mutex.synchronize do
|
661
583
|
state = current_state
|
662
584
|
end
|
663
585
|
|
664
586
|
"#{get_indent_string(state.nest_level, 0)}#{buff_string}"
|
665
587
|
end
|
666
588
|
end
|
667
|
-
|
668
|
-
DebugTrace.initialize
|
669
|
-
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: debugtrace
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Masato Kokubo
|
8
8
|
bindir: exe
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: Insert DebugTrace.enter and Debug.leave at the beginning and end of the
|
13
13
|
function you want to debug, and Debug.print('foo', foo) if there are any variables
|
@@ -28,10 +28,8 @@ files:
|
|
28
28
|
- lib/debugtrace/config.rb
|
29
29
|
- lib/debugtrace/log_buffer.rb
|
30
30
|
- lib/debugtrace/loggers.rb
|
31
|
-
- lib/debugtrace/print_options.rb
|
32
31
|
- lib/debugtrace/state.rb
|
33
32
|
- lib/debugtrace/version.rb
|
34
|
-
- lib/temp.rb
|
35
33
|
- sig/debugtrace.rbs
|
36
34
|
homepage: https://github.com/MasatoKokubo/DebugTrace-rb
|
37
35
|
licenses:
|
@@ -54,7 +52,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
52
|
- !ruby/object:Gem::Version
|
55
53
|
version: '0'
|
56
54
|
requirements: []
|
57
|
-
rubygems_version: 3.6.
|
55
|
+
rubygems_version: 3.6.8
|
58
56
|
specification_version: 4
|
59
57
|
summary: DebugTrace-rb is a library that helps debug ruby programs.
|
60
58
|
test_files: []
|
@@ -1,24 +0,0 @@
|
|
1
|
-
# print_options.rb
|
2
|
-
# (C) 2023 Masato Kokubo
|
3
|
-
# Hold output option values.
|
4
|
-
class PrintOptions
|
5
|
-
# Initializes this object.
|
6
|
-
# @param force_reflection (bool): If true, outputs using reflection even if it has a @_str__or @_repr__ method
|
7
|
-
# @param output_private (bool): If true, also outputs private members when using reflection
|
8
|
-
# @param output_method (bool): If true, also outputs method members when using reflection
|
9
|
-
# @param collection_limit (Integer): Output limit of collection elements (overrides debugtarace.ini value)
|
10
|
-
# @param string_limit (Integer): Output limit of string characters (overrides debugtarace.ini value)
|
11
|
-
# @param bytes_limit (Integer): Output limit of byte array elements (overrides debugtarace.ini value)
|
12
|
-
# @param reflection_nest_limit (Integer): Nest limits when using reflection (overrides debugtarace.ini value)
|
13
|
-
def initialize(
|
14
|
-
force_reflection, output_private, output_method,
|
15
|
-
collection_limit, string_limit, bytes_limit, reflection_nest_limit)
|
16
|
-
@force_reflection = force_reflection
|
17
|
-
@output_private = output_private
|
18
|
-
@output_method = output_method
|
19
|
-
@collection_limit = collection_limit == -1 ? @config.collection_limit : collection_limit
|
20
|
-
@string_limit = string_limit == -1 ? @config.string_limit : string_limit
|
21
|
-
@bytes_limit = bytes_limit == -1 ? @config.bytes_limit : bytes_limit
|
22
|
-
@reflection_nest_limit = reflection_nest_limit == -1 ? @config.reflection_nest_limit : reflection_nest_limit
|
23
|
-
end
|
24
|
-
end
|
data/lib/temp.rb
DELETED
@@ -1,74 +0,0 @@
|
|
1
|
-
class DebugTrace
|
2
|
-
# Outputs an entering log when initializing and outputs a leaving log when deleting.
|
3
|
-
#
|
4
|
-
# 初期化時に進入ログを出力し、削除時に退出ログを出力します。
|
5
|
-
|
6
|
-
attr_accessor :name, :filename, :lineno
|
7
|
-
|
8
|
-
def initialize(invoker)
|
9
|
-
return unless @@config.is_enabled
|
10
|
-
|
11
|
-
Mutex.new.synchronize do
|
12
|
-
print_start
|
13
|
-
|
14
|
-
state = current_state
|
15
|
-
if invoker.nil?
|
16
|
-
@name = ''
|
17
|
-
else
|
18
|
-
@name = invoker.class.name
|
19
|
-
if @name == 'Class'
|
20
|
-
@name = invoker.to_s
|
21
|
-
end
|
22
|
-
@name += '.'
|
23
|
-
end
|
24
|
-
|
25
|
-
# frame_summary = get_frame_summary(4)
|
26
|
-
location = caller_locations(1, 1)[0]
|
27
|
-
@name += location.base_label
|
28
|
-
@filename = File.basename(location.absolute_path)
|
29
|
-
@lineno = location.lineno
|
30
|
-
|
31
|
-
# parent_frame_summary = get_frame_summary(5)
|
32
|
-
parent_location = caller_locations(2, 2)[0]
|
33
|
-
parent_filename = File.basename(parent_location.absolute_path)
|
34
|
-
parent_lineno = parent_location.lineno
|
35
|
-
|
36
|
-
indent_string = get_indent_string(state.nest_level, 0)
|
37
|
-
if state.nest_level < state.previous_nest_level || _last_print_buff.is_multi_lines
|
38
|
-
_logger.print(indent_string) # Empty Line
|
39
|
-
end
|
40
|
-
|
41
|
-
_last_print_buff = LogBuffer.new(@@config.maximum_data_output_width)
|
42
|
-
_last_print_buff.no_break_append(
|
43
|
-
@@config.enter_format % [@name, @filename, @lineno, parent_filename, parent_lineno]
|
44
|
-
)
|
45
|
-
_last_print_buff.line_feed
|
46
|
-
_logger.print(indent_string + _last_print_buff.lines[0][1])
|
47
|
-
|
48
|
-
state.up_nest
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
def finalize
|
53
|
-
return unless @@config.is_enabled
|
54
|
-
|
55
|
-
Mutex.new.synchronize do
|
56
|
-
print_start
|
57
|
-
|
58
|
-
state = current_state
|
59
|
-
|
60
|
-
if _last_print_buff.is_multi_lines
|
61
|
-
_logger.print(get_indent_string(state.nest_level, 0)) # Empty Line
|
62
|
-
end
|
63
|
-
|
64
|
-
time = Time.now.utc - state.down_nest
|
65
|
-
|
66
|
-
_last_print_buff = LogBuffer.new(@@config.maximum_data_output_width)
|
67
|
-
_last_print_buff.no_break_append(
|
68
|
-
@@config.leave_format % [@name, @filename, @lineno, time]
|
69
|
-
)
|
70
|
-
_last_print_buff.line_feed
|
71
|
-
_logger.print(get_indent_string(state.nest_level, 0) + _last_print_buff.lines[0][1])
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|