unobservable 0.6.1 → 0.8.0
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.
- data/TODO.rdoc +1 -1
- data/VERSION +1 -1
- data/lib/unobservable.rb +11 -2
- data/spec/event_spec.rb +154 -1
- data/spec/spec_helper.rb +31 -0
- data/spec/unobservable_spec.rb +113 -48
- metadata +3 -3
data/TODO.rdoc
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
data/lib/unobservable.rb
CHANGED
@@ -165,7 +165,12 @@ module Unobservable
|
|
165
165
|
if block
|
166
166
|
return block
|
167
167
|
elsif args.size == 1
|
168
|
-
|
168
|
+
candidate = args[0]
|
169
|
+
if candidate.respond_to?(:to_proc)
|
170
|
+
return candidate.to_proc
|
171
|
+
else
|
172
|
+
raise ArgumentError, "The argument does not respond to the #to_proc method"
|
173
|
+
end
|
169
174
|
elsif args.size == 2
|
170
175
|
return args[0].method(args[1])
|
171
176
|
end
|
@@ -208,7 +213,11 @@ module Unobservable
|
|
208
213
|
else
|
209
214
|
# TODO: Add some form of error-handling
|
210
215
|
@handlers.each do |h|
|
211
|
-
|
216
|
+
begin
|
217
|
+
h.call(*args, &block)
|
218
|
+
rescue Exception
|
219
|
+
# TODO: Should probably log when this happens
|
220
|
+
end
|
212
221
|
end
|
213
222
|
|
214
223
|
return true
|
data/spec/event_spec.rb
CHANGED
@@ -2,5 +2,158 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module Unobservable
|
4
4
|
|
5
|
+
describe Event do
|
6
|
+
|
7
|
+
let(:e) { Event.new }
|
8
|
+
|
9
|
+
describe "#handler_for" do
|
10
|
+
it "raises an ArgumentError when it does not receive any arguments" do
|
11
|
+
expect{ e.handler_for() }.to raise_error(ArgumentError)
|
12
|
+
end
|
13
|
+
|
14
|
+
|
15
|
+
it "raises an ArgumentError when it is receives an argument that cannot be converted into a Proc" do
|
16
|
+
expect{ e.handler_for(Object.new) }.to raise_error(ArgumentError)
|
17
|
+
end
|
18
|
+
|
19
|
+
it "allows a Proc to be used has an event handler" do
|
20
|
+
p = Proc.new {}
|
21
|
+
e.handler_for(p).should == p
|
22
|
+
end
|
23
|
+
|
24
|
+
it "allows a Block to be used as an event handler" do
|
25
|
+
p = Proc.new {}
|
26
|
+
e.handler_for(&p).should == p
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can use a specified method on an object as an event handler" do
|
30
|
+
x = Object.new
|
31
|
+
e.handler_for(x, :class).should == x.method(:class)
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
it "raises an ArgumentError when it receives 3 or more arguments" do
|
36
|
+
expect{ e.handler_for(Proc.new, :foo, :bar) }.to raise_error(ArgumentError)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
describe "#register" do
|
44
|
+
|
45
|
+
it "returns the event handler that was added to the event's list of handlers" do
|
46
|
+
handler = Proc.new {}
|
47
|
+
e.register(handler).should == handler
|
48
|
+
end
|
49
|
+
|
50
|
+
it "adds an event handler to the event's list of handlers" do
|
51
|
+
handler = Proc.new {}
|
52
|
+
|
53
|
+
expect do
|
54
|
+
e.register(handler)
|
55
|
+
end.to change{ e.handlers.size }.from(0).to(1)
|
56
|
+
|
57
|
+
e.handlers.should include(handler)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "allows multiple event handlers to be registered to the same event" do
|
61
|
+
h1 = Proc.new { puts "one" }
|
62
|
+
h2 = Proc.new { puts "two" }
|
63
|
+
|
64
|
+
expect do
|
65
|
+
e.register h1
|
66
|
+
e.register h2
|
67
|
+
end.to change{ e.handlers.size }.from(0).to(2)
|
68
|
+
|
69
|
+
e.handlers.should include(h1)
|
70
|
+
e.handlers.should include(h2)
|
71
|
+
|
72
|
+
h1.should_not == h2
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
it "allows the same event handler to be registered multiple times" do
|
77
|
+
handler = Proc.new {}
|
78
|
+
|
79
|
+
expect do
|
80
|
+
3.times { e.register handler }
|
81
|
+
end.to change{ e.handlers.size }.from(0).to(3)
|
82
|
+
|
83
|
+
e.handlers.each {|h| h.should == handler}
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
describe "#unregister" do
|
90
|
+
|
91
|
+
it "returns nil when asked to unregister a handler that was never registered" do
|
92
|
+
p = Proc.new {}
|
93
|
+
e.handlers.should_not include(p)
|
94
|
+
e.unregister(p).should be_nil
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns the handler that was unregistered" do
|
98
|
+
p = Proc.new {}
|
99
|
+
e.register p
|
100
|
+
|
101
|
+
e.handlers.should include(p)
|
102
|
+
expect do
|
103
|
+
e.unregister(p).should == p
|
104
|
+
end.to change{ e.handlers.size }.from(1).to(0)
|
105
|
+
|
106
|
+
e.handlers.should_not include(p)
|
107
|
+
end
|
108
|
+
|
109
|
+
|
110
|
+
it "only unregisters 1 occurrence of the specified event handler" do
|
111
|
+
p = Proc.new { "hello" }
|
112
|
+
3.times { e.register p }
|
113
|
+
|
114
|
+
expect{ e.unregister p }.to change{ e.handlers.size }.from(3).to(2)
|
115
|
+
|
116
|
+
e.handlers.each{|h| h.should == p}
|
117
|
+
end
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
|
123
|
+
describe "#call" do
|
124
|
+
it "can be called when no event handlers have been registered" do
|
125
|
+
expect{ e.call }.to_not raise_error
|
126
|
+
expect{ e.call 1, 2, 3 }.to_not raise_error
|
127
|
+
end
|
128
|
+
|
129
|
+
it "passes its arguments to each event handler" do
|
130
|
+
3.times do |i|
|
131
|
+
h = mock("handler #{i}")
|
132
|
+
h.should_receive(:some_method).with("arg1", "arg2")
|
133
|
+
e.register h, :some_method
|
134
|
+
end
|
135
|
+
|
136
|
+
e.call "arg1", "arg2"
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
it "should invoke each event handler even if one or more event handlers raise an exception." do
|
141
|
+
3.times do |i|
|
142
|
+
h = mock("handler #{i}")
|
143
|
+
h.should_receive(:some_method).with("arg1", "arg2").and_raise(Exception)
|
144
|
+
e.register h, :some_method
|
145
|
+
end
|
146
|
+
|
147
|
+
expect{ e.call "arg1", "arg2" }.to_not raise_error
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
|
5
155
|
|
6
|
-
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
|
data/spec/spec_helper.rb
CHANGED
@@ -9,3 +9,34 @@ RSpec.configure do |config|
|
|
9
9
|
|
10
10
|
end
|
11
11
|
|
12
|
+
|
13
|
+
module Unobservable
|
14
|
+
module SpecHelper
|
15
|
+
|
16
|
+
def module_with_instance_events(*names, &block)
|
17
|
+
m = Module.new do
|
18
|
+
include Unobservable::Support
|
19
|
+
attr_event *names
|
20
|
+
end
|
21
|
+
|
22
|
+
m.class_exec(&block) if block_given?
|
23
|
+
|
24
|
+
return m
|
25
|
+
end
|
26
|
+
|
27
|
+
def class_with_instance_events(*names, &block)
|
28
|
+
args = {superclass: Object}
|
29
|
+
args.merge!(names.pop) if names[-1].is_a? Hash
|
30
|
+
|
31
|
+
c = Class.new(args[:superclass]) do
|
32
|
+
include Unobservable::Support
|
33
|
+
attr_event *names
|
34
|
+
end
|
35
|
+
|
36
|
+
c.class_exec(&block) if block_given?
|
37
|
+
|
38
|
+
return c
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
data/spec/unobservable_spec.rb
CHANGED
@@ -1,72 +1,137 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
include Unobservable::SpecHelper
|
3
4
|
|
4
5
|
|
5
|
-
|
6
|
+
shared_examples_for "instance event container" do
|
7
|
+
|
8
|
+
context "Unobservable::Support is not included" do
|
9
|
+
it "does not have any instance events" do
|
10
|
+
c = described_class.new
|
11
|
+
Unobservable::instance_events_for(c, true).should be_empty
|
12
|
+
Unobservable::instance_events_for(c, false).should be_empty
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
context "Unobservable::Support is included" do
|
6
18
|
|
7
|
-
|
8
|
-
|
9
|
-
it "raises a type error when it receives a non-Module" do
|
10
|
-
expect do
|
11
|
-
Unobservable.instance_events_for(Object.new)
|
12
|
-
end.to raise_error(TypeError)
|
19
|
+
let(:mixin_module) do
|
20
|
+
module_with_instance_events :one, :two
|
13
21
|
end
|
14
22
|
|
23
|
+
let(:instance_event_container) do
|
24
|
+
m = mixin_module
|
25
|
+
described_class.new do
|
26
|
+
include Unobservable::Support
|
27
|
+
include m
|
28
|
+
|
29
|
+
attr_event :three, :four
|
30
|
+
end
|
31
|
+
end
|
15
32
|
|
33
|
+
it "does not have any events by default" do
|
34
|
+
c = described_class.new { include Unobservable::Support }
|
35
|
+
Unobservable::instance_events_for(c, true).should be_empty
|
36
|
+
Unobservable::instance_events_for(c, false).should be_empty
|
37
|
+
end
|
38
|
+
|
39
|
+
it "knows which events have been defined explicitly" do
|
40
|
+
events = Unobservable::instance_events_for(instance_event_container, false)
|
41
|
+
events.size.should == 2
|
42
|
+
events.should include(:three)
|
43
|
+
events.should include(:four)
|
44
|
+
end
|
16
45
|
|
17
|
-
it "
|
18
|
-
|
19
|
-
|
20
|
-
|
46
|
+
it "inherits instance events defined by included Modules" do
|
47
|
+
events = Unobservable::instance_events_for(instance_event_container, true)
|
48
|
+
events.size.should == 4
|
49
|
+
events.should include(:one)
|
50
|
+
events.should include(:two)
|
51
|
+
events.should include(:three)
|
52
|
+
events.should include(:four)
|
21
53
|
end
|
22
54
|
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
23
59
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
60
|
+
describe Module do
|
61
|
+
it_behaves_like "instance event container"
|
62
|
+
end
|
63
|
+
|
64
|
+
describe Class do
|
65
|
+
it_behaves_like "instance event container"
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
describe Unobservable do
|
29
71
|
|
72
|
+
describe "#instance_events_for" do
|
30
73
|
|
74
|
+
it "raises TypeError when it receives a non-Module" do
|
75
|
+
expect{ Unobservable.instance_events_for(Object.new) }.to raise_error(TypeError)
|
76
|
+
end
|
31
77
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
m = mixin_module
|
44
|
-
Module.new do
|
45
|
-
include Unobservable::Support
|
46
|
-
include m
|
47
|
-
attr_event :three, :four
|
48
|
-
end
|
49
|
-
end
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
describe "#collect_instance_events_defined_by" do
|
82
|
+
let(:mixin_module) do
|
83
|
+
module_with_instance_events(:mixin_1, :mixin_2)
|
84
|
+
end
|
85
|
+
|
86
|
+
let(:baseclass) do
|
87
|
+
class_with_instance_events(:bc_1, :bc_2)
|
88
|
+
end
|
50
89
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
events.should include(:four)
|
90
|
+
let(:subclass) do
|
91
|
+
m = mixin_module
|
92
|
+
class_with_instance_events(:sc_1, :sc_2, superclass: baseclass) do
|
93
|
+
include m
|
56
94
|
end
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
it "only collects the instance events that the contributors define explicitly" do
|
99
|
+
events = Unobservable::collect_instance_events_defined_by([subclass])
|
100
|
+
events.size.should == 2
|
101
|
+
events.should include(:sc_1)
|
102
|
+
events.should include(:sc_2)
|
103
|
+
end
|
104
|
+
|
105
|
+
it "collects the instance events defined by each contributor" do
|
106
|
+
classes = (1..3).map{|i| class_with_instance_events("a#{i}", "b#{i}") }
|
107
|
+
modules = (1..3).map{|i| module_with_instance_events("c#{i}", "d#{i}") }
|
57
108
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
events.
|
62
|
-
events.should include(
|
63
|
-
events.should include(
|
64
|
-
events.should include(
|
65
|
-
events.should include(:four)
|
109
|
+
events = Unobservable::collect_instance_events_defined_by(classes + modules)
|
110
|
+
events.size.should == 12
|
111
|
+
(1..3).each do |i|
|
112
|
+
events.should include("a#{i}".to_sym)
|
113
|
+
events.should include("b#{i}".to_sym)
|
114
|
+
events.should include("c#{i}".to_sym)
|
115
|
+
events.should include("d#{i}".to_sym)
|
66
116
|
end
|
117
|
+
end
|
118
|
+
|
119
|
+
|
120
|
+
it "does not repeat duplicate instance events" do
|
121
|
+
c1 = class_with_instance_events(:one, :two)
|
122
|
+
c2 = class_with_instance_events(:one, :three)
|
123
|
+
m1 = module_with_instance_events(:two, :three)
|
124
|
+
m2 = module_with_instance_events(:two)
|
67
125
|
|
126
|
+
events = Unobservable::collect_instance_events_defined_by([c1, c2, m1, m2])
|
127
|
+
events.size.should == 3
|
128
|
+
events.should include(:one)
|
129
|
+
events.should include(:two)
|
130
|
+
events.should include(:three)
|
68
131
|
end
|
69
132
|
|
133
|
+
|
70
134
|
end
|
71
135
|
|
72
|
-
end
|
136
|
+
end
|
137
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: unobservable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: shoulda
|
@@ -127,7 +127,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
127
127
|
version: '0'
|
128
128
|
segments:
|
129
129
|
- 0
|
130
|
-
hash:
|
130
|
+
hash: 2686370053034444533
|
131
131
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
132
|
none: false
|
133
133
|
requirements:
|