eventually 0.0.1

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/.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