observed 0.1.1 → 0.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|