audible 0.2.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.document +5 -5
- data/.travis.yml +12 -12
- data/Gemfile +6 -6
- data/Gemfile.lock +64 -62
- data/LICENSE.txt +20 -20
- data/README.md +9 -9
- data/Rakefile +33 -41
- data/VERSION +1 -1
- data/audible.gemspec +69 -67
- data/lib/audible.rb +3 -3
- data/lib/audible/audible.rb +50 -42
- data/lib/audible/pidfile.rb +16 -16
- data/spec/spec_helper.rb +7 -7
- data/spec/system.tests/drb/bin/server +44 -44
- data/spec/system.tests/drb/drb_observers_spec.rb +65 -65
- data/spec/system.tests/drb/support/drb_server.rb +26 -26
- data/spec/{and_how_it_differs_from_observable_spec.rb → unit.tests/and_how_it_differs_from_observable_spec.rb} +101 -101
- data/spec/{examples → unit.tests/examples}/a_notifying_shell_spec.rb +28 -28
- data/spec/{examples → unit.tests/examples}/logging_as_notifications_spec.rb +51 -51
- data/spec/unit.tests/it_notifies_despite_errors_spec.rb +30 -0
- data/spec/{listening_spec.rb → unit.tests/listening_spec.rb} +53 -53
- data/spec/{relaying_notifications → unit.tests/relaying_notifications}/about_the_notifications_spec.rb +69 -69
- data/spec/{relaying_notifications → unit.tests/relaying_notifications}/can_relay_notifications_spec.rb +43 -43
- metadata +12 -22
data/lib/audible.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
dirname = File.expand_path File.dirname(__FILE__)
|
2
|
-
|
3
|
-
Dir.glob(File.join(dirname, "audible", "**", "*.rb")).each{|f| require f}
|
1
|
+
dirname = File.expand_path File.dirname(__FILE__)
|
2
|
+
|
3
|
+
Dir.glob(File.join(dirname, "audible", "**", "*.rb")).each{|f| require f}
|
data/lib/audible/audible.rb
CHANGED
@@ -1,42 +1,50 @@
|
|
1
|
-
module Audible
|
2
|
-
def on(*events, &block)
|
3
|
-
events.each{|e| attach(e, &block)}
|
4
|
-
end
|
5
|
-
|
6
|
-
def relay(source, event, opts ={})
|
7
|
-
name = opts[:as] || event
|
8
|
-
source.on(event){|e,args| notify name, args.first}
|
9
|
-
end
|
10
|
-
|
11
|
-
protected
|
12
|
-
|
13
|
-
def attach(event,&block)
|
14
|
-
fail no_way_to_notify unless block_given?
|
15
|
-
|
16
|
-
listeners_for(event) << block
|
17
|
-
end
|
18
|
-
|
19
|
-
def notify(event, *args)
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
def
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
40
|
-
|
41
|
-
def
|
42
|
-
|
1
|
+
module Audible
|
2
|
+
def on(*events, &block)
|
3
|
+
events.each{|e| attach(e, &block)}
|
4
|
+
end
|
5
|
+
|
6
|
+
def relay(source, event, opts ={})
|
7
|
+
name = opts[:as] || event
|
8
|
+
source.on(event){|e,args| notify name, args.first}
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def attach(event,&block)
|
14
|
+
fail no_way_to_notify unless block_given?
|
15
|
+
|
16
|
+
listeners_for(event) << block
|
17
|
+
end
|
18
|
+
|
19
|
+
def notify(event, *args)
|
20
|
+
the_first_error = nil
|
21
|
+
|
22
|
+
listeners_for(event).each do |listener|
|
23
|
+
begin
|
24
|
+
listener.call event, args
|
25
|
+
rescue Exception => e
|
26
|
+
the_first_error ||= e
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
raise the_first_error if the_first_error
|
31
|
+
end
|
32
|
+
|
33
|
+
def accepts?(e); accept_all_by_default; end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def no_way_to_notify
|
38
|
+
"No block supplied. How will I notify you?"
|
39
|
+
end
|
40
|
+
|
41
|
+
def accept_all_by_default; true; end
|
42
|
+
|
43
|
+
def listeners_for(event)
|
44
|
+
fail "Event <#{event}> not supported by #{self.class}." unless accepts?(event) === true
|
45
|
+
listeners[event] = [] unless listeners.has_key? event
|
46
|
+
listeners[event]
|
47
|
+
end
|
48
|
+
|
49
|
+
def listeners; @listeners ||= {}; end
|
50
|
+
end
|
data/lib/audible/pidfile.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
|
-
class Pidfile
|
2
|
-
require "fileutils"; extend FileUtils
|
3
|
-
|
4
|
-
class << self
|
5
|
-
def new(pid=$$)
|
6
|
-
File.open path, "w" do |f|
|
7
|
-
f.puts pid
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def missing?; false == exists? end
|
12
|
-
def exists?; File.exists? path; end
|
13
|
-
def delete; rm path; end
|
14
|
-
def path; File.join ".", ".pid"; end
|
15
|
-
end
|
16
|
-
end
|
1
|
+
class Pidfile
|
2
|
+
require "fileutils"; extend FileUtils
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def new(pid=$$)
|
6
|
+
File.open path, "w" do |f|
|
7
|
+
f.puts pid
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def missing?; false == exists? end
|
12
|
+
def exists?; File.exists? path; end
|
13
|
+
def delete; rm path; end
|
14
|
+
def path; File.join ".", ".pid"; end
|
15
|
+
end
|
16
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
require "rspec"
|
2
|
-
require File.join ".", "lib", "audible"
|
3
|
-
|
4
|
-
Object.class_eval do
|
5
|
-
alias :must :should
|
6
|
-
alias :must_not :should_not
|
7
|
-
end
|
1
|
+
require "rspec"
|
2
|
+
require File.join ".", "lib", "audible"
|
3
|
+
|
4
|
+
Object.class_eval do
|
5
|
+
alias :must :should
|
6
|
+
alias :must_not :should_not
|
7
|
+
end
|
@@ -1,45 +1,45 @@
|
|
1
|
-
require File.join ".", "lib", "audible"
|
2
|
-
|
3
|
-
class TimeServer
|
4
|
-
require 'drb/observer'
|
5
|
-
include DRb::DRbObservable
|
6
|
-
include Audible
|
7
|
-
|
8
|
-
def get_current_time
|
9
|
-
return Time.now
|
10
|
-
end
|
11
|
-
|
12
|
-
def progress
|
13
|
-
3.times do
|
14
|
-
changed
|
15
|
-
notify_observers Time.now.to_s
|
16
|
-
notify :progress, Time.now.to_s
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
FRONT_OBJECT=TimeServer.new
|
22
|
-
|
23
|
-
$SAFE = 0
|
24
|
-
|
25
|
-
require 'drb/drb'
|
26
|
-
|
27
|
-
port = 9999
|
28
|
-
|
29
|
-
uri="druby://127.0.0.1:#{port}"
|
30
|
-
|
31
|
-
DRb.start_service(uri, FRONT_OBJECT)
|
32
|
-
|
33
|
-
puts "Server has started on port <#{port}>, process <#{$$}>"
|
34
|
-
|
35
|
-
Signal.trap("INT") do
|
36
|
-
puts "Stopping..."
|
37
|
-
|
38
|
-
Pidfile.delete
|
39
|
-
|
40
|
-
exit
|
41
|
-
end
|
42
|
-
|
43
|
-
Pidfile.new $$
|
44
|
-
|
1
|
+
require File.join ".", "lib", "audible"
|
2
|
+
|
3
|
+
class TimeServer
|
4
|
+
require 'drb/observer'
|
5
|
+
include DRb::DRbObservable
|
6
|
+
include Audible
|
7
|
+
|
8
|
+
def get_current_time
|
9
|
+
return Time.now
|
10
|
+
end
|
11
|
+
|
12
|
+
def progress
|
13
|
+
3.times do
|
14
|
+
changed
|
15
|
+
notify_observers Time.now.to_s
|
16
|
+
notify :progress, Time.now.to_s
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
FRONT_OBJECT=TimeServer.new
|
22
|
+
|
23
|
+
$SAFE = 0
|
24
|
+
|
25
|
+
require 'drb/drb'
|
26
|
+
|
27
|
+
port = 9999
|
28
|
+
|
29
|
+
uri="druby://127.0.0.1:#{port}"
|
30
|
+
|
31
|
+
DRb.start_service(uri, FRONT_OBJECT)
|
32
|
+
|
33
|
+
puts "Server has started on port <#{port}>, process <#{$$}>"
|
34
|
+
|
35
|
+
Signal.trap("INT") do
|
36
|
+
puts "Stopping..."
|
37
|
+
|
38
|
+
Pidfile.delete
|
39
|
+
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
|
43
|
+
Pidfile.new $$
|
44
|
+
|
45
45
|
DRb.thread.join
|
@@ -1,65 +1,65 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
require_relative File.join 'support', 'drb_server'
|
3
|
-
|
4
|
-
describe "Basic drb connections" do
|
5
|
-
before :all do
|
6
|
-
@server = DrbServer.new("druby://127.0.0.1:9999").tap {|server| server.start}
|
7
|
-
@timeserver = DRbObject.new_with_uri @server.url
|
8
|
-
end
|
9
|
-
|
10
|
-
after :all do
|
11
|
-
@server.kill
|
12
|
-
end
|
13
|
-
|
14
|
-
before do
|
15
|
-
@an_observer = Class.new do
|
16
|
-
def update(args)
|
17
|
-
@notified = true
|
18
|
-
end
|
19
|
-
|
20
|
-
def notified?; @notified === true; end
|
21
|
-
|
22
|
-
def reset; @notified = false; end
|
23
|
-
end.new
|
24
|
-
end
|
25
|
-
|
26
|
-
it "tells me the time" do
|
27
|
-
expect(@timeserver.get_current_time.to_s).to match /^#{Time.now.year}/
|
28
|
-
end
|
29
|
-
|
30
|
-
it "can notify" do
|
31
|
-
@timeserver.add_observer @an_observer
|
32
|
-
|
33
|
-
@timeserver.progress
|
34
|
-
|
35
|
-
expect(@an_observer).to be_notified
|
36
|
-
end
|
37
|
-
|
38
|
-
it "can detach observer which stops notifications" do
|
39
|
-
@timeserver.add_observer @an_observer
|
40
|
-
|
41
|
-
@timeserver.progress
|
42
|
-
|
43
|
-
expect(@an_observer).to be_notified
|
44
|
-
|
45
|
-
@an_observer.reset
|
46
|
-
|
47
|
-
@timeserver.delete_observer @an_observer
|
48
|
-
|
49
|
-
@timeserver.progress
|
50
|
-
|
51
|
-
expect(@an_observer).to_not be_notified
|
52
|
-
end
|
53
|
-
|
54
|
-
it "can also get audible notifications" do
|
55
|
-
notified = false
|
56
|
-
|
57
|
-
@timeserver.on(:progress) do |e,args|
|
58
|
-
notified = true
|
59
|
-
end
|
60
|
-
|
61
|
-
@timeserver.progress
|
62
|
-
|
63
|
-
expect(notified).to be_true
|
64
|
-
end
|
65
|
-
end
|
1
|
+
require "spec_helper"
|
2
|
+
require_relative File.join 'support', 'drb_server'
|
3
|
+
|
4
|
+
describe "Basic drb connections" do
|
5
|
+
before :all do
|
6
|
+
@server = DrbServer.new("druby://127.0.0.1:9999").tap {|server| server.start}
|
7
|
+
@timeserver = DRbObject.new_with_uri @server.url
|
8
|
+
end
|
9
|
+
|
10
|
+
after :all do
|
11
|
+
@server.kill
|
12
|
+
end
|
13
|
+
|
14
|
+
before do
|
15
|
+
@an_observer = Class.new do
|
16
|
+
def update(args)
|
17
|
+
@notified = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def notified?; @notified === true; end
|
21
|
+
|
22
|
+
def reset; @notified = false; end
|
23
|
+
end.new
|
24
|
+
end
|
25
|
+
|
26
|
+
it "tells me the time" do
|
27
|
+
expect(@timeserver.get_current_time.to_s).to match /^#{Time.now.year}/
|
28
|
+
end
|
29
|
+
|
30
|
+
it "can notify" do
|
31
|
+
@timeserver.add_observer @an_observer
|
32
|
+
|
33
|
+
@timeserver.progress
|
34
|
+
|
35
|
+
expect(@an_observer).to be_notified
|
36
|
+
end
|
37
|
+
|
38
|
+
it "can detach observer which stops notifications" do
|
39
|
+
@timeserver.add_observer @an_observer
|
40
|
+
|
41
|
+
@timeserver.progress
|
42
|
+
|
43
|
+
expect(@an_observer).to be_notified
|
44
|
+
|
45
|
+
@an_observer.reset
|
46
|
+
|
47
|
+
@timeserver.delete_observer @an_observer
|
48
|
+
|
49
|
+
@timeserver.progress
|
50
|
+
|
51
|
+
expect(@an_observer).to_not be_notified
|
52
|
+
end
|
53
|
+
|
54
|
+
it "can also get audible notifications" do
|
55
|
+
notified = false
|
56
|
+
|
57
|
+
@timeserver.on(:progress) do |e,args|
|
58
|
+
notified = true
|
59
|
+
end
|
60
|
+
|
61
|
+
@timeserver.progress
|
62
|
+
|
63
|
+
expect(notified).to be_true
|
64
|
+
end
|
65
|
+
end
|
@@ -1,26 +1,26 @@
|
|
1
|
-
class DrbServer
|
2
|
-
attr_reader :url
|
3
|
-
|
4
|
-
def initialize(url)
|
5
|
-
@url = url
|
6
|
-
end
|
7
|
-
|
8
|
-
def start
|
9
|
-
exe = File.join(File.dirname(__FILE__), "..", "bin", "server")
|
10
|
-
|
11
|
-
@pid = Process.spawn "bundle exec ruby #{exe}", :err=>:out, :out => ".log"
|
12
|
-
|
13
|
-
while Pidfile.missing?
|
14
|
-
sleep 0.25
|
15
|
-
end
|
16
|
-
|
17
|
-
require 'drb/drb'
|
18
|
-
|
19
|
-
DRb.start_service
|
20
|
-
end
|
21
|
-
|
22
|
-
def kill
|
23
|
-
Process.kill("INT", @pid)
|
24
|
-
Process.wait
|
25
|
-
end
|
26
|
-
end
|
1
|
+
class DrbServer
|
2
|
+
attr_reader :url
|
3
|
+
|
4
|
+
def initialize(url)
|
5
|
+
@url = url
|
6
|
+
end
|
7
|
+
|
8
|
+
def start
|
9
|
+
exe = File.join(File.dirname(__FILE__), "..", "bin", "server")
|
10
|
+
|
11
|
+
@pid = Process.spawn "bundle exec ruby #{exe}", :err=>:out, :out => ".log"
|
12
|
+
|
13
|
+
while Pidfile.missing?
|
14
|
+
sleep 0.25
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'drb/drb'
|
18
|
+
|
19
|
+
DRb.start_service
|
20
|
+
end
|
21
|
+
|
22
|
+
def kill
|
23
|
+
Process.kill("INT", @pid)
|
24
|
+
Process.wait
|
25
|
+
end
|
26
|
+
end
|
@@ -1,101 +1,101 @@
|
|
1
|
-
require "spec_helper"
|
2
|
-
|
3
|
-
describe "Observable" do
|
4
|
-
let(:an_observable_object) do
|
5
|
-
Class.new do
|
6
|
-
require "observer"; include Observable
|
7
|
-
|
8
|
-
def poke
|
9
|
-
changed
|
10
|
-
notify_observers :poked
|
11
|
-
end
|
12
|
-
|
13
|
-
def tap
|
14
|
-
changed
|
15
|
-
notify_observers :tapped
|
16
|
-
end
|
17
|
-
end.new
|
18
|
-
end
|
19
|
-
|
20
|
-
it "you can register for notifications" do
|
21
|
-
an_observer = Class.new do
|
22
|
-
def update(args)
|
23
|
-
@notified = true
|
24
|
-
end
|
25
|
-
|
26
|
-
def notified?; @notified === true; end
|
27
|
-
end.new
|
28
|
-
|
29
|
-
an_observable_object.add_observer(an_observer)
|
30
|
-
|
31
|
-
an_observable_object.poke
|
32
|
-
|
33
|
-
an_observer.must be_notified
|
34
|
-
end
|
35
|
-
|
36
|
-
it "and you can send arguments with your notification" do
|
37
|
-
an_observer = Class.new do
|
38
|
-
def update(args)
|
39
|
-
@args = args
|
40
|
-
end
|
41
|
-
|
42
|
-
def args; @args; end
|
43
|
-
end.new
|
44
|
-
|
45
|
-
an_observable_object.add_observer(an_observer)
|
46
|
-
|
47
|
-
an_observable_object.poke
|
48
|
-
|
49
|
-
an_observer.args.must === :poked
|
50
|
-
end
|
51
|
-
|
52
|
-
it "for multiple events the observer must decide for themselves" do
|
53
|
-
an_observer = Class.new do
|
54
|
-
def initialize
|
55
|
-
@poked,@tapped = 0,0
|
56
|
-
end
|
57
|
-
|
58
|
-
def update(args)
|
59
|
-
@poked = true if args === :poked
|
60
|
-
@tapped = true if args === :tapped
|
61
|
-
end
|
62
|
-
|
63
|
-
def poked?; @poked === true; end
|
64
|
-
def tapped?; @tapped === true; end
|
65
|
-
end.new
|
66
|
-
|
67
|
-
an_observable_object.add_observer(an_observer)
|
68
|
-
|
69
|
-
an_observable_object.poke
|
70
|
-
an_observable_object.tap
|
71
|
-
|
72
|
-
an_observer.must be_tapped
|
73
|
-
an_observer.must be_poked
|
74
|
-
end
|
75
|
-
|
76
|
-
it "notifications are not sent if `changed` not called" do
|
77
|
-
a_bung_observable_object_that_does_not_call_changed = Class.new do
|
78
|
-
require "observer"; include Observable
|
79
|
-
|
80
|
-
def poke
|
81
|
-
notify_observers :poked
|
82
|
-
end
|
83
|
-
end.new
|
84
|
-
|
85
|
-
an_observer = Class.new do
|
86
|
-
def update(args)
|
87
|
-
@notified = true
|
88
|
-
end
|
89
|
-
|
90
|
-
def notified?; @notified === true; end
|
91
|
-
end.new
|
92
|
-
|
93
|
-
a_bung_observable_object_that_does_not_call_changed.add_observer an_observer
|
94
|
-
|
95
|
-
a_bung_observable_object_that_does_not_call_changed.poke
|
96
|
-
|
97
|
-
an_observer.must_not(be_notified,
|
98
|
-
"Even though `notify_observers` was called, no notification was sent because you have to call `changed` beforehand."
|
99
|
-
)
|
100
|
-
end
|
101
|
-
end
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe "Observable" do
|
4
|
+
let(:an_observable_object) do
|
5
|
+
Class.new do
|
6
|
+
require "observer"; include Observable
|
7
|
+
|
8
|
+
def poke
|
9
|
+
changed
|
10
|
+
notify_observers :poked
|
11
|
+
end
|
12
|
+
|
13
|
+
def tap
|
14
|
+
changed
|
15
|
+
notify_observers :tapped
|
16
|
+
end
|
17
|
+
end.new
|
18
|
+
end
|
19
|
+
|
20
|
+
it "you can register for notifications" do
|
21
|
+
an_observer = Class.new do
|
22
|
+
def update(args)
|
23
|
+
@notified = true
|
24
|
+
end
|
25
|
+
|
26
|
+
def notified?; @notified === true; end
|
27
|
+
end.new
|
28
|
+
|
29
|
+
an_observable_object.add_observer(an_observer)
|
30
|
+
|
31
|
+
an_observable_object.poke
|
32
|
+
|
33
|
+
an_observer.must be_notified
|
34
|
+
end
|
35
|
+
|
36
|
+
it "and you can send arguments with your notification" do
|
37
|
+
an_observer = Class.new do
|
38
|
+
def update(args)
|
39
|
+
@args = args
|
40
|
+
end
|
41
|
+
|
42
|
+
def args; @args; end
|
43
|
+
end.new
|
44
|
+
|
45
|
+
an_observable_object.add_observer(an_observer)
|
46
|
+
|
47
|
+
an_observable_object.poke
|
48
|
+
|
49
|
+
an_observer.args.must === :poked
|
50
|
+
end
|
51
|
+
|
52
|
+
it "for multiple events the observer must decide for themselves" do
|
53
|
+
an_observer = Class.new do
|
54
|
+
def initialize
|
55
|
+
@poked,@tapped = 0,0
|
56
|
+
end
|
57
|
+
|
58
|
+
def update(args)
|
59
|
+
@poked = true if args === :poked
|
60
|
+
@tapped = true if args === :tapped
|
61
|
+
end
|
62
|
+
|
63
|
+
def poked?; @poked === true; end
|
64
|
+
def tapped?; @tapped === true; end
|
65
|
+
end.new
|
66
|
+
|
67
|
+
an_observable_object.add_observer(an_observer)
|
68
|
+
|
69
|
+
an_observable_object.poke
|
70
|
+
an_observable_object.tap
|
71
|
+
|
72
|
+
an_observer.must be_tapped
|
73
|
+
an_observer.must be_poked
|
74
|
+
end
|
75
|
+
|
76
|
+
it "notifications are not sent if `changed` not called" do
|
77
|
+
a_bung_observable_object_that_does_not_call_changed = Class.new do
|
78
|
+
require "observer"; include Observable
|
79
|
+
|
80
|
+
def poke
|
81
|
+
notify_observers :poked
|
82
|
+
end
|
83
|
+
end.new
|
84
|
+
|
85
|
+
an_observer = Class.new do
|
86
|
+
def update(args)
|
87
|
+
@notified = true
|
88
|
+
end
|
89
|
+
|
90
|
+
def notified?; @notified === true; end
|
91
|
+
end.new
|
92
|
+
|
93
|
+
a_bung_observable_object_that_does_not_call_changed.add_observer an_observer
|
94
|
+
|
95
|
+
a_bung_observable_object_that_does_not_call_changed.poke
|
96
|
+
|
97
|
+
an_observer.must_not(be_notified,
|
98
|
+
"Even though `notify_observers` was called, no notification was sent because you have to call `changed` beforehand."
|
99
|
+
)
|
100
|
+
end
|
101
|
+
end
|