Module: Eventable

Defined in:
lib/eventable/eventable.rb,
lib/eventable/errors.rb,
lib/eventable/version.rb

Overview

Incredibly simple framework for adding events

Defined Under Namespace

Modules: Errors, EventableEventMethods

Constant Summary collapse

VERSION =
"0.2.1"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#callbacksObject (readonly)

Allows for dynamic discovery of hooked callbacks



26
27
28
# File 'lib/eventable/eventable.rb', line 26

def callbacks
  @callbacks
end

Class Method Details

.included(base) ⇒ Object

Add the #event method to the extending class not instances of that class



29
30
31
# File 'lib/eventable/eventable.rb', line 29

def self.included(base)
  base.extend(EventableEventMethods)
end

Instance Method Details

#eventsObject



21
22
23
# File 'lib/eventable/eventable.rb', line 21

def events
  self.class.events
end

#fire_event(event, *return_value, &block) ⇒ Object

When the event happens the class where it happens runs this



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/eventable/eventable.rb', line 39

def fire_event(event, *return_value, &block)
  check_mutex
  @eventable_mutex.synchronize {

    return false unless @callbacks && @callbacks[event] && !@callbacks[event].empty?

    @callbacks[event].each do |listener_id, callbacks|
      begin
        listener = ObjectSpace._id2ref(listener_id)
        callbacks.each do |callback|
          Thread.new {listener.send callback, *return_value, &block}
        end
      rescue RangeError => re
        # Don't bubble up a missing recycled object, I don't care if it's not there, I just won't call it
        raise re unless re.message.match(/is recycled object/)
      end
    end
  }
  true
end

#initialize(*args) ⇒ Object



33
34
35
36
# File 'lib/eventable/eventable.rb', line 33

def initialize(*args)
  super
  @eventable_mutex = Mutex.new
end

#register_for_event(args) ⇒ Object

Allows an object to listen for an event and have a callback run when the event happens



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/eventable/eventable.rb', line 61

def register_for_event(args)
  [:event, :listener, :callback].each do |parameter|
    raise ArgumentError, "Missing parameter :#{parameter}" unless args[parameter]
  end

  # Make access to the callback array threadsafe
  check_mutex
  @eventable_mutex.synchronize {
    event = args[:event]
    raise Errors::UnknownEvent unless events.include? event

    @callbacks ||= {}
    @callbacks[event] ||= {}

    listener    = args[:listener]
    listener_id = listener.object_id
    callback    = args[:callback]

    # save the callback info without creating a reference to the object
    @callbacks[event][listener_id] ||= []
    @callbacks[event][listener_id] << callback

    # will remove the object from the callback list if it is destroyed
    ObjectSpace.define_finalizer(
      listener, 
      unregister_finalizer(event, listener_id, callback)
    )
  }
end

#unregister_for_event(args) ⇒ Object

Allows objects to stop listening to events



92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/eventable/eventable.rb', line 92

def unregister_for_event(args)
  check_mutex
  @eventable_mutex.synchronize {
    event = args[:event]
    return unless @callbacks && @callbacks[event]

    listener_id = args[:listener_id] || args[:listener].object_id
    callback    = args[:callback]
    @callbacks[event].delete_if do |listener, callbacks|
      callbacks.delete(callback) if listener == listener_id
      callbacks.empty?
    end
  }
end