noah 0.1 → 0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/bin/noah-watcher.rb +8 -60
- data/config/warble.rb +1 -1
- data/examples/custom-watcher.rb +3 -3
- data/examples/httpclient.rb +4 -4
- data/examples/logger.rb +4 -4
- data/examples/reconfiguring-sinatra-watcher.rb +4 -4
- data/examples/reconfiguring-sinatra.rb +1 -0
- data/examples/websocket.html +1 -1
- data/examples/websocket.rb +1 -1
- data/lib/noah.rb +1 -0
- data/lib/noah/agent.rb +53 -0
- data/lib/noah/agents/dummy_agent.rb +29 -0
- data/lib/noah/agents/http_agent.rb +37 -0
- data/lib/noah/{watcher.rb → custom_watcher.rb} +6 -2
- data/lib/noah/ephemeral_routes.rb +2 -3
- data/lib/noah/models/ephemerals.rb +5 -3
- data/lib/noah/models/watchers.rb +9 -0
- data/lib/noah/version.rb +1 -1
- data/noah.gemspec +4 -1
- metadata +62 -46
- data/lib/vendor/em-hiredis/Gemfile +0 -4
- data/lib/vendor/em-hiredis/README.md +0 -61
- data/lib/vendor/em-hiredis/Rakefile +0 -2
- data/lib/vendor/em-hiredis/em-hiredis.gemspec +0 -23
- data/lib/vendor/em-hiredis/lib/em-hiredis.rb +0 -22
- data/lib/vendor/em-hiredis/lib/em-hiredis/client.rb +0 -131
- data/lib/vendor/em-hiredis/lib/em-hiredis/connection.rb +0 -61
- data/lib/vendor/em-hiredis/lib/em-hiredis/event_emitter.rb +0 -29
- data/lib/vendor/em-hiredis/lib/em-hiredis/version.rb +0 -5
data/.gitignore
CHANGED
data/bin/noah-watcher.rb
CHANGED
@@ -12,6 +12,7 @@ begin
|
|
12
12
|
require 'eventmachine'
|
13
13
|
require 'em-http-request'
|
14
14
|
require 'noah'
|
15
|
+
require 'noah/agent'
|
15
16
|
require 'json'
|
16
17
|
rescue LoadError
|
17
18
|
puts HELP
|
@@ -20,78 +21,25 @@ end
|
|
20
21
|
|
21
22
|
LOGGER = Logger.new(STDOUT)
|
22
23
|
|
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.debug("Worker initiated")
|
35
|
-
logger.info("got event on http worker: #{event}")
|
36
|
-
logger.info("got message on http worker: #{message}")
|
37
|
-
matches = watch_list.find_all{|w| event =~ /^#{Base64.decode64(w)}/}
|
38
|
-
logger.debug("Found #{matches.size} matches for #{event}")
|
39
|
-
EM::Iterator.new(matches).each do |watch, iter|
|
40
|
-
p, ep = Base64.decode64(watch).split("|")
|
41
|
-
logger.info("Sending message to: #{ep} for pattern: #{p}")
|
42
|
-
http = EM::HttpRequest.new(ep, :connection_timeout => 2, :inactivity_timeout => 4).post :body => message
|
43
|
-
http.callback {
|
44
|
-
LOGGER.debug("Message posted to #{ep} successfully")
|
45
|
-
#iter.next
|
46
|
-
}
|
47
|
-
http.errback {
|
48
|
-
LOGGER.debug("Something went wrong")
|
49
|
-
#iter.net
|
50
|
-
}
|
51
|
-
iter.next
|
52
|
-
end
|
53
|
-
}
|
54
|
-
else
|
55
|
-
logger.fatal("Must be inside a reactor!")
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def watchers
|
60
|
-
@@watchers.size
|
61
|
-
end
|
62
|
-
|
63
|
-
def reread_watchers
|
64
|
-
@logger.debug("Found new watches")
|
65
|
-
@logger.debug("Current watch count: #{@@watchers.size}")
|
66
|
-
@@watchers = Noah::Watcher.watch_list
|
67
|
-
@logger.debug("New watch count: #{@@watchers.size}")
|
68
|
-
#@logger.debug(@@watchers)
|
69
|
-
end
|
70
|
-
|
71
|
-
def broker(msg)
|
72
|
-
# This is just for testing for now
|
73
|
-
@logger.warn(msg)
|
74
|
-
e,m = msg.split("|")
|
75
|
-
be = Base64.encode64(e).gsub("\n","")
|
76
|
-
@logger.info("Encoded event: #{be}")
|
77
|
-
@worker.notify e, m, @@watchers.clone
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
24
|
EventMachine.run do
|
25
|
+
EM.error_handler do |e|
|
26
|
+
LOGGER.warn(e)
|
27
|
+
end
|
82
28
|
logger = LOGGER
|
83
29
|
trap("INT") { logger.debug("Shutting down. Watches will not be fired");EM.stop }
|
84
|
-
noah =
|
30
|
+
noah = Noah::Agent.new
|
31
|
+
noah.errback{|x| logger.error("Errback: #{x}")}
|
32
|
+
noah.callback{|y| logger.info("Callback: #{y}")}
|
85
33
|
# Passing messages...like a boss
|
86
34
|
master_channel = EventMachine::Channel.new
|
87
35
|
|
88
36
|
r = EventMachine::Hiredis::Client.connect
|
37
|
+
r.errback{|x| logger.error("Unable to connect to redis: #{x}")}
|
89
38
|
logger.debug("Starting up")
|
90
39
|
r.psubscribe("//noah/*")
|
91
40
|
r.on(:pmessage) do |pattern, event, message|
|
92
41
|
noah.reread_watchers if event =~ /^\/\/noah\/watcher\/.*/
|
93
42
|
master_channel.push "#{event}|#{message}"
|
94
|
-
logger.debug("Saw[#{event}]")
|
95
43
|
end
|
96
44
|
|
97
45
|
sub = master_channel.subscribe {|msg|
|
data/config/warble.rb
CHANGED
@@ -8,7 +8,7 @@ Warbler::Config.new do |config|
|
|
8
8
|
config.includes = FileList["config.ru"]
|
9
9
|
config.excludes = FileList["noah.gemspec", "Gemfile", "Gemfile.lock"]
|
10
10
|
config.bundler = false
|
11
|
-
config.gems += ["json", "ohm", "ohm-contrib", "sinatra", "
|
11
|
+
config.gems += ["json", "ohm", "ohm-contrib", "sinatra", "haml"]
|
12
12
|
config.gem_excludes = [/^(test|spec)\//]
|
13
13
|
config.public_html = FileList["views/**/*"]
|
14
14
|
config.webxml.booter = :rack
|
data/examples/custom-watcher.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require File.join(File.dirname(__FILE__), '..','lib','noah','
|
3
|
+
require File.join(File.dirname(__FILE__), '..','lib','noah','custom_watcher')
|
4
4
|
|
5
|
-
class StdoutWatcher < Noah::
|
5
|
+
class StdoutWatcher < Noah::CustomWatcher
|
6
6
|
redis_host "redis://127.0.0.1:6379/0"
|
7
|
-
pattern "//noah/configuration/
|
7
|
+
pattern "//noah/configuration/redis"
|
8
8
|
destination Proc.new {|x| puts x}
|
9
9
|
run!
|
10
10
|
end
|
data/examples/httpclient.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require File.join(File.dirname(__FILE__), '..','lib','noah','
|
4
|
-
require '
|
3
|
+
require File.join(File.dirname(__FILE__), '..','lib','noah','custom_watcher')
|
4
|
+
require 'em-http-request'
|
5
5
|
|
6
6
|
|
7
|
-
class HttpPostWatch < Noah::
|
7
|
+
class HttpPostWatch < Noah::CustomWatcher
|
8
8
|
redis_host "redis://127.0.0.1:6379/0"
|
9
9
|
pattern "//noah/configuration"
|
10
|
-
destination Proc.new {|x| ::
|
10
|
+
destination Proc.new {|x| ::EM::HttpRequest.new('http://localhost:4567/webhook', :connection_timeout => 2, :inactivity_timeout => 4).post :body => x}
|
11
11
|
run!
|
12
12
|
end
|
data/examples/logger.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require File.join(File.dirname(__FILE__), '..','lib','noah','
|
3
|
+
require File.join(File.dirname(__FILE__), '..','lib','noah','custom_watcher')
|
4
4
|
require 'logger'
|
5
5
|
|
6
|
-
class LoggingWatcher < Noah::
|
7
|
-
redis_host "redis://127.0.0.1:6379/
|
8
|
-
pattern "//noah
|
6
|
+
class LoggingWatcher < Noah::CustomWatcher
|
7
|
+
redis_host "redis://127.0.0.1:6379/0"
|
8
|
+
pattern "//noah"
|
9
9
|
destination Proc.new {|x| log = Logger.new(STDOUT); log.debug(x)}
|
10
10
|
run!
|
11
11
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require File.join(File.dirname(__FILE__), '..','lib','noah','
|
4
|
-
require '
|
3
|
+
require File.join(File.dirname(__FILE__), '..','lib','noah','custom_watcher')
|
4
|
+
require 'em-http-request'
|
5
5
|
|
6
|
-
class HttpPostWatch < Noah::
|
6
|
+
class HttpPostWatch < Noah::CustomWatcher
|
7
7
|
redis_host "redis://127.0.0.1:6379/0"
|
8
8
|
pattern "//noah/configuration/redis_server"
|
9
|
-
destination Proc.new {|x|
|
9
|
+
destination Proc.new {|x| ::EM::HttpRequest.new('http://localhost:4567/webhook', :connection_timeout => 2, :inactivity_timeout => 4).post :body => x}
|
10
10
|
run!
|
11
11
|
end
|
data/examples/websocket.html
CHANGED
@@ -8,7 +8,7 @@
|
|
8
8
|
$(document).ready(function(){
|
9
9
|
function debug(str){ $("#debug").append("<p>" + str); };
|
10
10
|
|
11
|
-
ws = new WebSocket("ws://localhost:
|
11
|
+
ws = new WebSocket("ws://localhost:3009/");
|
12
12
|
ws.onmessage = function(evt) { $("#msg").append("<p>"+evt.data+"</p>"); };
|
13
13
|
ws.onclose = function() { debug("socket closed"); };
|
14
14
|
ws.onopen = function() {
|
data/examples/websocket.rb
CHANGED
@@ -20,7 +20,7 @@ EventMachine.run do
|
|
20
20
|
@channel.push "(#{event}) #{message}"
|
21
21
|
end
|
22
22
|
|
23
|
-
EventMachine::WebSocket.start(:host => "0.0.0.0", :port =>
|
23
|
+
EventMachine::WebSocket.start(:host => "0.0.0.0", :port => 3009) do |ws|
|
24
24
|
ws.onopen {
|
25
25
|
sub = @channel.subscribe { |msg|
|
26
26
|
ws.send msg
|
data/lib/noah.rb
CHANGED
@@ -10,6 +10,7 @@ require 'haml'
|
|
10
10
|
require 'yaml'
|
11
11
|
require 'sinatra/base'
|
12
12
|
|
13
|
+
require File.join(File.dirname(__FILE__), 'noah', 'custom_watcher')
|
13
14
|
require File.join(File.dirname(__FILE__), 'noah','validations')
|
14
15
|
require File.join(File.dirname(__FILE__), 'noah','models')
|
15
16
|
require File.join(File.dirname(__FILE__), 'noah','app')
|
data/lib/noah/agent.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
$:.unshift(File.expand_path(File.join(File.dirname(__FILE__), "..", "lib")))
|
2
|
+
require 'rubygems'
|
3
|
+
require 'logger'
|
4
|
+
require 'noah'
|
5
|
+
require 'noah/agents/http_agent'
|
6
|
+
require 'noah/agents/dummy_agent'
|
7
|
+
|
8
|
+
module Noah
|
9
|
+
class Agent
|
10
|
+
include EM::Deferrable
|
11
|
+
|
12
|
+
Noah::Agents::HttpAgent.register
|
13
|
+
Noah::Agents::DummyAgent.register
|
14
|
+
|
15
|
+
@@watchers = Noah::Watcher.watch_list
|
16
|
+
@@agents = Noah::Watchers.agents
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@logger = LOGGER
|
20
|
+
@logger.debug("Initializing with #{@@watchers.size} registered watches")
|
21
|
+
@logger.debug("#{@@agents} agents registered")
|
22
|
+
if EventMachine.reactor_running?
|
23
|
+
self.succeed("Succeed callback")
|
24
|
+
else
|
25
|
+
logger.fatal("Must be inside a reactor!")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def watchers
|
30
|
+
@@watchers.size
|
31
|
+
end
|
32
|
+
|
33
|
+
def http_worker
|
34
|
+
@http_worker
|
35
|
+
end
|
36
|
+
|
37
|
+
def reread_watchers
|
38
|
+
@logger.debug("Found new watches")
|
39
|
+
@logger.debug("Current watch count: #{@@watchers.size}")
|
40
|
+
@@watchers = Noah::Watcher.watch_list
|
41
|
+
@logger.debug("New watch count: #{@@watchers.size}")
|
42
|
+
end
|
43
|
+
|
44
|
+
def broker(msg)
|
45
|
+
e,m = msg.split("|")
|
46
|
+
be = Base64.encode64(e).gsub("\n","")
|
47
|
+
EM::Iterator.new(@@agents).each do |agent, iter|
|
48
|
+
agent.send(:notify, e, m, @@watchers.clone)
|
49
|
+
iter.next
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Noah::Agents
|
2
|
+
class DummyAgent
|
3
|
+
include EM::Deferrable
|
4
|
+
|
5
|
+
PREFIX = "dummy"
|
6
|
+
NAME = "dummy"
|
7
|
+
|
8
|
+
def self.register
|
9
|
+
Noah::Watchers.register_agent(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.notify(event, message, watch_list)
|
13
|
+
logger = LOGGER
|
14
|
+
logger.info("#{NAME}: Worker initiated")
|
15
|
+
logger.debug("#{NAME}: got event - #{event}")
|
16
|
+
matches = watch_list.find_all{|w| event =~ /^#{Base64.decode64(w)}/}
|
17
|
+
logger.debug("#{NAME}: Found #{matches.size} possible matches for #{event}")
|
18
|
+
EM::Iterator.new(matches).each do |watch, iter|
|
19
|
+
p, ep = Base64.decode64(watch).split("|")
|
20
|
+
if ep =~ /^#{PREFIX}/
|
21
|
+
logger.info("#{NAME}: Sending message to: #{ep} for pattern: #{p}")
|
22
|
+
logger.debug("#{NAME}: message received: #{message}")
|
23
|
+
end
|
24
|
+
iter.next
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Noah::Agents
|
4
|
+
class HttpAgent
|
5
|
+
include EM::Deferrable
|
6
|
+
|
7
|
+
PREFIX = "http"
|
8
|
+
NAME = "http"
|
9
|
+
|
10
|
+
def self.register
|
11
|
+
Noah::Watchers.register_agent(self)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.notify(event, message, watch_list)
|
15
|
+
logger = LOGGER
|
16
|
+
logger.info("#{NAME}: Worker initiated")
|
17
|
+
logger.debug("#{NAME}: got event - #{event}")
|
18
|
+
matches = watch_list.find_all{|w| event =~ /^#{Base64.decode64(w)}/}
|
19
|
+
logger.debug("#{PREFIX}: Found #{matches.size} possible matches for #{event}")
|
20
|
+
EM::Iterator.new(matches).each do |watch, iter|
|
21
|
+
p, ep = Base64.decode64(watch).split("|")
|
22
|
+
if ep =~ /^#{PREFIX}/
|
23
|
+
logger.info("#{NAME}: Sending message to (#{ep}) for pattern (#{p})")
|
24
|
+
http = EM::HttpRequest.new(ep, :connection_timeout => 2, :inactivity_timeout => 4).post :body => message
|
25
|
+
http.callback {
|
26
|
+
logger.info("#{NAME}: Message posted to #{ep} successfully")
|
27
|
+
}
|
28
|
+
http.errback {
|
29
|
+
logger.error("#{NAME}: Something went wrong with #{ep}")
|
30
|
+
}
|
31
|
+
iter.next
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
@@ -1,16 +1,20 @@
|
|
1
1
|
require 'eventmachine'
|
2
2
|
require 'uri'
|
3
3
|
require 'logger'
|
4
|
+
begin
|
5
|
+
require 'em-hiredis'
|
6
|
+
rescue LoadError
|
7
|
+
puts "Please install: em-hiredis"
|
8
|
+
end
|
4
9
|
|
5
10
|
@log = Logger.new(STDOUT)
|
6
11
|
@log.level = Logger::DEBUG
|
7
12
|
|
8
13
|
require File.join(File.dirname(__FILE__), 'passthrough')
|
9
|
-
require File.join(File.dirname(__FILE__), '..','vendor','em-hiredis','lib','em-hiredis')
|
10
14
|
|
11
15
|
module Noah
|
12
16
|
|
13
|
-
class
|
17
|
+
class CustomWatcher
|
14
18
|
extend Passthrough
|
15
19
|
|
16
20
|
passthrough :redis_host, :pattern, :destination, :run!, :run_watcher
|
@@ -21,10 +21,9 @@ class Noah::App
|
|
21
21
|
put '/e/*' do
|
22
22
|
raise("Data too large") if request.body.size > 512
|
23
23
|
d = request.body.read || nil
|
24
|
-
|
25
|
-
e
|
24
|
+
opts = {:path => "/#{params[:splat][0]}", :data => d}
|
25
|
+
e = Noah::Ephemeral.find_or_create(opts)
|
26
26
|
if e.valid?
|
27
|
-
e.save
|
28
27
|
action = e.is_new? ? "create" : "update"
|
29
28
|
r = {"action" => action, "result" => "success", "id" => e.id, "path" => e.path, "data" => e.data}
|
30
29
|
r.to_json
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module Noah
|
2
|
-
class Ephemeral < Model
|
2
|
+
class Ephemeral < Model
|
3
3
|
|
4
4
|
attribute :path
|
5
5
|
attribute :data
|
@@ -18,14 +18,16 @@ module Noah
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def to_hash
|
21
|
-
h = {:path => path, :data => data, :created_at => created_at, :updated_at =>
|
21
|
+
h = {:path => path, :data => data, :created_at => created_at, :updated_at => updated_at}
|
22
22
|
super.merge(h)
|
23
23
|
end
|
24
24
|
|
25
25
|
class << self
|
26
26
|
def find_or_create(opts = {})
|
27
27
|
begin
|
28
|
-
|
28
|
+
path, data = opts[:path], opts[:data]
|
29
|
+
find(:path => path).first.nil? ? (eph = new(:path => path)) : (eph = find(:path => path).first)
|
30
|
+
eph.data = data
|
29
31
|
if eph.valid?
|
30
32
|
eph.save
|
31
33
|
end
|
data/lib/noah/models/watchers.rb
CHANGED
@@ -55,8 +55,17 @@ module Noah
|
|
55
55
|
end
|
56
56
|
|
57
57
|
class Watchers
|
58
|
+
@@agents = []
|
58
59
|
def self.all(options = {})
|
59
60
|
options.empty? ? Watcher.all.sort : Watcher.find(options).sort
|
60
61
|
end
|
62
|
+
|
63
|
+
def self.register_agent(agent_class)
|
64
|
+
@@agents << agent_class
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.agents
|
68
|
+
@@agents
|
69
|
+
end
|
61
70
|
end
|
62
71
|
end
|
data/lib/noah/version.rb
CHANGED
data/noah.gemspec
CHANGED
@@ -6,6 +6,7 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "noah"
|
7
7
|
s.version = Noah::VERSION
|
8
8
|
s.platform = Gem::Platform::RUBY
|
9
|
+
#s.platform = "jruby"
|
9
10
|
s.authors = ["John E. Vincent"]
|
10
11
|
s.email = ["lusis.org+rubygems.org@gmail.com"]
|
11
12
|
s.homepage = "https://github.com/lusis/noah"
|
@@ -19,7 +20,8 @@ Gem::Specification.new do |s|
|
|
19
20
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
21
|
s.require_paths = ["lib"]
|
21
22
|
|
22
|
-
s.add_dependency("
|
23
|
+
s.add_dependency("eventmachine", ["1.0.0.beta.3"])
|
24
|
+
s.add_dependency("em-http-request", ["1.0.0.beta.3"])
|
23
25
|
s.add_dependency("redis", ["= 2.1.1"])
|
24
26
|
s.add_dependency("nest", ["= 1.1.0"])
|
25
27
|
s.add_dependency("rack", ["= 1.2.1"])
|
@@ -36,6 +38,7 @@ Gem::Specification.new do |s|
|
|
36
38
|
s.add_dependency("json")
|
37
39
|
s.add_development_dependency("warbler", ["= 1.2.1"])
|
38
40
|
else
|
41
|
+
s.add_dependency("hiredis", ["= 0.3.1"])
|
39
42
|
s.add_dependency("yajl-ruby")
|
40
43
|
s.add_dependency("SystemTimer") if RUBY_VERSION =~ /1.8/
|
41
44
|
s.add_dependency("thin")
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: noah
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: "0.
|
5
|
+
version: "0.2"
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- John E. Vincent
|
@@ -10,196 +10,218 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-03-
|
13
|
+
date: 2011-03-15 00:00:00 -04:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
|
-
name:
|
17
|
+
name: eventmachine
|
18
18
|
prerelease: false
|
19
19
|
requirement: &id001 !ruby/object:Gem::Requirement
|
20
20
|
none: false
|
21
21
|
requirements:
|
22
22
|
- - "="
|
23
23
|
- !ruby/object:Gem::Version
|
24
|
-
version: 0.3
|
24
|
+
version: 1.0.0.beta.3
|
25
25
|
type: :runtime
|
26
26
|
version_requirements: *id001
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: em-http-request
|
29
29
|
prerelease: false
|
30
30
|
requirement: &id002 !ruby/object:Gem::Requirement
|
31
31
|
none: false
|
32
32
|
requirements:
|
33
33
|
- - "="
|
34
34
|
- !ruby/object:Gem::Version
|
35
|
-
version:
|
35
|
+
version: 1.0.0.beta.3
|
36
36
|
type: :runtime
|
37
37
|
version_requirements: *id002
|
38
38
|
- !ruby/object:Gem::Dependency
|
39
|
-
name:
|
39
|
+
name: redis
|
40
40
|
prerelease: false
|
41
41
|
requirement: &id003 !ruby/object:Gem::Requirement
|
42
42
|
none: false
|
43
43
|
requirements:
|
44
44
|
- - "="
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version: 1.1
|
46
|
+
version: 2.1.1
|
47
47
|
type: :runtime
|
48
48
|
version_requirements: *id003
|
49
49
|
- !ruby/object:Gem::Dependency
|
50
|
-
name:
|
50
|
+
name: nest
|
51
51
|
prerelease: false
|
52
52
|
requirement: &id004 !ruby/object:Gem::Requirement
|
53
53
|
none: false
|
54
54
|
requirements:
|
55
55
|
- - "="
|
56
56
|
- !ruby/object:Gem::Version
|
57
|
-
version: 1.
|
57
|
+
version: 1.1.0
|
58
58
|
type: :runtime
|
59
59
|
version_requirements: *id004
|
60
60
|
- !ruby/object:Gem::Dependency
|
61
|
-
name:
|
61
|
+
name: rack
|
62
62
|
prerelease: false
|
63
63
|
requirement: &id005 !ruby/object:Gem::Requirement
|
64
64
|
none: false
|
65
65
|
requirements:
|
66
66
|
- - "="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: 1.2.
|
68
|
+
version: 1.2.1
|
69
69
|
type: :runtime
|
70
70
|
version_requirements: *id005
|
71
71
|
- !ruby/object:Gem::Dependency
|
72
|
-
name:
|
72
|
+
name: tilt
|
73
73
|
prerelease: false
|
74
74
|
requirement: &id006 !ruby/object:Gem::Requirement
|
75
75
|
none: false
|
76
76
|
requirements:
|
77
77
|
- - "="
|
78
78
|
- !ruby/object:Gem::Version
|
79
|
-
version: 1.
|
79
|
+
version: 1.2.2
|
80
80
|
type: :runtime
|
81
81
|
version_requirements: *id006
|
82
82
|
- !ruby/object:Gem::Dependency
|
83
|
-
name:
|
83
|
+
name: sinatra
|
84
84
|
prerelease: false
|
85
85
|
requirement: &id007 !ruby/object:Gem::Requirement
|
86
86
|
none: false
|
87
87
|
requirements:
|
88
88
|
- - "="
|
89
89
|
- !ruby/object:Gem::Version
|
90
|
-
version:
|
90
|
+
version: 1.1.2
|
91
91
|
type: :runtime
|
92
92
|
version_requirements: *id007
|
93
93
|
- !ruby/object:Gem::Dependency
|
94
|
-
name: ohm
|
94
|
+
name: ohm
|
95
95
|
prerelease: false
|
96
96
|
requirement: &id008 !ruby/object:Gem::Requirement
|
97
97
|
none: false
|
98
98
|
requirements:
|
99
99
|
- - "="
|
100
100
|
- !ruby/object:Gem::Version
|
101
|
-
version: 0.1.
|
101
|
+
version: 0.1.3
|
102
102
|
type: :runtime
|
103
103
|
version_requirements: *id008
|
104
104
|
- !ruby/object:Gem::Dependency
|
105
|
-
name:
|
105
|
+
name: ohm-contrib
|
106
106
|
prerelease: false
|
107
107
|
requirement: &id009 !ruby/object:Gem::Requirement
|
108
108
|
none: false
|
109
109
|
requirements:
|
110
110
|
- - "="
|
111
111
|
- !ruby/object:Gem::Version
|
112
|
-
version:
|
112
|
+
version: 0.1.1
|
113
113
|
type: :runtime
|
114
114
|
version_requirements: *id009
|
115
115
|
- !ruby/object:Gem::Dependency
|
116
|
-
name:
|
116
|
+
name: haml
|
117
117
|
prerelease: false
|
118
118
|
requirement: &id010 !ruby/object:Gem::Requirement
|
119
119
|
none: false
|
120
120
|
requirements:
|
121
121
|
- - "="
|
122
122
|
- !ruby/object:Gem::Version
|
123
|
-
version: 0.
|
123
|
+
version: 3.0.25
|
124
124
|
type: :runtime
|
125
125
|
version_requirements: *id010
|
126
126
|
- !ruby/object:Gem::Dependency
|
127
|
-
name:
|
127
|
+
name: vegas
|
128
128
|
prerelease: false
|
129
129
|
requirement: &id011 !ruby/object:Gem::Requirement
|
130
|
+
none: false
|
131
|
+
requirements:
|
132
|
+
- - "="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: 0.1.8
|
135
|
+
type: :runtime
|
136
|
+
version_requirements: *id011
|
137
|
+
- !ruby/object:Gem::Dependency
|
138
|
+
name: hiredis
|
139
|
+
prerelease: false
|
140
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
141
|
+
none: false
|
142
|
+
requirements:
|
143
|
+
- - "="
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: 0.3.1
|
146
|
+
type: :runtime
|
147
|
+
version_requirements: *id012
|
148
|
+
- !ruby/object:Gem::Dependency
|
149
|
+
name: yajl-ruby
|
150
|
+
prerelease: false
|
151
|
+
requirement: &id013 !ruby/object:Gem::Requirement
|
130
152
|
none: false
|
131
153
|
requirements:
|
132
154
|
- - ">="
|
133
155
|
- !ruby/object:Gem::Version
|
134
156
|
version: "0"
|
135
157
|
type: :runtime
|
136
|
-
version_requirements: *
|
158
|
+
version_requirements: *id013
|
137
159
|
- !ruby/object:Gem::Dependency
|
138
160
|
name: thin
|
139
161
|
prerelease: false
|
140
|
-
requirement: &
|
162
|
+
requirement: &id014 !ruby/object:Gem::Requirement
|
141
163
|
none: false
|
142
164
|
requirements:
|
143
165
|
- - ">="
|
144
166
|
- !ruby/object:Gem::Version
|
145
167
|
version: "0"
|
146
168
|
type: :runtime
|
147
|
-
version_requirements: *
|
169
|
+
version_requirements: *id014
|
148
170
|
- !ruby/object:Gem::Dependency
|
149
171
|
name: diff-lcs
|
150
172
|
prerelease: false
|
151
|
-
requirement: &
|
173
|
+
requirement: &id015 !ruby/object:Gem::Requirement
|
152
174
|
none: false
|
153
175
|
requirements:
|
154
176
|
- - "="
|
155
177
|
- !ruby/object:Gem::Version
|
156
178
|
version: 1.1.2
|
157
179
|
type: :development
|
158
|
-
version_requirements: *
|
180
|
+
version_requirements: *id015
|
159
181
|
- !ruby/object:Gem::Dependency
|
160
182
|
name: sinatra-reloader
|
161
183
|
prerelease: false
|
162
|
-
requirement: &
|
184
|
+
requirement: &id016 !ruby/object:Gem::Requirement
|
163
185
|
none: false
|
164
186
|
requirements:
|
165
187
|
- - "="
|
166
188
|
- !ruby/object:Gem::Version
|
167
189
|
version: 0.5.0
|
168
190
|
type: :development
|
169
|
-
version_requirements: *
|
191
|
+
version_requirements: *id016
|
170
192
|
- !ruby/object:Gem::Dependency
|
171
193
|
name: rspec
|
172
194
|
prerelease: false
|
173
|
-
requirement: &
|
195
|
+
requirement: &id017 !ruby/object:Gem::Requirement
|
174
196
|
none: false
|
175
197
|
requirements:
|
176
198
|
- - ~>
|
177
199
|
- !ruby/object:Gem::Version
|
178
200
|
version: "2.5"
|
179
201
|
type: :development
|
180
|
-
version_requirements: *
|
202
|
+
version_requirements: *id017
|
181
203
|
- !ruby/object:Gem::Dependency
|
182
204
|
name: rcov
|
183
205
|
prerelease: false
|
184
|
-
requirement: &
|
206
|
+
requirement: &id018 !ruby/object:Gem::Requirement
|
185
207
|
none: false
|
186
208
|
requirements:
|
187
209
|
- - "="
|
188
210
|
- !ruby/object:Gem::Version
|
189
211
|
version: 0.9.9
|
190
212
|
type: :development
|
191
|
-
version_requirements: *
|
213
|
+
version_requirements: *id018
|
192
214
|
- !ruby/object:Gem::Dependency
|
193
215
|
name: rack-test
|
194
216
|
prerelease: false
|
195
|
-
requirement: &
|
217
|
+
requirement: &id019 !ruby/object:Gem::Requirement
|
196
218
|
none: false
|
197
219
|
requirements:
|
198
220
|
- - "="
|
199
221
|
- !ruby/object:Gem::Version
|
200
222
|
version: 0.5.7
|
201
223
|
type: :development
|
202
|
-
version_requirements: *
|
224
|
+
version_requirements: *id019
|
203
225
|
description: Application registry based on Apache Zookeeper
|
204
226
|
email:
|
205
227
|
- lusis.org+rubygems.org@gmail.com
|
@@ -241,10 +263,14 @@ files:
|
|
241
263
|
- examples/websocket.html
|
242
264
|
- examples/websocket.rb
|
243
265
|
- lib/noah.rb
|
266
|
+
- lib/noah/agent.rb
|
267
|
+
- lib/noah/agents/dummy_agent.rb
|
268
|
+
- lib/noah/agents/http_agent.rb
|
244
269
|
- lib/noah/app.rb
|
245
270
|
- lib/noah/application_routes.rb
|
246
271
|
- lib/noah/ark.rb
|
247
272
|
- lib/noah/configuration_routes.rb
|
273
|
+
- lib/noah/custom_watcher.rb
|
248
274
|
- lib/noah/ephemeral_routes.rb
|
249
275
|
- lib/noah/helpers.rb
|
250
276
|
- lib/noah/host_routes.rb
|
@@ -260,17 +286,7 @@ files:
|
|
260
286
|
- lib/noah/validations.rb
|
261
287
|
- lib/noah/validations/watcher_validations.rb
|
262
288
|
- lib/noah/version.rb
|
263
|
-
- lib/noah/watcher.rb
|
264
289
|
- lib/noah/watcher_routes.rb
|
265
|
-
- lib/vendor/em-hiredis/Gemfile
|
266
|
-
- lib/vendor/em-hiredis/README.md
|
267
|
-
- lib/vendor/em-hiredis/Rakefile
|
268
|
-
- lib/vendor/em-hiredis/em-hiredis.gemspec
|
269
|
-
- lib/vendor/em-hiredis/lib/em-hiredis.rb
|
270
|
-
- lib/vendor/em-hiredis/lib/em-hiredis/client.rb
|
271
|
-
- lib/vendor/em-hiredis/lib/em-hiredis/connection.rb
|
272
|
-
- lib/vendor/em-hiredis/lib/em-hiredis/event_emitter.rb
|
273
|
-
- lib/vendor/em-hiredis/lib/em-hiredis/version.rb
|
274
290
|
- noah.gemspec
|
275
291
|
- spec/application_spec.rb
|
276
292
|
- spec/configuration_spec.rb
|
@@ -1,61 +0,0 @@
|
|
1
|
-
Getting started
|
2
|
-
===============
|
3
|
-
|
4
|
-
Connect to redis
|
5
|
-
|
6
|
-
redis_client = EM::Hiredis.connect
|
7
|
-
|
8
|
-
The client is a deferrable which succeeds when the underlying connection is established so you can bind to this. This isn't necessary however - any commands sent before the connection is established (or while reconnecting) will be sent to redis on connect.
|
9
|
-
|
10
|
-
redis_client.callback { puts "Redis now connected" }
|
11
|
-
|
12
|
-
All redis commands are available without any remapping of names
|
13
|
-
|
14
|
-
redis.set('foo', 'bar').callback {
|
15
|
-
redis.get('foo').callback { |value|
|
16
|
-
p [:returned, value]
|
17
|
-
}
|
18
|
-
}
|
19
|
-
|
20
|
-
As a shortcut, if you're only interested in binding to the success case you can simply provide a block to any command
|
21
|
-
|
22
|
-
redis.get('foo') { |value|
|
23
|
-
p [:returned, value]
|
24
|
-
}
|
25
|
-
|
26
|
-
Handling failure
|
27
|
-
----------------
|
28
|
-
|
29
|
-
All commands return a deferrable. In the case that redis replies with an error (for example you called a hash operation against a set), or in the case that the redis connection is broken before the command returns, the deferrable will fail. If you care about the failure case you should bind to the errback - for example:
|
30
|
-
|
31
|
-
redis.sadd('aset', 'member').callback {
|
32
|
-
response_deferrable = redis.hget('aset', 'member')
|
33
|
-
response_deferrable.errback { |e|
|
34
|
-
p e # => #<RuntimeError: ERR Operation against a key holding the wrong kind of value>
|
35
|
-
}
|
36
|
-
}
|
37
|
-
|
38
|
-
Pubsub
|
39
|
-
------
|
40
|
-
|
41
|
-
This example should explain things. Once a redis connection is in a pubsub state, you must make sure you only send pubsub commands.
|
42
|
-
|
43
|
-
redis = EM::Hiredis::Client.connect
|
44
|
-
subscriber = EM::Hiredis::Client.connect
|
45
|
-
|
46
|
-
subscriber.subscribe('bar.0')
|
47
|
-
subscriber.psubscribe('bar.*')
|
48
|
-
|
49
|
-
subscriber.on(:message) { |channel, message|
|
50
|
-
p [:message, channel, message]
|
51
|
-
}
|
52
|
-
|
53
|
-
subscriber.on(:pmessage) { |key, channel, message|
|
54
|
-
p [:pmessage, key, channel, message]
|
55
|
-
}
|
56
|
-
|
57
|
-
EM.add_periodic_timer(1) {
|
58
|
-
redis.publish("bar.#{rand(2)}", "hello").errback { |e|
|
59
|
-
p [:publisherror, e]
|
60
|
-
}
|
61
|
-
}
|
@@ -1,23 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
$:.push File.expand_path("../lib", __FILE__)
|
3
|
-
require "em-hiredis/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |s|
|
6
|
-
s.name = "em-hiredis"
|
7
|
-
s.version = EM::Hiredis::VERSION
|
8
|
-
s.platform = Gem::Platform::RUBY
|
9
|
-
s.authors = ["Martyn Loughran"]
|
10
|
-
s.email = ["me@mloughran.com"]
|
11
|
-
s.homepage = "http://github.com/mloughran/em-hiredis"
|
12
|
-
s.summary = %q{Eventmachine redis client}
|
13
|
-
s.description = %q{Eventmachine redis client using hiredis native parser}
|
14
|
-
|
15
|
-
s.add_dependency 'hiredis', '~> 0.2.0'
|
16
|
-
|
17
|
-
s.rubyforge_project = "em-hiredis"
|
18
|
-
|
19
|
-
s.files = `git ls-files`.split("\n")
|
20
|
-
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
-
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
-
s.require_paths = ["lib"]
|
23
|
-
end
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'eventmachine'
|
2
|
-
|
3
|
-
module EM
|
4
|
-
module Hiredis
|
5
|
-
class << self
|
6
|
-
attr_writer :logger
|
7
|
-
|
8
|
-
def logger
|
9
|
-
@logger ||= begin
|
10
|
-
require 'logger'
|
11
|
-
log = Logger.new(STDOUT)
|
12
|
-
log.level = Logger::WARN
|
13
|
-
log
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
require 'em-hiredis/event_emitter'
|
21
|
-
require 'em-hiredis/connection'
|
22
|
-
require 'em-hiredis/client'
|
@@ -1,131 +0,0 @@
|
|
1
|
-
module EM::Hiredis
|
2
|
-
class Client
|
3
|
-
PUBSUB_MESSAGES = %w{message pmessage}.freeze
|
4
|
-
|
5
|
-
include EM::Hiredis::EventEmitter
|
6
|
-
include EM::Deferrable
|
7
|
-
|
8
|
-
def self.connect(host = 'localhost', port = 6379)
|
9
|
-
new(host, port)
|
10
|
-
end
|
11
|
-
|
12
|
-
def initialize(host, port)
|
13
|
-
@host, @port = host, port
|
14
|
-
@subs, @psubs = [], []
|
15
|
-
@defs = []
|
16
|
-
@connection = EM.connect(host, port, Connection, host, port)
|
17
|
-
|
18
|
-
@connection.on(:closed) {
|
19
|
-
if @connected
|
20
|
-
@defs.each { |d| d.fail("Redis disconnected") }
|
21
|
-
@defs = []
|
22
|
-
@deferred_status = nil
|
23
|
-
@connected = false
|
24
|
-
@reconnecting = true
|
25
|
-
reconnect
|
26
|
-
else
|
27
|
-
EM.add_timer(1) { reconnect }
|
28
|
-
end
|
29
|
-
}
|
30
|
-
|
31
|
-
@connection.on(:connected) {
|
32
|
-
@connected = true
|
33
|
-
select(@db) if @db
|
34
|
-
@subs.each { |s| method_missing(:subscribe, s) }
|
35
|
-
@psubs.each { |s| method_missing(:psubscribe, s) }
|
36
|
-
succeed
|
37
|
-
|
38
|
-
if @reconnecting
|
39
|
-
@reconnecting = false
|
40
|
-
emit(:reconnected)
|
41
|
-
end
|
42
|
-
}
|
43
|
-
|
44
|
-
@connection.on(:message) { |reply|
|
45
|
-
if RuntimeError === reply
|
46
|
-
raise "Replies out of sync: #{reply.inspect}" if @defs.empty?
|
47
|
-
deferred = @defs.shift
|
48
|
-
deferred.fail(reply) if deferred
|
49
|
-
else
|
50
|
-
if reply && PUBSUB_MESSAGES.include?(reply[0]) # reply can be nil
|
51
|
-
kind, subscription, d1, d2 = *reply
|
52
|
-
|
53
|
-
case kind.to_sym
|
54
|
-
when :message
|
55
|
-
emit(:message, subscription, d1)
|
56
|
-
when :pmessage
|
57
|
-
emit(:pmessage, subscription, d1, d2)
|
58
|
-
end
|
59
|
-
else
|
60
|
-
raise "Replies out of sync: #{reply.inspect}" if @defs.empty?
|
61
|
-
deferred = @defs.shift
|
62
|
-
deferred.succeed(reply) if deferred
|
63
|
-
end
|
64
|
-
end
|
65
|
-
}
|
66
|
-
|
67
|
-
@connected = false
|
68
|
-
@reconnecting = false
|
69
|
-
end
|
70
|
-
|
71
|
-
# Indicates that commands have been sent to redis but a reply has not yet
|
72
|
-
# been received.
|
73
|
-
#
|
74
|
-
# This can be useful for example to avoid stopping the
|
75
|
-
# eventmachine reactor while there are outstanding commands
|
76
|
-
#
|
77
|
-
def pending_commands?
|
78
|
-
@connected && @defs.size > 0
|
79
|
-
end
|
80
|
-
|
81
|
-
def subscribe(channel)
|
82
|
-
@subs << channel
|
83
|
-
method_missing(:subscribe, channel)
|
84
|
-
end
|
85
|
-
|
86
|
-
def unsubscribe(channel)
|
87
|
-
@subs.delete(channel)
|
88
|
-
method_missing(:unsubscribe, channel)
|
89
|
-
end
|
90
|
-
|
91
|
-
def psubscribe(channel)
|
92
|
-
@psubs << channel
|
93
|
-
method_missing(:psubscribe, channel)
|
94
|
-
end
|
95
|
-
|
96
|
-
def punsubscribe(channel)
|
97
|
-
@psubs.delete(channel)
|
98
|
-
method_missing(:punsubscribe, channel)
|
99
|
-
end
|
100
|
-
|
101
|
-
def select(db)
|
102
|
-
@db = db
|
103
|
-
method_missing(:select, db)
|
104
|
-
end
|
105
|
-
|
106
|
-
def method_missing(sym, *args)
|
107
|
-
deferred = EM::DefaultDeferrable.new
|
108
|
-
# Shortcut for defining the callback case with just a block
|
109
|
-
deferred.callback { |result| yield(result) } if block_given?
|
110
|
-
|
111
|
-
if @connected
|
112
|
-
@connection.send_command(sym, *args)
|
113
|
-
@defs.push(deferred)
|
114
|
-
else
|
115
|
-
callback {
|
116
|
-
@connection.send_command(sym, *args)
|
117
|
-
@defs.push(deferred)
|
118
|
-
}
|
119
|
-
end
|
120
|
-
|
121
|
-
return deferred
|
122
|
-
end
|
123
|
-
|
124
|
-
private
|
125
|
-
|
126
|
-
def reconnect
|
127
|
-
EM::Hiredis.logger.debug("Trying to reconnect to Redis")
|
128
|
-
@connection.reconnect @host, @port
|
129
|
-
end
|
130
|
-
end
|
131
|
-
end
|
@@ -1,61 +0,0 @@
|
|
1
|
-
require 'hiredis/reader'
|
2
|
-
|
3
|
-
module EM::Hiredis
|
4
|
-
class Connection < EM::Connection
|
5
|
-
include EM::Hiredis::EventEmitter
|
6
|
-
|
7
|
-
def initialize(host, port)
|
8
|
-
super
|
9
|
-
@host, @port = host, port
|
10
|
-
end
|
11
|
-
|
12
|
-
def connection_completed
|
13
|
-
EM::Hiredis.logger.info("Connected to Redis")
|
14
|
-
@reader = ::Hiredis::Reader.new
|
15
|
-
emit(:connected)
|
16
|
-
end
|
17
|
-
|
18
|
-
def receive_data(data)
|
19
|
-
@reader.feed(data)
|
20
|
-
until (reply = @reader.gets) == false
|
21
|
-
emit(:message, reply)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def unbind
|
26
|
-
EM::Hiredis.logger.info("Disconnected from Redis")
|
27
|
-
emit(:closed)
|
28
|
-
end
|
29
|
-
|
30
|
-
def send_command(sym, *args)
|
31
|
-
send_data(command(sym, *args))
|
32
|
-
end
|
33
|
-
|
34
|
-
protected
|
35
|
-
|
36
|
-
COMMAND_DELIMITER = "\r\n"
|
37
|
-
|
38
|
-
def command(*args)
|
39
|
-
command = []
|
40
|
-
command << "*#{args.size}"
|
41
|
-
|
42
|
-
args.each do |arg|
|
43
|
-
arg = arg.to_s
|
44
|
-
command << "$#{string_size arg}"
|
45
|
-
command << arg
|
46
|
-
end
|
47
|
-
|
48
|
-
command.join(COMMAND_DELIMITER) + COMMAND_DELIMITER
|
49
|
-
end
|
50
|
-
|
51
|
-
if "".respond_to?(:bytesize)
|
52
|
-
def string_size(string)
|
53
|
-
string.to_s.bytesize
|
54
|
-
end
|
55
|
-
else
|
56
|
-
def string_size(string)
|
57
|
-
string.to_s.size
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|
61
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
module EM::Hiredis
|
2
|
-
module EventEmitter
|
3
|
-
def on(event, &listener)
|
4
|
-
_listeners[event] << listener
|
5
|
-
end
|
6
|
-
|
7
|
-
def emit(event, *args)
|
8
|
-
_listeners[event].each { |l| l.call(*args) }
|
9
|
-
end
|
10
|
-
|
11
|
-
def remove_listener(event, &listener)
|
12
|
-
_listeners[event].delete(listener)
|
13
|
-
end
|
14
|
-
|
15
|
-
def remove_all_listeners(event)
|
16
|
-
_listeners.delete(event)
|
17
|
-
end
|
18
|
-
|
19
|
-
def listeners(event)
|
20
|
-
_listeners[event]
|
21
|
-
end
|
22
|
-
|
23
|
-
private
|
24
|
-
|
25
|
-
def _listeners
|
26
|
-
@_listeners ||= Hash.new { |h,k| h[k] = [] }
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|