observed 0.1.1 → 0.2.0.rc1
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 +9 -9
- data/.travis.yml +4 -0
- data/README.md +53 -78
- data/examples/observed.rb +1 -1
- data/exe/observed-oneshot +3 -1
- data/features/explicit_routing.feature +33 -0
- data/features/oneshot.feature +4 -0
- data/features/test_in_single_ruby_source.feature +4 -0
- data/integrations/observed-clockwork/features/run_observed_inside_clockwork.feature +6 -7
- data/integrations/observed-clockwork/lib/observed/clockwork/version.rb +1 -1
- data/integrations/observed-clockwork/lib/observed/clockwork.rb +0 -1
- data/integrations/observed-clockwork/observed-clockwork.gemspec +1 -1
- data/integrations/observed-eventmachine/.gitignore +17 -0
- data/integrations/observed-eventmachine/Gemfile +8 -0
- data/integrations/observed-eventmachine/LICENSE.txt +22 -0
- data/integrations/observed-eventmachine/README.md +29 -0
- data/integrations/observed-eventmachine/Rakefile +1 -0
- data/integrations/observed-eventmachine/examples/observed.rb +30 -0
- data/integrations/observed-eventmachine/features/integration_via_single_ruby_source.feature +48 -0
- data/integrations/observed-eventmachine/features/support/env.rb +8 -0
- data/integrations/observed-eventmachine/lib/observed/eventmachine/version.rb +5 -0
- data/integrations/observed-eventmachine/lib/observed/eventmachine.rb +70 -0
- data/integrations/observed-eventmachine/observed-eventmachine.gemspec +28 -0
- data/lib/observed/application/oneshot.rb +14 -37
- data/lib/observed/builtin_plugins/file.rb +5 -14
- data/lib/observed/builtin_plugins/stdout.rb +7 -14
- data/lib/observed/config.rb +4 -4
- data/lib/observed/config_builder.rb +154 -87
- data/lib/observed/config_dsl.rb +2 -8
- data/lib/observed/configurable.rb +61 -3
- data/lib/observed/context.rb +90 -0
- data/lib/observed/default/observer.rb +2 -5
- data/lib/observed/default.rb +0 -1
- data/lib/observed/event_bus.rb +31 -0
- data/lib/observed/execution_job_factory.rb +95 -0
- data/lib/observed/job.rb +163 -0
- data/lib/observed/jobbed_event_bus.rb +33 -0
- data/lib/observed/logging.rb +40 -0
- data/lib/observed/observer.rb +1 -0
- data/lib/observed/observer_helpers/timer.rb +13 -5
- data/lib/observed/pluggable.rb +11 -0
- data/lib/observed/reporter/regexp_matching.rb +2 -1
- data/lib/observed/reporter/report_formatting.rb +57 -0
- data/lib/observed/reporter.rb +0 -2
- data/lib/observed/system.rb +11 -78
- data/lib/observed/translator.rb +22 -0
- data/lib/observed/version.rb +1 -1
- data/lib/observed.rb +10 -12
- data/omnibus-observed/.gitignore +9 -0
- data/omnibus-observed/Berksfile +3 -0
- data/omnibus-observed/Berksfile.lock +52 -0
- data/omnibus-observed/Gemfile +4 -0
- data/omnibus-observed/README.md +102 -0
- data/omnibus-observed/Vagrantfile +93 -0
- data/omnibus-observed/config/projects/observed.rb +20 -0
- data/omnibus-observed/config/software/observed.rb +19 -0
- data/omnibus-observed/package-scripts/observed/makeselfinst +27 -0
- data/omnibus-observed/package-scripts/observed/postinst +17 -0
- data/omnibus-observed/package-scripts/observed/postrm +9 -0
- data/plugins/observed-fluentd/lib/observed/fluentd/version.rb +1 -1
- data/plugins/observed-gauge/README.md +5 -0
- data/plugins/observed-gauge/lib/observed/gauge/version.rb +1 -1
- data/plugins/observed-gauge/lib/observed/gauge.rb +11 -13
- data/plugins/observed-gauge/observed-gauge.gemspec +1 -1
- data/plugins/observed-gauge/spec/gauge_spec.rb +7 -7
- data/plugins/observed-growl/Gemfile +6 -0
- data/plugins/observed-growl/lib/observed/growl.rb +80 -0
- data/plugins/observed-http/lib/observed/http/version.rb +1 -1
- data/plugins/observed-http/lib/observed/http.rb +10 -8
- data/plugins/observed-http/observed-http.gemspec +1 -1
- data/plugins/observed-http/spec/http_spec.rb +62 -7
- data/plugins/observed-http/spec/integration_spec.rb +14 -0
- data/plugins/observed-shell/Gemfile +5 -0
- data/plugins/observed-shell/lib/observed/shell.rb +54 -0
- data/run-integration-tests +81 -0
- data/spec/builtin_plugins/stdout_spec.rb +7 -3
- data/spec/config_builder_spec.rb +42 -59
- data/spec/config_dsl_spec.rb +4 -0
- data/spec/configurable_spec.rb +141 -31
- data/spec/event_bus_spec.rb +16 -0
- data/spec/execution_job_factory_spec.rb +35 -0
- data/spec/job_factory_spec.rb +16 -0
- data/spec/job_spec.rb +228 -0
- data/spec/jobbed_event_bus_spec.rb +38 -0
- data/spec/observed_spec.rb +203 -0
- data/spec/observer_helpers/timer_spec.rb +187 -0
- data/spec/oneshot_spec.rb +7 -2
- data/spec/system_spec.rb +5 -39
- metadata +55 -12
- data/lib/observed/default/reporter.rb +0 -17
- data/lib/observed/reader.rb +0 -14
- data/lib/observed/writer.rb +0 -14
- data/spec/reader_spec.rb +0 -15
- data/spec/writer_spec.rb +0 -16
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
require 'observed/system'
|
4
|
+
require 'observed/config_builder'
|
5
|
+
require 'observed/config_dsl'
|
6
|
+
require 'observed/job'
|
7
|
+
require 'observed/jobbed_event_bus'
|
8
|
+
|
9
|
+
module Observed
|
10
|
+
# The run context of an Observed system.
|
11
|
+
# It can be initialized via parameters to automatically configure the system and everything needed such as the config
|
12
|
+
# builder, the DSL, the logger, etc.
|
13
|
+
class Context
|
14
|
+
|
15
|
+
def initialize(args={})
|
16
|
+
configure args
|
17
|
+
end
|
18
|
+
|
19
|
+
def configure(args)
|
20
|
+
@logger ||= begin
|
21
|
+
logger_out = if args[:log_file]
|
22
|
+
File.open(args[:log_file], 'a')
|
23
|
+
else
|
24
|
+
STDOUT
|
25
|
+
end
|
26
|
+
Logger.new(logger_out)
|
27
|
+
end
|
28
|
+
|
29
|
+
@executor = args[:executor]
|
30
|
+
|
31
|
+
set_log_level_to_debug(!!args[:debug])
|
32
|
+
|
33
|
+
if args[:config_file]
|
34
|
+
load_config_file(args[:config_file])
|
35
|
+
end
|
36
|
+
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def logger
|
41
|
+
@logger
|
42
|
+
end
|
43
|
+
|
44
|
+
def system
|
45
|
+
@system ||= Observed::System.new(logger: logger, context: self)
|
46
|
+
end
|
47
|
+
|
48
|
+
def executor
|
49
|
+
@executor ||= Observed::BlockingJobExecutor.new
|
50
|
+
end
|
51
|
+
|
52
|
+
def jobbed_event_bus
|
53
|
+
@event_bus ||= Observed::JobbedEventBus.new(job_factory: job_factory)
|
54
|
+
end
|
55
|
+
|
56
|
+
def job_factory
|
57
|
+
@job_factory ||= Observed::JobFactory.new(executor: executor)
|
58
|
+
end
|
59
|
+
|
60
|
+
def execution_job_factory
|
61
|
+
@execution_job_factory ||= Observed::ExecutionJobFactory.new(job_factory: job_factory)
|
62
|
+
end
|
63
|
+
|
64
|
+
def config_builder
|
65
|
+
@config_builder ||= Observed::ConfigBuilder.new(system: system, logger: logger, context: self)
|
66
|
+
end
|
67
|
+
|
68
|
+
def config_dsl
|
69
|
+
Observed::ConfigDSL.new(builder: config_builder, logger: logger)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def set_log_level_to_debug(enabled)
|
75
|
+
@logger.level = if enabled
|
76
|
+
Logger::DEBUG
|
77
|
+
else
|
78
|
+
Logger::INFO
|
79
|
+
end
|
80
|
+
|
81
|
+
@logger.debug "Enabling Debug logs." if enabled
|
82
|
+
end
|
83
|
+
|
84
|
+
def load_config_file(path)
|
85
|
+
config_dsl.eval_file(path)
|
86
|
+
system.config = config_dsl.config
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
data/lib/observed/default.rb
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Observed
|
4
|
+
class EventBus
|
5
|
+
def initialize
|
6
|
+
@mutex = ::Mutex.new
|
7
|
+
@subscribers = []
|
8
|
+
end
|
9
|
+
def emit(tag, *params)
|
10
|
+
handle_event(tag, *params)
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_receive(pattern, &block)
|
14
|
+
@mutex.synchronize do
|
15
|
+
@subscribers.push [pattern, block]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def handle_event(tag, *params)
|
22
|
+
@mutex.synchronize do
|
23
|
+
@subscribers.each do |pattern, s|
|
24
|
+
if pattern.match(tag)
|
25
|
+
s.call *params
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'observed/observer'
|
2
|
+
require 'observed/reporter'
|
3
|
+
require 'observed/translator'
|
4
|
+
require 'observed/job'
|
5
|
+
|
6
|
+
module Observed
|
7
|
+
# An yet another cushion from the deprecated plugin interface to the new plugin interface
|
8
|
+
class FakeSystem
|
9
|
+
|
10
|
+
def initialize(args)
|
11
|
+
@time = args[:time] || Time.now
|
12
|
+
end
|
13
|
+
|
14
|
+
def report(tag, time, data=nil)
|
15
|
+
options = nil
|
16
|
+
if tag.is_a?(::Hash)
|
17
|
+
data = tag
|
18
|
+
options = time || {}
|
19
|
+
tag = nil
|
20
|
+
elsif tag.is_a?(String) && time.is_a?(::Hash)
|
21
|
+
options = data
|
22
|
+
data = time
|
23
|
+
else
|
24
|
+
options = {tag: tag, time: time}
|
25
|
+
end
|
26
|
+
options ||= {}
|
27
|
+
options[:tag] ||= tag
|
28
|
+
options[:time] ||= now
|
29
|
+
@reported = [data, options]
|
30
|
+
end
|
31
|
+
|
32
|
+
def reported
|
33
|
+
@reported
|
34
|
+
end
|
35
|
+
|
36
|
+
def now
|
37
|
+
@time
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class ExecutionJobFactory
|
42
|
+
|
43
|
+
def initialize(args={})
|
44
|
+
@job_factory = args[:job_factory] || Observed::JobFactory.new(executor: Observed::BlockingJobExecutor.new)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Convert the observer/translator/reporter to a job
|
48
|
+
def convert_to_job(underlying)
|
49
|
+
if underlying.is_a? Observed::Observer
|
50
|
+
@job_factory.job {|data, options|
|
51
|
+
options ||= {}
|
52
|
+
m = underlying.method(:observe)
|
53
|
+
fake_system = FakeSystem.new(time: options[:time])
|
54
|
+
# For 0.1.0 compatibility
|
55
|
+
underlying.configure(system: fake_system)
|
56
|
+
underlying.configure(tag: options[:tag]) unless underlying.get_attribute_value(:tag)
|
57
|
+
result = dispatch_method m, data, options
|
58
|
+
fake_system.reported || result
|
59
|
+
}
|
60
|
+
elsif underlying.is_a? Observed::Reporter
|
61
|
+
@job_factory.job {|data, options|
|
62
|
+
options ||= {}
|
63
|
+
m = underlying.method(:report)
|
64
|
+
dispatch_method m, data, options
|
65
|
+
}
|
66
|
+
elsif underlying.is_a? Observed::Translator
|
67
|
+
@job_factory.job {|data, options|
|
68
|
+
options ||= {}
|
69
|
+
m = underlying.method(:translate)
|
70
|
+
dispatch_method m, data, options
|
71
|
+
}
|
72
|
+
else
|
73
|
+
fail "Unexpected type of object which can not be converted to a job: #{underlying}"
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def dispatch_method(m, data, options)
|
78
|
+
num_parameters = m.parameters.size
|
79
|
+
case num_parameters
|
80
|
+
when 0
|
81
|
+
m.call
|
82
|
+
when 1
|
83
|
+
m.call data
|
84
|
+
when 2
|
85
|
+
m.call data, options
|
86
|
+
when 3
|
87
|
+
# Deprecated. This is here for backward compatiblity
|
88
|
+
m.call options[:tag], options[:time], data
|
89
|
+
else
|
90
|
+
fail "Unexpected number of parameters for the method `#{m}`: #{num_parameters}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
end
|
data/lib/observed/job.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'thread'
|
3
|
+
|
4
|
+
module Observed
|
5
|
+
class Job
|
6
|
+
def then(*jobs)
|
7
|
+
next_job = if jobs.size == 1
|
8
|
+
jobs.first
|
9
|
+
elsif jobs.size > 1
|
10
|
+
ParallelJob.new(jobs)
|
11
|
+
else
|
12
|
+
raise 'No jobs to be executed'
|
13
|
+
end
|
14
|
+
SequenceJob.new(self, next_job)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class MutableJob
|
19
|
+
def initialize(current_job)
|
20
|
+
@current_job = current_job
|
21
|
+
@mutex = Mutex.new
|
22
|
+
end
|
23
|
+
def now(data={}, options=nil)
|
24
|
+
@current_job.now(data, options) do |data, options2|
|
25
|
+
yield data, (options2 || options) if block_given?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
def then(*jobs)
|
29
|
+
@mutex.synchronize do
|
30
|
+
@current_job = @current_job.then(*jobs)
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class SequenceJob < Job
|
37
|
+
attr_reader :base_job
|
38
|
+
def initialize(base_job, next_job)
|
39
|
+
@base_job = base_job
|
40
|
+
@next_job = next_job
|
41
|
+
end
|
42
|
+
def now(data={}, options=nil)
|
43
|
+
@base_job.now(data, options) do |data, options2|
|
44
|
+
@next_job.now(data, (options2 || options)) do |data, options3|
|
45
|
+
yield data, (options3 || options2 || options) if block_given?
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class ParallelJob < Job
|
52
|
+
def initialize(jobs)
|
53
|
+
@jobs = jobs || fail('jobs missing')
|
54
|
+
@next_job = NoOpJob.instance
|
55
|
+
end
|
56
|
+
def now(data={}, options=nil)
|
57
|
+
@jobs.each do |job|
|
58
|
+
job.now(data, options) do |data, options2|
|
59
|
+
yield data, (options2 || options) if block_given?
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class NoOpJob < Job
|
66
|
+
def now(data={}, options={}); end
|
67
|
+
def self.instance
|
68
|
+
SINGLETON_INSTANCE
|
69
|
+
end
|
70
|
+
SINGLETON_INSTANCE = NoOpJob.new
|
71
|
+
end
|
72
|
+
|
73
|
+
class ProcJob < Job
|
74
|
+
def initialize(args, &block)
|
75
|
+
@executor = args[:executor] || fail('Missing a value for :executor')
|
76
|
+
@listener = args[:listener] || fail('Missing a value for :listener')
|
77
|
+
@logger = args[:logger]
|
78
|
+
@block = block
|
79
|
+
@next_job = NoOpJob.instance
|
80
|
+
|
81
|
+
if @logger.nil?
|
82
|
+
@logger = ::Logger.new(STDERR)
|
83
|
+
@logger.level = ::Logger::WARN
|
84
|
+
end
|
85
|
+
end
|
86
|
+
def now(data={}, options=nil)
|
87
|
+
@executor.execute {
|
88
|
+
result = @block.call(data, options)
|
89
|
+
yield result if block_given?
|
90
|
+
notify_listener(data: data, options: options, result: result)
|
91
|
+
}
|
92
|
+
end
|
93
|
+
|
94
|
+
private
|
95
|
+
|
96
|
+
def notify_listener(args)
|
97
|
+
return unless @listener
|
98
|
+
|
99
|
+
data = args[:data]
|
100
|
+
options = args[:options]
|
101
|
+
result = args[:result]
|
102
|
+
|
103
|
+
@logger.debug "Notifying listeners with the result(#{result}) generated from the input data(#{data}) and the options(#{options})"
|
104
|
+
|
105
|
+
if result.is_a? ::Hash
|
106
|
+
if options
|
107
|
+
@listener.on_result(result, options)
|
108
|
+
else
|
109
|
+
@listener.on_result(result)
|
110
|
+
end
|
111
|
+
elsif result.is_a? ::Array
|
112
|
+
if result.size == 1 && options
|
113
|
+
@listener.on_result(result, options)
|
114
|
+
elsif result.size == 2
|
115
|
+
@listener.on_result(*result)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class JobListener
|
122
|
+
def on_result(data={}, options={})
|
123
|
+
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
class JobFactory
|
128
|
+
def initialize(args)
|
129
|
+
@executor = args[:executor] || fail('Missing a value for :executor')
|
130
|
+
@listener = args[:listener] || JobListener.new
|
131
|
+
end
|
132
|
+
|
133
|
+
def job(&block)
|
134
|
+
ProcJob.new(executor: @executor, listener: @listener, &block)
|
135
|
+
end
|
136
|
+
|
137
|
+
def mutable_job(&block)
|
138
|
+
MutableJob.new(job(&block))
|
139
|
+
end
|
140
|
+
|
141
|
+
def parallel(jobs)
|
142
|
+
ParallelJob.new(jobs)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
class JobExecutor
|
147
|
+
def execute; end
|
148
|
+
end
|
149
|
+
|
150
|
+
class BlockingJobExecutor
|
151
|
+
def execute
|
152
|
+
yield
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class ThreadedJobExecutor
|
157
|
+
def execute
|
158
|
+
Thread.start {
|
159
|
+
yield
|
160
|
+
}
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'observed/event_bus'
|
3
|
+
|
4
|
+
module Observed
|
5
|
+
class JobbedEventBus
|
6
|
+
def initialize(args={})
|
7
|
+
@bus = Observed::EventBus.new
|
8
|
+
@receives = {}
|
9
|
+
@job_factory = args[:job_factory] || fail("The parameter :job_factory is missing in args(#{args}")
|
10
|
+
@mutex = ::Mutex.new
|
11
|
+
end
|
12
|
+
def pipe_to_emit(tag)
|
13
|
+
@job_factory.job { |*params|
|
14
|
+
self.emit(tag, *params)
|
15
|
+
params
|
16
|
+
}
|
17
|
+
end
|
18
|
+
def emit(tag, *params)
|
19
|
+
@bus.emit tag, *params
|
20
|
+
end
|
21
|
+
def receive(pattern)
|
22
|
+
job = @job_factory.mutable_job {|data, options|
|
23
|
+
[data, options]
|
24
|
+
}
|
25
|
+
@bus.on_receive(pattern) do |*params|
|
26
|
+
@mutex.synchronize do
|
27
|
+
job.now(*params)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
job
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'observed/configurable'
|
2
|
+
|
3
|
+
module Observed
|
4
|
+
module Logging
|
5
|
+
include Observed::Configurable
|
6
|
+
|
7
|
+
# !@attribute [r] logger
|
8
|
+
# @return [Logger]
|
9
|
+
attribute :logger
|
10
|
+
|
11
|
+
# @return [Boolean] `true` if a value is set for the attribute :logger
|
12
|
+
def logging_enabled?
|
13
|
+
has_attribute_value? :logger
|
14
|
+
end
|
15
|
+
|
16
|
+
# Log the debug message through the logger configured via the :logger attribute
|
17
|
+
# @param [String] message
|
18
|
+
def log_debug(message)
|
19
|
+
logger.debug message if logging_enabled?
|
20
|
+
end
|
21
|
+
|
22
|
+
# Log the info message through the logger configured via the :logger attribute
|
23
|
+
# @param [String] message
|
24
|
+
def log_info(message)
|
25
|
+
logger.info message if logging_enabled?
|
26
|
+
end
|
27
|
+
|
28
|
+
# Log the warn message through the logger configured via the :logger attribute
|
29
|
+
# @param [String] message
|
30
|
+
def log_warn(message)
|
31
|
+
logger.warn message if logging_enabled?
|
32
|
+
end
|
33
|
+
|
34
|
+
# Log the error message through the logger configured via the :logger attribute
|
35
|
+
# @param [String] message
|
36
|
+
def log_error(message)
|
37
|
+
logger.error message if logging_enabled?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/observed/observer.rb
CHANGED
@@ -17,10 +17,12 @@ module Observed
|
|
17
17
|
elapsed_time = after - before
|
18
18
|
r[:elapsed_time] = elapsed_time
|
19
19
|
r
|
20
|
-
rescue Timeout::Error =>
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
rescue Timeout::Error => e
|
21
|
+
log_debug "Handled the error but logging it just for your info: #{e.message}\n#{e.backtrace.join("\n")}" if self.is_a? Logging
|
22
|
+
{ status: :error, error: {message: 'Timed out.'}, timed_out: true }
|
23
|
+
rescue => e
|
24
|
+
log_error "Handled the error: #{e.message}\n#{e.backtrace.join("\n")}" if self.is_a? Logging
|
25
|
+
{ status: :error, error: {message: e.message} }
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
@@ -29,7 +31,13 @@ module Observed
|
|
29
31
|
format = options[:format] || ->(r){ r }
|
30
32
|
result = time(options, &block)
|
31
33
|
|
32
|
-
|
34
|
+
data = ["#{tag}.#{result[:status]}", format.call(result)]
|
35
|
+
|
36
|
+
if self.method(:observe).parameters.size != 1
|
37
|
+
system.report(*data)
|
38
|
+
end
|
39
|
+
|
40
|
+
data
|
33
41
|
end
|
34
42
|
|
35
43
|
end
|
data/lib/observed/pluggable.rb
CHANGED
@@ -1,4 +1,14 @@
|
|
1
1
|
module Observed
|
2
|
+
# Indicates that the class is pluggable (or extensible or a extension point).
|
3
|
+
# "pluggable" means that the class included this module will be the outlet in where Observed plug-ins are plugged.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# class Reader
|
7
|
+
# include Pluggable
|
8
|
+
# end
|
9
|
+
# class FooReader < Reader; end
|
10
|
+
# class BarReader < Reader; end
|
11
|
+
# Reader.plugins #=> [FooReader, BarReader]
|
2
12
|
module Pluggable
|
3
13
|
|
4
14
|
module ClassMethods
|
@@ -7,6 +17,7 @@ module Observed
|
|
7
17
|
end
|
8
18
|
|
9
19
|
def inherited(klass)
|
20
|
+
super if defined? super
|
10
21
|
plugins << klass
|
11
22
|
end
|
12
23
|
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'observed/configurable'
|
2
|
+
require 'observed/hash/fetcher'
|
3
|
+
|
4
|
+
module Observed
|
5
|
+
class Reporter
|
6
|
+
# The module to equip an observer with a `formatter` to format the data being reported.
|
7
|
+
# The `formatter` is just a proc and is able to be configured via an attribute.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# class YourObserver < Observed::Reporter
|
11
|
+
# include Observed::Reporter::Configurable
|
12
|
+
# include Observed::Reporter::ReportFormatting
|
13
|
+
#
|
14
|
+
# attribute :format, default: -> tag, time, data { "#{Time.at(time)} #{tag} #{data}" }
|
15
|
+
#
|
16
|
+
# def report(tag, time, data)
|
17
|
+
# formatted_data = format_report(tag, time, data)
|
18
|
+
# # The output of your choice
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# observer = YourObserver.new(format: -> tag, time, data { "The data being reported: #{tag} #{time} #{data}" })
|
23
|
+
# observer.report('test', Time.now, {data: 1})
|
24
|
+
# #=> outputs "#{Time.at(Time.now)} test {:data=>1}"
|
25
|
+
module ReportFormatting
|
26
|
+
|
27
|
+
include Observed::Configurable
|
28
|
+
|
29
|
+
attribute :format, default: -> tag, time, data {
|
30
|
+
begin
|
31
|
+
"#{Time.at(time)} #{tag} #{data}"
|
32
|
+
rescue => e
|
33
|
+
nested = Exception.new("Error while formatting the data: tag=#{tag} time=#{time} data=#{data} " + e.message)
|
34
|
+
nested.set_backtrace(e.backtrace)
|
35
|
+
raise nested
|
36
|
+
end
|
37
|
+
}
|
38
|
+
|
39
|
+
# Format the data being reported. The data includes 3 parameters: `tag`, `time` and `data`.
|
40
|
+
# @param [String] tag
|
41
|
+
# @param [Time] time
|
42
|
+
# @param [Hash] data
|
43
|
+
def format_report(tag, time, data)
|
44
|
+
num_params = format.parameters.size
|
45
|
+
case num_params
|
46
|
+
when 3
|
47
|
+
format.call(tag, time, data)
|
48
|
+
when 4
|
49
|
+
format.call(tag, time, data, Observed::Hash::Fetcher.new(data))
|
50
|
+
else
|
51
|
+
fail "Number of parameters for the function for the key :format must be 3 or 4, but was #{num_params}(#{format.parameters}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|