pantheios-ruby 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+