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.
Files changed (94) hide show
  1. checksums.yaml +9 -9
  2. data/.travis.yml +4 -0
  3. data/README.md +53 -78
  4. data/examples/observed.rb +1 -1
  5. data/exe/observed-oneshot +3 -1
  6. data/features/explicit_routing.feature +33 -0
  7. data/features/oneshot.feature +4 -0
  8. data/features/test_in_single_ruby_source.feature +4 -0
  9. data/integrations/observed-clockwork/features/run_observed_inside_clockwork.feature +6 -7
  10. data/integrations/observed-clockwork/lib/observed/clockwork/version.rb +1 -1
  11. data/integrations/observed-clockwork/lib/observed/clockwork.rb +0 -1
  12. data/integrations/observed-clockwork/observed-clockwork.gemspec +1 -1
  13. data/integrations/observed-eventmachine/.gitignore +17 -0
  14. data/integrations/observed-eventmachine/Gemfile +8 -0
  15. data/integrations/observed-eventmachine/LICENSE.txt +22 -0
  16. data/integrations/observed-eventmachine/README.md +29 -0
  17. data/integrations/observed-eventmachine/Rakefile +1 -0
  18. data/integrations/observed-eventmachine/examples/observed.rb +30 -0
  19. data/integrations/observed-eventmachine/features/integration_via_single_ruby_source.feature +48 -0
  20. data/integrations/observed-eventmachine/features/support/env.rb +8 -0
  21. data/integrations/observed-eventmachine/lib/observed/eventmachine/version.rb +5 -0
  22. data/integrations/observed-eventmachine/lib/observed/eventmachine.rb +70 -0
  23. data/integrations/observed-eventmachine/observed-eventmachine.gemspec +28 -0
  24. data/lib/observed/application/oneshot.rb +14 -37
  25. data/lib/observed/builtin_plugins/file.rb +5 -14
  26. data/lib/observed/builtin_plugins/stdout.rb +7 -14
  27. data/lib/observed/config.rb +4 -4
  28. data/lib/observed/config_builder.rb +154 -87
  29. data/lib/observed/config_dsl.rb +2 -8
  30. data/lib/observed/configurable.rb +61 -3
  31. data/lib/observed/context.rb +90 -0
  32. data/lib/observed/default/observer.rb +2 -5
  33. data/lib/observed/default.rb +0 -1
  34. data/lib/observed/event_bus.rb +31 -0
  35. data/lib/observed/execution_job_factory.rb +95 -0
  36. data/lib/observed/job.rb +163 -0
  37. data/lib/observed/jobbed_event_bus.rb +33 -0
  38. data/lib/observed/logging.rb +40 -0
  39. data/lib/observed/observer.rb +1 -0
  40. data/lib/observed/observer_helpers/timer.rb +13 -5
  41. data/lib/observed/pluggable.rb +11 -0
  42. data/lib/observed/reporter/regexp_matching.rb +2 -1
  43. data/lib/observed/reporter/report_formatting.rb +57 -0
  44. data/lib/observed/reporter.rb +0 -2
  45. data/lib/observed/system.rb +11 -78
  46. data/lib/observed/translator.rb +22 -0
  47. data/lib/observed/version.rb +1 -1
  48. data/lib/observed.rb +10 -12
  49. data/omnibus-observed/.gitignore +9 -0
  50. data/omnibus-observed/Berksfile +3 -0
  51. data/omnibus-observed/Berksfile.lock +52 -0
  52. data/omnibus-observed/Gemfile +4 -0
  53. data/omnibus-observed/README.md +102 -0
  54. data/omnibus-observed/Vagrantfile +93 -0
  55. data/omnibus-observed/config/projects/observed.rb +20 -0
  56. data/omnibus-observed/config/software/observed.rb +19 -0
  57. data/omnibus-observed/package-scripts/observed/makeselfinst +27 -0
  58. data/omnibus-observed/package-scripts/observed/postinst +17 -0
  59. data/omnibus-observed/package-scripts/observed/postrm +9 -0
  60. data/plugins/observed-fluentd/lib/observed/fluentd/version.rb +1 -1
  61. data/plugins/observed-gauge/README.md +5 -0
  62. data/plugins/observed-gauge/lib/observed/gauge/version.rb +1 -1
  63. data/plugins/observed-gauge/lib/observed/gauge.rb +11 -13
  64. data/plugins/observed-gauge/observed-gauge.gemspec +1 -1
  65. data/plugins/observed-gauge/spec/gauge_spec.rb +7 -7
  66. data/plugins/observed-growl/Gemfile +6 -0
  67. data/plugins/observed-growl/lib/observed/growl.rb +80 -0
  68. data/plugins/observed-http/lib/observed/http/version.rb +1 -1
  69. data/plugins/observed-http/lib/observed/http.rb +10 -8
  70. data/plugins/observed-http/observed-http.gemspec +1 -1
  71. data/plugins/observed-http/spec/http_spec.rb +62 -7
  72. data/plugins/observed-http/spec/integration_spec.rb +14 -0
  73. data/plugins/observed-shell/Gemfile +5 -0
  74. data/plugins/observed-shell/lib/observed/shell.rb +54 -0
  75. data/run-integration-tests +81 -0
  76. data/spec/builtin_plugins/stdout_spec.rb +7 -3
  77. data/spec/config_builder_spec.rb +42 -59
  78. data/spec/config_dsl_spec.rb +4 -0
  79. data/spec/configurable_spec.rb +141 -31
  80. data/spec/event_bus_spec.rb +16 -0
  81. data/spec/execution_job_factory_spec.rb +35 -0
  82. data/spec/job_factory_spec.rb +16 -0
  83. data/spec/job_spec.rb +228 -0
  84. data/spec/jobbed_event_bus_spec.rb +38 -0
  85. data/spec/observed_spec.rb +203 -0
  86. data/spec/observer_helpers/timer_spec.rb +187 -0
  87. data/spec/oneshot_spec.rb +7 -2
  88. data/spec/system_spec.rb +5 -39
  89. metadata +55 -12
  90. data/lib/observed/default/reporter.rb +0 -17
  91. data/lib/observed/reader.rb +0 -14
  92. data/lib/observed/writer.rb +0 -14
  93. data/spec/reader_spec.rb +0 -15
  94. data/spec/writer_spec.rb +0 -16
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'observed/eventmachine/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "observed-eventmachine"
8
+ spec.version = Observed::EM::VERSION
9
+ spec.authors = ["KUOKA Yusuke"]
10
+ spec.email = ["yusuke.kuoka@gree.net"]
11
+ spec.description = %q{An integration of Observed with EventMachine}
12
+ spec.summary = %q{observed-eventmachine allows running Observed observations on EventMachine to achieve scalability}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'eventmachine', '~> 1.0.3'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "cucumber"
27
+ spec.add_development_dependency "aruba"
28
+ end
@@ -3,10 +3,7 @@ require 'optparse'
3
3
  require 'pathname'
4
4
 
5
5
  require 'observed/config'
6
- require 'observed/config_builder'
7
- require 'observed/config_dsl'
8
- require 'observed/observer'
9
- require 'observed/system'
6
+ require 'observed/context'
10
7
 
11
8
  module Observed
12
9
 
@@ -28,17 +25,12 @@ module Observed
28
25
  end
29
26
 
30
27
  def run(observation_name=nil)
31
- system.run(observation_name)
32
- end
33
-
34
- def logger
35
- @logger ||= Logger.new(STDOUT)
28
+ @system.run(observation_name)
36
29
  end
37
30
 
38
31
  class << self
39
- def from_argv(argv)
40
-
41
- command_line_args = argv.dup
32
+ def parse_argv!(argv)
33
+ command_line_args = argv
42
34
 
43
35
  args = {}
44
36
 
@@ -55,38 +47,29 @@ module Observed
55
47
 
56
48
  opts.parse!(command_line_args)
57
49
 
58
- if command_line_args.size != 1
50
+ unless command_line_args.size == 1 || command_line_args.size == 2
59
51
  fail InvalidArgumentError, "Invalid number of arguments #{command_line_args.size} where arguments are #{command_line_args}"
60
52
  end
61
53
 
62
- args[:config_file] = command_line_args.first
54
+ args[:config_file] = command_line_args.shift
55
+
56
+ args
57
+ end
63
58
 
59
+ def from_argv(argv)
60
+ args = parse_argv!(argv.dup)
64
61
  create(args)
65
62
  end
66
63
 
67
64
  # @param [Hash<Symbol,String>] args
68
65
  # @option args [Array<String>] :argv The Ruby's `ARGV` like object which is treated as intialization parameters for Oneshoft application.
69
66
  def create(args)
70
- logger_out = if args[:log_file]
71
- File.open(args[:log_file], 'a')
72
- else
73
- STDOUT
74
- end
75
- logger = Logger.new(logger_out)
76
- logger.level = if args[:debug]
77
- Logger::DEBUG
78
- else
79
- Logger::INFO
80
- end
81
- sys = Observed::System.new(logger: logger)
67
+ ctx = Observed::Context.new(args)
68
+ sys = ctx.system
82
69
  config = if args[:yaml_file]
83
70
  YAML.load_file(args[:yaml_file])
84
71
  elsif args[:config_file]
85
- path = args[:config_file]
86
- config_builder = Observed::ConfigBuilder.new(system: sys, logger: logger)
87
- config_dsl = Observed::ConfigDSL.new(builder: config_builder, logger: logger)
88
- config_dsl.eval_file(path)
89
- config_dsl.config
72
+ sys.config
90
73
  elsif args[:config]
91
74
  c = args[:config]
92
75
  c
@@ -103,12 +86,6 @@ module Observed
103
86
  end
104
87
  end
105
88
 
106
- private
107
-
108
- def system
109
- @system
110
- end
111
-
112
89
  end
113
90
  end
114
91
  end
@@ -1,16 +1,17 @@
1
1
  require 'observed/hash/fetcher'
2
2
  require 'observed/reporter'
3
3
  require 'observed/reporter/regexp_matching'
4
+ require 'observed/reporter/report_formatting'
4
5
 
5
6
  module Observed
6
7
  module BuiltinPlugins
7
8
  class File < Observed::Reporter
8
9
 
9
10
  include Observed::Reporter::RegexpMatching
11
+ include Observed::Reporter::ReportFormatting
10
12
 
11
13
  UNDEFINED = Object.new
12
14
 
13
- attribute :format, default: -> tag, time, data { "#{Time.at(time)} #{tag} #{data}" }
14
15
  attribute :path
15
16
  attribute :mode, default: :append
16
17
 
@@ -18,15 +19,7 @@ module Observed
18
19
  # @param [Time] time
19
20
  # @param [Hash] data
20
21
  def report(tag, time, data)
21
- num_params = format.parameters.size
22
- formatted_data = case num_params
23
- when 3
24
- format.call(tag, time, data)
25
- when 4
26
- format.call(tag, time, data, Observed::Hash::Fetcher.new(data))
27
- else
28
- fail "Number of parameters for the function for the key :format must be 3 or 4, but was #{num_params}(#{format.parameters}"
29
- end
22
+ formatted_data = format_report(tag, time, data)
30
23
  mode = case self.mode
31
24
  when :append, 'a'
32
25
  'a'
@@ -34,16 +27,14 @@ module Observed
34
27
  'w'
35
28
  else
36
29
  fail "Unsupported value for the parameter `:mode`: Supported values are :append, :overwrite, " +
37
- "'a', 'w'. The specified value is #{self.mode.inspect}"
30
+ "'a', 'w'. The specified value is #{self.mode.inspect}"
38
31
  end
39
32
  ::File.open(path, mode) do |f|
40
33
  f.puts formatted_data
41
34
  end
42
35
  end
43
36
 
44
- def self.plugin_name
45
- 'file'
46
- end
37
+ plugin_name 'file'
47
38
  end
48
39
  end
49
40
  end
@@ -2,34 +2,27 @@ require 'observed/hash/fetcher'
2
2
  require 'observed/observer'
3
3
  require 'observed/reporter'
4
4
  require 'observed/reporter/regexp_matching'
5
+ require 'observed/reporter/report_formatting'
5
6
 
6
7
  module Observed
7
8
  module BuiltinPlugins
8
9
  class Stdout < Observed::Reporter
9
10
 
10
11
  include Observed::Reporter::RegexpMatching
12
+ include Observed::Reporter::ReportFormatting
11
13
 
12
- attribute :format, default: -> tag, time, data { "#{Time.at(time)} #{tag} #{data}" }
14
+ attribute :output, default: STDOUT
13
15
 
14
16
  # @param [String] tag
15
17
  # @param [Time] time
16
18
  # @param [Hash] data
19
+ # @param [Object] format_result
17
20
  def report(tag, time, data)
18
- num_params = format.parameters.size
19
- formatted_data = case num_params
20
- when 3
21
- format.call(tag, time, data)
22
- when 4
23
- format.call(tag, time, data, Observed::Hash::Fetcher.new(data))
24
- else
25
- fail "Number of parameters for the function for the key :format must be 3 or 4, but was #{num_params}(#{format.parameters}"
26
- end
27
- puts formatted_data
21
+ formatted_data = format_report(tag, time, data)
22
+ output.puts formatted_data
28
23
  end
29
24
 
30
- def self.plugin_name
31
- 'stdout'
32
- end
25
+ plugin_name 'stdout'
33
26
  end
34
27
  end
35
28
  end
@@ -7,10 +7,6 @@ module Observed
7
7
 
8
8
  include Observed::Configurable
9
9
 
10
- # !@attribute [rw] writers
11
- # @return [Array<Observed::Writer>]
12
- attribute :writers
13
-
14
10
  # !@attribute [rw] readers
15
11
  # @return [Array<Observed::Reader>]
16
12
  attribute :readers
@@ -23,5 +19,9 @@ module Observed
23
19
  # @return [Array<Observed::Observer>]
24
20
  attribute :observers
25
21
 
22
+ # !@attribuet [rw] translators
23
+ # @return [Array<Observed::Translator]
24
+ attribute :translators
25
+
26
26
  end
27
27
  end
@@ -1,36 +1,65 @@
1
+ require 'logger'
2
+ require 'thread'
3
+
1
4
  require 'observed/config'
2
5
  require 'observed/configurable'
3
6
  require 'observed/default'
4
7
  require 'observed/hash'
5
- require 'observed/reader'
6
- require 'observed/writer'
8
+ require 'observed/translator'
9
+ require 'observed/execution_job_factory'
7
10
 
8
11
  module Observed
9
12
 
13
+ class ProcObserver < Observed::Observer
14
+ def initialize(&block)
15
+ @block = block
16
+ end
17
+ def observe(data=nil, options=nil)
18
+ @block.call data, options
19
+ end
20
+ end
21
+
22
+ class ProcTranslator < Observed::Translator
23
+ def initialize(&block)
24
+ @block = block
25
+ end
26
+ def translate(tag, time, data)
27
+ @block.call data, {tag: tag, time: time}
28
+ end
29
+ end
30
+
31
+ class ProcReporter < Observed::Reporter
32
+ def initialize(tag_pattern, &block)
33
+ @tag_pattern = tag_pattern
34
+ @block = block
35
+ end
36
+ def match(tag)
37
+ tag.match(@tag_pattern) if tag && @tag_pattern
38
+ end
39
+ def report(tag, time, data)
40
+ @block.call data, {tag: tag, time: time}
41
+ end
42
+ end
43
+
10
44
  class ConfigBuilder
11
45
  include Observed::Configurable
12
46
 
47
+ attribute :logger, default: Logger.new(STDOUT, Logger::DEBUG)
48
+
13
49
  def initialize(args)
14
- @writer_plugins = args[:writer_plugins] if args[:writer_plugins]
15
- @reader_plugins = args[:reader_plugins] if args[:reader_plugins]
50
+ @group_mutex = ::Mutex.new
51
+ @context = args[:context]
16
52
  @observer_plugins = args[:observer_plugins] if args[:observer_plugins]
17
53
  @reporter_plugins = args[:reporter_plugins] if args[:reporter_plugins]
54
+ @translator_plugins = args[:translator_plugins] if args[:translator_plugins]
18
55
  @system = args[:system] || fail("The key :system must be in #{args}")
19
- @logger = args[:logger] || Logger.new(STDOUT, Logger::DEBUG)
56
+ configure args
20
57
  end
21
58
 
22
59
  def system
23
60
  @system
24
61
  end
25
62
 
26
- def writer_plugins
27
- @writer_plugins || select_named_plugins_of(Observed::Writer)
28
- end
29
-
30
- def reader_plugins
31
- @reader_plugins || select_named_plugins_of(Observed::Reader)
32
- end
33
-
34
63
  def observer_plugins
35
64
  @observer_plugins || select_named_plugins_of(Observed::Observer)
36
65
  end
@@ -39,6 +68,10 @@ module Observed
39
68
  @reporter_plugins || select_named_plugins_of(Observed::Reporter)
40
69
  end
41
70
 
71
+ def translator_plugins
72
+ @translator_plugins || select_named_plugins_of(Observed::Translator)
73
+ end
74
+
42
75
  def select_named_plugins_of(klass)
43
76
  plugins = {}
44
77
  klass.select_named_plugins.each do |plugin|
@@ -49,8 +82,6 @@ module Observed
49
82
 
50
83
  def build
51
84
  Observed::Config.new(
52
- writers: writers,
53
- readers: readers,
54
85
  observers: observers,
55
86
  reporters: reporters
56
87
  )
@@ -60,31 +91,53 @@ module Observed
60
91
  # @param [Hash] args The configuration for each reporter which may or may not contain (1) which reporter plugin to
61
92
  # use or which writer plugin to use (in combination with the default reporter plugin) (2) initialization parameters
62
93
  # to instantiate the reporter/writer plugin
63
- def report(tag_pattern, args)
64
- writer = write(args)
65
- tag_pattern || fail("Tag pattern missing: #{tag_pattern} where args: #{args}")
66
- reporter = if writer
67
- Observed::Default::Reporter.new.configure(tag_pattern: tag_pattern, writer: writer, system: system)
68
- else
94
+ def report(tag_pattern=nil, args={}, &block)
95
+ if tag_pattern.is_a? ::Hash
96
+ args = tag_pattern
97
+ tag_pattern = nil
98
+ end
99
+ reporter = if args[:via] || args[:using]
69
100
  via = args[:via] || args[:using]
70
101
  with = args[:with] || args[:which] || {}
71
- with = ({logger: @logger}).merge(with).merge({tag_pattern: tag_pattern})
102
+ with = ({logger: @logger}).merge(with).merge({tag_pattern: tag_pattern, system: system})
72
103
  plugin = reporter_plugins[via] ||
73
104
  fail(RuntimeError, %Q|The reporter plugin named "#{via}" is not found in "#{reporter_plugins}"|)
74
105
  plugin.new(with)
106
+ elsif block_given?
107
+ Observed::ProcReporter.new tag_pattern, &block
108
+ else
109
+ fail "Invalid combination of arguments: #{tag_pattern} #{args}"
75
110
  end
76
- begin
77
- reporter.match('test')
78
- rescue => e
79
- fail "A mis-configured reporter plugin found: #{reporter}"
80
- rescue NotImplementedError => e
81
- builtin_methods = Object.methods
82
- info = (reporter.methods - builtin_methods).map {|sym| reporter.method(sym) }.map(&:source_location).compact
83
- fail "Incomplete reporter plugin found: #{reporter}, defined in: #{info}"
84
- end
85
111
 
86
112
  reporters << reporter
87
- reporter
113
+ report_it = convert_to_job(reporter)
114
+ if tag_pattern
115
+ receive(tag_pattern).then(report_it)
116
+ end
117
+ report_it
118
+ end
119
+
120
+ class ObserverCompatibilityAdapter < Observed::Observer
121
+ include Observed::Configurable
122
+ attribute :observer
123
+ attribute :system
124
+ attribute :tag
125
+
126
+ def configure(args)
127
+ super
128
+ observer.configure(args)
129
+ end
130
+
131
+ def observe(data=nil, options=nil)
132
+ case observer.method(:observe).parameters.size
133
+ when 0
134
+ observer.observe
135
+ when 1
136
+ observer.observe data
137
+ when 2
138
+ observer.observe data, options
139
+ end
140
+ end
88
141
  end
89
142
 
90
143
  # @param [String] tag The tag which is assigned to data which is generated from this observer, and is sent to
@@ -92,67 +145,74 @@ module Observed
92
145
  # @param [Hash] args The configuration for each observer which may or may not contain (1) which observer plugin to
93
146
  # use or which reader plugin to use (in combination with the default observer plugin) (2) initialization parameters
94
147
  # to instantiate the observer/reader plugin
95
- def observe(tag, args)
96
- reader = read(args)
97
- observer = if reader
98
- Observed::Default::Observer.new.configure(tag: tag, reader: reader, system: system)
99
- else
148
+ def observe(tag=nil, args={}, &block)
149
+ if tag.is_a? ::Hash
150
+ args = tag
151
+ tag = nil
152
+ end
153
+ observer = if args[:via] || args[:using]
100
154
  via = args[:via] || args[:using] ||
101
155
  fail(RuntimeError, %Q|Missing observer plugin name for the tag "#{tag}" in "#{args}"|)
102
156
  with = args[:with] || args[:which] || {}
103
157
  plugin = observer_plugins[via] ||
104
158
  fail(RuntimeError, %Q|The observer plugin named "#{via}" is not found in "#{observer_plugins}"|)
105
- plugin.new(({logger: @logger}).merge(with).merge(tag: tag, system: system))
159
+ observer = plugin.new(({logger: logger}).merge(with).merge(tag: tag, system: system))
160
+ ObserverCompatibilityAdapter.new(
161
+ system: system,
162
+ observer: observer,
163
+ tag: tag
164
+ )
165
+ elsif block_given?
166
+ Observed::ProcObserver.new &block
167
+ else
168
+ fail "No args valid args (in args=#{args}) or a block given"
169
+ end
170
+ observe_that = convert_to_job(observer)
171
+ result = if tag
172
+ a = observe_that.then(emit(tag))
173
+ group tag, (group(tag) + [a])
174
+ a
175
+ else
176
+ observe_that
177
+ end
178
+ observers << result
179
+ result
180
+ end
181
+
182
+ def translate(args={}, &block)
183
+ translator = if args[:via] || args[:using]
184
+ #tag_pattern || fail("Tag pattern missing: #{tag_pattern} where args: #{args}")
185
+ via = args[:via] || args[:using]
186
+ with = args[:with] || args[:which] || {}
187
+ with = ({logger: logger}).merge(with).merge({system: system})
188
+ plugin = translator_plugins[via] ||
189
+ fail(RuntimeError, %Q|The reporter plugin named "#{via}" is not found in "#{translator_plugins}"|)
190
+ plugin.new(with)
191
+ else
192
+ Observed::ProcTranslator.new &block
106
193
  end
107
- observers << observer
108
- observer
109
- end
110
-
111
- def write(args)
112
- to = args[:to]
113
- with = args[:with] || args[:which]
114
- writer = case to
115
- when String
116
- plugin = writer_plugins[to] ||
117
- fail(RuntimeError, %Q|The writer plugin named "#{to}" is not found in "#{writer_plugins}"|)
118
- with = ({logger: @logger}).merge(with)
119
- plugin.new(with)
120
- when Observed::Writer
121
- to
122
- when nil
123
- nil
124
- else
125
- fail "Unexpected type of value for the key :to in: #{args}"
126
- end
127
- writers << writer if writer
128
- writer
129
- end
130
-
131
- def read(args)
132
- from = args[:from]
133
- with = args[:with] || [:which]
134
- reader = case from
135
- when String
136
- plugin = reader_plugins[from] || fail(RuntimeError, %Q|The reader plugin named "#{from}" is not found in "#{reader_plugins}"|)
137
- with = ({logger: @logger}).merge(with)
138
- plugin.new(with)
139
- when Observed::Reader
140
- from
141
- when nil
142
- nil
143
- else
144
- fail "Unexpected type of value for the key :from in: #{args}"
145
- end
146
- readers << reader if reader
147
- reader
148
- end
149
-
150
- def writers
151
- @writers ||= []
152
- end
153
-
154
- def readers
155
- @readers ||= []
194
+ convert_to_job(translator)
195
+ end
196
+
197
+ def emit(tag)
198
+ @context.jobbed_event_bus.pipe_to_emit(tag)
199
+ end
200
+
201
+ def receive(pattern)
202
+ @context.jobbed_event_bus.receive(pattern)
203
+ end
204
+
205
+ # Updates or get the observations belongs to the group named `name`
206
+ def group(name, observations=nil)
207
+ @group_mutex.synchronize do
208
+ @observations ||= {}
209
+ @observations[name] = observations if observations
210
+ @observations[name] || []
211
+ end
212
+ end
213
+
214
+ def run_group(name)
215
+ @context.job_factory.parallel(group(name))
156
216
  end
157
217
 
158
218
  def reporters
@@ -162,6 +222,13 @@ module Observed
162
222
  def observers
163
223
  @observers ||= []
164
224
  end
225
+
226
+ private
227
+
228
+ def convert_to_job(underlying)
229
+ @execution_job_factory ||= @context.execution_job_factory
230
+ @execution_job_factory.convert_to_job(underlying)
231
+ end
165
232
  end
166
233
 
167
234
  end
@@ -15,16 +15,15 @@ module Observed
15
15
 
16
16
  include Observed::Configurable
17
17
 
18
- def_delegators :@builder, :observe, :report, :read, :write
18
+ def_delegators :@builder, :observe, :translate, :report, :read, :write, :emit, :group, :run_group, :receive
19
19
 
20
20
  attribute :builder
21
+ attribute :logger, default: Logger.new(STDOUT)
21
22
 
22
23
  def initialize(args)
23
24
  args[:builder] || fail("The key :builder must exist in #{args}")
24
25
  @builder = args[:builder]
25
26
 
26
- @logger = args[:logger] if args[:logger]
27
-
28
27
  configure(args)
29
28
  end
30
29
 
@@ -68,10 +67,5 @@ module Observed
68
67
  eval_file file
69
68
  end
70
69
 
71
- private
72
-
73
- def logger
74
- @logger ||= Logger.new(STDOUT)
75
- end
76
70
  end
77
71
  end
@@ -1,4 +1,7 @@
1
1
  module Observed
2
+ # Indicates that classes included this module to have attributes which are configurable.
3
+ # `configurable` means that the attributes can be configured via named parameters of
4
+ # the constructor and the `configure` instance method of the class included this module.
2
5
  module Configurable
3
6
 
4
7
  def initialize(args={})
@@ -14,11 +17,25 @@ module Observed
14
17
  self
15
18
  end
16
19
 
20
+ # @param [String|Symbol] name
21
+ def has_attribute_value?(name)
22
+ !! get_attribute_value(name)
23
+ end
24
+
25
+ # @param [String|Symbol] name
26
+ # @return [Object] In order of precedence, the value of the instance variable named `"@" + name`,
27
+ # or the value `@attributes[name]`, or the default value for the attribute named `name`
28
+ def get_attribute_value(name)
29
+ instance_variable_get("@#{name.to_s}") || @attributes[name] || self.class.defaults[name]
30
+ end
31
+
17
32
  module ClassMethods
18
33
  # @param [String|Symbol] name
19
34
  def attribute(name, options={})
20
- define_method(name) do
21
- instance_variable_get("@#{name.to_s}") || @attributes[name] || self.class.defaults[name] || fail_for_not_configured_parameter(name)
35
+ unless instance_methods.include? name.intern
36
+ define_method(name) do
37
+ get_attribute_value(name) || fail_for_not_configured_parameter(name)
38
+ end
22
39
  end
23
40
  default_value = options && options[:default]
24
41
  default name => default_value if default_value
@@ -36,13 +53,54 @@ module Observed
36
53
  self.new(args)
37
54
  end
38
55
 
56
+ # Inherits the default values stored in @defaults to the sub-class
57
+ def inherited(klass)
58
+ super if defined? super
59
+ klass.default defaults
60
+ end
61
+
62
+ end
63
+
64
+ module ModuleMethods
65
+ # @param [String|Symbol] name
66
+ def attribute(name, options={})
67
+ @attributes ||= {}
68
+ @attributes = @attributes.merge(name => options)
69
+ end
70
+
71
+ def attributes
72
+ @attributes ||
73
+ fail(<<EOS
74
+ #{self} includes Observed::Configurable. Though, no attributes are configured for #{self}.
75
+ We don't need to include Observed::Configurable, or it might be a bug?
76
+ EOS
77
+ )
78
+ end
79
+
80
+ def included(klass)
81
+ ensure_configurable klass
82
+
83
+ attributes.each do |name, options|
84
+ klass.attribute name, options
85
+ end
86
+ end
87
+
88
+ def ensure_configurable(klass)
89
+ unless klass.include? Configurable
90
+ fail "The class #{klass} must include Observed::Configurable to include #{self}"
91
+ end
92
+ end
39
93
  end
40
94
 
41
95
  class NotConfiguredError < RuntimeError; end
42
96
 
43
97
  class << self
44
98
  def included(klass)
45
- klass.extend ClassMethods
99
+ if klass.is_a? Class
100
+ klass.extend ClassMethods
101
+ else
102
+ klass.extend ModuleMethods
103
+ end
46
104
  end
47
105
  end
48
106