ably 0.1.4 → 0.1.5
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.
- checksums.yaml +4 -4
- data/ably.gemspec +1 -0
- data/lib/ably/auth.rb +9 -13
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +27 -39
- data/lib/ably/modules/conversions.rb +31 -10
- data/lib/ably/modules/enum.rb +201 -0
- data/lib/ably/modules/event_emitter.rb +81 -0
- data/lib/ably/modules/event_machine_helpers.rb +21 -0
- data/lib/ably/modules/http_helpers.rb +13 -0
- data/lib/ably/modules/state.rb +67 -0
- data/lib/ably/realtime.rb +6 -1
- data/lib/ably/realtime/channel.rb +117 -56
- data/lib/ably/realtime/client.rb +7 -50
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +116 -0
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +63 -0
- data/lib/ably/realtime/connection.rb +97 -14
- data/lib/ably/realtime/models/error_info.rb +3 -2
- data/lib/ably/realtime/models/message.rb +28 -3
- data/lib/ably/realtime/models/nil_channel.rb +21 -0
- data/lib/ably/realtime/models/protocol_message.rb +35 -27
- data/lib/ably/rest/client.rb +39 -23
- data/lib/ably/rest/middleware/external_exceptions.rb +1 -1
- data/lib/ably/rest/middleware/parse_json.rb +7 -2
- data/lib/ably/rest/middleware/parse_message_pack.rb +23 -0
- data/lib/ably/rest/models/paged_resource.rb +4 -4
- data/lib/ably/util/pub_sub.rb +32 -0
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_spec.rb +1 -0
- data/spec/acceptance/realtime/message_spec.rb +136 -0
- data/spec/acceptance/rest/base_spec.rb +51 -1
- data/spec/acceptance/rest/presence_spec.rb +7 -2
- data/spec/integration/modules/state_spec.rb +66 -0
- data/spec/{unit → integration/rest}/auth.rb +0 -0
- data/spec/support/api_helper.rb +5 -2
- data/spec/support/protocol_msgbus_helper.rb +29 -0
- data/spec/support/test_app.rb +14 -3
- data/spec/unit/{conversions.rb → modules/conversions_spec.rb} +1 -1
- data/spec/unit/modules/enum_spec.rb +263 -0
- data/spec/unit/modules/event_emitter_spec.rb +81 -0
- data/spec/unit/modules/pub_sub_spec.rb +74 -0
- data/spec/unit/realtime/channel_spec.rb +27 -0
- data/spec/unit/realtime/client_spec.rb +8 -0
- data/spec/unit/realtime/connection_spec.rb +40 -0
- data/spec/unit/realtime/error_info_spec.rb +9 -1
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +36 -0
- data/spec/unit/realtime/message_spec.rb +2 -2
- data/spec/unit/realtime/protocol_message_spec.rb +78 -9
- data/spec/unit/rest/{rest_spec.rb → client_spec.rb} +0 -0
- data/spec/unit/rest/message_spec.rb +1 -1
- metadata +51 -9
- data/lib/ably/realtime/callbacks.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5b99a13d28dd5bd7e6b1d35e6ad0d718e759fbb3
|
4
|
+
data.tar.gz: fa7e7aac5a7acf5cc74bfc1da6faf48913ef0f36
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d2771237651cc84bc83f3de5c6e5438f6e04f57b36432d4394995f5ed7deae8e097d397d149a13e23993116d1f1f2266de046e7c1cca42e36ae776a92a6b5b6
|
7
|
+
data.tar.gz: 9f12b4aef062743e6bbfd7b40388dd7582b42d9634efb062a66ea4a181f815d5c44473e97a48b6c9b3c6862bd07d1bcf12e2d366006da5925f20f9c7d1d8a35e
|
data/ably.gemspec
CHANGED
@@ -22,6 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
spec.add_runtime_dependency "faraday", "~> 0.9"
|
23
23
|
spec.add_runtime_dependency "json"
|
24
24
|
spec.add_runtime_dependency "websocket-driver"
|
25
|
+
spec.add_runtime_dependency "msgpack"
|
25
26
|
|
26
27
|
spec.add_development_dependency "bundler", "~> 1.3"
|
27
28
|
spec.add_development_dependency "rake"
|
data/lib/ably/auth.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
1
|
+
require 'json'
|
2
|
+
require 'faraday'
|
3
|
+
require 'securerandom'
|
4
4
|
|
5
5
|
require "ably/rest/middleware/external_exceptions"
|
6
|
-
require "ably/rest/middleware/parse_json"
|
7
6
|
|
8
7
|
module Ably
|
9
8
|
# Auth is responsible for authentication with {https://ably.io Ably} using basic or token authentication
|
@@ -369,7 +368,7 @@ module Ably
|
|
369
368
|
@connection_options ||= {
|
370
369
|
builder: middleware,
|
371
370
|
headers: {
|
372
|
-
accept:
|
371
|
+
accept: client.mime_type,
|
373
372
|
user_agent: user_agent
|
374
373
|
},
|
375
374
|
request: {
|
@@ -384,18 +383,15 @@ module Ably
|
|
384
383
|
# @see http://mislav.uniqpath.com/2011/07/faraday-advanced-http/
|
385
384
|
def middleware
|
386
385
|
@middleware ||= Faraday::RackBuilder.new do |builder|
|
387
|
-
|
388
|
-
builder.use Faraday::Request::UrlEncoded
|
389
|
-
|
390
|
-
# Parse JSON response bodies
|
391
|
-
builder.use Ably::Rest::Middleware::ParseJson
|
392
|
-
|
393
|
-
# Log HTTP requests if debug_http option set
|
394
|
-
builder.response :logger if @debug_http
|
386
|
+
setup_middleware builder
|
395
387
|
|
396
388
|
# Raise exceptions if response code is invalid
|
397
389
|
builder.use Ably::Rest::Middleware::ExternalExceptions
|
398
390
|
|
391
|
+
|
392
|
+
# Log HTTP requests if log level is DEBUG option set
|
393
|
+
builder.response :logger if client.log_level == Logger::DEBUG
|
394
|
+
|
399
395
|
# Set Faraday's HTTP adapter
|
400
396
|
builder.adapter Faraday.default_adapter
|
401
397
|
end
|
@@ -1,30 +1,46 @@
|
|
1
1
|
require 'logger'
|
2
2
|
|
3
|
+
module Ably::Modules
|
4
|
+
module Conversions
|
5
|
+
private
|
6
|
+
# Creates or returns an {IdiomaticRubyWrapper} ensuring it never wraps itself
|
7
|
+
#
|
8
|
+
# @return {IdiomaticRubyWrapper}
|
9
|
+
def IdiomaticRubyWrapper(object, options = {})
|
10
|
+
case object
|
11
|
+
when Ably::Models::IdiomaticRubyWrapper
|
12
|
+
object
|
13
|
+
else
|
14
|
+
Ably::Models::IdiomaticRubyWrapper.new(object, options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
3
20
|
module Ably::Models
|
4
21
|
# Wraps JSON objects returned by Ably service to appear as Idiomatic Ruby Hashes with symbol keys
|
5
22
|
# It recursively wraps containing Hashes, but will stop wrapping at arrays, any other non Hash object, or any key matching the `:stops_at` options
|
6
23
|
# It also provides methods matching the symbolic keys for convenience
|
7
24
|
#
|
8
25
|
# @example
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
|
13
|
-
# ruby_hash[:key_value] # => 'true'
|
14
|
-
# ruby_hash.key_value # => 'true'
|
15
|
-
# ruby_hash[:key_value] = 'new_value'
|
16
|
-
# ruby_hash.key_value # => 'new_value'
|
26
|
+
# ruby_hash = IdiomaticRubyWrapper.new({ 'keyValue' => 'true' })
|
27
|
+
# # or recommended to avoid wrapping wrapped objects
|
28
|
+
# ruby_hash = IdiomaticRubyWrapper({ 'keyValue' => 'true' })
|
17
29
|
#
|
18
|
-
#
|
19
|
-
#
|
30
|
+
# ruby_hash[:key_value] # => 'true'
|
31
|
+
# ruby_hash.key_value # => 'true'
|
32
|
+
# ruby_hash[:key_value] = 'new_value'
|
33
|
+
# ruby_hash.key_value # => 'new_value'
|
20
34
|
#
|
21
|
-
#
|
35
|
+
# ruby_hash[:none] # => nil
|
36
|
+
# ruby_hash.none # => nil
|
22
37
|
#
|
23
38
|
# @!attribute [r] stop_at
|
24
39
|
# @return [Array<Symbol,String>] array of keys that this wrapper should stop wrapping at to preserve the underlying JSON hash as is
|
25
40
|
#
|
26
41
|
class IdiomaticRubyWrapper
|
27
42
|
include Enumerable
|
43
|
+
include Ably::Modules::Conversions
|
28
44
|
|
29
45
|
attr_reader :stop_at
|
30
46
|
|
@@ -172,33 +188,5 @@ module Ably::Models
|
|
172
188
|
|
173
189
|
preferred_format.call(symbolized_key)
|
174
190
|
end
|
175
|
-
|
176
|
-
# Convert key to mixedCase from mixed_case
|
177
|
-
def convert_to_mixed_case(key, force_camel: false)
|
178
|
-
key.to_s.
|
179
|
-
split('_').
|
180
|
-
each_with_index.map do |str, index|
|
181
|
-
if index > 0 || force_camel
|
182
|
-
str.capitalize
|
183
|
-
else
|
184
|
-
str
|
185
|
-
end
|
186
|
-
end.
|
187
|
-
join
|
188
|
-
end
|
189
|
-
|
190
|
-
# Convert key to :snake_case from snakeCase
|
191
|
-
def convert_to_snake_case_symbol(key)
|
192
|
-
key.to_s.gsub(/::/, '/').
|
193
|
-
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
194
|
-
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
195
|
-
tr("-", "_").
|
196
|
-
downcase.
|
197
|
-
to_sym
|
198
|
-
end
|
199
|
-
|
200
|
-
def convert_to_lower_case(key)
|
201
|
-
key.to_s.gsub('_', '')
|
202
|
-
end
|
203
191
|
end
|
204
192
|
end
|
@@ -1,16 +1,8 @@
|
|
1
1
|
module Ably::Modules
|
2
2
|
module Conversions
|
3
|
-
|
4
|
-
# Returns object as {IdiomaticRubyWrapper}
|
5
|
-
def IdiomaticRubyWrapper(object, options = {})
|
6
|
-
case object
|
7
|
-
when Ably::Models::IdiomaticRubyWrapper
|
8
|
-
object
|
9
|
-
else
|
10
|
-
Ably::Models::IdiomaticRubyWrapper.new(object, options)
|
11
|
-
end
|
12
|
-
end
|
3
|
+
extend self
|
13
4
|
|
5
|
+
private
|
14
6
|
def as_since_epoch(time, granularity: :ms)
|
15
7
|
case time
|
16
8
|
when Time
|
@@ -43,5 +35,34 @@ module Ably::Modules
|
|
43
35
|
raise ArgumentError, "invalid granularity"
|
44
36
|
end
|
45
37
|
end
|
38
|
+
|
39
|
+
# Convert key to mixedCase from mixed_case
|
40
|
+
def convert_to_mixed_case(key, force_camel: false)
|
41
|
+
key.to_s.
|
42
|
+
split('_').
|
43
|
+
each_with_index.map do |str, index|
|
44
|
+
if index > 0 || force_camel
|
45
|
+
str.capitalize
|
46
|
+
else
|
47
|
+
str
|
48
|
+
end
|
49
|
+
end.
|
50
|
+
join
|
51
|
+
end
|
52
|
+
|
53
|
+
# Convert key to :snake_case from snakeCase
|
54
|
+
def convert_to_snake_case_symbol(key)
|
55
|
+
key.to_s.gsub(/::/, '/').
|
56
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
57
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
58
|
+
gsub(/([a-zA-Z])(\d)/,'\1_\2').
|
59
|
+
tr("-", "_").
|
60
|
+
downcase.
|
61
|
+
to_sym
|
62
|
+
end
|
63
|
+
|
64
|
+
def convert_to_lower_case(key)
|
65
|
+
key.to_s.gsub('_', '')
|
66
|
+
end
|
46
67
|
end
|
47
68
|
end
|
@@ -0,0 +1,201 @@
|
|
1
|
+
module Ably::Modules
|
2
|
+
# Enum brings Enum like functionality used in other languages to Ruby
|
3
|
+
#
|
4
|
+
# @example
|
5
|
+
# class House
|
6
|
+
# extend Ably::Moduels::Enum
|
7
|
+
# CONSTRUCTION = ruby_enum('CONSTRUCTION',
|
8
|
+
# :brick,
|
9
|
+
# :steel,
|
10
|
+
# :wood
|
11
|
+
# )
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# House::CONSTRUCTION(:brick).to_i # => 0
|
15
|
+
# House::CONSTRUCTION('Wood').to_i # => 2
|
16
|
+
# House::CONSTRUCTION.Wood == :wood # => true
|
17
|
+
#
|
18
|
+
module Enum
|
19
|
+
private
|
20
|
+
|
21
|
+
class Base; end
|
22
|
+
|
23
|
+
# ruby_enum returns an Enum-like class that should be assigned to a constant in your class
|
24
|
+
# The first `enum_name` argument must match the constant name so that the coercion method is available
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# class House
|
28
|
+
# extend Ably::Moduels::Enum
|
29
|
+
# CONSTRUCTION = ruby_enum('CONSTRUCTION', :brick)
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # ensures the following coercion method is available
|
33
|
+
# House::CONSTRUCTION(:brick) # => CONSTRUCTION.Brick
|
34
|
+
#
|
35
|
+
def ruby_enum(enum_name, *values)
|
36
|
+
enum_class = Class.new(Enum::Base) do
|
37
|
+
include Conversions
|
38
|
+
extend Conversions
|
39
|
+
|
40
|
+
@enum_name = enum_name
|
41
|
+
@by_index = {}
|
42
|
+
@by_symbol = {}
|
43
|
+
|
44
|
+
class << self
|
45
|
+
include Enumerable
|
46
|
+
|
47
|
+
def get(identifier)
|
48
|
+
case identifier
|
49
|
+
when Symbol
|
50
|
+
by_symbol.fetch(identifier)
|
51
|
+
when String
|
52
|
+
by_symbol.fetch(convert_to_snake_case_symbol(identifier))
|
53
|
+
when Numeric
|
54
|
+
by_index.fetch(identifier)
|
55
|
+
when ancestors.first
|
56
|
+
identifier
|
57
|
+
else
|
58
|
+
if identifier.class.ancestors.include?(Enum::Base)
|
59
|
+
by_symbol.fetch(identifier.to_sym)
|
60
|
+
else
|
61
|
+
raise KeyError, "Cannot find Enum matching identifier '#{identifier}' argument as it is an unacceptable type: #{identifier.class}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def [](*args)
|
67
|
+
get(*args)
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_s
|
71
|
+
name
|
72
|
+
end
|
73
|
+
|
74
|
+
def size
|
75
|
+
by_symbol.keys.length
|
76
|
+
end
|
77
|
+
alias_method :length, :size
|
78
|
+
|
79
|
+
# Method ensuring this {Enum} is {http://ruby-doc.org/core-2.1.3/Enumerable.html Enumerable}
|
80
|
+
def each(&block)
|
81
|
+
by_symbol.each do |key, value|
|
82
|
+
yield value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# The name provided in the constructor for this Enum
|
87
|
+
def name
|
88
|
+
@enum_name
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
attr_reader :by_index, :by_symbol
|
93
|
+
|
94
|
+
# Define constants for each of the Enum values
|
95
|
+
# e.g. define_constants(:dog) creates Enum::Dog
|
96
|
+
def define_values(values)
|
97
|
+
raise RuntimeError, "#{name} Enum cannot be modified" if by_index.frozen?
|
98
|
+
|
99
|
+
# Allow another Enum to be used as a set of values
|
100
|
+
if values.length == 1 && klass = values.first
|
101
|
+
if klass.kind_of?(Class) && klass.ancestors.include?(Enum::Base)
|
102
|
+
values = values.first.map(&:to_sym)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
values.map do |value|
|
107
|
+
# Convert any key => index_value pairs into array pairs
|
108
|
+
Array(value)
|
109
|
+
end.flatten(1).each_with_index do |name, index|
|
110
|
+
name, index = name if name.kind_of?(Array) # name is key => index_value pair
|
111
|
+
raise ArgumentError, "Index value '#{index}' is invalid" unless index.kind_of?(Numeric)
|
112
|
+
|
113
|
+
camel_name = convert_to_mixed_case(name, force_camel: true)
|
114
|
+
name_symbol = convert_to_snake_case_symbol(name)
|
115
|
+
enum = new(camel_name, name_symbol, index.to_i)
|
116
|
+
|
117
|
+
by_index[index.to_i] = enum
|
118
|
+
by_symbol[name_symbol] = enum
|
119
|
+
|
120
|
+
define_singleton_method camel_name do
|
121
|
+
enum
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
by_index.freeze
|
126
|
+
by_symbol.freeze
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def initialize(name, symbol, index)
|
131
|
+
@name = name
|
132
|
+
@index = index
|
133
|
+
@symbol = symbol
|
134
|
+
end
|
135
|
+
|
136
|
+
def to_s
|
137
|
+
"#{self.class}.#{name}"
|
138
|
+
end
|
139
|
+
|
140
|
+
def to_i
|
141
|
+
index
|
142
|
+
end
|
143
|
+
|
144
|
+
def to_sym
|
145
|
+
symbol
|
146
|
+
end
|
147
|
+
|
148
|
+
def to_json(*args)
|
149
|
+
%{"#{symbol}"}
|
150
|
+
end
|
151
|
+
|
152
|
+
# Allow comparison of Enum objects based on:
|
153
|
+
#
|
154
|
+
# * Other equivalent Enum objects
|
155
|
+
# * Symbol
|
156
|
+
# * String
|
157
|
+
# * Integer index of Enum
|
158
|
+
#
|
159
|
+
def ==(other)
|
160
|
+
case other
|
161
|
+
when Symbol
|
162
|
+
self.to_sym == convert_to_snake_case_symbol(other)
|
163
|
+
when String
|
164
|
+
self.to_sym == convert_to_snake_case_symbol(other)
|
165
|
+
when Numeric
|
166
|
+
self.to_i == other.to_i
|
167
|
+
when self.class
|
168
|
+
self.to_i == other.to_i
|
169
|
+
else
|
170
|
+
false
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
private
|
175
|
+
attr_reader :name, :index, :symbol
|
176
|
+
|
177
|
+
define_values values
|
178
|
+
end
|
179
|
+
|
180
|
+
# Convert any comparable object into this Enum
|
181
|
+
# @example
|
182
|
+
# class Example
|
183
|
+
# DOGS = ruby_enum('DOGS', :terrier, :labrador, :great_dane)
|
184
|
+
# end
|
185
|
+
#
|
186
|
+
# Example.DOGS(:great_dane) # => <DOGS.GreatDane>
|
187
|
+
# Example.DOGS(0) # => <DOGS.Terrier>
|
188
|
+
# Example.new.DOGS(0) # => <DOGS.Terrier>
|
189
|
+
#
|
190
|
+
define_singleton_method enum_name do |val|
|
191
|
+
enum_class.get(val)
|
192
|
+
end
|
193
|
+
|
194
|
+
define_method enum_name do |val|
|
195
|
+
enum_class.get(val)
|
196
|
+
end
|
197
|
+
|
198
|
+
enum_class
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Ably
|
2
|
+
module Modules
|
3
|
+
# EventEmitter provides methods to attach to public events and trigger events on any class instance
|
4
|
+
#
|
5
|
+
# EventEmitter are typically used for public interfaces, and as such, may be overriden in
|
6
|
+
# the classes to enforce `event` names match expected values.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# class Example
|
10
|
+
# include Modules::EventEmitter
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# event_emitter = Example.new
|
14
|
+
# event_emitter.on(:signal) { |name| puts "Signal #{name} received" }
|
15
|
+
# event_emitter.trigger :signal, "Test"
|
16
|
+
# #=> "Signal Test received"
|
17
|
+
#
|
18
|
+
module EventEmitter
|
19
|
+
module ClassMethods
|
20
|
+
attr_reader :event_emitter_coerce_proc
|
21
|
+
|
22
|
+
# Configure included EventEmitter
|
23
|
+
#
|
24
|
+
# @param [Hash] options the options for the {EventEmitter}
|
25
|
+
# @option options [Proc] :coerce_into A lambda/Proc that is used to coerce the event names for all events. This is useful to ensure the event names conform to a naming or type convention.
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# configure_event_emitter coerce_into: Proc.new { |event| event.to_sym }
|
29
|
+
#
|
30
|
+
def configure_event_emitter(options = {})
|
31
|
+
@event_emitter_coerce_proc = options[:coerce_into]
|
32
|
+
end
|
33
|
+
|
34
|
+
# Ensure @event_emitter_coerce_proc option is passed down to any classes that inherit the class with callbacks
|
35
|
+
def inherited(subclass)
|
36
|
+
subclass.instance_variable_set('@event_emitter_coerce_proc', @event_emitter_coerce_proc)
|
37
|
+
super
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# On receiving an event matching the event_name, call the provided block
|
42
|
+
def on(event_name, &block)
|
43
|
+
callbacks[callbacks_event_coerced(event_name)] << block
|
44
|
+
end
|
45
|
+
|
46
|
+
# Trigger an event with event_name that will in turn call all matching callbacks setup with `on`
|
47
|
+
def trigger(event_name, *args)
|
48
|
+
callbacks[callbacks_event_coerced(event_name)].each { |cb| cb.call(*args) }
|
49
|
+
end
|
50
|
+
|
51
|
+
# Remove all callbacks for event_name.
|
52
|
+
#
|
53
|
+
# If a block is provided, only callbacks matching that block signature will be removed.
|
54
|
+
# If block is not provided, all callbacks matching the event_name will be removed.
|
55
|
+
def off(event_name, &block)
|
56
|
+
if block_given?
|
57
|
+
callbacks[callbacks_event_coerced(event_name)].delete(block)
|
58
|
+
else
|
59
|
+
callbacks[callbacks_event_coerced(event_name)].clear
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def self.included(klass)
|
65
|
+
klass.extend ClassMethods
|
66
|
+
end
|
67
|
+
|
68
|
+
def callbacks
|
69
|
+
@callbacks ||= Hash.new { |hash, key| hash[key] = [] }
|
70
|
+
end
|
71
|
+
|
72
|
+
def callbacks_event_coerced(event_name)
|
73
|
+
if self.class.event_emitter_coerce_proc
|
74
|
+
self.class.event_emitter_coerce_proc.call(event_name)
|
75
|
+
else
|
76
|
+
event_name
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|