noah 0.0.5-jruby → 0.1-jruby

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +10 -0
  3. data/LICENSE +201 -0
  4. data/README.md +68 -212
  5. data/Rakefile +65 -41
  6. data/TODO.md +65 -0
  7. data/bin/noah +2 -1
  8. data/bin/noah-watcher.rb +103 -0
  9. data/config.ru +6 -3
  10. data/config/warble.rb +18 -0
  11. data/examples/README.md +116 -0
  12. data/examples/cluster.ru +2 -0
  13. data/examples/custom-watcher.rb +10 -0
  14. data/examples/httpclient-server.rb +7 -0
  15. data/examples/httpclient.rb +12 -0
  16. data/examples/httpclient2.rb +28 -0
  17. data/examples/js/FABridge.js +1452 -0
  18. data/examples/js/WebSocketMain.swf +830 -0
  19. data/examples/js/swfobject.js +851 -0
  20. data/examples/js/web_socket.js +312 -0
  21. data/examples/logger.rb +11 -0
  22. data/examples/reconfiguring-sinatra-watcher.rb +11 -0
  23. data/examples/reconfiguring-sinatra.rb +33 -0
  24. data/examples/simple-post.rb +17 -0
  25. data/examples/websocket.html +24 -0
  26. data/examples/websocket.rb +41 -0
  27. data/lib/noah.rb +6 -8
  28. data/lib/noah/app.rb +20 -268
  29. data/lib/noah/application_routes.rb +70 -0
  30. data/lib/noah/ark.rb +0 -0
  31. data/lib/noah/configuration_routes.rb +81 -0
  32. data/lib/noah/custom_watcher.rb +79 -0
  33. data/lib/noah/ephemeral_routes.rb +47 -0
  34. data/lib/noah/helpers.rb +37 -14
  35. data/lib/noah/host_routes.rb +69 -0
  36. data/lib/noah/models.rb +86 -5
  37. data/lib/noah/models/applications.rb +41 -0
  38. data/lib/noah/models/configurations.rb +49 -0
  39. data/lib/noah/models/ephemerals.rb +54 -0
  40. data/lib/noah/models/hosts.rb +56 -0
  41. data/lib/noah/models/services.rb +54 -0
  42. data/lib/noah/models/watchers.rb +62 -0
  43. data/lib/noah/passthrough.rb +11 -0
  44. data/lib/noah/service_routes.rb +71 -0
  45. data/lib/noah/validations.rb +1 -0
  46. data/lib/noah/validations/watcher_validations.rb +48 -0
  47. data/lib/noah/version.rb +1 -1
  48. data/lib/noah/watcher_routes.rb +45 -0
  49. data/noah.gemspec +25 -17
  50. data/spec/application_spec.rb +30 -30
  51. data/spec/configuration_spec.rb +78 -14
  52. data/spec/ephemeral_spec.rb +59 -0
  53. data/spec/host_spec.rb +21 -21
  54. data/spec/noahapp_application_spec.rb +6 -6
  55. data/spec/noahapp_configuration_spec.rb +5 -5
  56. data/spec/noahapp_ephemeral_spec.rb +115 -0
  57. data/spec/noahapp_host_spec.rb +3 -3
  58. data/spec/noahapp_service_spec.rb +10 -10
  59. data/spec/noahapp_watcher_spec.rb +123 -0
  60. data/spec/service_spec.rb +27 -27
  61. data/spec/spec_helper.rb +13 -22
  62. data/spec/support/db/.keep +0 -0
  63. data/spec/support/test-redis.conf +8 -0
  64. data/spec/watcher_spec.rb +62 -0
  65. data/views/index.haml +21 -15
  66. metadata +189 -146
  67. data/Gemfile.lock +0 -83
  68. data/doc/coverage/index.html +0 -138
  69. data/doc/coverage/jquery-1.3.2.min.js +0 -19
  70. data/doc/coverage/jquery.tablesorter.min.js +0 -15
  71. data/doc/coverage/lib-helpers_rb.html +0 -393
  72. data/doc/coverage/lib-models_rb.html +0 -1449
  73. data/doc/coverage/noah_rb.html +0 -2019
  74. data/doc/coverage/print.css +0 -12
  75. data/doc/coverage/rcov.js +0 -42
  76. data/doc/coverage/screen.css +0 -270
  77. data/lib/noah/applications.rb +0 -46
  78. data/lib/noah/configurations.rb +0 -49
  79. data/lib/noah/hosts.rb +0 -54
  80. data/lib/noah/services.rb +0 -57
  81. data/lib/noah/watchers.rb +0 -18
data/Rakefile CHANGED
@@ -1,48 +1,70 @@
1
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "lib")))
1
2
  require 'bundler'
2
- begin
3
- Bundler.setup(:default, :development)
4
- rescue Bundler::BundlerError => e
5
- $stderr.puts e.message
6
- $stderr.puts "Run `bundle install` to install missing gems"
7
- exit e.status_code
8
- end
9
- require 'rake'
10
-
11
3
  require 'rspec/core'
12
4
  require 'rspec/core/rake_task'
13
5
 
6
+ REDIS_DIR = File.expand_path(File.join("..", "spec", "support"), __FILE__)
7
+ REDIS_CNF = File.join(REDIS_DIR, "test-redis.conf")
8
+
14
9
  Bundler::GemHelper.install_tasks
10
+ RSpec::Core::RakeTask.new(:spec) do |spec|
11
+ spec.pattern = FileList['spec/**/*_spec.rb']
12
+ end
13
+
14
+
15
+ task :default => :run
16
+ task :test => :run
17
+
18
+ desc "Run tests and manage server start/stop"
19
+ task :run => [:start, :spec, :stop]
20
+
21
+ task :start do
22
+ puts "Starting redis-server"
23
+ system "redis-server #{REDIS_CNF}"
24
+ end
25
+
26
+ task :stop do
27
+ puts "Killing redis"
28
+ system "killall -TERM redis-server"
29
+ end
30
+
31
+ namespace :coverage do
32
+ task(:clean) { rm_f "coverage.data" }
33
+ end
34
+ RSpec::Core::RakeTask.new(:coverage) do |t|
35
+ t.rcov = true
36
+ t.verbose = true
37
+ t.rcov_opts = %q[--aggregate coverage.data --sort coverage --text-report --exclude "config,.bundle/*,gems/*,spec/*" -o doc/coverage -Ilib -i "noah.rb"]
38
+ end
15
39
 
16
40
  desc "Populate database with sample dataset"
17
41
  task :sample, :redis_url do |t, args|
18
- require 'ohm'
19
- begin
20
- require 'yajl'
21
- rescue LoadError
22
- require 'json'
23
- end
24
- require File.join(File.dirname(__FILE__), 'lib','noah')
42
+ require 'noah'
25
43
 
26
-
27
44
  Ohm::connect(:url => args.redis_url)
28
45
  Ohm::redis.flushdb
46
+ puts "Creating watchers..."
47
+ Noah::Watcher.create :endpoint => "http://localhost:3000/webhook", :pattern => "//noah/application"
48
+ Noah::Watcher.create :endpoint => "http://localhost:3001/webhook", :pattern => "//noah/configuration"
49
+ Noah::Watcher.create :endpoint => "http://localhost:3002/webhook", :pattern => "//noah/host"
50
+ Noah::Watcher.create :endpoint => "http://localhost:3003/webhook", :pattern => "//noah/service"
29
51
  puts "Creating Host entry for 'localhost'"
30
- h = Host.create(:name => 'localhost', :status => "up")
52
+ h = Noah::Host.create(:name => 'localhost', :status => "up")
31
53
  if h.save
32
54
  %w[redis noah].each do |service|
33
55
  puts "Create Service entry for #{service}"
34
- s = Service.create(:name => service, :status => "up", :host => h)
56
+ s = Noah::Service.create(:name => service, :status => "up", :host => h)
35
57
  h.services << s
36
58
  end
37
59
  end
38
60
 
39
61
  puts "Creating Application entry for 'noah'"
40
- a = Application.create(:name => 'noah')
62
+ a = Noah::Application.create(:name => 'noah')
41
63
  if a.save
42
64
  puts "Creating Configuration entry for 'noah'"
43
- cr = Configuration.create(:name => 'redis', :format => 'string', :body => 'redis://127.0.0.1:6379/0', :application => a)
44
- ch = Configuration.create(:name => 'host', :format => 'string', :body => 'localhost', :application => a)
45
- cp = Configuration.create(:name => 'port', :format => 'string', :body => '9292', :application => a)
65
+ cr = Noah::Configuration.create(:name => 'redis', :format => 'string', :body => 'redis://127.0.0.1:6379/0', :application => a)
66
+ ch = Noah::Configuration.create(:name => 'host', :format => 'string', :body => 'localhost', :application => a)
67
+ cp = Noah::Configuration.create(:name => 'port', :format => 'string', :body => '9292', :application => a)
46
68
  %w[cr ch cp].each do |c|
47
69
  a.configurations << eval(c)
48
70
  end
@@ -50,10 +72,10 @@ task :sample, :redis_url do |t, args|
50
72
 
51
73
  puts "Creating sample entries - Host and Service"
52
74
  %w[host1.domain.com host2.domain.com host3.domain.com].each do |host|
53
- h = Host.create(:name => host, :status => "up")
75
+ h = Noah::Host.create(:name => host, :status => "up")
54
76
  if h.save
55
77
  %w[http https smtp mysql].each do |service|
56
- s = Service.create(:name => service, :status => "pending", :host => h)
78
+ s = Noah::Service.create(:name => service, :status => "pending", :host => h)
57
79
  h.services << s
58
80
  end
59
81
  end
@@ -74,32 +96,34 @@ EOY
74
96
  }
75
97
  EOJ
76
98
 
77
- a1 = Application.create(:name => 'myrailsapp1')
99
+ a1 = Noah::Application.create(:name => 'myrailsapp1')
78
100
  if a1.save
79
- c1 = Configuration.create(:name => 'database.yml', :format => 'yaml', :body => my_yaml, :application => a1)
101
+ c1 = Noah::Configuration.create(:name => 'database.yml', :format => 'yaml', :body => my_yaml, :application => a1)
80
102
  a1.configurations << c1
81
103
  end
82
104
 
83
- a2 = Application.create(:name => 'myrestapp1')
105
+ a2 = Noah::Application.create(:name => 'myrestapp1')
84
106
  if a2.save
85
- c2 = Configuration.create(:name => 'config.json', :format => 'json', :body => my_json, :application => a2)
107
+ c2 = Noah::Configuration.create(:name => 'config.json', :format => 'json', :body => my_json, :application => a2)
86
108
  a2.configurations << c2
87
109
  end
88
110
  puts "Sample data populated!"
89
111
  end
90
-
91
112
 
92
- RSpec::Core::RakeTask.new(:spec) do |spec|
93
- spec.pattern = FileList['spec/**/*_spec.rb']
113
+ begin
114
+ require 'yard'
115
+ require 'yard/sinatra'
116
+ desc "Generate documentation"
117
+ YARD::Rake::YardocTask.new do |t|
118
+ t.files = ['lib/**/*.rb'] # optional
119
+ t.options = ['--title', "Noah #{Noah::VERSION} Documentation"]
120
+ t.options += ['--plugin', "yard-sinatra"]
121
+ t.options += ['--protected', '--private'] # optional
122
+ end
123
+ rescue LoadError
124
+ "You need YARD installed to generate docs"
94
125
  end
95
126
 
96
- namespace :coverage do
97
- desc "Delete aggregate coverage data."
98
- task(:clean) { rm_f "coverage.data" }
99
- end
100
- desc "Run Rcov code coverage analysis"
101
- RSpec::Core::RakeTask.new(:coverage) do |t|
102
- t.rcov = true
103
- t.verbose = true
104
- t.rcov_opts = %q[--aggregate coverage.data --sort coverage --text-report --exclude "config,.bundle/*,gems/*,spec/*" -o doc/coverage -Ilib -i "noah.rb"]
127
+ task :start_demo do
128
+ puts "Soon, young padawan"
105
129
  end
data/TODO.md ADDED
@@ -0,0 +1,65 @@
1
+ # Brain dump of stuff
2
+ * Fix JRuby
3
+
4
+ This is an ongoing issue for me. I can't rely on stuff that's MRI only.
5
+
6
+ * Break agent into separate gem
7
+
8
+ Again so that the server gem can be as lean as possible
9
+
10
+ * Documentation
11
+
12
+ Need to finish documenting everything for YARD to pick up.
13
+
14
+ * Stabilize API
15
+
16
+ I'm seriously considering moving to a single endpoint with JSON request bodies. Still up in the air. I'm not totally sold on the current layout.
17
+
18
+ * Write my own CLI class
19
+
20
+ I think I might be bumping into some issues related to Vegas as well.
21
+
22
+ * Watchers
23
+
24
+ **IN PROGRESS**
25
+ Need to resolve some issues around pathing for Configuration and Service nodes however watch registration and listing are up.
26
+ Need to address alternative watcher URIs. Webhook is the only one supported right now.
27
+
28
+ * Ephemeral nodes
29
+
30
+ **IN PROGRESS**
31
+ implement lifetime support
32
+
33
+ * Examples
34
+
35
+ Pretty happy with the examples I have but I need more. Want some in other languages - Python, Java, whatever!
36
+
37
+ * Clean up deps
38
+
39
+ Feels like I'm relying too much on external libraries.
40
+
41
+ * Benchmark
42
+
43
+ Need some client libs for this
44
+
45
+ # Done TODOS
46
+ * Github pages
47
+ - see [[http://lusis.github.com/Noah/]]
48
+
49
+ * Examples
50
+ - Sort of done. Demo app is up! [[http://noah-demo.heroku.com/]]. Also see `examples` directory.
51
+
52
+ * Consider ditching Sinatra::Namespace
53
+ - Done. It was also causing issues with YARD generation. @rkh confirmed known issue via twitter.
54
+
55
+ * Namespace models
56
+ - Done.
57
+
58
+ * Watchers
59
+ - Done. GET PUT DELETE are all available at the '/w/' endpoint now!
60
+
61
+
62
+ ## Watcher specific stuff
63
+ * Implement AMQP
64
+ * Implement REST
65
+ * Implement JMX on JRuby
data/bin/noah CHANGED
@@ -1,8 +1,9 @@
1
1
  #!/usr/bin/env ruby
2
2
  $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
3
+ require 'rubygems'
3
4
  require 'noah'
4
5
  require 'vegas'
5
6
 
6
7
  Vegas::Runner.new(Noah::App, 'noah') do |runner, opts, app|
7
- opts.on("-r", "--redis URL", "redis url to connect to (default: redis://localhost:6379/0)") {|r| ENV["REDIS_URL"] = r }
8
+ opts.on("-r", "--redis URL", "redis url to connect to (default: redis://localhost:6379/0)") {|r| ENV["REDIS_URL"] = r; Noah::App.set :redis_url, r }
8
9
  end
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
3
+ HELP = <<-EOH
4
+ Unfortunately, the agent script has some difficult requirements right now.
5
+ Please see https://github.com/lusis/Noah/Watcher-Agent for details.
6
+ EOH
7
+ begin
8
+ require 'rubygems'
9
+ require 'logger'
10
+ require 'optparse'
11
+ require 'em-hiredis'
12
+ require 'eventmachine'
13
+ require 'em-http-request'
14
+ require 'noah'
15
+ require 'json'
16
+ rescue LoadError
17
+ puts HELP
18
+ exit
19
+ end
20
+
21
+ LOGGER = Logger.new(STDOUT)
22
+
23
+ class EventMachine::NoahAgent
24
+ include EM::Deferrable
25
+
26
+ @@watchers = Noah::Watcher.watch_list
27
+
28
+ def initialize
29
+ @logger = LOGGER
30
+ @logger.debug("Initializing with #{@@watchers.size} registered watches")
31
+ if EventMachine.reactor_running?
32
+ @worker = EM.spawn {|event, message, watch_list|
33
+ logger = LOGGER
34
+ logger.info("Worker initiated")
35
+ logger.debug("got event on http worker: #{event}")
36
+ matches = watch_list.find_all{|w| event =~ /^#{Base64.decode64(w)}/}
37
+ logger.debug("Found #{matches.size} matches for #{event}")
38
+ EM::Iterator.new(matches).each do |watch, iter|
39
+ p, ep = Base64.decode64(watch).split("|")
40
+ logger.info("Sending message to: #{ep} for pattern: #{p}")
41
+ http = EM::HttpRequest.new(ep, :connection_timeout => 2, :inactivity_timeout => 4).post :body => message
42
+ http.callback {
43
+ logger.info("Message posted to #{ep} successfully")
44
+ }
45
+ http.errback {
46
+ logger.error("Something went wrong")
47
+ }
48
+ iter.next
49
+ end
50
+ }
51
+ self.succeed("Succeed callback")
52
+ else
53
+ logger.fatal("Must be inside a reactor!")
54
+ end
55
+ end
56
+
57
+ def watchers
58
+ @@watchers.size
59
+ end
60
+
61
+ def reread_watchers
62
+ @logger.debug("Found new watches")
63
+ @logger.debug("Current watch count: #{@@watchers.size}")
64
+ @@watchers = Noah::Watcher.watch_list
65
+ @logger.debug("New watch count: #{@@watchers.size}")
66
+ #@logger.debug(@@watchers)
67
+ end
68
+
69
+ def broker(msg)
70
+ # This is just for testing for now
71
+ e,m = msg.split("|")
72
+ be = Base64.encode64(e).gsub("\n","")
73
+ @worker.notify e, m, @@watchers.clone
74
+ end
75
+ end
76
+
77
+ EventMachine.run do
78
+ EM.error_handler do |e|
79
+ Logger.new(STDOUT).warn(e)
80
+ end
81
+ logger = LOGGER
82
+ trap("INT") { logger.debug("Shutting down. Watches will not be fired");EM.stop }
83
+ noah = EventMachine::NoahAgent.new
84
+ noah.errback{|x| logger.error("Errback: #{x}")}
85
+ noah.callback{|y| logger.info("Callback: #{y}")}
86
+ # Passing messages...like a boss
87
+ master_channel = EventMachine::Channel.new
88
+
89
+ r = EventMachine::Hiredis::Client.connect
90
+ r.errback{|x| logger.error("Unable to connect to redis: #{x}")}
91
+ logger.debug("Starting up")
92
+ r.psubscribe("//noah/*")
93
+ r.on(:pmessage) do |pattern, event, message|
94
+ noah.reread_watchers if event =~ /^\/\/noah\/watcher\/.*/
95
+ master_channel.push "#{event}|#{message}"
96
+ logger.debug("Saw[#{event}]")
97
+ end
98
+
99
+ sub = master_channel.subscribe {|msg|
100
+ # We short circuit if we have no watchers
101
+ noah.broker(msg) unless noah.watchers == 0
102
+ }
103
+ end
data/config.ru CHANGED
@@ -1,3 +1,6 @@
1
- require File.join(File.dirname(__FILE__), 'lib','noah')
2
- ENV['REDIS_URL'] = "redis://localhost:6379/0"
3
- run Noah::App
1
+ require 'rubygems'
2
+ require File.join('.', 'lib','noah')
3
+ ## Uncomment the following to hardcode a redis url
4
+ ENV['REDIS_URL'] = "redis://localhost:6379/1"
5
+ noah = Noah::App.new
6
+ run noah
data/config/warble.rb ADDED
@@ -0,0 +1,18 @@
1
+ # Disable automatic framework detection by uncommenting/setting to false
2
+ # Warbler.framework_detection = false
3
+
4
+ # Warbler web application assembly configuration file
5
+ Warbler::Config.new do |config|
6
+ #config.features = %w(gemjar)
7
+ config.dirs = %w(config lib views)
8
+ config.includes = FileList["config.ru"]
9
+ config.excludes = FileList["noah.gemspec", "Gemfile", "Gemfile.lock"]
10
+ config.bundler = false
11
+ config.gems += ["json", "ohm", "ohm-contrib", "sinatra", "sinatra-namespace", "haml"]
12
+ config.gem_excludes = [/^(test|spec)\//]
13
+ config.public_html = FileList["views/**/*"]
14
+ config.webxml.booter = :rack
15
+ #config.webxml.rackup.path = 'WEB-INF/config.ru'
16
+ #config.webxml.rackup = %{require './lib/noah'; run Noah::App}
17
+ # config.webxml.rackup = require 'cgi' && CGI::escapeHTML(File.read("config.ru"))
18
+ end
@@ -0,0 +1,116 @@
1
+ # Examples
2
+ The following is a list of notes regarding the examples in this directory.
3
+
4
+ ## General Requirements
5
+
6
+ You'll need a few additional gems
7
+
8
+ * [em-hiredis](https://github.com/mloughran/em-hiredis)
9
+ You'll have to compile/install from source. Sorry. Should pull in the `hiredis` native ext.
10
+ * [em-http-request](https://github.com/igrigorik/em-http-request)
11
+ Available via rubygems
12
+ * [em-websocket](https://github.com/igrigorik/em-websocket)
13
+ Available via rubygems
14
+
15
+ ## custom-watcher.rb
16
+ This is an idea I'm tossing around for allowing easy custom watchers to be written.
17
+ Essentially the idea is that you tap into the Redis subscription with a defined pattern and a destination.
18
+
19
+ ### Example
20
+
21
+ require './watcher-idea.rb'
22
+
23
+ Noah::Watcher.watch do
24
+ pattern "//noah/configuration/*"
25
+ destination Proc.new {|x| something_with(x)}
26
+ run!
27
+ end
28
+
29
+ ## logger.rb
30
+ An example using logger as a watcher. Pretty straighforward.
31
+
32
+
33
+ ## httpclient.rb/httpclient-server.rb
34
+ This is an example of how the Webhook system would work
35
+
36
+ ### Running
37
+
38
+ To get the maximum effect, start with a clean Redis database
39
+
40
+ * Start the webhook reciever
41
+
42
+ noah/examples$ ruby httpclient-server.rb
43
+ == Sinatra/1.1.2 has taken the stage on 4567 for development with backup from Thin
44
+ >> Thin web server (v1.2.7 codename No Hup)
45
+ >> Maximum connections set to 1024
46
+ >> Listening on 0.0.0.0:4567, CTRL+C to stop
47
+
48
+ * Start the webhook publisher
49
+
50
+ noah/examples$ ruby httpclient.rb
51
+
52
+ * Run the rake sample script
53
+
54
+ In the publisher window, you should see some messages like so:
55
+
56
+ Got message for noah.Host[localhost].create
57
+ Got message for noah.Host[localhost].save
58
+ Got message for noah.Host[localhost].save
59
+ Got message for noah.Host[localhost].update
60
+
61
+ In the server window, you should see the following:
62
+
63
+ "{\"id\":\"1\",\"name\":\"localhost\",\"status\":\"up\",\"created_at\":\"2011-02-15 05:19:05 UTC\",\"updated_at\":\"2011-02-15 05:19:05 UTC\",\"services\":[]}"
64
+ 127.0.0.1 - - [15/Feb/2011 00:19:05] "POST /webhook HTTP/1.1" 200 135 0.0024
65
+ "{\"id\":\"1\",\"name\":\"localhost\",\"status\":\"up\",\"created_at\":\"2011-02-15 05:19:05 UTC\",\"updated_at\":\"2011-02-15 05:19:05 UTC\",\"services\":[]}"
66
+ 127.0.0.1 - - [15/Feb/2011 00:19:05] "POST /webhook HTTP/1.1" 200 135 0.0004
67
+ "{\"id\":\"1\",\"name\":\"localhost\",\"status\":\"up\",\"created_at\":\"2011-02-15 05:19:05 UTC\",\"updated_at\":\"2011-02-15 05:19:05 UTC\",\"services\":[]}"
68
+
69
+
70
+ ## websocket.rb
71
+ This is an example of using Websockets, EventMachine and Redis PubSub to provide a "status" console of sorts.
72
+
73
+ ### Running
74
+
75
+ To get the maximum effect, start with a clean Redis database.
76
+
77
+ * Start the server:
78
+
79
+ ~/development/noah/examples$ ./websocket.rb
80
+ >> Thin web server (v1.2.7 codename No Hup)
81
+ >> Maximum connections set to 1024
82
+ >> Listening on 0.0.0.0:3000, CTRL+C to stop
83
+
84
+ You should be able to load up the "normal" Noah sample page on [http://localhost:3000].
85
+
86
+ * Load the "websocket" file
87
+
88
+ In another browser window, open the `websocket.html` file.
89
+
90
+ * Send a message
91
+
92
+ From another terminal window send the following:
93
+
94
+ curl -X PUT -d '{"name":"testhost2","status":"down"}' http://localhost:3000/h/testhost2
95
+
96
+ You should see the message come across in the browser window like so:
97
+
98
+ connected...
99
+
100
+ 2 connected and waiting....
101
+
102
+ (noah.Host[testhost2].create) {"id":"1","name":"testhost2","status":"down","created_at":"2011-02-14 20:58:04 UTC","updated_at":"2011-02-14 20:58:04 UTC","services":[]}
103
+
104
+ (noah.Host[testhost2].save) {"id":"1","name":"testhost2","status":"down","created_at":"2011-02-14 20:58:04 UTC","updated_at":"2011-02-14 20:58:04 UTC","services":[]}
105
+
106
+ (noah.Host[testhost2].save) {"id":"1","name":"testhost2","status":"down","created_at":"2011-02-14 20:58:04 UTC","updated_at":"2011-02-14 20:58:04 UTC","services":[]}
107
+
108
+ (noah.Host[testhost2].update) {"id":"1","name":"testhost2","status":"down","created_at":"2011-02-14 20:58:04 UTC","updated_at":"2011-02-14 20:58:04 UTC","services":[]}
109
+
110
+ You can see the Watcher pattern in the parenthesis and then the JSON message body.
111
+
112
+ For fun, refresh the page to clear it and then run the sample data population rake task.
113
+
114
+ ### Known issues
115
+ When I started working on the Watcher stuff, I realized that I'm sending A LOT of extranous messages. These are mostly the result of the way I'm creating new objects with Ohm (i.e. via `.create`).
116
+ I'll be cleaning that up and trying to get down to a single message per operation.