semantic_logger 4.3.1 → 4.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/semantic_logger.rb +7 -1
- data/lib/semantic_logger/appender.rb +3 -0
- data/lib/semantic_logger/appender/async.rb +29 -10
- data/lib/semantic_logger/appender/rabbitmq.rb +120 -0
- data/lib/semantic_logger/appenders.rb +89 -0
- data/lib/semantic_logger/base.rb +3 -3
- data/lib/semantic_logger/concerns/compatibility.rb +2 -2
- data/lib/semantic_logger/formatters.rb +1 -0
- data/lib/semantic_logger/formatters/base.rb +28 -6
- data/lib/semantic_logger/formatters/color.rb +4 -3
- data/lib/semantic_logger/formatters/fluentd.rb +37 -0
- data/lib/semantic_logger/formatters/json.rb +4 -2
- data/lib/semantic_logger/formatters/raw.rb +2 -2
- data/lib/semantic_logger/formatters/signalfx.rb +4 -3
- data/lib/semantic_logger/levels.rb +38 -0
- data/lib/semantic_logger/log.rb +11 -6
- data/lib/semantic_logger/loggable.rb +1 -1
- data/lib/semantic_logger/logger.rb +43 -1
- data/lib/semantic_logger/processor.rb +10 -130
- data/lib/semantic_logger/reporters/minitest.rb +49 -0
- data/lib/semantic_logger/semantic_logger.rb +40 -75
- data/lib/semantic_logger/version.rb +1 -1
- metadata +9 -81
- data/test/appender/async_batch_test.rb +0 -60
- data/test/appender/async_test.rb +0 -44
- data/test/appender/bugsnag_test.rb +0 -81
- data/test/appender/elasticsearch_http_test.rb +0 -74
- data/test/appender/elasticsearch_test.rb +0 -248
- data/test/appender/file_test.rb +0 -120
- data/test/appender/graylog_test.rb +0 -82
- data/test/appender/honeybadger_test.rb +0 -45
- data/test/appender/http_test.rb +0 -63
- data/test/appender/kafka_test.rb +0 -35
- data/test/appender/mongodb_test.rb +0 -104
- data/test/appender/new_relic_test.rb +0 -80
- data/test/appender/newrelic_rpm.rb +0 -14
- data/test/appender/sentry_test.rb +0 -47
- data/test/appender/splunk_http_test.rb +0 -79
- data/test/appender/splunk_test.rb +0 -83
- data/test/appender/syslog_test.rb +0 -61
- data/test/appender/tcp_test.rb +0 -66
- data/test/appender/udp_test.rb +0 -59
- data/test/appender/wrapper_test.rb +0 -95
- data/test/concerns/compatibility_test.rb +0 -117
- data/test/debug_as_trace_logger_test.rb +0 -81
- data/test/formatters/color_test.rb +0 -153
- data/test/formatters/default_test.rb +0 -175
- data/test/formatters/one_line_test.rb +0 -60
- data/test/formatters/signalfx_test.rb +0 -197
- data/test/formatters_test.rb +0 -36
- data/test/in_memory_appender.rb +0 -8
- data/test/in_memory_appender_helper.rb +0 -43
- data/test/in_memory_batch_appender.rb +0 -8
- data/test/in_memory_metrics_appender.rb +0 -13
- data/test/loggable_test.rb +0 -103
- data/test/logger_test.rb +0 -334
- data/test/measure_test.rb +0 -346
- data/test/metric/new_relic_test.rb +0 -35
- data/test/metric/signalfx_test.rb +0 -77
- data/test/semantic_logger_test.rb +0 -303
- data/test/test_helper.rb +0 -31
@@ -1,35 +0,0 @@
|
|
1
|
-
# So that the NewRelic appender will load the mock
|
2
|
-
$LOAD_PATH.unshift File.dirname(__FILE__)
|
3
|
-
require_relative '../test_helper'
|
4
|
-
|
5
|
-
# Unit Test for SemanticLogger::Appender::NewRelic
|
6
|
-
module Metric
|
7
|
-
class NewRelicTest < Minitest::Test
|
8
|
-
describe SemanticLogger::Appender::NewRelic do
|
9
|
-
before do
|
10
|
-
@appender = SemanticLogger::Metric::NewRelic.new
|
11
|
-
@message = 'AppenderNewRelicTest log message'
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'logs counter metric' do
|
15
|
-
name = amount = nil
|
16
|
-
NewRelic::Agent.stub(:increment_metric, ->(name_, amount_) { name = name_, amount = amount_ }) do
|
17
|
-
@appender.info(message: @message, metric: 'User/authenticated')
|
18
|
-
end
|
19
|
-
assert_equal 'Custom/User/authenticated', name.first
|
20
|
-
assert_equal 1, amount
|
21
|
-
end
|
22
|
-
|
23
|
-
it 'logs duration metric' do
|
24
|
-
name = duration = nil
|
25
|
-
NewRelic::Agent.stub(:record_metric, ->(name_, duration_) { name = name_, duration = duration_ }) do
|
26
|
-
@appender.measure_info(message: @message, metric: 'User/authenticate') do
|
27
|
-
sleep 0.001
|
28
|
-
end
|
29
|
-
end
|
30
|
-
assert_equal 'Custom/User/authenticate', name.first
|
31
|
-
assert duration
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
end
|
@@ -1,77 +0,0 @@
|
|
1
|
-
require_relative '../test_helper'
|
2
|
-
|
3
|
-
module Appender
|
4
|
-
class SignalfxTest < Minitest::Test
|
5
|
-
describe SemanticLogger::Metric::Signalfx do
|
6
|
-
before do
|
7
|
-
@metric = '/user/login'
|
8
|
-
@log = SemanticLogger::Log.new('User', :debug)
|
9
|
-
@log.metric = @metric
|
10
|
-
end
|
11
|
-
|
12
|
-
let :appender do
|
13
|
-
if ENV['SIGNALFX_TOKEN']
|
14
|
-
SemanticLogger::Metric::Signalfx.new(token: ENV['SIGNALFX_TOKEN'])
|
15
|
-
else
|
16
|
-
Net::HTTP.stub_any_instance(:start, true) do
|
17
|
-
@appender = SemanticLogger::Metric::Signalfx.new(token: 'TEST')
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
describe 'log message' do
|
23
|
-
let :response do
|
24
|
-
# Do not stub if the token is available in the environment
|
25
|
-
if ENV['SIGNALFX_TOKEN']
|
26
|
-
appender.log(@log)
|
27
|
-
else
|
28
|
-
response_mock = Struct.new(:code, :body)
|
29
|
-
request = nil
|
30
|
-
appender.http.stub(:request, ->(r) { request = r; response_mock.new('200', 'ok') }) do
|
31
|
-
appender.log(@log)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
it 'send counter metric when there is no duration' do
|
37
|
-
assert response
|
38
|
-
end
|
39
|
-
|
40
|
-
it 'send custom counter metric when there is no duration' do
|
41
|
-
@log.metric = 'Filter/count'
|
42
|
-
@log.dimensions = {action: 'hit', user: 'jbloggs', state: 'FL'}
|
43
|
-
assert response
|
44
|
-
end
|
45
|
-
|
46
|
-
it 'send gauge metric when log includes duration' do
|
47
|
-
@log.duration = 1234
|
48
|
-
assert response
|
49
|
-
end
|
50
|
-
|
51
|
-
it 'whitelists dimensions' do
|
52
|
-
@log.named_tags = {user_id: 47, application: 'sample', tracking_number: 7474, session_id: 'hsdhngsd'}
|
53
|
-
appender.formatter.dimensions = %i[user_id application]
|
54
|
-
assert response
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
describe 'should_log?' do
|
59
|
-
it 'logs metric only metric' do
|
60
|
-
assert appender.should_log?(@log)
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'not logs when no metric' do
|
64
|
-
@log.message = 'blah'
|
65
|
-
@log.metric = nil
|
66
|
-
refute appender.should_log?(@log)
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'logs metric only metric with dimensions' do
|
70
|
-
@log.metric = 'Filter/count'
|
71
|
-
@log.dimensions = {action: 'hit', user: 'jbloggs', state: 'FL'}
|
72
|
-
assert appender.should_log?(@log)
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
end
|
@@ -1,303 +0,0 @@
|
|
1
|
-
require_relative 'test_helper'
|
2
|
-
|
3
|
-
class SemanticLoggerTest < Minitest::Test
|
4
|
-
describe SemanticLogger do
|
5
|
-
describe '.add_appender' do
|
6
|
-
before do
|
7
|
-
@appender = nil
|
8
|
-
end
|
9
|
-
|
10
|
-
after do
|
11
|
-
SemanticLogger.remove_appender(@appender)
|
12
|
-
File.delete('sample.log') if File.exist?('sample.log')
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'adds file appender' do
|
16
|
-
@appender = SemanticLogger.add_appender(file_name: 'sample.log')
|
17
|
-
assert @appender.is_a?(SemanticLogger::Appender::File)
|
18
|
-
assert SemanticLogger.appenders.include?(@appender)
|
19
|
-
assert @appender.formatter.is_a?(SemanticLogger::Formatters::Default)
|
20
|
-
end
|
21
|
-
|
22
|
-
it 'adds file appender with json format' do
|
23
|
-
@appender = SemanticLogger.add_appender(file_name: 'sample.log', formatter: :json)
|
24
|
-
assert @appender.is_a?(SemanticLogger::Appender::File)
|
25
|
-
assert SemanticLogger.appenders.include?(@appender)
|
26
|
-
assert @appender.formatter.is_a?(SemanticLogger::Formatters::Json), @appender.formatter.inspect
|
27
|
-
end
|
28
|
-
|
29
|
-
it 'adds stream appender' do
|
30
|
-
@appender = SemanticLogger.add_appender(io: STDOUT)
|
31
|
-
assert @appender.is_a?(SemanticLogger::Appender::File)
|
32
|
-
assert SemanticLogger.appenders.include?(@appender)
|
33
|
-
end
|
34
|
-
|
35
|
-
it 'adds symbol appender' do
|
36
|
-
@appender = SemanticLogger.add_appender(appender: :wrapper, logger: Logger.new(STDOUT))
|
37
|
-
assert @appender.is_a?(SemanticLogger::Appender::Wrapper), -> { @appender.ai }
|
38
|
-
assert SemanticLogger.appenders.include?(@appender)
|
39
|
-
end
|
40
|
-
|
41
|
-
it 'adds symbol appender with underscores' do
|
42
|
-
@appender = SemanticLogger.add_appender(appender: :new_relic)
|
43
|
-
assert @appender.is_a?(SemanticLogger::Appender::NewRelic), -> { @appender.ai }
|
44
|
-
assert SemanticLogger.appenders.include?(@appender)
|
45
|
-
end
|
46
|
-
|
47
|
-
it 'adds logger wrapper appender' do
|
48
|
-
@appender = SemanticLogger.add_appender(logger: ::Logger.new(STDOUT))
|
49
|
-
assert @appender.is_a?(SemanticLogger::Appender::Wrapper)
|
50
|
-
assert @appender.logger.is_a?(::Logger)
|
51
|
-
assert SemanticLogger.appenders.include?(@appender)
|
52
|
-
assert @appender.formatter.is_a?(SemanticLogger::Formatters::Default)
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'adds logger wrapper appender with color formatter' do
|
56
|
-
@appender = SemanticLogger.add_appender(logger: ::Logger.new(STDOUT), formatter: :color)
|
57
|
-
assert @appender.is_a?(SemanticLogger::Appender::Wrapper)
|
58
|
-
assert @appender.logger.is_a?(::Logger)
|
59
|
-
assert SemanticLogger.appenders.include?(@appender)
|
60
|
-
assert @appender.formatter.is_a?(SemanticLogger::Formatters::Color)
|
61
|
-
end
|
62
|
-
|
63
|
-
it 'adds appender' do
|
64
|
-
@appender = SemanticLogger.add_appender(appender: SemanticLogger::Appender::File.new(io: STDOUT))
|
65
|
-
assert @appender.is_a?(SemanticLogger::Appender::File), @appender.ai
|
66
|
-
assert SemanticLogger.appenders.include?(@appender)
|
67
|
-
end
|
68
|
-
|
69
|
-
it 'fails to add invalid logger appender' do
|
70
|
-
assert_raises do
|
71
|
-
SemanticLogger.add_appender(logger: 'blah')
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
describe '.add_appender DEPRECATED' do
|
77
|
-
before do
|
78
|
-
@appender = nil
|
79
|
-
end
|
80
|
-
|
81
|
-
after do
|
82
|
-
SemanticLogger.remove_appender(@appender) if @appender
|
83
|
-
File.delete('sample.log') if File.exist?('sample.log')
|
84
|
-
end
|
85
|
-
|
86
|
-
it 'adds file appender' do
|
87
|
-
@appender = SemanticLogger.add_appender('sample.log')
|
88
|
-
assert @appender.is_a?(SemanticLogger::Appender::File)
|
89
|
-
assert SemanticLogger.appenders.include?(@appender)
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'adds stream appender' do
|
93
|
-
@appender = SemanticLogger.add_appender(STDOUT)
|
94
|
-
assert @appender.is_a?(SemanticLogger::Appender::File)
|
95
|
-
assert SemanticLogger.appenders.include?(@appender)
|
96
|
-
end
|
97
|
-
|
98
|
-
it 'adds appender' do
|
99
|
-
@appender = SemanticLogger.add_appender(SemanticLogger::Appender::File.new(io: STDOUT))
|
100
|
-
assert @appender.is_a?(SemanticLogger::Appender::File), @appender.ai
|
101
|
-
assert SemanticLogger.appenders.include?(@appender)
|
102
|
-
end
|
103
|
-
|
104
|
-
it 'adds logger wrapper appender' do
|
105
|
-
@appender = SemanticLogger.add_appender(::Logger.new(STDOUT))
|
106
|
-
assert @appender.is_a?(SemanticLogger::Appender::Wrapper)
|
107
|
-
assert @appender.logger.is_a?(::Logger)
|
108
|
-
assert SemanticLogger.appenders.include?(@appender)
|
109
|
-
end
|
110
|
-
|
111
|
-
it 'fails to add invalid logger appender' do
|
112
|
-
assert_raises do
|
113
|
-
SemanticLogger.add_appender(logger: 'blah')
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
describe 'mock_logger' do
|
119
|
-
include InMemoryAppenderHelper
|
120
|
-
|
121
|
-
describe '.tagged' do
|
122
|
-
it 'add tags to log entries' do
|
123
|
-
SemanticLogger.tagged('12345', 'DJHSFK') do
|
124
|
-
logger.info('Hello world')
|
125
|
-
|
126
|
-
assert log = log_message
|
127
|
-
assert_equal %w[12345 DJHSFK], log.tags
|
128
|
-
end
|
129
|
-
end
|
130
|
-
|
131
|
-
it 'add embedded tags to log entries' do
|
132
|
-
SemanticLogger.tagged('First Level', 'tags') do
|
133
|
-
SemanticLogger.tagged('Second Level') do
|
134
|
-
logger.info('Hello world')
|
135
|
-
|
136
|
-
assert log = log_message
|
137
|
-
assert_equal ['First Level', 'tags', 'Second Level'], log.tags
|
138
|
-
end
|
139
|
-
assert_equal 2, SemanticLogger.tags.count, SemanticLogger.tags
|
140
|
-
assert_equal 'First Level', SemanticLogger.tags.first
|
141
|
-
assert_equal 'tags', SemanticLogger.tags.last
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'also supports named tagging' do
|
146
|
-
SemanticLogger.tagged(level1: 1) do
|
147
|
-
assert_equal({level1: 1}, SemanticLogger.named_tags)
|
148
|
-
SemanticLogger.tagged(level2: 2, more: 'data') do
|
149
|
-
assert_equal({level1: 1, level2: 2, more: 'data'}, SemanticLogger.named_tags)
|
150
|
-
SemanticLogger.tagged(level3: 3) do
|
151
|
-
assert_equal({level1: 1, level2: 2, more: 'data', level3: 3}, SemanticLogger.named_tags)
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
describe '.named_tags' do
|
159
|
-
it 'returns named tags in creation order' do
|
160
|
-
SemanticLogger.named_tagged(level1: 1) do
|
161
|
-
assert_equal({level1: 1}, SemanticLogger.named_tags)
|
162
|
-
SemanticLogger.named_tagged(level2: 2, more: 'data') do
|
163
|
-
assert_equal({level1: 1, level2: 2, more: 'data'}, SemanticLogger.named_tags)
|
164
|
-
SemanticLogger.named_tagged(level3: 3) do
|
165
|
-
assert_equal({level1: 1, level2: 2, more: 'data', level3: 3}, SemanticLogger.named_tags)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
describe '.named_tagged' do
|
173
|
-
it 'logs named tags in creation order' do
|
174
|
-
SemanticLogger.named_tagged(level1: 1) do
|
175
|
-
SemanticLogger.named_tagged(level2: 2, more: 'data') do
|
176
|
-
SemanticLogger.named_tagged(level3: 3) do
|
177
|
-
logger.info('Hello world')
|
178
|
-
|
179
|
-
assert log = log_message
|
180
|
-
assert_equal({level1: 1, level2: 2, more: 'data', level3: 3}, log.named_tags)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
end
|
184
|
-
end
|
185
|
-
end
|
186
|
-
|
187
|
-
describe '.fast_tag' do
|
188
|
-
it 'add string tag to log entries' do
|
189
|
-
logger.fast_tag('12345') do
|
190
|
-
logger.info('Hello world')
|
191
|
-
|
192
|
-
assert log = log_message
|
193
|
-
assert_equal %w[12345], log.tags
|
194
|
-
end
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
describe '.default_level' do
|
199
|
-
before do
|
200
|
-
SemanticLogger.default_level = :debug
|
201
|
-
end
|
202
|
-
|
203
|
-
it 'not log at a level below the global default' do
|
204
|
-
assert_equal :debug, SemanticLogger.default_level
|
205
|
-
assert_equal :debug, logger.level
|
206
|
-
logger.trace('hello world')
|
207
|
-
|
208
|
-
refute log_message
|
209
|
-
end
|
210
|
-
|
211
|
-
it 'log at the instance level' do
|
212
|
-
assert_equal :debug, SemanticLogger.default_level
|
213
|
-
logger.level = :trace
|
214
|
-
assert_equal :trace, logger.level
|
215
|
-
logger.trace('hello world')
|
216
|
-
|
217
|
-
assert log = log_message
|
218
|
-
assert_equal :trace, log.level
|
219
|
-
assert_equal 'hello world', log.message
|
220
|
-
end
|
221
|
-
|
222
|
-
it 'not log at a level below the instance level' do
|
223
|
-
assert_equal :debug, SemanticLogger.default_level
|
224
|
-
logger.level = :warn
|
225
|
-
assert_equal :warn, logger.level
|
226
|
-
logger.debug('hello world')
|
227
|
-
|
228
|
-
refute log_message
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
describe '.silence' do
|
233
|
-
before do
|
234
|
-
SemanticLogger.default_level = :info
|
235
|
-
end
|
236
|
-
|
237
|
-
it 'not log at a level below the silence level' do
|
238
|
-
assert_equal :info, SemanticLogger.default_level
|
239
|
-
assert_equal :info, logger.level
|
240
|
-
logger.silence do
|
241
|
-
logger.warn('hello world')
|
242
|
-
logger.info('hello world')
|
243
|
-
logger.debug('hello world')
|
244
|
-
logger.trace('hello world')
|
245
|
-
end
|
246
|
-
|
247
|
-
refute log_message
|
248
|
-
end
|
249
|
-
|
250
|
-
it 'log at the instance level even with the silencer at a higher level' do
|
251
|
-
logger.level = :trace
|
252
|
-
assert_equal :trace, logger.level
|
253
|
-
logger.silence do
|
254
|
-
logger.trace('hello world')
|
255
|
-
end
|
256
|
-
|
257
|
-
assert log = log_message
|
258
|
-
assert_equal :trace, log.level
|
259
|
-
assert_equal 'hello world', log.message
|
260
|
-
end
|
261
|
-
|
262
|
-
it 'log at a silence level below the default level' do
|
263
|
-
assert_equal :info, SemanticLogger.default_level
|
264
|
-
assert_equal :info, logger.level
|
265
|
-
logger.silence(:debug) do
|
266
|
-
logger.debug('hello world')
|
267
|
-
end
|
268
|
-
|
269
|
-
assert log = log_message
|
270
|
-
assert_equal :debug, log.level
|
271
|
-
assert_equal 'hello world', log.message
|
272
|
-
end
|
273
|
-
end
|
274
|
-
|
275
|
-
describe '.on_log' do
|
276
|
-
before do
|
277
|
-
SemanticLogger.default_level = :info
|
278
|
-
end
|
279
|
-
|
280
|
-
after do
|
281
|
-
SemanticLogger::Processor.instance.appender.log_subscribers = nil
|
282
|
-
end
|
283
|
-
|
284
|
-
it 'registers a log listener' do
|
285
|
-
SemanticLogger.on_log do |log|
|
286
|
-
log.set_context(:custom_info, 'test')
|
287
|
-
end
|
288
|
-
|
289
|
-
assert_equal :info, SemanticLogger.default_level
|
290
|
-
assert_equal :info, logger.level
|
291
|
-
logger.silence(:debug) do
|
292
|
-
logger.debug('hello world')
|
293
|
-
end
|
294
|
-
|
295
|
-
assert log = log_message
|
296
|
-
assert_equal :debug, log.level
|
297
|
-
assert_equal 'hello world', log.message
|
298
|
-
assert_equal 'test', log.context[:custom_info]
|
299
|
-
end
|
300
|
-
end
|
301
|
-
end
|
302
|
-
end
|
303
|
-
end
|
data/test/test_helper.rb
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
# Allow test to be run in-place without requiring a gem install
|
2
|
-
$LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
|
3
|
-
|
4
|
-
# Configure Rails Environment
|
5
|
-
ENV['RAILS_ENV'] = 'test'
|
6
|
-
|
7
|
-
require 'minitest/autorun'
|
8
|
-
require 'minitest/reporters'
|
9
|
-
require 'minitest/stub_any_instance'
|
10
|
-
require 'semantic_logger'
|
11
|
-
# require 'logger'
|
12
|
-
require_relative 'in_memory_appender'
|
13
|
-
require_relative 'in_memory_batch_appender'
|
14
|
-
require_relative 'in_memory_metrics_appender'
|
15
|
-
require_relative 'in_memory_appender_helper'
|
16
|
-
require 'awesome_print'
|
17
|
-
|
18
|
-
# Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new
|
19
|
-
class Minitest::Test
|
20
|
-
# Use AwesomePrint to display diffs
|
21
|
-
define_method :mu_pp, &:awesome_inspect
|
22
|
-
|
23
|
-
# Use AwesomePrint to display messages
|
24
|
-
def message(msg = nil, ending = nil)
|
25
|
-
proc {
|
26
|
-
msg = msg.call.chomp('.') if Proc === msg
|
27
|
-
custom_message = "#{msg.ai}.\n" unless msg.nil? || msg.to_s.empty?
|
28
|
-
"#{custom_message}#{yield}#{ending || '.'}"
|
29
|
-
}
|
30
|
-
end
|
31
|
-
end
|