pantheios-ruby 0.9.6

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.
@@ -0,0 +1,446 @@
1
+
2
+ # ######################################################################## #
3
+ # File: lib/pantheios/core.rb
4
+ #
5
+ # Purpose: The Pantheios.Ruby core (::Pantheios::Core)
6
+ #
7
+ # Created: 2nd April 2011
8
+ # Updated: 25th December 2017
9
+ #
10
+ # Home: http://github.com/synesissoftware/Pantheios-Ruby
11
+ #
12
+ # Author: Matthew Wilson
13
+ #
14
+ # Copyright (c) 2011-2017, Matthew Wilson and Synesis Software
15
+ # All rights reserved.
16
+ #
17
+ # Redistribution and use in source and binary forms, with or without
18
+ # modification, are permitted provided that the following conditions are
19
+ # met:
20
+ #
21
+ # * Redistributions of source code must retain the above copyright
22
+ # notice, this list of conditions and the following disclaimer.
23
+ #
24
+ # * Redistributions in binary form must reproduce the above copyright
25
+ # notice, this list of conditions and the following disclaimer in the
26
+ # documentation and/or other materials provided with the distribution.
27
+ #
28
+ # * Neither the names of the copyright holder nor the names of its
29
+ # contributors may be used to endorse or promote products derived from
30
+ # this software without specific prior written permission.
31
+ #
32
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
44
+ # ######################################################################## #
45
+
46
+
47
+ require 'pantheios/globals'
48
+
49
+ require 'pantheios/application_layer/stock_severity_levels'
50
+ require 'pantheios/util/process_util'
51
+
52
+ require 'pantheios/services/simple_console_service'
53
+
54
+ =begin
55
+ =end
56
+
57
+ module Pantheios
58
+ module Core
59
+
60
+ module Constants_
61
+
62
+ REQUIRED_SERVICE_METHODS = %w{ severity_logged? log }.map { |name| name.to_sym }
63
+ REQUIRED_FRONTEND_METHODS = %w{ severity_logged? }.map { |name| name.to_sym }
64
+ REQUIRED_BACKEND_METHODS = %w{ log }.map { |name| name.to_sym }
65
+
66
+ end # module Constants_
67
+
68
+ module Internals_
69
+
70
+ class DefaultDiscriminator
71
+
72
+ def severity_logged? severity
73
+
74
+ return true if $DEBUG
75
+
76
+ levels = ::Pantheios::ApplicationLayer::StockSeverityLevels::STOCK_SEVERITY_LEVEL_VALUES
77
+
78
+ v_info = levels[:informational]
79
+ v_sev = levels[severity] if ::Symbol === severity
80
+
81
+ return false if v_sev > v_info
82
+
83
+ true
84
+ end
85
+ end
86
+
87
+ # :nodoc:
88
+ class State
89
+
90
+ def initialize default_fe
91
+
92
+ @mx_service = Mutex.new
93
+ @front_end = nil
94
+ @back_end = nil
95
+ @requires_prefix = false;
96
+ @default_fe = default_fe
97
+ end
98
+
99
+ def set_front_end fe
100
+
101
+ raise ::TypeError, "front-end instance (#{fe.class}) does not respond to all the required messages (#{Constants_::REQUIRED_FRONTEND_METHODS.join(', ')})" unless fe && Constants_::REQUIRED_FRONTEND_METHODS.all? { |m| fe.respond_to? m }
102
+
103
+ r = nil
104
+
105
+ fe ||= @default_fe
106
+
107
+ @mx_service.synchronize do
108
+
109
+ r, @front_end = @front_end, fe
110
+ end
111
+
112
+ r = nil if r.object_id == @default_fe.object_id
113
+
114
+ return r
115
+ end
116
+
117
+ def set_back_end be
118
+
119
+ raise ::TypeError, "back-end instance (#{fe.class}) does not respond to all the required messages (#{Constants_::REQUIRED_BACKEND_METHODS.join(', ')})" unless be && Constants_::REQUIRED_BACKEND_METHODS.all? { |m| be.respond_to? m }
120
+
121
+ r = nil
122
+ srp = svc.respond_to?(:requires_prefix?) ? svc.requires_prefix? : true
123
+
124
+ @mx_service.synchronize do
125
+
126
+ r, @back_end, @requires_prefix = @back_end, be, srp
127
+ end
128
+
129
+ return r
130
+ end
131
+
132
+ def set_service svc
133
+
134
+ raise ::TypeError, "service instance (#{svc.class}) does not respond to all the required messages (#{Constants_::REQUIRED_SERVICE_METHODS.join(', ')})" unless svc && Constants_::REQUIRED_SERVICE_METHODS.all? { |m| svc.respond_to? m }
135
+
136
+ r = []
137
+ srp = svc.respond_to?(:requires_prefix?) ? svc.requires_prefix? : true
138
+
139
+ @mx_service.synchronize do
140
+
141
+ r << @front_end
142
+ r << @back_end
143
+
144
+ @front_end, @back_end, @requires_prefix = svc, svc, srp
145
+ end
146
+
147
+ return r
148
+ end
149
+
150
+ def severity_logged? severity
151
+
152
+ @mx_service.synchronize do
153
+
154
+ return nil unless @front_end
155
+
156
+ @front_end.severity_logged? severity
157
+ end
158
+ end
159
+
160
+ def log
161
+
162
+ end
163
+
164
+ def discriminator
165
+
166
+ @mx_service.synchronize do
167
+
168
+ if @service && @service.respond_to?(:severity_logged?)
169
+
170
+ return @service
171
+ end
172
+
173
+ @front_end
174
+ end
175
+ end
176
+
177
+ attr_reader :service
178
+ attr_reader :front_end
179
+ attr_reader :back_end
180
+ def requires_prefix?; @requires_prefix; end
181
+ end
182
+ end # module Internals_
183
+
184
+ def self.included receiver
185
+
186
+ abort "Attempt to include #{self} into #{receiver}. This is not allowed"
187
+ end
188
+
189
+ # :nodoc:
190
+ def self.core_init
191
+
192
+ @@state = Internals_::State.new Internals_::DefaultDiscriminator.new
193
+
194
+ self.set_default_service
195
+ end
196
+
197
+ # :nodoc:
198
+ def self.set_default_service **options
199
+
200
+ # determine which log service to initialise as the default
201
+
202
+ (::Pantheios::Globals.INITIAL_SERVICE_INSTANCES || []).each do |inst|
203
+
204
+ next unless inst
205
+
206
+ return @@state.set_service inst
207
+ end
208
+
209
+ (::Pantheios::Globals.INITIAL_SERVICE_CLASSES || []).each do |cls|
210
+
211
+ inst = cls.new
212
+
213
+ return @@state.set_service inst
214
+ end
215
+
216
+ @@state.set_service ::Pantheios::Services::SimpleConsoleService.new
217
+ end
218
+
219
+ # Sets the front-end that will be used to evaluate whether a given log
220
+ # statement will be logged
221
+ #
222
+ # * *Parameters:*
223
+ # - +fe+ The front-end instance. It must respond to the
224
+ # +severity_logged?+ message, or a ::TypeError will be raised
225
+ #
226
+ # * *Returns:*
227
+ # The previously registered instance, or +nil+ if no previous one was
228
+ # registered
229
+ def self.set_front_end fe
230
+
231
+ @@state.set_front_end fe
232
+ end
233
+
234
+ # Sets the back-end used to emit the given log statement
235
+ #
236
+ # * *Parameters:*
237
+ # - +be+ The back-end instance. It must respond to the
238
+ # +log+ message, or a ::TypeError will be raised. It may also respond
239
+ # to the +requires_prefix?+ message, which can be used to indicate
240
+ # whether a prepared prefix is required; if not present, the
241
+ # framework assumes that the back-end requires a prefix
242
+ #
243
+ # * *Returns:*
244
+ # The previously registered instance, or +nil+ if no previous one was
245
+ # registered
246
+ def self.set_back_end be
247
+
248
+ @@state.set_back_end be
249
+ end
250
+
251
+ # Sets the service that will be used to evaluate whether a given log
252
+ # statement will be logged and to emit it
253
+ #
254
+ # * *Parameters:*
255
+ # - +svc+ The service instance. It must respond to the
256
+ # +severity_logged?+ and +log+ messages, or a ::TypeError will be
257
+ # raised. It may also respond to the +requires_prefix?+ message,
258
+ # which can be used to indicate whether a prepared prefix is
259
+ # required; if not present, the framework assumes that the service
260
+ # (back-end) requires a prefix
261
+ #
262
+ # * *Returns:*
263
+ # An array of two elements, representing the previous front-end and
264
+ # previous back-end
265
+ def self.set_service svc
266
+
267
+ @@state.set_service svc
268
+ end
269
+
270
+ self.core_init
271
+
272
+ # :nodoc:
273
+ def self.register_include includee, includer
274
+
275
+ $stderr.puts "#{includee} included into #{includer}" if $DEBUG
276
+ end
277
+
278
+ # Default implementation to determine whether the given severity is
279
+ # logged
280
+ #
281
+ # * *Returns:*
282
+ # If +$DEBUG+ is +true+, then returns +true+ - all statements are
283
+ # emitted in debug mode. In normal operation, if the integral value of
284
+ # +severity+ is greater than that of +:informational+ then it returns
285
+ # +false+; otherwise it return +true+
286
+ def self.severity_logged? severity
287
+
288
+ @@state.severity_logged? severity
289
+ end
290
+
291
+ # Default implementation to obtain the process id
292
+ #
293
+ # * *Returns:*
294
+ # +Process.pid+
295
+ def self.process_id
296
+
297
+ Process.pid
298
+ end
299
+
300
+ # Default implementation to obtain the program name
301
+ #
302
+ # * *Returns:*
303
+ # The file stem of +$0+
304
+ #
305
+ # NOTE: this is implemented in terms of Process_Util.derive_process_name
306
+ # and the result is cached
307
+ def self.program_name
308
+
309
+ @program_name ||= ::Pantheios::Util::ProcessUtil.derive_process_name $0
310
+ end
311
+
312
+ def self.severity_string severity
313
+
314
+ r = ApplicationLayer::StockSeverityLevels::STOCK_SEVERITY_LEVEL_STRINGS[severity] and return r
315
+
316
+ severity.to_s
317
+ end
318
+
319
+ # Default implementation to obtain the thread_id
320
+ #
321
+ # * *Returns:*
322
+ # From the current thread either the value obtained via the attribute
323
+ # +thread_name+ (if it responds to that) or via +object_id+
324
+ def self.thread_id
325
+
326
+ t = Thread.current
327
+
328
+ return t.thread_name if t.respond_to? :thread_name
329
+
330
+ t.object_id
331
+ end
332
+
333
+ def self.timestamp_format
334
+
335
+ '%Y-%m-%d %H:%M:%S.%6N'
336
+ end
337
+
338
+ # Default implementation to obtain the timestamp according to a given
339
+ # format
340
+ #
341
+ # * *Parameters:*
342
+ # - +t+ [::Time] The time
343
+ # - +fmt+ [::String, nil] The format to be used. If +nil+ the value
344
+ # obtained by +timestamp_format+ is used
345
+ #
346
+ # * *Returns:*
347
+ # A string representing the time
348
+ def self.timestamp t, fmt
349
+
350
+ fmt ||= self.timestamp_format
351
+
352
+ t.strftime fmt
353
+ end
354
+
355
+
356
+ # Internal implementation method, not to be called by application code
357
+ def self.trace_v_prep prefix_provider, call_depth, argv
358
+
359
+ if ApplicationLayer::ParamNameList === argv[0]
360
+
361
+ self.trace_v_impl prefix_provider, 1 + call_depth, argv[0], :trace, argv[1..-1]
362
+ else
363
+
364
+ self.trace_v_impl prefix_provider, 1 + call_depth, nil, :trace, argv
365
+ end
366
+ end
367
+
368
+ # Internal implementation method, not to be called by application code
369
+ def self.trace_v_impl prefix_provider, call_depth, param_list, severity, argv
370
+
371
+ case param_list
372
+ when nil
373
+ ;
374
+ when ApplicationLayer::ParamNameList
375
+ ;
376
+ else
377
+
378
+ warn "param_list (#{param_list.class}) must be nil or an instance of #{ApplicationLayer::ParamNameList}" unless param_list
379
+ end
380
+
381
+ fl = nil
382
+ rx = nil
383
+ fn = caller(call_depth + 1, 1)[0]
384
+
385
+ if ::Class === prefix_provider
386
+
387
+ rx = "#{prefix_provider}::"
388
+ else
389
+
390
+ rx = "#{prefix_provider.class}#"
391
+ end
392
+
393
+ if false;
394
+ elsif fn =~ /(.+)\:in\s*\`(.+)\'\s*$/
395
+
396
+ fl = $1
397
+ fn = $2
398
+
399
+ f = "#{fl}: #{rx}#{fn}"
400
+ elsif fn =~ /.*in\s*\`(.+)\'\s*$/
401
+
402
+ f = $1
403
+ else
404
+
405
+ f = fn
406
+ end
407
+
408
+ if param_list
409
+
410
+ sig = ''
411
+
412
+ argv.each_with_index do |arg, index0|
413
+
414
+ n = param_list[index0]
415
+
416
+ s = arg.to_s
417
+ s = "'#{s}'" if s.index(/[,\s]/)
418
+
419
+ sig += ', ' unless sig.empty?
420
+
421
+ sig += n ? "#{n} (#{arg.class})=#{s}" : s
422
+ end
423
+ else
424
+
425
+ sig = argv.join(', ')
426
+ end
427
+
428
+ stmt = "#{f}(#{sig})"
429
+
430
+ self.log_raw prefix_provider, severity, stmt
431
+ end
432
+
433
+ # Internal implementation method, not to be called by application code
434
+ def self.log_raw prefix_provider, severity, message
435
+
436
+ now = Time.now
437
+
438
+ prf = @@state.requires_prefix? ? '[' + prefix_provider.prefix(now, severity) + ']: ' : nil
439
+
440
+ @@state.back_end.log severity, now, prf, message
441
+ end
442
+
443
+ end # Core
444
+ end # Pantheios
445
+
446
+
@@ -0,0 +1,109 @@
1
+
2
+ # ######################################################################## #
3
+ # File: lib/pantheios/globals.rb
4
+ #
5
+ # Purpose: The Pantheios.Ruby "globals" (::Pantheios::Globals)
6
+ #
7
+ # Created: 24th December 2017
8
+ # Updated: 24th December 2017
9
+ #
10
+ # Home: http://github.com/synesissoftware/Pantheios-Ruby
11
+ #
12
+ # Author: Matthew Wilson
13
+ #
14
+ # Copyright (c) 2017, Matthew Wilson and Synesis Software
15
+ # All rights reserved.
16
+ #
17
+ # Redistribution and use in source and binary forms, with or without
18
+ # modification, are permitted provided that the following conditions are
19
+ # met:
20
+ #
21
+ # * Redistributions of source code must retain the above copyright
22
+ # notice, this list of conditions and the following disclaimer.
23
+ #
24
+ # * Redistributions in binary form must reproduce the above copyright
25
+ # notice, this list of conditions and the following disclaimer in the
26
+ # documentation and/or other materials provided with the distribution.
27
+ #
28
+ # * Neither the names of the copyright holder nor the names of its
29
+ # contributors may be used to endorse or promote products derived from
30
+ # this software without specific prior written permission.
31
+ #
32
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
33
+ # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
34
+ # THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
35
+ # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
36
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
38
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
39
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
40
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
41
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
42
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
43
+ #
44
+ # ######################################################################## #
45
+
46
+
47
+ =begin
48
+ =end
49
+
50
+ module Pantheios
51
+ # A utility namespace for the sole purpose of defining "globals" - actually
52
+ # module constants' values and module attributes - that control the
53
+ # behaviour of Pantheios globally
54
+ #
55
+ # NOTE: The "globals" in this namespace are operative before
56
+ # +::Pantheios::Core+ and +::Pantheios::API+
57
+ module Globals
58
+
59
+ module Internals_
60
+
61
+ BOOLEAN_CLASSES = [ ::FalseClass, ::TrueClass ]
62
+ TRUTHY_CLASSES = BOOLEAN_CLASSES + [ ::NilClass ]
63
+ end
64
+
65
+ module Helpers_
66
+
67
+ def self.cattr receiver, name, types, initial_value
68
+
69
+ types = nil if !types.nil? && types.empty?
70
+
71
+ receiver.class_eval do
72
+
73
+ field_name = '@' + name
74
+
75
+ instance_variable_set field_name, initial_value
76
+
77
+ define_singleton_method(name) do
78
+
79
+ instance_variable_get field_name
80
+ end
81
+
82
+ define_singleton_method(name + '=') do |v|
83
+
84
+ if types
85
+
86
+ warn "Assigning to #{__method__} with argument of invalid type - #{v.class} given; one of #{types.join(', ')} required" unless types.any? { |c| c === v }
87
+ end
88
+
89
+ instance_variable_set field_name, v
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ Helpers_.cattr self, 'HAS_CASCADED_INCLUDES', Internals_::BOOLEAN_CLASSES, true
96
+
97
+ Helpers_.cattr self, 'INITIAL_SERVICE_INSTANCES', nil, nil
98
+
99
+ Helpers_.cattr self, 'INITIAL_SERVICE_CLASSES', nil, nil
100
+
101
+ def self.included receiver
102
+
103
+ abort "Attempt to include #{self} into #{receiver}. This is not allowed"
104
+ end
105
+
106
+ end # module Globals
107
+ end # module Pantheios
108
+
109
+