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.
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