observed 0.1.1

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 (119) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +20 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +6 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +204 -0
  7. data/Rakefile +4 -0
  8. data/examples/00readme/Gemfile +5 -0
  9. data/examples/00readme/clockwork.rb +16 -0
  10. data/examples/00readme/observed.rb +17 -0
  11. data/examples/observed.rb +10 -0
  12. data/exe/observed-oneshot +11 -0
  13. data/features/oneshot.feature +24 -0
  14. data/features/support/env.rb +8 -0
  15. data/features/test_in_single_ruby_source.feature +32 -0
  16. data/integrations/observed-clockwork/.gitignore +17 -0
  17. data/integrations/observed-clockwork/Gemfile +4 -0
  18. data/integrations/observed-clockwork/LICENSE.txt +22 -0
  19. data/integrations/observed-clockwork/README.md +53 -0
  20. data/integrations/observed-clockwork/Rakefile +1 -0
  21. data/integrations/observed-clockwork/bin/clockwork +16 -0
  22. data/integrations/observed-clockwork/bin/clockworkd +16 -0
  23. data/integrations/observed-clockwork/bin/observed-clockwork +16 -0
  24. data/integrations/observed-clockwork/bin/rake +16 -0
  25. data/integrations/observed-clockwork/examples/clockwork/clockwork.rb +9 -0
  26. data/integrations/observed-clockwork/examples/clockwork/foo_plugin.rb +19 -0
  27. data/integrations/observed-clockwork/examples/clockwork/observed.conf +6 -0
  28. data/integrations/observed-clockwork/features/run_observed_inside_clockwork.feature +59 -0
  29. data/integrations/observed-clockwork/features/support/env.rb +8 -0
  30. data/integrations/observed-clockwork/lib/observed/clockwork/version.rb +5 -0
  31. data/integrations/observed-clockwork/lib/observed/clockwork.rb +20 -0
  32. data/integrations/observed-clockwork/observed-clockwork.gemspec +29 -0
  33. data/lib/observed/application/oneshot.rb +114 -0
  34. data/lib/observed/application.rb +1 -0
  35. data/lib/observed/builtin_plugins/file.rb +49 -0
  36. data/lib/observed/builtin_plugins/stdout.rb +35 -0
  37. data/lib/observed/builtin_plugins.rb +2 -0
  38. data/lib/observed/config.rb +27 -0
  39. data/lib/observed/config_builder.rb +167 -0
  40. data/lib/observed/config_dsl.rb +77 -0
  41. data/lib/observed/configurable.rb +56 -0
  42. data/lib/observed/default/observer.rb +16 -0
  43. data/lib/observed/default/reporter.rb +17 -0
  44. data/lib/observed/default.rb +2 -0
  45. data/lib/observed/hash/builder.rb +24 -0
  46. data/lib/observed/hash/fetcher.rb +19 -0
  47. data/lib/observed/hash/key_path_encoding.rb +62 -0
  48. data/lib/observed/hash.rb +3 -0
  49. data/lib/observed/observer.rb +18 -0
  50. data/lib/observed/observer_helpers/timer.rb +37 -0
  51. data/lib/observed/pluggable.rb +34 -0
  52. data/lib/observed/reader.rb +14 -0
  53. data/lib/observed/reporter/regexp_matching.rb +11 -0
  54. data/lib/observed/reporter.rb +25 -0
  55. data/lib/observed/system.rb +109 -0
  56. data/lib/observed/version.rb +3 -0
  57. data/lib/observed/writer.rb +14 -0
  58. data/lib/observed.rb +77 -0
  59. data/observed.gemspec +30 -0
  60. data/plugins/observed-fluentd/.gitignore +20 -0
  61. data/plugins/observed-fluentd/Gemfile +12 -0
  62. data/plugins/observed-fluentd/LICENSE.txt +22 -0
  63. data/plugins/observed-fluentd/README.md +99 -0
  64. data/plugins/observed-fluentd/Rakefile +1 -0
  65. data/plugins/observed-fluentd/features/plugin.feature +61 -0
  66. data/plugins/observed-fluentd/features/support/env.rb +32 -0
  67. data/plugins/observed-fluentd/fluent.d/fluent.conf +93 -0
  68. data/plugins/observed-fluentd/fluentd.pid +1 -0
  69. data/plugins/observed-fluentd/lib/observed/fluentd/version.rb +5 -0
  70. data/plugins/observed-fluentd/lib/observed/fluentd.rb +30 -0
  71. data/plugins/observed-fluentd/observe.d/clockwork.rb +9 -0
  72. data/plugins/observed-fluentd/observe.d/observed.conf +17 -0
  73. data/plugins/observed-fluentd/observed-fluentd.gemspec +26 -0
  74. data/plugins/observed-gauge/.gitignore +18 -0
  75. data/plugins/observed-gauge/Gemfile +4 -0
  76. data/plugins/observed-gauge/LICENSE.txt +22 -0
  77. data/plugins/observed-gauge/README.md +29 -0
  78. data/plugins/observed-gauge/Rakefile +1 -0
  79. data/plugins/observed-gauge/lib/observed/gauge/version.rb +5 -0
  80. data/plugins/observed-gauge/lib/observed/gauge.rb +117 -0
  81. data/plugins/observed-gauge/observed-gauge.gemspec +30 -0
  82. data/plugins/observed-gauge/spec/gauge_spec.rb +195 -0
  83. data/plugins/observed-gauge/spec/spec_helper.rb +25 -0
  84. data/plugins/observed-http/.gitignore +17 -0
  85. data/plugins/observed-http/Gemfile +4 -0
  86. data/plugins/observed-http/LICENSE.txt +22 -0
  87. data/plugins/observed-http/README.md +37 -0
  88. data/plugins/observed-http/Rakefile +1 -0
  89. data/plugins/observed-http/features/observe_web_services_via_http.feature +32 -0
  90. data/plugins/observed-http/features/support/env.rb +8 -0
  91. data/plugins/observed-http/lib/observed/http/version.rb +5 -0
  92. data/plugins/observed-http/lib/observed/http.rb +60 -0
  93. data/plugins/observed-http/observed-http.gemspec +31 -0
  94. data/plugins/observed-http/spec/fixtures/observed.conf +10 -0
  95. data/plugins/observed-http/spec/http_spec.rb +14 -0
  96. data/plugins/observed-http/spec/spec_helper.rb +25 -0
  97. data/spec/builtin_plugins/file_spec.rb +145 -0
  98. data/spec/builtin_plugins/stdout_spec.rb +56 -0
  99. data/spec/config_builder_spec.rb +146 -0
  100. data/spec/config_dsl_spec.rb +50 -0
  101. data/spec/configurable_spec.rb +65 -0
  102. data/spec/fixtures/configure_by_conf/foo_plugin.rb +12 -0
  103. data/spec/fixtures/configure_by_conf/observed.conf +6 -0
  104. data/spec/fixtures/configure_by_conf_dot_d/foo_plugin.rb +18 -0
  105. data/spec/fixtures/configure_by_conf_dot_d/observed.conf.d/check_foo_1.rb +1 -0
  106. data/spec/fixtures/configure_by_conf_dot_d/observed.conf.d/plugins.rb +1 -0
  107. data/spec/fixtures/configure_by_require/observed_conf.rb +6 -0
  108. data/spec/hash/builder_spec.rb +36 -0
  109. data/spec/hash/fetcher_spec.rb +29 -0
  110. data/spec/input_helpers/timer_spec.rb +54 -0
  111. data/spec/observed_spec.rb +79 -0
  112. data/spec/observer_spec.rb +123 -0
  113. data/spec/oneshot_spec.rb +58 -0
  114. data/spec/reader_spec.rb +15 -0
  115. data/spec/reporter_spec.rb +19 -0
  116. data/spec/spec_helper.rb +65 -0
  117. data/spec/system_spec.rb +75 -0
  118. data/spec/writer_spec.rb +16 -0
  119. metadata +299 -0
@@ -0,0 +1,59 @@
1
+ Feature: Running Observed inside Clockwork
2
+
3
+ In order to integrate Observed with Clockwork,
4
+ I want to configure Clockwork to run Observed.
5
+
6
+ Scenario: Create a .rb file containing Observed code and run it with the ruby command
7
+ Given a file named "observed.conf" with:
8
+ """
9
+ require 'observed/builtin_plugins'
10
+ require_relative 'foo_plugin'
11
+
12
+ observe 'foo_1', via: 'foo'
13
+
14
+ report /foo_1/, via: 'stdout'
15
+ """
16
+ Given a file named "foo_plugin.rb" with:
17
+ """
18
+ module OneshotSpec
19
+ class FooPlugin < Observed::Observer
20
+
21
+ plugin_name 'foo'
22
+
23
+ default timeout_in_milliseconds: 5000
24
+ default number_of_trials: 10
25
+
26
+ def observe
27
+ sleep_duration = rand / 20
28
+ sleep sleep_duration
29
+ system.report(tag, "Foo #{sleep_duration}")
30
+
31
+ # For testing purpose,
32
+ # we want the `clockwork` process to exit as soon as we ran Observed to report the test
33
+ # output to the stdout
34
+ exit
35
+ end
36
+
37
+ def logger
38
+ @logger ||= Logger.new(STDOUT)
39
+ end
40
+ end
41
+ end
42
+ """
43
+ Given a file named "clockwork.rb" with:
44
+ """
45
+ require 'clockwork'
46
+ require 'observed/clockwork'
47
+
48
+ include Clockwork
49
+ include Observed::Clockwork
50
+
51
+ register_observed_handler :config_file => File.dirname(__FILE__) + '/observed.conf'
52
+
53
+ every(10.seconds, 'foo_1')
54
+ """
55
+ When I run `clockwork clockwork.rb`
56
+ Then the output should contain:
57
+ """
58
+ foo_1 Foo
59
+ """
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__) + '/../../lib')
2
+ require 'aruba/cucumber'
3
+
4
+ World(Aruba::Api)
5
+
6
+ Before do
7
+ @aruba_timeout_seconds = 20
8
+ end
@@ -0,0 +1,5 @@
1
+ module Observed
2
+ module Clockwork
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,20 @@
1
+ require 'clockwork'
2
+ require 'observed'
3
+ require "observed/clockwork/version"
4
+ require "observed/application"
5
+
6
+ module Observed
7
+ module Clockwork
8
+ extend self
9
+
10
+ def register_observed_handler(args)
11
+ Observed.init!
12
+ Observed.configure args
13
+ Observed.load! args[:config_file]
14
+ ::Clockwork.handler do |job|
15
+ Observed.run job
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'observed/clockwork/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "observed-clockwork"
8
+ spec.version = Observed::Clockwork::VERSION
9
+ spec.authors = ["KUOKA Yusuke"]
10
+ spec.email = ["yusuke.kuoka@gree.net"]
11
+ spec.description = %q{Observed Clockwork Integration}
12
+ spec.summary = %q{An integration of Observed with Clockwork}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.bindir = 'exe'
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_dependency 'clockwork'
23
+ spec.add_dependency 'daemons'
24
+ spec.add_dependency 'observed', "~> 0.1.0"
25
+ spec.add_development_dependency "bundler", "~> 1.3"
26
+ spec.add_development_dependency "rake"
27
+ spec.add_development_dependency "cucumber"
28
+ spec.add_development_dependency "aruba"
29
+ end
@@ -0,0 +1,114 @@
1
+ require 'logger'
2
+ require 'optparse'
3
+ require 'pathname'
4
+
5
+ require 'observed/config'
6
+ require 'observed/config_builder'
7
+ require 'observed/config_dsl'
8
+ require 'observed/observer'
9
+ require 'observed/system'
10
+
11
+ module Observed
12
+
13
+ module Application
14
+ # The application which is usually ran from CLI to run health-checks and write the results to a log file, and then exit.
15
+ # An "Oneshot" application is the opposite of a "Daemon" or "Resident" application.
16
+ class Oneshot
17
+
18
+ class InvalidArgumentError < RuntimeError; end
19
+
20
+ # @param [Observed::Config] config
21
+ def initialize(config, sys)
22
+ @config = config
23
+ @system = sys
24
+ end
25
+
26
+ def config
27
+ @config || fail('Missing configuration for Application::Oneshot')
28
+ end
29
+
30
+ def run(observation_name=nil)
31
+ system.run(observation_name)
32
+ end
33
+
34
+ def logger
35
+ @logger ||= Logger.new(STDOUT)
36
+ end
37
+
38
+ class << self
39
+ def from_argv(argv)
40
+
41
+ command_line_args = argv.dup
42
+
43
+ args = {}
44
+
45
+ opts = OptionParser.new
46
+ opts.accept(Pathname) do |s,|
47
+ Pathname.new(s)
48
+ end
49
+ opts.on('-d', '--debug') do
50
+ args[:debug] = true
51
+ end
52
+ opts.on('-l LOG_FILE', '--l LOG_FILE', Pathname) do |log_file|
53
+ args[:log_file] = log_file
54
+ end
55
+
56
+ opts.parse!(command_line_args)
57
+
58
+ if command_line_args.size != 1
59
+ fail InvalidArgumentError, "Invalid number of arguments #{command_line_args.size} where arguments are #{command_line_args}"
60
+ end
61
+
62
+ args[:config_file] = command_line_args.first
63
+
64
+ create(args)
65
+ end
66
+
67
+ # @param [Hash<Symbol,String>] args
68
+ # @option args [Array<String>] :argv The Ruby's `ARGV` like object which is treated as intialization parameters for Oneshoft application.
69
+ 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)
82
+ config = if args[:yaml_file]
83
+ YAML.load_file(args[:yaml_file])
84
+ 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
90
+ elsif args[:config]
91
+ c = args[:config]
92
+ c
93
+ else
94
+ fail 'No configuration provided'
95
+ end
96
+ config = if config.is_a? Hash
97
+ Observed::Config.create(config)
98
+ else
99
+ config
100
+ end
101
+ sys.config = config
102
+ new(config, sys)
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def system
109
+ @system
110
+ end
111
+
112
+ end
113
+ end
114
+ end
@@ -0,0 +1 @@
1
+ require 'observed/application/oneshot'
@@ -0,0 +1,49 @@
1
+ require 'observed/hash/fetcher'
2
+ require 'observed/reporter'
3
+ require 'observed/reporter/regexp_matching'
4
+
5
+ module Observed
6
+ module BuiltinPlugins
7
+ class File < Observed::Reporter
8
+
9
+ include Observed::Reporter::RegexpMatching
10
+
11
+ UNDEFINED = Object.new
12
+
13
+ attribute :format, default: -> tag, time, data { "#{Time.at(time)} #{tag} #{data}" }
14
+ attribute :path
15
+ attribute :mode, default: :append
16
+
17
+ # @param [String] tag
18
+ # @param [Time] time
19
+ # @param [Hash] data
20
+ 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
30
+ mode = case self.mode
31
+ when :append, 'a'
32
+ 'a'
33
+ when :overwrite, 'w'
34
+ 'w'
35
+ else
36
+ fail "Unsupported value for the parameter `:mode`: Supported values are :append, :overwrite, " +
37
+ "'a', 'w'. The specified value is #{self.mode.inspect}"
38
+ end
39
+ ::File.open(path, mode) do |f|
40
+ f.puts formatted_data
41
+ end
42
+ end
43
+
44
+ def self.plugin_name
45
+ 'file'
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,35 @@
1
+ require 'observed/hash/fetcher'
2
+ require 'observed/observer'
3
+ require 'observed/reporter'
4
+ require 'observed/reporter/regexp_matching'
5
+
6
+ module Observed
7
+ module BuiltinPlugins
8
+ class Stdout < Observed::Reporter
9
+
10
+ include Observed::Reporter::RegexpMatching
11
+
12
+ attribute :format, default: -> tag, time, data { "#{Time.at(time)} #{tag} #{data}" }
13
+
14
+ # @param [String] tag
15
+ # @param [Time] time
16
+ # @param [Hash] data
17
+ 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
28
+ end
29
+
30
+ def self.plugin_name
31
+ 'stdout'
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,2 @@
1
+ require 'observed/builtin_plugins/file'
2
+ require 'observed/builtin_plugins/stdout'
@@ -0,0 +1,27 @@
1
+ require 'observed/configurable'
2
+
3
+ module Observed
4
+ # The configuration for Observed which may be built by Observed::Builder,
5
+ # which may contains configured writers, readers, reporters, observers.
6
+ class Config
7
+
8
+ include Observed::Configurable
9
+
10
+ # !@attribute [rw] writers
11
+ # @return [Array<Observed::Writer>]
12
+ attribute :writers
13
+
14
+ # !@attribute [rw] readers
15
+ # @return [Array<Observed::Reader>]
16
+ attribute :readers
17
+
18
+ # !@attribute [rw] reporters
19
+ # @return [Array<Observed::Reporter>]
20
+ attribute :reporters
21
+
22
+ # !@attribute [rw] observers
23
+ # @return [Array<Observed::Observer>]
24
+ attribute :observers
25
+
26
+ end
27
+ end
@@ -0,0 +1,167 @@
1
+ require 'observed/config'
2
+ require 'observed/configurable'
3
+ require 'observed/default'
4
+ require 'observed/hash'
5
+ require 'observed/reader'
6
+ require 'observed/writer'
7
+
8
+ module Observed
9
+
10
+ class ConfigBuilder
11
+ include Observed::Configurable
12
+
13
+ def initialize(args)
14
+ @writer_plugins = args[:writer_plugins] if args[:writer_plugins]
15
+ @reader_plugins = args[:reader_plugins] if args[:reader_plugins]
16
+ @observer_plugins = args[:observer_plugins] if args[:observer_plugins]
17
+ @reporter_plugins = args[:reporter_plugins] if args[:reporter_plugins]
18
+ @system = args[:system] || fail("The key :system must be in #{args}")
19
+ @logger = args[:logger] || Logger.new(STDOUT, Logger::DEBUG)
20
+ end
21
+
22
+ def system
23
+ @system
24
+ end
25
+
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
+ def observer_plugins
35
+ @observer_plugins || select_named_plugins_of(Observed::Observer)
36
+ end
37
+
38
+ def reporter_plugins
39
+ @reporter_plugins || select_named_plugins_of(Observed::Reporter)
40
+ end
41
+
42
+ def select_named_plugins_of(klass)
43
+ plugins = {}
44
+ klass.select_named_plugins.each do |plugin|
45
+ plugins[plugin.plugin_name] = plugin
46
+ end
47
+ plugins
48
+ end
49
+
50
+ def build
51
+ Observed::Config.new(
52
+ writers: writers,
53
+ readers: readers,
54
+ observers: observers,
55
+ reporters: reporters
56
+ )
57
+ end
58
+
59
+ # @param [Regexp] tag_pattern The pattern to match tags added to data from observers
60
+ # @param [Hash] args The configuration for each reporter which may or may not contain (1) which reporter plugin to
61
+ # use or which writer plugin to use (in combination with the default reporter plugin) (2) initialization parameters
62
+ # 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
69
+ via = args[:via] || args[:using]
70
+ with = args[:with] || args[:which] || {}
71
+ with = ({logger: @logger}).merge(with).merge({tag_pattern: tag_pattern})
72
+ plugin = reporter_plugins[via] ||
73
+ fail(RuntimeError, %Q|The reporter plugin named "#{via}" is not found in "#{reporter_plugins}"|)
74
+ plugin.new(with)
75
+ 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
+
86
+ reporters << reporter
87
+ reporter
88
+ end
89
+
90
+ # @param [String] tag The tag which is assigned to data which is generated from this observer, and is sent to
91
+ # reporters later
92
+ # @param [Hash] args The configuration for each observer which may or may not contain (1) which observer plugin to
93
+ # use or which reader plugin to use (in combination with the default observer plugin) (2) initialization parameters
94
+ # 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
100
+ via = args[:via] || args[:using] ||
101
+ fail(RuntimeError, %Q|Missing observer plugin name for the tag "#{tag}" in "#{args}"|)
102
+ with = args[:with] || args[:which] || {}
103
+ plugin = observer_plugins[via] ||
104
+ 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))
106
+ 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 ||= []
156
+ end
157
+
158
+ def reporters
159
+ @reporters ||= []
160
+ end
161
+
162
+ def observers
163
+ @observers ||= []
164
+ end
165
+ end
166
+
167
+ end
@@ -0,0 +1,77 @@
1
+ require 'observed/observer'
2
+ require 'observed/configurable'
3
+ require 'forwardable'
4
+ require 'logger'
5
+
6
+ module Observed
7
+ # The DSL to describe Observed's configuration.
8
+ # @example
9
+ # context = ConfigDSL.new(builder: the_builder)
10
+ # context.eval_file observed_conf_file(a.k.a user code describes Observed configuration)
11
+ # context.config #=> can be used to instantiate Observed::System
12
+ class ConfigDSL
13
+
14
+ extend Forwardable
15
+
16
+ include Observed::Configurable
17
+
18
+ def_delegators :@builder, :observe, :report, :read, :write
19
+
20
+ attribute :builder
21
+
22
+ def initialize(args)
23
+ args[:builder] || fail("The key :builder must exist in #{args}")
24
+ @builder = args[:builder]
25
+
26
+ @logger = args[:logger] if args[:logger]
27
+
28
+ configure(args)
29
+ end
30
+
31
+ def eval_file(file)
32
+ @file = File.expand_path(file)
33
+ working_directory File.dirname(@file)
34
+ logger.debug "Reading the file: #{@file}"
35
+ code = File.read(@file)
36
+ logger.debug "Evaluating: #{code}"
37
+ instance_eval(code, @file)
38
+ end
39
+
40
+ # The `current directory` in which `require_relative` finds source files
41
+ def working_directory(wd=nil)
42
+ @working_directory = wd if wd
43
+ @working_directory
44
+ end
45
+
46
+ # The replacement for Ruby's built-in `require_relative`.
47
+ # Although the built-in one can not be used in `eval` or `instance_eval` or etc because
48
+ # there is no `current file` semantics in `eval`, this replacement takes the file which is going to be evaluated
49
+ # as the `current file`.
50
+ # Thanks to this method, we can use `require_relative` in observed.conf files both when it is evaluated with `eval`
51
+ # and when it is evaluated in result of `require`.
52
+ def require_relative(lib)
53
+ path = File.expand_path("#{working_directory}/#{lib}")
54
+ logger.debug "Require '#{path}'"
55
+ require path
56
+ end
57
+
58
+ # Build and returns the Observed configuration
59
+ # @return [Observed::Config]
60
+ def config
61
+ @builder.build
62
+ end
63
+
64
+ # Load the file and evaluate the containing code in context of this object(a.k.a DSL).
65
+ # @param [String|Pathname] file The path to Ruby script containing the code in Observed's configuration DSL,
66
+ # typically `observed.conf`.
67
+ def load!(file)
68
+ eval_file file
69
+ end
70
+
71
+ private
72
+
73
+ def logger
74
+ @logger ||= Logger.new(STDOUT)
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,56 @@
1
+ module Observed
2
+ module Configurable
3
+
4
+ def initialize(args={})
5
+ configure(args)
6
+ end
7
+
8
+ def configure(args={})
9
+ if @attributes
10
+ @attributes.merge! args
11
+ else
12
+ @attributes ||= args.dup
13
+ end
14
+ self
15
+ end
16
+
17
+ module ClassMethods
18
+ # @param [String|Symbol] name
19
+ 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)
22
+ end
23
+ default_value = options && options[:default]
24
+ default name => default_value if default_value
25
+ end
26
+
27
+ def default(args)
28
+ @defaults = defaults.merge(args)
29
+ end
30
+
31
+ def defaults
32
+ @defaults ||= {}
33
+ end
34
+
35
+ def create(args)
36
+ self.new(args)
37
+ end
38
+
39
+ end
40
+
41
+ class NotConfiguredError < RuntimeError; end
42
+
43
+ class << self
44
+ def included(klass)
45
+ klass.extend ClassMethods
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def fail_for_not_configured_parameter(name)
52
+ fail NotConfiguredError.new("The parameter `#{name}` is not configured. attributes=#{@attributes}, defaults=#{self.class.defaults}")
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,16 @@
1
+ require 'observed/observer'
2
+
3
+ module Observed
4
+ module Default
5
+ class Observer < Observed::Observer
6
+
7
+ attribute :reader
8
+
9
+ def observe
10
+ data = reader.read
11
+ system.report(tag, data)
12
+ end
13
+
14
+ end
15
+ end
16
+ end