| 1 | #!/usr/bin/ruby |
|---|
| 2 | # |
|---|
| 3 | # This file contains the FaerieMUD::Entity class, a derivative of |
|---|
| 4 | # FaerieMUD::GameObject. It is the base class for all objects which are capable |
|---|
| 5 | # of interaction with other objects via events. |
|---|
| 6 | # |
|---|
| 7 | # It provides event-handling behavior via the #handle_event method, which |
|---|
| 8 | # delegates handling to a secondary method which is based on the class of the |
|---|
| 9 | # event being handled. The event being handled is queried for |
|---|
| 10 | # appropriately-named handlers via its #handler_names method, and the first one |
|---|
| 11 | # which is implemented by the entity recieving the event is called with the |
|---|
| 12 | # event object as its argument. If no specific handlers are implemented, the |
|---|
| 13 | # #handle_any_event method is used instead, which by default raises a |
|---|
| 14 | # #FaerieMUD::UnhandledEventError. |
|---|
| 15 | # |
|---|
| 16 | # == Synopsis |
|---|
| 17 | # |
|---|
| 18 | # |
|---|
| 19 | # |
|---|
| 20 | # == To Do |
|---|
| 21 | # * Perhaps make the event handlers hash into something that dispatches |
|---|
| 22 | # intelligently to superclasses, ala MUES::Event::Handler. |
|---|
| 23 | # |
|---|
| 24 | # == Subversion ID |
|---|
| 25 | # |
|---|
| 26 | # $Id$ |
|---|
| 27 | # |
|---|
| 28 | # == Authors |
|---|
| 29 | # |
|---|
| 30 | # * Michael Granger <ged@FaerieMUD.org> |
|---|
| 31 | # |
|---|
| 32 | # :include: LICENSE |
|---|
| 33 | # |
|---|
| 34 | #--- |
|---|
| 35 | # |
|---|
| 36 | # Please see the file LICENSE for licensing details. |
|---|
| 37 | # |
|---|
| 38 | |
|---|
| 39 | |
|---|
| 40 | # 11. Tools |
|---|
| 41 | # Thirty spokes meet at a nave; |
|---|
| 42 | # Because of the hole we may use the wheel. |
|---|
| 43 | # Clay is moulded into a vessel; |
|---|
| 44 | # Because of the hollow we may use the cup. |
|---|
| 45 | # Walls are built around a hearth; |
|---|
| 46 | # Because of the doors we may use the house. |
|---|
| 47 | # Thus tools come from what exists, |
|---|
| 48 | # But use from what does not. |
|---|
| 49 | # |
|---|
| 50 | # - Lao Tse, _Tao te Ching_ |
|---|
| 51 | # translated by Peter Merel |
|---|
| 52 | |
|---|
| 53 | require 'fm/gameobject' |
|---|
| 54 | require 'fm/mixins' |
|---|
| 55 | require 'fm/exceptions' |
|---|
| 56 | require 'fm/event' |
|---|
| 57 | require 'fm/linguistics' |
|---|
| 58 | |
|---|
| 59 | |
|---|
| 60 | ### The base class for all objects which are capable of interaction with |
|---|
| 61 | ### other objects via events. |
|---|
| 62 | class FaerieMUD::Entity < FaerieMUD::GameObject |
|---|
| 63 | contributors :ged |
|---|
| 64 | include FaerieMUD::Hookable, |
|---|
| 65 | FaerieMUD::Debuggable |
|---|
| 66 | |
|---|
| 67 | # SVN Revision |
|---|
| 68 | SVNRev = %q$Rev$ |
|---|
| 69 | |
|---|
| 70 | # SVN Id |
|---|
| 71 | SVNId = %q$Id$ |
|---|
| 72 | |
|---|
| 73 | |
|---|
| 74 | ############################################################# |
|---|
| 75 | ### I N S T A N C E M E T H O D S |
|---|
| 76 | ############################################################# |
|---|
| 77 | |
|---|
| 78 | ### Initialize the entity's instance variables |
|---|
| 79 | def initialize( args={} ) # :notnew: |
|---|
| 80 | @event_handler_cache = {} |
|---|
| 81 | super |
|---|
| 82 | end |
|---|
| 83 | |
|---|
| 84 | |
|---|
| 85 | ###### |
|---|
| 86 | public |
|---|
| 87 | ###### |
|---|
| 88 | |
|---|
| 89 | # The event-handler lookup cache (a Hash of method objects keyed by |
|---|
| 90 | # event class). |
|---|
| 91 | attr_reader :event_handler_cache |
|---|
| 92 | |
|---|
| 93 | |
|---|
| 94 | ### Multi-event dispatch method. Calls #handle_event for each of the given |
|---|
| 95 | ### +events+. |
|---|
| 96 | def handle_events( *events ) |
|---|
| 97 | events.collect {|e| self.handle_event(e) }.flatten |
|---|
| 98 | end |
|---|
| 99 | |
|---|
| 100 | |
|---|
| 101 | ### Event dispatcher method. This method does dynamic dispatch to |
|---|
| 102 | ### class-specific event handler methods. For each +event+ given, it will |
|---|
| 103 | ### look for a method called <tt>handle<em>eventClass</em>>()</tt>, where |
|---|
| 104 | ### <tt><em>eventClass</em></tt> is the class of the event to handle. If no |
|---|
| 105 | ### explicit handler is found, each of the event's superclasses is tried as |
|---|
| 106 | ### well. If no explicit handler is defined, it tries to call a fallback |
|---|
| 107 | ### handler <tt>handle_any_event()</tt>. If no handler is found, a |
|---|
| 108 | ### FaerieMUD::UnhandledEventError is raised. |
|---|
| 109 | def handle_event( event ) |
|---|
| 110 | debug_msg( 1, "Handling a #{event.class.name} event." ) |
|---|
| 111 | handler = self.get_handler_for_event( event ) |
|---|
| 112 | result = handler.call( event ) |
|---|
| 113 | |
|---|
| 114 | return Array( result ) |
|---|
| 115 | end |
|---|
| 116 | |
|---|
| 117 | |
|---|
| 118 | ### Callback invoked whenever a singleton method is added to the |
|---|
| 119 | ### receiver. Clears the event handler lookup cache if the method being |
|---|
| 120 | ### added contains the word 'handle'. Also aliased to |
|---|
| 121 | ### #singleton_method_removed and #singleton_method_undefined. |
|---|
| 122 | def singleton_method_added( sym ) |
|---|
| 123 | debug_msg( 3, "Flushing event handler lookup cache" ) |
|---|
| 124 | @event_handler_cache.clear if /handler/ =~ sym.to_s |
|---|
| 125 | super |
|---|
| 126 | end |
|---|
| 127 | alias_method :singleton_method_removed, :singleton_method_added |
|---|
| 128 | alias_method :singleton_method_undefined, :singleton_method_added |
|---|
| 129 | |
|---|
| 130 | |
|---|
| 131 | # |
|---|
| 132 | # Linguistics support |
|---|
| 133 | # |
|---|
| 134 | |
|---|
| 135 | ### Returns +true+ if the entity should be considered plural for the |
|---|
| 136 | ### purposes of generating english text. |
|---|
| 137 | def plural? |
|---|
| 138 | return false |
|---|
| 139 | end |
|---|
| 140 | |
|---|
| 141 | |
|---|
| 142 | |
|---|
| 143 | ######### |
|---|
| 144 | protected |
|---|
| 145 | ######### |
|---|
| 146 | |
|---|
| 147 | ### Fallback event handler. The default implementation of this method |
|---|
| 148 | ### just raises an UnhandledEventError, so deriviatives will probably |
|---|
| 149 | ### want to override it. |
|---|
| 150 | def handle_any_event( event ) |
|---|
| 151 | raise FaerieMUD::UnhandledEventError, |
|---|
| 152 | "No handler defined for #{event.class.name}s" |
|---|
| 153 | end |
|---|
| 154 | |
|---|
| 155 | |
|---|
| 156 | ### Get a handler as a callable object for the given +event+. |
|---|
| 157 | def get_handler_for_event( event ) |
|---|
| 158 | handler = @event_handler_cache[event.class] |
|---|
| 159 | |
|---|
| 160 | # Search the event's class heirarchy for Event subclasses, and |
|---|
| 161 | # look up handler methods based on the class name |
|---|
| 162 | if handler.nil? |
|---|
| 163 | event.handler_names.each do |methodName| |
|---|
| 164 | debug_msg( 2, "Checking for a #{methodName} method..." ) |
|---|
| 165 | if self.respond_to?( methodName ) |
|---|
| 166 | debug_msg( 2, " found #{methodName}." ) |
|---|
| 167 | handler = self.method( methodName ) |
|---|
| 168 | break |
|---|
| 169 | end |
|---|
| 170 | end |
|---|
| 171 | |
|---|
| 172 | # If no handler has yet been found, try to fetch the fallback |
|---|
| 173 | # handler. |
|---|
| 174 | if handler.nil? |
|---|
| 175 | if self.respond_to?( :handle_any_event ) |
|---|
| 176 | handler = self.method( :handle_any_event ) |
|---|
| 177 | else |
|---|
| 178 | raise UnhandledEventError, |
|---|
| 179 | "No handler defined for #{event.class.name}s" |
|---|
| 180 | end |
|---|
| 181 | end |
|---|
| 182 | |
|---|
| 183 | @event_handler_cache[ event.class ] = handler |
|---|
| 184 | end |
|---|
| 185 | |
|---|
| 186 | return handler |
|---|
| 187 | end |
|---|
| 188 | |
|---|
| 189 | |
|---|
| 190 | end # class FaerieMUD::Entity |
|---|