hallon 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.yardopts +1 -0
- data/{CHANGELOG → CHANGELOG.md} +82 -27
- data/Gemfile +1 -0
- data/README.markdown +1 -1
- data/Rakefile +8 -6
- data/examples/adding_tracks_to_playlist.rb +3 -0
- data/examples/logging_in.rb +3 -0
- data/examples/playing_audio.rb +130 -0
- data/examples/printing_link_information.rb +3 -0
- data/examples/show_published_playlists_of_user.rb +5 -13
- data/hallon.gemspec +2 -2
- data/lib/hallon.rb +15 -0
- data/lib/hallon/album.rb +1 -1
- data/lib/hallon/album_browse.rb +4 -3
- data/lib/hallon/artist.rb +1 -1
- data/lib/hallon/artist_browse.rb +5 -4
- data/lib/hallon/base.rb +7 -2
- data/lib/hallon/error.rb +1 -1
- data/lib/hallon/ext/spotify.rb +26 -42
- data/lib/hallon/image.rb +7 -8
- data/lib/hallon/observable.rb +134 -62
- data/lib/hallon/observable/album_browse.rb +30 -0
- data/lib/hallon/observable/artist_browse.rb +31 -0
- data/lib/hallon/observable/image.rb +31 -0
- data/lib/hallon/observable/player.rb +13 -0
- data/lib/hallon/observable/playlist.rb +194 -0
- data/lib/hallon/observable/playlist_container.rb +74 -0
- data/lib/hallon/observable/post.rb +30 -0
- data/lib/hallon/observable/search.rb +29 -0
- data/lib/hallon/observable/session.rb +236 -0
- data/lib/hallon/observable/toplist.rb +30 -0
- data/lib/hallon/player.rb +8 -17
- data/lib/hallon/playlist.rb +11 -7
- data/lib/hallon/playlist_container.rb +11 -4
- data/lib/hallon/queue.rb +71 -0
- data/lib/hallon/search.rb +10 -7
- data/lib/hallon/session.rb +18 -21
- data/lib/hallon/toplist.rb +4 -3
- data/lib/hallon/user.rb +5 -5
- data/lib/hallon/version.rb +1 -1
- data/spec/hallon/album_browse_spec.rb +4 -0
- data/spec/hallon/artist_browse_spec.rb +4 -0
- data/spec/hallon/base_spec.rb +26 -9
- data/spec/hallon/hallon_spec.rb +0 -18
- data/spec/hallon/image_spec.rb +0 -1
- data/spec/hallon/link_spec.rb +14 -0
- data/spec/hallon/observable/album_browse_spec.rb +7 -0
- data/spec/hallon/observable/artist_browse_spec.rb +7 -0
- data/spec/hallon/observable/image_spec.rb +8 -0
- data/spec/hallon/observable/playlist_container_spec.rb +21 -0
- data/spec/hallon/observable/playlist_spec.rb +85 -0
- data/spec/hallon/observable/post_spec.rb +8 -0
- data/spec/hallon/observable/search_spec.rb +7 -0
- data/spec/hallon/observable/session_spec.rb +143 -0
- data/spec/hallon/observable/toplist_spec.rb +7 -0
- data/spec/hallon/observable_spec.rb +134 -65
- data/spec/hallon/playlist_container_spec.rb +24 -18
- data/spec/hallon/playlist_spec.rb +2 -0
- data/spec/hallon/queue_spec.rb +35 -0
- data/spec/hallon/session_spec.rb +4 -4
- data/spec/hallon/spotify_spec.rb +35 -9
- data/spec/mockspotify.rb +2 -3
- data/spec/spec_helper.rb +0 -1
- data/spec/support/common_objects.rb +27 -15
- data/spec/support/enumerable_comparison.rb +9 -0
- data/spec/support/shared_for_callbacks.rb +60 -0
- data/spec/support/shared_for_linkable_objects.rb +1 -1
- metadata +56 -20
data/hallon.gemspec
CHANGED
@@ -21,8 +21,8 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.platform = Gem::Platform::RUBY
|
22
22
|
gem.required_ruby_version = '~> 1.8'
|
23
23
|
|
24
|
-
gem.add_dependency '
|
25
|
-
gem.
|
24
|
+
gem.add_dependency 'ref', '~> 1.0'
|
25
|
+
gem.add_dependency 'spotify', '~> 10.3.0'
|
26
26
|
gem.add_development_dependency 'rake', '~> 0.8'
|
27
27
|
gem.add_development_dependency 'rspec', '~> 2'
|
28
28
|
gem.add_development_dependency 'yard'
|
data/lib/hallon.rb
CHANGED
@@ -9,8 +9,20 @@ require 'hallon/linkable'
|
|
9
9
|
require 'hallon/version'
|
10
10
|
require 'hallon/error'
|
11
11
|
require 'hallon/base'
|
12
|
+
require 'hallon/queue'
|
12
13
|
require 'hallon/enumerator'
|
13
14
|
|
15
|
+
require 'hallon/observable/album_browse'
|
16
|
+
require 'hallon/observable/artist_browse'
|
17
|
+
require 'hallon/observable/image'
|
18
|
+
require 'hallon/observable/playlist_container'
|
19
|
+
require 'hallon/observable/playlist'
|
20
|
+
require 'hallon/observable/post'
|
21
|
+
require 'hallon/observable/session'
|
22
|
+
require 'hallon/observable/search'
|
23
|
+
require 'hallon/observable/toplist'
|
24
|
+
require 'hallon/observable/player'
|
25
|
+
|
14
26
|
require 'hallon/session'
|
15
27
|
require 'hallon/link'
|
16
28
|
require 'hallon/user'
|
@@ -26,6 +38,9 @@ require 'hallon/artist_browse'
|
|
26
38
|
require 'hallon/player'
|
27
39
|
require 'hallon/search'
|
28
40
|
|
41
|
+
# Why is this not the default in Ruby?
|
42
|
+
Thread.abort_on_exception = true
|
43
|
+
|
29
44
|
# The Hallon module wraps around all Hallon objects to avoid polluting
|
30
45
|
# the global namespace. To start using Hallon, you most likely want to
|
31
46
|
# be looking for the documentation on {Hallon::Session}.
|
data/lib/hallon/album.rb
CHANGED
data/lib/hallon/album_browse.rb
CHANGED
@@ -7,7 +7,7 @@ module Hallon
|
|
7
7
|
# @see Album
|
8
8
|
# @see http://developer.spotify.com/en/libspotify/docs/group__albumbrowse.html
|
9
9
|
class AlbumBrowse < Base
|
10
|
-
|
10
|
+
extend Observable::AlbumBrowse
|
11
11
|
|
12
12
|
# Creates an AlbumBrowse instance from an Album or an Album pointer.
|
13
13
|
#
|
@@ -22,8 +22,9 @@ module Hallon
|
|
22
22
|
raise TypeError, "expected album pointer, was given #{given}"
|
23
23
|
end
|
24
24
|
|
25
|
-
|
26
|
-
|
25
|
+
subscribe_for_callbacks do |callback|
|
26
|
+
@pointer = Spotify.albumbrowse_create!(session.pointer, pointer, callback, nil)
|
27
|
+
end
|
27
28
|
|
28
29
|
raise FFI::NullPointerError, "album browsing failed" if @pointer.null?
|
29
30
|
end
|
data/lib/hallon/artist.rb
CHANGED
@@ -49,7 +49,7 @@ module Hallon
|
|
49
49
|
def portrait(as_image = true)
|
50
50
|
if as_image
|
51
51
|
portrait = Spotify.artist_portrait(pointer)
|
52
|
-
Image.
|
52
|
+
Image.from(portrait)
|
53
53
|
else
|
54
54
|
portrait = Spotify.link_create_from_artist_portrait!(pointer)
|
55
55
|
Link.from(portrait)
|
data/lib/hallon/artist_browse.rb
CHANGED
@@ -5,7 +5,7 @@ module Hallon
|
|
5
5
|
# @see Artist
|
6
6
|
# @see http://developer.spotify.com/en/libspotify/docs/group__artistbrowse.html
|
7
7
|
class ArtistBrowse < Base
|
8
|
-
|
8
|
+
extend Observable::ArtistBrowse
|
9
9
|
|
10
10
|
# @return [Array<Symbol>] artist browsing types for use in {#initialize}
|
11
11
|
def self.types
|
@@ -26,8 +26,9 @@ module Hallon
|
|
26
26
|
raise TypeError, "expected artist pointer, was given #{given}"
|
27
27
|
end
|
28
28
|
|
29
|
-
|
30
|
-
|
29
|
+
subscribe_for_callbacks do |callback|
|
30
|
+
@pointer = Spotify.artistbrowse_create!(session.pointer, pointer, type, callback, nil)
|
31
|
+
end
|
31
32
|
|
32
33
|
raise FFI::NullPointerError, "artist browsing failed" if @pointer.null?
|
33
34
|
end
|
@@ -70,7 +71,7 @@ module Hallon
|
|
70
71
|
size = Spotify.artistbrowse_num_portraits(pointer)
|
71
72
|
Enumerator.new(size) do |i|
|
72
73
|
if as_image
|
73
|
-
id = Spotify.artistbrowse_portrait(pointer, i)
|
74
|
+
id = Spotify.artistbrowse_portrait(pointer, i)
|
74
75
|
Image.new(id)
|
75
76
|
else
|
76
77
|
link = Spotify.link_create_from_artistbrowse_portrait!(pointer, i)
|
data/lib/hallon/base.rb
CHANGED
@@ -3,10 +3,15 @@ module Hallon
|
|
3
3
|
# All objects in Hallon are mere representations of Spotify objects.
|
4
4
|
# Hallon::Base covers basic functionality shared by all of these.
|
5
5
|
class Base
|
6
|
-
# @param [#null?] pointer
|
6
|
+
# @param [#nil?, #null?] pointer
|
7
7
|
# @return [self, nil] a new instance of self, unless given pointer is #null?
|
8
8
|
def self.from(pointer, *args, &block)
|
9
|
-
|
9
|
+
is_nil = pointer.nil?
|
10
|
+
is_null = pointer.null? if pointer.respond_to?(:null?)
|
11
|
+
|
12
|
+
unless is_nil or is_null
|
13
|
+
new(pointer, *args, &block)
|
14
|
+
end
|
10
15
|
end
|
11
16
|
|
12
17
|
# Underlying FFI pointer.
|
data/lib/hallon/error.rb
CHANGED
@@ -49,7 +49,7 @@ module Hallon
|
|
49
49
|
# @param [Fixnum, Symbol] error
|
50
50
|
# @return [nil]
|
51
51
|
def maybe_raise(x)
|
52
|
-
return nil if [nil, :timeout].include?(x)
|
52
|
+
return nil if [nil, :timeout, :is_loading].include?(x)
|
53
53
|
|
54
54
|
error, symbol = disambiguate(x)
|
55
55
|
return symbol if symbol == :ok
|
data/lib/hallon/ext/spotify.rb
CHANGED
@@ -122,7 +122,7 @@ module Spotify
|
|
122
122
|
|
123
123
|
# @return [String] representation of the spotify pointer
|
124
124
|
def to_s
|
125
|
-
"<#{self.class} address
|
125
|
+
"<#{self.class} address=0x#{address.to_s(16)} type=#{type}>"
|
126
126
|
end
|
127
127
|
|
128
128
|
# Create a proc that will accept a pointer of a given type and
|
@@ -142,14 +142,8 @@ module Spotify
|
|
142
142
|
# @param [Object] pointer
|
143
143
|
# @param [Symbol] type (optional, no type checking is done if not given)
|
144
144
|
# @return [Boolean] true if object is a spotify pointer and of correct type
|
145
|
-
def self.typechecks?(object, type
|
146
|
-
if
|
147
|
-
false
|
148
|
-
elsif type
|
149
|
-
object.type == type.to_s
|
150
|
-
else
|
151
|
-
true
|
152
|
-
end
|
145
|
+
def self.typechecks?(object, type)
|
146
|
+
!! (object.type == type.to_s) if object.is_a?(Spotify::Pointer)
|
153
147
|
end
|
154
148
|
end
|
155
149
|
|
@@ -200,51 +194,41 @@ module Spotify
|
|
200
194
|
end
|
201
195
|
|
202
196
|
# Makes it easier binding callbacks safely to callback structs.
|
203
|
-
#
|
197
|
+
#
|
198
|
+
# @see add
|
199
|
+
# @see remove
|
204
200
|
module CallbackStruct
|
205
|
-
#
|
206
|
-
#
|
207
|
-
# storage does not get garbage collected as long as these callbacks
|
208
|
-
# are needed!**
|
201
|
+
# Before assigning [member]=(callback), inspect the arity of
|
202
|
+
# said callback and raise an ArgumentError if they don‘t match.
|
209
203
|
#
|
210
|
-
# @
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
end
|
204
|
+
# @raise ArgumentError if the arity of the given callback does not match the member
|
205
|
+
def []=(member, callback)
|
206
|
+
unless callback.arity < 0 or callback.arity == arity_of(member)
|
207
|
+
raise ArgumentError, "#{member} callback takes #{arity_of(member)} arguments, was #{callback.arity}"
|
208
|
+
else
|
209
|
+
super
|
217
210
|
end
|
218
211
|
end
|
219
|
-
end
|
220
212
|
|
221
|
-
|
222
|
-
include CallbackStruct
|
213
|
+
protected
|
223
214
|
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
215
|
+
# @param [Symbol] member
|
216
|
+
# @return [Integer] arity of the given callback member
|
217
|
+
def arity_of(member)
|
218
|
+
fn = layout[member].type
|
219
|
+
fn.param_types.size
|
220
|
+
end
|
229
221
|
end
|
230
222
|
|
231
|
-
|
223
|
+
SessionCallbacks.instance_eval do
|
232
224
|
include CallbackStruct
|
233
|
-
|
234
|
-
private
|
235
|
-
# @see CallbackStruct
|
236
|
-
def proc_for(target, member)
|
237
|
-
lambda { |pointer, *args, userdata| target.trigger(member, *args) }
|
238
|
-
end
|
239
225
|
end
|
240
226
|
|
241
|
-
|
227
|
+
PlaylistCallbacks.instance_eval do
|
242
228
|
include CallbackStruct
|
229
|
+
end
|
243
230
|
|
244
|
-
|
245
|
-
|
246
|
-
def proc_for(target, member)
|
247
|
-
lambda { |pointer, *args, userdata| target.trigger(member, *args) }
|
248
|
-
end
|
231
|
+
PlaylistContainerCallbacks.instance_eval do
|
232
|
+
include CallbackStruct
|
249
233
|
end
|
250
234
|
end
|
data/lib/hallon/image.rb
CHANGED
@@ -12,8 +12,7 @@ module Hallon
|
|
12
12
|
|
13
13
|
to_link :from_image
|
14
14
|
|
15
|
-
|
16
|
-
include Observable
|
15
|
+
extend Observable::Image
|
17
16
|
|
18
17
|
# Create a new instance of an Image.
|
19
18
|
#
|
@@ -30,13 +29,13 @@ module Hallon
|
|
30
29
|
end
|
31
30
|
|
32
31
|
@pointer = to_pointer(link, :image) do
|
33
|
-
|
34
|
-
ptr.write_bytes(link)
|
35
|
-
Spotify.image_create!(session.pointer, ptr)
|
32
|
+
Spotify.image_create!(session.pointer, link)
|
36
33
|
end
|
37
34
|
|
38
|
-
|
39
|
-
|
35
|
+
subscribe_for_callbacks do |callbacks|
|
36
|
+
Spotify.image_remove_load_callback(pointer, callbacks, nil)
|
37
|
+
Spotify.image_add_load_callback(pointer, callbacks, nil)
|
38
|
+
end
|
40
39
|
end
|
41
40
|
|
42
41
|
# @return [Boolean] true if the image is loaded.
|
@@ -58,7 +57,7 @@ module Hallon
|
|
58
57
|
# @param [Boolean] raw true if you want the image id as a hexadecimal string
|
59
58
|
# @return [String] image ID as a string.
|
60
59
|
def id(raw = false)
|
61
|
-
id = Spotify.image_image_id(pointer)
|
60
|
+
id = Spotify.image_image_id(pointer)
|
62
61
|
raw ? id : to_hex(id)
|
63
62
|
end
|
64
63
|
|
data/lib/hallon/observable.rb
CHANGED
@@ -1,87 +1,159 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
require 'ref'
|
3
|
+
|
2
4
|
module Hallon
|
3
5
|
# A module providing event capabilities to Hallon objects.
|
4
6
|
#
|
5
7
|
# @private
|
6
8
|
module Observable
|
9
|
+
# This module is responsible for creating methods for registering
|
10
|
+
# callbacks. It expects a certain protocol to already available.
|
11
|
+
module ClassMethods
|
12
|
+
# When extended it’ll call #initialize_observable to set-up `other`.
|
13
|
+
def self.extended(other)
|
14
|
+
other.send(:initialize_observable)
|
15
|
+
end
|
16
|
+
|
17
|
+
# @return [Method, Struct] callbacks to attach to this object
|
18
|
+
attr_reader :callbacks
|
19
|
+
|
20
|
+
# Subscribe to callbacks for a given pointer.
|
21
|
+
#
|
22
|
+
# @param [Object] object
|
23
|
+
# @param [FFI::Pointer] pointer
|
24
|
+
def subscribe(object, pointer)
|
25
|
+
key = pointer.address
|
26
|
+
ref = Ref::WeakReference.new(object)
|
27
|
+
|
28
|
+
@lock.synchronize do
|
29
|
+
if @subscribers_rev[ref.referenced_object_id]
|
30
|
+
raise ArgumentError, "already subscribed to callbacks"
|
31
|
+
end
|
32
|
+
|
33
|
+
@subscribers[key] ||= {} # use a hash for fast reverse lookups
|
34
|
+
@subscribers[key][ref.referenced_object_id] = ref
|
35
|
+
@subscribers_rev[ref.referenced_object_id] = key
|
36
|
+
end
|
37
|
+
|
38
|
+
ObjectSpace.define_finalizer(object, @unsubscriber)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Retrieve all subscribers for a given pointer.
|
42
|
+
#
|
43
|
+
# @param [FFI::Pointer] pointer
|
44
|
+
def subscribers_for(pointer)
|
45
|
+
key = pointer.address
|
46
|
+
|
47
|
+
@lock.synchronize do
|
48
|
+
@subscribers.fetch(key, {}).values.map(&:object).compact
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
|
54
|
+
# Run when ClassMethods are extended.
|
55
|
+
#
|
56
|
+
# It sets up the callbacks and all book-keeping required to keep
|
57
|
+
# track of all subscribers properly.
|
58
|
+
def initialize_observable
|
59
|
+
@callbacks = initialize_callbacks
|
60
|
+
|
61
|
+
@lock = Ref::SafeMonitor.new
|
62
|
+
@subscribers = {}
|
63
|
+
@subscribers_rev = {}
|
64
|
+
@unsubscriber = proc do |object_id|
|
65
|
+
@lock.synchronize do
|
66
|
+
if key = @subscribers_rev.delete(object_id)
|
67
|
+
@subscribers[key].delete(object_id)
|
68
|
+
@subscribers.delete(key) if @subscribers[key].empty?
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param [#to_s] name
|
75
|
+
# @return [Method]
|
76
|
+
def callback_for(name)
|
77
|
+
method("#{name}_callback")
|
78
|
+
end
|
79
|
+
|
80
|
+
# Scans through the list of subscribers, trying to find any
|
81
|
+
# subscriber attached to this pointer. For each subscriber,
|
82
|
+
# trigger the appropriate event.
|
83
|
+
#
|
84
|
+
# @param [FFI::Pointer] pointer
|
85
|
+
# @param [Symbol] event
|
86
|
+
# @param […] arguments
|
87
|
+
# @return whatever the (last) handler returned
|
88
|
+
def trigger(pointer, event, *arguments)
|
89
|
+
subscribers_for(pointer).inject(nil) do |_, subscriber|
|
90
|
+
# trigger is protected, inconvenient but symbolic
|
91
|
+
subscriber.send(:trigger, event, *arguments)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Will extend `other` with {ClassMethods}.
|
97
|
+
def self.included(other)
|
98
|
+
other.extend(ClassMethods)
|
99
|
+
end
|
100
|
+
|
7
101
|
# Defines a handler for the given event.
|
8
102
|
#
|
9
|
-
# @
|
10
|
-
#
|
11
|
-
# puts message
|
12
|
-
# end
|
13
|
-
#
|
14
|
-
# trigger(:callback, "Moo!") # => prints "Moo!"
|
15
|
-
#
|
16
|
-
# @example multiple events with one handler
|
17
|
-
# on(:a, :b, :c) do |name, *args|
|
18
|
-
# puts "#{name} called with: #{args.inspect}"
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# trigger(:a) # => prints ":a called with: []"
|
22
|
-
# trigger(:b, :c) # => prints ":b called with: [:c]"
|
23
|
-
#
|
24
|
-
# @note when defining a handler for multiple events, the
|
25
|
-
# first argument passed to the handler is the name
|
26
|
-
# of the event that called it
|
27
|
-
# @param [#to_sym] event name of event to handle
|
103
|
+
# @param [#to_s] event name of event to handle
|
104
|
+
# @return [Proc] the given block
|
28
105
|
# @yield (*args) event handler block
|
29
|
-
def on(
|
106
|
+
def on(event, &block)
|
30
107
|
raise ArgumentError, "no block given" unless block
|
31
|
-
|
32
|
-
|
33
|
-
block = proc { |*args| yield(event, *args) } if wrap
|
34
|
-
__handlers[event] = [] unless __handlers.has_key?(event)
|
35
|
-
__handlers[event] << block
|
36
|
-
end
|
108
|
+
raise NameError, "no such callback: #{event}" unless has_callback?(event)
|
109
|
+
handlers[event.to_s] = block
|
37
110
|
end
|
38
111
|
|
39
|
-
#
|
40
|
-
#
|
41
|
-
|
42
|
-
|
43
|
-
def trigger(event, *params, &block)
|
44
|
-
catch :return do
|
45
|
-
return_value = nil
|
46
|
-
__handlers[event.to_sym].each do |handler|
|
47
|
-
return_value = handler.call(*params, &block)
|
48
|
-
end
|
49
|
-
return_value
|
50
|
-
end
|
112
|
+
# @param [#to_s] name
|
113
|
+
# @return [Boolean] true if a callback with `name` exists.
|
114
|
+
def has_callback?(name)
|
115
|
+
self.class.respond_to?("#{name}_callback", true)
|
51
116
|
end
|
52
117
|
|
53
|
-
# Run
|
54
|
-
#
|
55
|
-
# @example
|
56
|
-
# o = Object.new
|
57
|
-
# o.instance_eval { include Hallon::Base }
|
58
|
-
# o.on(:method) { "outside" }
|
118
|
+
# Run a given block, and once it exits restore all handlers
|
119
|
+
# to the way they were before running the block.
|
59
120
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# o.on(:method) { "inside" }
|
63
|
-
# puts o.on_method # => "inside"
|
64
|
-
# end
|
65
|
-
# puts o.on_method # => "outside"
|
121
|
+
# This allows you to temporarily use different handlers for
|
122
|
+
# some events.
|
66
123
|
#
|
67
124
|
# @yield
|
68
|
-
# @return whatever the given block returns
|
69
125
|
def protecting_handlers
|
70
|
-
|
71
|
-
__handlers.each do |k, v|
|
72
|
-
deep_copy[k] = v.dup
|
73
|
-
end
|
126
|
+
old_handlers = handlers.dup
|
74
127
|
yield
|
75
128
|
ensure
|
76
|
-
|
129
|
+
handlers.replace(old_handlers)
|
77
130
|
end
|
78
131
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
132
|
+
protected
|
133
|
+
|
134
|
+
# Register this object as interested in callbacks.
|
135
|
+
#
|
136
|
+
# @yield [callback]
|
137
|
+
# @yieldparam [Method, Struct] callback (always the same object)
|
138
|
+
# @return whatever the block returns
|
139
|
+
def subscribe_for_callbacks
|
140
|
+
yield(self.class.callbacks).tap do
|
141
|
+
self.class.subscribe(self, pointer)
|
85
142
|
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# @param [#to_s] name
|
146
|
+
# @param [...] arguments
|
147
|
+
# @return whatever the handler returns
|
148
|
+
def trigger(name, *arguments, &block)
|
149
|
+
if handler = handlers[name.to_s]
|
150
|
+
handler.call(*arguments, self, &block)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
# @return [Hash<String, Proc>]
|
155
|
+
def handlers
|
156
|
+
@__handlers ||= Hash.new(proc {})
|
157
|
+
end
|
86
158
|
end
|
87
159
|
end
|