eventually 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.2@eventually --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in eventually.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Eventually
2
+
3
+ `Eventually` is a module that facilitates evented callback management *similar* to the [EventEmitter API](http://nodejs.org/docs/v0.4.7/api/events.html) in NodeJS. Support for Ruby's various lambda-ish callback styles is heavily baked in, so using blocks, lambdas, procs, or event detached methods works out of the box, batteries included. Simply include `Eventually` in the class you will be emitting events from, register some listeners and fire away.
4
+
5
+ ```ruby
6
+ class Car
7
+ include Eventually
8
+ def stop
9
+ #...
10
+ emit(:stopped, 0)
11
+ end
12
+ end
13
+
14
+ car = Car.new
15
+ car.on(:stopped) do |mph|
16
+ puts 'the car stopped, sitting at %d mph' % mph
17
+ end
18
+ car.stop # this will indirectly invoke the above callback
19
+ ```
20
+
21
+ ## Pre-define Events
22
+
23
+ For documentation purposes, it can often be nice to define up front what events you'll be expecting to emit from the instances of a certain class. Anyone who's ever spent a couple of minutes trying to dig up their database columns from an ActiveRecord model knows what I'm talking about. Annoying. So `Eventually` let's you put that all up front in a nice DSL.
24
+
25
+ ```ruby
26
+ class Car
27
+ include Eventually
28
+ emits :stopped, :started, :turning
29
+ emits :reversing
30
+ end
31
+ ```
32
+
33
+ The previous snippet acts mostly as documentation.
34
+
35
+ *See the **examples/basic.rb** file for a slightly more complicated setup than this one.*
36
+
37
+ ## Callback arity validation
38
+
39
+ However, sometimes you want to be sure that a given registered callback will conform to your event interface, so specify an **arity validation**. Let's add another event to our definition and ensure that callbacks registering for that event must have an arity of 1.
40
+
41
+ ```ruby
42
+ class Car
43
+ include Eventually
44
+ emits :driving, :arity => 1
45
+ end
46
+ car = Car.new
47
+ car.on(:driving) do
48
+ puts 'The car is driving'
49
+ end
50
+ # Error will be raise explaining the arity mismatch (expected 1, received -1)
51
+ ```
52
+
53
+ *See the **examples/arity.rb** file for more on this.*
54
+
55
+ ## Strict Mode
56
+
57
+ Strict mode is useful if you want to enforce the `#emits` documentation as being the **ONLY** events that your instances can emit or be registered against.
58
+
59
+ ```ruby
60
+ class Car
61
+ include Eventually
62
+
63
+ enable_strict!
64
+ emits :started, :stopped
65
+
66
+ def turn
67
+ # Emitting :turning event here will throw an error in strict mode
68
+ emit(:turning)
69
+ end
70
+ end
71
+
72
+ car = Car.new
73
+ # Registering for the :turning event here will throw an error in strict mode
74
+ car.on(:turning) do
75
+ puts 'the car is turning'
76
+ end
77
+
78
+ *See the **examples/scrict.rb** file for more on this.*
79
+
80
+ ## More?
81
+
82
+ Further examples can be found in the examples directory. I know, novel idea that one.
83
+
84
+ ## Contact
85
+
86
+ [@localshred](http://twitter.com/localshred) wrote this. He [sometimes blogs](http://www.rand9.com) too.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "eventually/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "eventually"
7
+ s.version = Eventually::VERSION
8
+ s.authors = ["BJ Neilsen"]
9
+ s.email = ["bj.neilsen@gmail.com"]
10
+ s.homepage = "http://www.rand9.com"
11
+ s.summary = %q{Eventually is an event library built to loosely mirror the NodeJS EventEmitter API.}
12
+ s.description = s.summary
13
+
14
+ s.rubyforge_project = "eventually"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ end
data/examples/arity.rb ADDED
@@ -0,0 +1,39 @@
1
+ $LOAD_PATH << File.expand_path('../lib', File.dirname(__FILE__))
2
+ require 'eventually'
3
+
4
+ class Car
5
+ include Eventually
6
+ emits(:driving, :arity => 1)
7
+ emits(:stopped, :arity => 0)
8
+ emits(:reversing, :arity => -1)
9
+ emits(:turning, :arity => 3)
10
+ end
11
+
12
+ car = Car.new
13
+ car.on(:driving) do |mph|
14
+ puts 'The car is traveling %d mph' % mph
15
+ end
16
+
17
+ # Notice the odd empty "pipes" below...
18
+ # Checking arity on a block will give -1 for no args.
19
+ # If you're expecting arity == 0 you have to pass empty pipes (e.g. do ||)
20
+ # In other words, it doesn't make a ton of sense to
21
+ # expect an arity of zero, better a -1 validation such
22
+ # as on the :reversing event
23
+ car.on(:stopped) do ||
24
+ puts 'The car stopped'
25
+ end
26
+
27
+ # Validated on -1 (no arguments)
28
+ car.on(:reversing) do
29
+ puts 'The car is reversing'
30
+ end
31
+
32
+ begin
33
+ car.on(:turning) do |direction|
34
+ puts 'Car is turning %s' % direction
35
+ end
36
+ rescue => e
37
+ # "Invalid callback arity for event :turning (expected 3, received 1)"
38
+ puts e.message
39
+ end
data/examples/basic.rb ADDED
@@ -0,0 +1,53 @@
1
+ $LOAD_PATH << File.expand_path('../lib', File.dirname(__FILE__))
2
+ require 'eventually'
3
+
4
+ SPEED_LIMIT = 65
5
+
6
+ class SpeedingCar
7
+ include Eventually
8
+
9
+ # Document the events we'll likely emit
10
+ emits :stopped, :driving
11
+
12
+ def stop
13
+ puts 'Car is stopped'
14
+ emit(:stopped)
15
+ end
16
+
17
+ def go(mph)
18
+ puts 'Car is driving %d mph' % mph
19
+ emit(:driving, mph)
20
+ end
21
+ end
22
+
23
+ class PoliceCar
24
+ def initialize(speeding_car)
25
+ speeding_car.on(:stopped, method(:eat_donut))
26
+ speeding_car.on(:driving, method(:arrest_if_speeding))
27
+ end
28
+
29
+ def eat_donut
30
+ puts 'CHOMP'
31
+ end
32
+
33
+ def arrest_if_speeding(speed)
34
+ if speed > SPEED_LIMIT
35
+ puts 'ARREST THEM!'
36
+ else
37
+ eat_donut
38
+ end
39
+ end
40
+ end
41
+
42
+ car = SpeedingCar.new
43
+ police = PoliceCar.new(car)
44
+ car.stop
45
+ car.go(100)
46
+
47
+ # The above will write to stdout:
48
+ #
49
+ # Car is stopped
50
+ # CHOMP
51
+ # Car is driving 100 mph
52
+ # ARREST THEM!
53
+ #
@@ -0,0 +1,27 @@
1
+ $LOAD_PATH << File.expand_path('../lib', File.dirname(__FILE__))
2
+ require 'eventually'
3
+
4
+ class Door
5
+ include Eventually
6
+ enable_strict!
7
+ emits :opened, :closed
8
+ end
9
+
10
+ door = Door.new
11
+ door.on(:opened) do
12
+ puts 'door was opened'
13
+ end
14
+
15
+ door.on(:closed) do
16
+ puts 'door was closed'
17
+ end
18
+
19
+ begin
20
+ # This will raise an error!
21
+ door.on(:slammed) do
22
+ puts 'oh noes'
23
+ end
24
+ rescue => e
25
+ # "Event type :slammed will not be emitted. Use Door.emits(:slammed)"
26
+ puts e.message
27
+ end
data/lib/eventually.rb ADDED
@@ -0,0 +1,169 @@
1
+ require "eventually/version"
2
+
3
+ # Eventually is a module that facilitates evented callback
4
+ # management similar to the EventEmitter API in NodeJS.
5
+ # Simply include in the class you will be emitting events
6
+ # from and fire away.
7
+ #
8
+ # Support exists for strict mode, pre-defining the events
9
+ # you plan on emitting, and arity validation on callbacks.
10
+ # See the docs below or the examples folder for further documentation.
11
+ module Eventually
12
+ def self.included(base)
13
+ base.extend(ClassMethods)
14
+ end
15
+
16
+ module ClassMethods
17
+ NO_CHECK_ARITY = -1
18
+ attr_accessor :emittable_events
19
+
20
+ # Define an event or list of events
21
+ # that instances of this class will potentially emit.
22
+ #
23
+ # Usage (list of events):
24
+ # emits :started, :stopped
25
+ #
26
+ # Usage (single event):
27
+ # emits :started
28
+ #
29
+ # Usage (single event with arity validation):
30
+ # emits :started, :arity => 2
31
+ #
32
+ def emits(*evts)
33
+ if evts && !evts.empty?
34
+ if evts.all?{|e| e.is_a?(Symbol) }
35
+ evts.each{|e| emittable[e.to_sym] = NO_CHECK_ARITY }
36
+ elsif evts.count == 2
37
+ emittable[evts[0].to_sym] = (evts[1].fetch(:arity) { NO_CHECK_ARITY }).to_i
38
+ end
39
+ else
40
+ emittable.keys
41
+ end
42
+ end
43
+
44
+ # Check if instances of this class have pre-defined
45
+ # the given event as potentially emittable
46
+ def emits?(evt)
47
+ emittable.key?(evt.to_sym)
48
+ end
49
+
50
+ # Puts instances into strict mode. This does two things:
51
+ # - Raise an error if registering a callback for an event
52
+ # that has not already been pre-defined (e.g. with #emits)
53
+ # - Raise an error if instance attempts to emit an event
54
+ # that has not already been pre-defined (e.g. with #emits)
55
+ def enable_strict!
56
+ @strict = true
57
+ end
58
+
59
+ # The default state of an Eventually instance. Does not require
60
+ # pre-definition of an event to register against it or emit it
61
+ def disable_strict!
62
+ @strict = false
63
+ end
64
+
65
+ # Are we strict or not
66
+ def strict?
67
+ @strict || false
68
+ end
69
+
70
+ # Determines if the given event has an arity validation assigned
71
+ def validates_arity?(evt)
72
+ emits?(evt) && emittable[evt.to_sym] > NO_CHECK_ARITY
73
+ end
74
+
75
+ # Returns the arity validation, nil if event isn't defined
76
+ def arity_for_event(evt)
77
+ emits?(evt) ? emittable[evt.to_sym] : nil
78
+ end
79
+
80
+ # Reset the known emittable events (events defined with #emits)
81
+ # More useful for tests probably, but leaving it in API just 'cause
82
+ def emits_none
83
+ @emittable = {}
84
+ nil
85
+ end
86
+
87
+ # Shorthand predicate to determine if a given event is
88
+ # "emittable" or "registerable"
89
+ def can_emit_or_register?(event)
90
+ !strict? || emits?(event)
91
+ end
92
+
93
+ private
94
+
95
+ def emittable
96
+ @emittable ||= {}
97
+ end
98
+ end # ClassMethods
99
+
100
+ # Event registration method. Takes an event to register against and either
101
+ # a callable object (e.g. proc/lambda/detached method) or a block
102
+ #
103
+ # Usage: see Eventually#emit or examples directory
104
+ #
105
+ def on(event, callable=nil, &blk)
106
+ raise "Event type :#{event} will not be emitted. Use #{self.class.name}.emits(:#{event})" unless self.class.can_emit_or_register?(event)
107
+
108
+ cbk = nil
109
+ if callable.respond_to?(:call)
110
+ cbk = callable
111
+ elsif block_given? && !blk.nil?
112
+ cbk = blk
113
+ else
114
+ raise 'Cannot register callback. Neither callable nor block was given'
115
+ end
116
+
117
+ # if self.class.validates_arity?(event) && cbk.arity != (expected_arity = self.class.arity_for_event(event))
118
+ unless valid_event_arity?(event, cbk.arity)
119
+ raise "Invalid callback arity for event :#{event} (expected #{self.class.arity_for_event(event)}, received #{cbk.arity})"
120
+ end
121
+
122
+ (__registered__[event.to_sym] ||= []) << cbk
123
+ end
124
+
125
+ # Emit the payload arguments back to the registered listeners
126
+ # on the given event. FIFO calling, and we won't deal with
127
+ # concurrency, so it should be handled at the callback level.
128
+ #
129
+ # Usage:
130
+ # class Car
131
+ # include Eventually
132
+ # def stop
133
+ # #...
134
+ # emit(:stopped, 0)
135
+ # end
136
+ # end
137
+ #
138
+ # car = Car.new
139
+ # car.on(:stopped) do |mph|
140
+ # puts 'the car stopped, sitting at %d mph' % mph
141
+ # end
142
+ # car.stop # this will indirectly invoke the above callback
143
+ #
144
+ def emit(event, *payload)
145
+ raise "Event type :#{event} cannot be emitted. Use #{self.class.name}.emits(:#{event})" unless self.class.can_emit_or_register?(event)
146
+
147
+ unless valid_event_arity?(event, payload.length)
148
+ raise "Invalid emit arity for event :#{event} (expected #{self.class.arity_for_event(event)}, received #{payload.length})"
149
+ end
150
+
151
+ (__registered__[event.to_sym] || []).each do |cbk|
152
+ cbk.call(*payload)
153
+ end
154
+ end
155
+
156
+ # Shorthand predicate to determine if the given cbk for this event
157
+ # has a valid arity amount
158
+ def valid_event_arity?(event, arity_count)
159
+ expected_arity = self.class.arity_for_event(event)
160
+ !self.class.validates_arity?(event) || arity_count == expected_arity
161
+ end
162
+
163
+ private
164
+
165
+ def __registered__
166
+ @__registered__ ||= {}
167
+ end
168
+
169
+ end
@@ -0,0 +1,3 @@
1
+ module Eventually
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,325 @@
1
+ require 'spec_helper'
2
+
3
+ class Emitter
4
+ include Eventually
5
+ end
6
+
7
+ describe Eventually do
8
+ before(:each) do
9
+ Emitter.disable_strict!
10
+ Emitter.emits_none
11
+ end
12
+
13
+ let(:emitter) { Emitter.new }
14
+ let(:defined_events) { [:one, :two, :three] }
15
+
16
+ context 'external api' do
17
+ describe '.emits_none' do
18
+ it 'clears out emitter definitions' do
19
+ Emitter.emits(:jigger)
20
+ Emitter.emits?(:jigger).should be_true
21
+ Emitter.emits_none
22
+ Emitter.emits?(:jigger).should be_false
23
+ end
24
+ end
25
+
26
+ describe '.emits' do
27
+ it 'allows event definition at class level' do
28
+ Emitter.emits(:jigger)
29
+ Emitter.emits?(:jigger).should be_true
30
+ end
31
+
32
+ it 'can register multiple event symbols at once' do
33
+ Emitter.emits(*defined_events)
34
+ defined_events.each {|e| Emitter.emits?(e).should be_true }
35
+ end
36
+
37
+ it 'provides a list of pre-defined emittable events' do
38
+ Emitter.emits(*defined_events)
39
+ Emitter.emits.should eq defined_events
40
+ end
41
+
42
+ describe '.enable_strict!' do
43
+ it 'requires the event to have been pre-defined for watchers to register callbacks to it' do
44
+ Emitter.enable_strict!
45
+ Emitter.emits(:start)
46
+ Emitter.emits?(:start).should be_true
47
+ expect {
48
+ emitter.on(:start, lambda{ puts 'hi' })
49
+ }.should_not raise_error
50
+ expect {
51
+ emitter.on(:stop, lambda{ puts 'hi' })
52
+ }.should raise_error(/Event type :stop will not be emitted/)
53
+ end
54
+ end
55
+
56
+ describe '.disable_strict!' do
57
+ it 'disables strict mode' do
58
+ Emitter.disable_strict!
59
+ Emitter.emits?(:start).should be_false
60
+ expect {
61
+ emitter.on(:start, lambda{ puts 'hi' })
62
+ }.should_not raise_error
63
+ end
64
+ end
65
+
66
+ describe '.strict?' do
67
+ context 'when strict mode is enabled' do
68
+ it 'returns true' do
69
+ Emitter.enable_strict!
70
+ Emitter.strict?.should be_true
71
+ end
72
+ end
73
+
74
+ context 'when strict mode is disabled' do
75
+ it 'returns false' do
76
+ Emitter.disable_strict!
77
+ Emitter.strict?.should be_false
78
+ end
79
+ end
80
+ end
81
+
82
+ context 'when providing an arity validation' do
83
+ it 'sets an arity expectation for future event callbacks' do
84
+ Emitter.emits(:jigger, arity: 5)
85
+ Emitter.emits?(:jigger)
86
+ Emitter.validates_arity?(:jigger).should be_true
87
+ end
88
+
89
+ it 'allows 0 as a specified arity' do
90
+ Emitter.emits(:jigger, arity: 0)
91
+ Emitter.emits?(:jigger)
92
+ Emitter.validates_arity?(:jigger).should be_true
93
+ end
94
+
95
+ describe '.arity_for_event' do
96
+ it 'reports the arity requirement for the event, if any' do
97
+ Emitter.emits(:jigger, arity: 5)
98
+ Emitter.arity_for_event(:jigger).should eq 5
99
+ Emitter.emits(:pingpong)
100
+ Emitter.arity_for_event(:pingpong).should eq -1
101
+ Emitter.arity_for_event(:nonevent).should eq nil
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ it 'allows event registration with lambda' do
108
+ emitter.on(:start, lambda{ puts 'hi' })
109
+ end
110
+
111
+ it 'allows event registration with proc' do
112
+ emitter.on(:start, proc{ puts 'hi' })
113
+ end
114
+
115
+ it 'allows event registration with block' do
116
+ emitter.on(:start) do
117
+ puts 'hi'
118
+ end
119
+ end
120
+
121
+ it 'allows event registration with detached method' do
122
+ def event_handler; end
123
+ emitter.on(:start, method(:event_handler))
124
+ end
125
+
126
+ it 'allows multiple registrations for a given event' do
127
+ emitter.on(:start) { puts 'hello' }
128
+ emitter.on(:start) { puts 'world' }
129
+ end
130
+
131
+ describe '.can_emit_or_register?' do
132
+ context 'when strict mode enabled' do
133
+ before { Emitter.enable_strict! }
134
+ context 'when given event is registered' do
135
+ it 'returns true' do
136
+ Emitter.emits(:known)
137
+ Emitter.emits?(:known).should be_true
138
+ Emitter.can_emit_or_register?(:known).should be_true
139
+ end
140
+ end
141
+ context 'when given event is not registered' do
142
+ it 'returns false' do
143
+ Emitter.emits?(:unknown).should be_false
144
+ Emitter.can_emit_or_register?(:unknown).should be_false
145
+ end
146
+ end
147
+ end
148
+
149
+ context 'when strict mode disabled' do
150
+ before { Emitter.disable_strict! }
151
+ context 'when given event is registered' do
152
+ it 'returns true' do
153
+ Emitter.emits(:known)
154
+ Emitter.emits?(:known).should be_true
155
+ Emitter.can_emit_or_register?(:known).should be_true
156
+ end
157
+ end
158
+ context 'when given event is not registered' do
159
+ it 'returns true' do
160
+ Emitter.emits?(:unknown).should be_false
161
+ Emitter.can_emit_or_register?(:unknown).should be_true
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ context 'when emitting events' do
169
+ let(:emitter) { Emitter.new }
170
+ let(:watcher) { Watcher.new }
171
+
172
+ class Watcher
173
+ attr_accessor :value
174
+ def initialize
175
+ @value = 1
176
+ end
177
+ def block_callback
178
+ Proc.new{|payload| @value += payload }
179
+ end
180
+ def lambda_callback
181
+ lambda{|payload| @value += payload }
182
+ end
183
+ def method_callback
184
+ method(:update_method)
185
+ end
186
+ def proc_callback
187
+ proc{|payload| @value += payload }
188
+ end
189
+
190
+ def update_method(payload)
191
+ @value += payload
192
+ end
193
+ end
194
+
195
+ shared_examples_for 'emitting an event' do |cbk_type|
196
+ it "by invoking a #{cbk_type} callback" do
197
+ expect {
198
+ case cbk_type
199
+ when :block then
200
+ emitter.on(:start, &watcher.block_callback)
201
+ when :lambda then
202
+ emitter.on(:start, watcher.lambda_callback)
203
+ when :method then
204
+ emitter.on(:start, watcher.method_callback)
205
+ when :proc then
206
+ emitter.on(:start, watcher.proc_callback)
207
+ end
208
+ }.should_not raise_error(/Cannot register callback/)
209
+ emitter.__send__(:emit, :start, 1)
210
+ watcher.value.should eq 2
211
+ end
212
+ end
213
+
214
+ it_behaves_like 'emitting an event', :block
215
+ it_behaves_like 'emitting an event', :lambda
216
+ it_behaves_like 'emitting an event', :method
217
+ it_behaves_like 'emitting an event', :proc
218
+
219
+ it 'emits nothing when no event callbacks are given' do
220
+ expect { emitter.__send__(:emit, :hullabaloo) }.should_not raise_error
221
+ end
222
+
223
+ it 'raises an error when a given callback is invalid' do
224
+ expect { emitter.on(:start, nil, &nil) }.should raise_error(/Cannot register callback/)
225
+ expect { emitter.on(:start, 10_000) }.should raise_error(/Cannot register callback/)
226
+ expect { emitter.on(:start, "callback") }.should raise_error(/Cannot register callback/)
227
+ end
228
+
229
+ it 'invokes registered callbacks in a FIFO manner' do
230
+ watcher1 = Watcher.new
231
+ watcher1.value = []
232
+
233
+ watcher2 = Watcher.new
234
+ watcher2.value = []
235
+
236
+ emitter.on(:push) {|v| watcher1.value << "A"+v }
237
+ emitter.on(:push) {|v| watcher2.value << "B"+v }
238
+ emitter.on(:push) {|v| watcher2.value << "C"+v }
239
+ emitter.on(:push) {|v| watcher1.value << "D"+v }
240
+ emitter.__send__(:emit, :push, "-VALUE")
241
+
242
+ watcher1.value.should eq ["A-VALUE", "D-VALUE"]
243
+ watcher2.value.should eq ["B-VALUE", "C-VALUE"]
244
+ end
245
+
246
+ context 'when arity validation is enabled' do
247
+ before { Emitter.emits(:hello_world, arity: 2) }
248
+ it 'accepts a callback with matching arity' do
249
+ expect {
250
+ emitter.on(:hello_world) do |param1, param2|
251
+ #not invoked
252
+ end
253
+ }.should_not raise_error
254
+ end
255
+
256
+ it 'rejects a callback if the given arity is not exact' do
257
+ expect {
258
+ emitter.on(:hello_world) do |param1, param2, param3|
259
+ #not invoked
260
+ end
261
+ }.should raise_error(/Invalid callback arity for event :hello_world \(expected 2, received 3\)/)
262
+ end
263
+
264
+ it 'accepts emitting an event when arity is valid' do
265
+ expect {
266
+ emitter.__send__(:emit, :hello_world, "hello", "world")
267
+ }.should_not raise_error
268
+ end
269
+
270
+ it 'rejects emitting an event when the arity is not exact' do
271
+ expect {
272
+ emitter.__send__(:emit, :hello_world, "hello")
273
+ }.should raise_error(/Invalid emit arity for event :hello_world \(expected 2, received 1\)/)
274
+ end
275
+ end
276
+
277
+ context 'when strict mode is enabled' do
278
+ context 'when emitting an event not previously defined' do
279
+ it 'raises an error concerning the unknown event type' do
280
+ emitter.class.enable_strict!
281
+ emitter.class.strict?.should be_true
282
+ emitter.class.emits?(:stop).should be_false
283
+ expect {
284
+ emitter.__send__(:emit, :stop)
285
+ }.should raise_error(/Event type :stop cannot be emitted/)
286
+ end
287
+ end
288
+ end
289
+
290
+ describe '#valid_event_arity?' do
291
+ context 'when arity is validated for event' do
292
+ context 'and the arity matches' do
293
+ it 'doesn\'t raise an error' do
294
+ Emitter.emits(:jump, arity: 2)
295
+ expect {
296
+ emitter.on(:jump) do |param1, param2|
297
+ puts 'hi'
298
+ end
299
+ }.should_not raise_error
300
+ end
301
+ end
302
+ context 'and the arity does not match' do
303
+ it 'raises an error' do
304
+ Emitter.emits(:jump, arity: 2)
305
+ expect {
306
+ emitter.on(:jump) do |param1|
307
+ puts 'hi'
308
+ end
309
+ }.should raise_error(/expected 2, received 1/)
310
+ end
311
+ end
312
+ end
313
+ context 'when arity is not for event' do
314
+ it 'doesn\'t raise an error' do
315
+ Emitter.emits?(:jump).should be_false
316
+ expect {
317
+ emitter.on(:jump) do |param1, param2|
318
+ puts 'hi'
319
+ end
320
+ }.should_not raise_error
321
+ end
322
+ end
323
+ end
324
+ end
325
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH << File.expand_path('../lib', File.dirname(__FILE__))
2
+ require 'eventually'
3
+ require 'rspec'
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: eventually
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - BJ Neilsen
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-12-20 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &2153560520 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *2153560520
25
+ description: Eventually is an event library built to loosely mirror the NodeJS EventEmitter
26
+ API.
27
+ email:
28
+ - bj.neilsen@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - .gitignore
34
+ - .rvmrc
35
+ - Gemfile
36
+ - README.md
37
+ - Rakefile
38
+ - eventually.gemspec
39
+ - examples/arity.rb
40
+ - examples/basic.rb
41
+ - examples/strict.rb
42
+ - lib/eventually.rb
43
+ - lib/eventually/version.rb
44
+ - spec/lib/eventually_spec.rb
45
+ - spec/spec_helper.rb
46
+ homepage: http://www.rand9.com
47
+ licenses: []
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project: eventually
66
+ rubygems_version: 1.8.10
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Eventually is an event library built to loosely mirror the NodeJS EventEmitter
70
+ API.
71
+ test_files:
72
+ - spec/lib/eventually_spec.rb
73
+ - spec/spec_helper.rb