noah 0.0.5 → 0.0.9

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 (90) hide show
  1. data/.gemtest +0 -0
  2. data/.gitignore +9 -0
  3. data/LICENSE +201 -0
  4. data/README.md +68 -212
  5. data/Rakefile +70 -41
  6. data/TODO.md +59 -0
  7. data/bin/noah +2 -1
  8. data/bin/noah-watcher.rb +93 -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 +32 -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 +5 -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/ephemeral_routes.rb +19 -0
  33. data/lib/noah/helpers.rb +12 -14
  34. data/lib/noah/host_routes.rb +69 -0
  35. data/lib/noah/models.rb +86 -5
  36. data/lib/noah/models/applications.rb +41 -0
  37. data/lib/noah/models/configurations.rb +49 -0
  38. data/lib/noah/models/ephemerals.rb +33 -0
  39. data/lib/noah/models/hosts.rb +56 -0
  40. data/lib/noah/models/services.rb +54 -0
  41. data/lib/noah/models/watchers.rb +54 -0
  42. data/lib/noah/passthrough.rb +11 -0
  43. data/lib/noah/service_routes.rb +71 -0
  44. data/lib/noah/validations.rb +1 -0
  45. data/lib/noah/validations/watcher_validations.rb +48 -0
  46. data/lib/noah/version.rb +1 -1
  47. data/lib/noah/watcher.rb +75 -0
  48. data/lib/noah/watcher_routes.rb +12 -0
  49. data/lib/vendor/em-hiredis/Gemfile +4 -0
  50. data/lib/vendor/em-hiredis/README.md +61 -0
  51. data/lib/vendor/em-hiredis/Rakefile +2 -0
  52. data/lib/vendor/em-hiredis/em-hiredis-0.0.1.gem +0 -0
  53. data/lib/vendor/em-hiredis/em-hiredis.gemspec +23 -0
  54. data/lib/vendor/em-hiredis/lib/em-hiredis.rb +22 -0
  55. data/lib/vendor/em-hiredis/lib/em-hiredis/client.rb +131 -0
  56. data/lib/vendor/em-hiredis/lib/em-hiredis/connection.rb +61 -0
  57. data/lib/vendor/em-hiredis/lib/em-hiredis/event_emitter.rb +29 -0
  58. data/lib/vendor/em-hiredis/lib/em-hiredis/version.rb +5 -0
  59. data/noah.gemspec +21 -17
  60. data/spec/application_spec.rb +30 -30
  61. data/spec/configuration_spec.rb +81 -14
  62. data/spec/ephemeral_spec.rb +52 -0
  63. data/spec/host_spec.rb +21 -21
  64. data/spec/noahapp_application_spec.rb +6 -6
  65. data/spec/noahapp_configuration_spec.rb +3 -3
  66. data/spec/noahapp_host_spec.rb +2 -2
  67. data/spec/noahapp_service_spec.rb +9 -9
  68. data/spec/noahapp_watcher_spec.rb +34 -0
  69. data/spec/service_spec.rb +27 -27
  70. data/spec/spec_helper.rb +13 -22
  71. data/spec/support/db/.keep +0 -0
  72. data/spec/support/test-redis.conf +8 -0
  73. data/spec/watcher_spec.rb +62 -0
  74. data/views/index.haml +21 -15
  75. metadata +124 -148
  76. data/Gemfile.lock +0 -85
  77. data/doc/coverage/index.html +0 -138
  78. data/doc/coverage/jquery-1.3.2.min.js +0 -19
  79. data/doc/coverage/jquery.tablesorter.min.js +0 -15
  80. data/doc/coverage/lib-helpers_rb.html +0 -393
  81. data/doc/coverage/lib-models_rb.html +0 -1449
  82. data/doc/coverage/noah_rb.html +0 -2019
  83. data/doc/coverage/print.css +0 -12
  84. data/doc/coverage/rcov.js +0 -42
  85. data/doc/coverage/screen.css +0 -270
  86. data/lib/noah/applications.rb +0 -46
  87. data/lib/noah/configurations.rb +0 -49
  88. data/lib/noah/hosts.rb +0 -54
  89. data/lib/noah/services.rb +0 -57
  90. data/lib/noah/watchers.rb +0 -18
data/Rakefile CHANGED
@@ -1,48 +1,74 @@
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 => [:start, :spec, :stop]
17
+
18
+ desc "Run tests and manage server start/stop"
19
+ task :run => [:start, :spec, :stop]
20
+
21
+ desc "Start the Redis server"
22
+ task :start do
23
+ puts "Starting redis-server"
24
+ system "redis-server #{REDIS_CNF}"
25
+ end
26
+
27
+ desc "Stop the Redis server"
28
+ task :stop do
29
+ puts "Killing redis"
30
+ system "killall -TERM redis-server"
31
+ end
32
+
33
+ namespace :coverage do
34
+ desc "Delete aggregate coverage data."
35
+ task(:clean) { rm_f "coverage.data" }
36
+ end
37
+ desc "Run Rcov code coverage analysis"
38
+ RSpec::Core::RakeTask.new(:coverage) do |t|
39
+ t.rcov = true
40
+ t.verbose = true
41
+ t.rcov_opts = %q[--aggregate coverage.data --sort coverage --text-report --exclude "config,.bundle/*,gems/*,spec/*" -o doc/coverage -Ilib -i "noah.rb"]
42
+ end
15
43
 
16
44
  desc "Populate database with sample dataset"
17
45
  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')
46
+ require 'noah'
25
47
 
26
-
27
48
  Ohm::connect(:url => args.redis_url)
28
49
  Ohm::redis.flushdb
50
+ puts "Creating watchers..."
51
+ Noah::Watcher.create :endpoint => "http://localhost:3000/webhook", :pattern => "//noah/application"
52
+ Noah::Watcher.create :endpoint => "http://localhost:3001/webhook", :pattern => "//noah/configuration"
53
+ Noah::Watcher.create :endpoint => "http://localhost:3002/webhook", :pattern => "//noah/host"
54
+ Noah::Watcher.create :endpoint => "http://localhost:3003/webhook", :pattern => "//noah/service"
29
55
  puts "Creating Host entry for 'localhost'"
30
- h = Host.create(:name => 'localhost', :status => "up")
56
+ h = Noah::Host.create(:name => 'localhost', :status => "up")
31
57
  if h.save
32
58
  %w[redis noah].each do |service|
33
59
  puts "Create Service entry for #{service}"
34
- s = Service.create(:name => service, :status => "up", :host => h)
60
+ s = Noah::Service.create(:name => service, :status => "up", :host => h)
35
61
  h.services << s
36
62
  end
37
63
  end
38
64
 
39
65
  puts "Creating Application entry for 'noah'"
40
- a = Application.create(:name => 'noah')
66
+ a = Noah::Application.create(:name => 'noah')
41
67
  if a.save
42
68
  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)
69
+ cr = Noah::Configuration.create(:name => 'redis', :format => 'string', :body => 'redis://127.0.0.1:6379/0', :application => a)
70
+ ch = Noah::Configuration.create(:name => 'host', :format => 'string', :body => 'localhost', :application => a)
71
+ cp = Noah::Configuration.create(:name => 'port', :format => 'string', :body => '9292', :application => a)
46
72
  %w[cr ch cp].each do |c|
47
73
  a.configurations << eval(c)
48
74
  end
@@ -50,10 +76,10 @@ task :sample, :redis_url do |t, args|
50
76
 
51
77
  puts "Creating sample entries - Host and Service"
52
78
  %w[host1.domain.com host2.domain.com host3.domain.com].each do |host|
53
- h = Host.create(:name => host, :status => "up")
79
+ h = Noah::Host.create(:name => host, :status => "up")
54
80
  if h.save
55
81
  %w[http https smtp mysql].each do |service|
56
- s = Service.create(:name => service, :status => "pending", :host => h)
82
+ s = Noah::Service.create(:name => service, :status => "pending", :host => h)
57
83
  h.services << s
58
84
  end
59
85
  end
@@ -74,32 +100,35 @@ EOY
74
100
  }
75
101
  EOJ
76
102
 
77
- a1 = Application.create(:name => 'myrailsapp1')
103
+ a1 = Noah::Application.create(:name => 'myrailsapp1')
78
104
  if a1.save
79
- c1 = Configuration.create(:name => 'database.yml', :format => 'yaml', :body => my_yaml, :application => a1)
105
+ c1 = Noah::Configuration.create(:name => 'database.yml', :format => 'yaml', :body => my_yaml, :application => a1)
80
106
  a1.configurations << c1
81
107
  end
82
108
 
83
- a2 = Application.create(:name => 'myrestapp1')
109
+ a2 = Noah::Application.create(:name => 'myrestapp1')
84
110
  if a2.save
85
- c2 = Configuration.create(:name => 'config.json', :format => 'json', :body => my_json, :application => a2)
111
+ c2 = Noah::Configuration.create(:name => 'config.json', :format => 'json', :body => my_json, :application => a2)
86
112
  a2.configurations << c2
87
113
  end
88
114
  puts "Sample data populated!"
89
115
  end
90
-
91
116
 
92
- RSpec::Core::RakeTask.new(:spec) do |spec|
93
- spec.pattern = FileList['spec/**/*_spec.rb']
117
+ begin
118
+ require 'yard'
119
+ require 'yard/sinatra'
120
+ desc "Generate documentation"
121
+ YARD::Rake::YardocTask.new do |t|
122
+ t.files = ['lib/**/*.rb'] # optional
123
+ t.options = ['--title', "Noah #{Noah::VERSION} Documentation"]
124
+ t.options += ['--plugin', "yard-sinatra"]
125
+ t.options += ['--protected', '--private'] # optional
126
+ end
127
+ rescue LoadError
128
+ "You need YARD installed to generate docs"
94
129
  end
95
130
 
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"]
131
+ desc "Demo environment"
132
+ task :start_demo do
133
+ puts "Soon, young padawan"
105
134
  end
data/TODO.md ADDED
@@ -0,0 +1,59 @@
1
+ # Brain dump of stuff
2
+ * Documentation
3
+
4
+ Need to finish documenting everything for YARD to pick up.
5
+
6
+ * Stabilize API
7
+
8
+ 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.
9
+
10
+ * Write my own CLI class
11
+
12
+ I think I might be bumping into some issues related to Vegas as well.
13
+
14
+ * Watchers
15
+
16
+ This is going to be a fun task. I'm serious.
17
+
18
+ * Ephemeral nodes
19
+
20
+ Not sure how I want to implement that. Not too keen on storing them as in-memory hashes. Maybe a LRU in Redis?
21
+
22
+ * Examples
23
+
24
+ I need to make some example apps to really demonstrate what I'm trying to accomplish.
25
+
26
+ * Clean up deps
27
+
28
+ Feels like I'm relying too much on external libraries.
29
+
30
+ * Benchmark
31
+
32
+ Need some client libs for this
33
+
34
+ # Done TODOS
35
+ * Github pages
36
+ - see [[http://lusis.github.com/Noah/]]
37
+
38
+ * Bundle a war
39
+ - see [[https://github.com/downloads/lusis/Noah/noah.war]]
40
+
41
+ * Examples
42
+ - Sort of done. Demo app is up! [[http://noah-demo.heroku.com/]]. Also see `examples` directory.
43
+
44
+ * Consider ditching Sinatra::Namespace
45
+ - Done. It was also causing issues with YARD generation. @rkh confirmed known issue via twitter.
46
+
47
+ * Namespace models
48
+ - Done.
49
+
50
+ * Watchers
51
+ - Partially done. Framework is in place to create a custom Watcher by hooking directly into Redis. Need to expand that to "official" watchers
52
+
53
+
54
+ ## Watcher specific stuff
55
+ * Implement a watcher endpoint
56
+ * Implement webhooks
57
+ * Implement AMQP
58
+ * Implement REST
59
+ * 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,93 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
3
+ require 'rubygems'
4
+ require 'rbtrace'
5
+ require 'logger'
6
+ require 'optparse'
7
+ require 'em-hiredis'
8
+ require 'eventmachine'
9
+ require 'em-http-request'
10
+ require 'thin'
11
+ require 'noah'
12
+ require 'json'
13
+
14
+ LOGGER = Logger.new(STDOUT)
15
+
16
+ class EventMachine::NoahAgent
17
+ include EM::Deferrable
18
+
19
+ @@watchers = Noah::Watcher.watch_list
20
+
21
+ def initialize
22
+ @logger = LOGGER
23
+ @logger.debug("Initializing with #{@@watchers.size} registered watches")
24
+ if EventMachine.reactor_running?
25
+ @worker = EM.spawn {|event, message, watch_list|
26
+ logger = LOGGER
27
+ logger.debug("Worker initiated")
28
+ logger.info("got event on http worker: #{event}")
29
+ logger.info("got message on http worker: #{message}")
30
+ matches = watch_list.find_all{|w| event =~ /^#{Base64.decode64(w)}/}
31
+ logger.debug("Found #{matches.size} matches for #{event}")
32
+ EM::Iterator.new(matches).each do |watch, iter|
33
+ p, ep = Base64.decode64(watch).split("|")
34
+ logger.info("Sending message to: #{ep} for pattern: #{p}")
35
+ http = EM::HttpRequest.new(ep, :connection_timeout => 2, :inactivity_timeout => 4).post :body => message
36
+ http.callback {
37
+ LOGGER.debug("Message posted to #{ep} successfully")
38
+ #iter.next
39
+ }
40
+ http.errback {
41
+ LOGGER.debug("Something went wrong")
42
+ #iter.net
43
+ }
44
+ iter.next
45
+ end
46
+ }
47
+ else
48
+ logger.fatal("Must be inside a reactor!")
49
+ end
50
+ end
51
+
52
+ def watchers
53
+ @@watchers.size
54
+ end
55
+
56
+ def reread_watchers
57
+ @logger.debug("Found new watches")
58
+ @logger.debug("Current watch count: #{@@watchers.size}")
59
+ @@watchers = Noah::Watcher.watch_list
60
+ @logger.debug("New watch count: #{@@watchers.size}")
61
+ #@logger.debug(@@watchers)
62
+ end
63
+
64
+ def broker(msg)
65
+ # This is just for testing for now
66
+ @logger.warn(msg)
67
+ e,m = msg.split("|")
68
+ be = Base64.encode64(e).gsub("\n","")
69
+ @logger.info("Encoded event: #{be}")
70
+ @worker.notify e, m, @@watchers.clone
71
+ end
72
+ end
73
+
74
+ EventMachine.run do
75
+ logger = LOGGER
76
+ noah = EventMachine::NoahAgent.new
77
+ # Passing messages...like a boss
78
+ master_channel = EventMachine::Channel.new
79
+
80
+ r = EventMachine::Hiredis::Client.connect
81
+ logger.debug("Starting up")
82
+ r.psubscribe("//noah/*")
83
+ r.on(:pmessage) do |pattern, event, message|
84
+ noah.reread_watchers if event =~ /^\/\/noah\/watcher\/.*/
85
+ master_channel.push "#{event}|#{message}"
86
+ logger.debug("Saw[#{event}]")
87
+ end
88
+
89
+ sub = master_channel.subscribe {|msg|
90
+ # We short circuit if we have no watchers
91
+ noah.broker(msg) unless noah.watchers == 0
92
+ }
93
+ 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.