module ObjectTeam class AbstractMethodCall < StandardError end class TeamException < StandardError end # Regexp to match weaved methods inside call stack Caller_RE = /^[^:]+:\d+:in\s`((call)|(call_role)|(call_method)|(method_missing)|(get_callin)|(send)|(#{Interceptor::PREFIX}.+))'$/ # This module implements delegating functionality. # Any expected method will be forwarded to handle_expected # and delegated to base. Therefore it is important, to call # set_base during initialization. module Role # The receiver of the message queue (delegate). attr_accessor :__baseproxy # the basecall-object. only valid during a replacement call. attr_accessor :__basecall # Indicates, if we have a valid basecall. def has_base? return (not @__basecall.nil?) end # Call base method of current executed replaced method. # If the current method is not an replaced method, this # will throw an exexption! def base(*parameter, &block) if (@__basecall.nil?) raise ObjectTeam::TeamException, "No replacement-method!\n base()-calls are only valid via a replacement callin", caller end return @__basecall.call_base(*parameter, &block) end # Get current associated connector. # The connector should be used only to suspend/resume the team. def get_connector() return @__baseproxy.connector end def myTeam() nestedstructure = self.class.name.split(/::/) nestedstructure.pop klass = Object nestedstructure.each { |nestlevel| klass = klass.const_get(nestlevel) } return klass end if (ROTConfig::WEAKREFERENCE) def __baseobject() @baseobject.ref end def __baseobject=(base) @baseobject = WeakReference.new(base) end else attr_accessor :__baseobject end end # This module enhances including classes to register/unregister # Callin-Bindings that will be handled as additional methods. module Compound # classname -> {methodname -> call-object} # Callins[Class] = ClassHash => ClassHash[String] = Call Callins=Hash.new # classname -> {roleclass -> [connectors]} # Roles[Class] = RoleHash => RoleHash[RoleClass] = Array of connectors Roles=Hash.new # Get a call object to the given methodname. # If no callin exists, take the real method, whose name is {Interceptor::PREFIX}#{classname}_method # [methodname] the methodname as string # [wrapmethod] indicates, if no callin is found, the specified method # should be wrapped by a MethodCallin object # [return] a Call-Object def get_callin(methodname, wrapmethod=false) #search for callins, starting by self until Object aclass = self acallin = nil #climb until object while (aclass!=Object and acallin.nil?) classcallins = Callins[aclass] if (!classcallins.nil? and !classcallins[methodname].nil?) acallin = classcallins[methodname] end aclass = aclass.superclass end #found no callin? - shall we generate a method wrapper if (acallin.nil? and wrapmethod) #climb until method is defined aclass = self while (aclass!=Object and acallin.nil?) if (self.method_defined?(Interceptor::PREFIX+aclass.name+"_"+methodname)) acallin = MethodCall.new(Interceptor::PREFIX+aclass.name+"_"+methodname) end aclass = aclass.superclass end raise(NoMethodError, "undefined method `#{methodname}' for #{self.inspect}", caller) if (acallin.nil?) end #could be nil return acallin end # Declare a given Call-object under a given name. # If a method with the given name is called, this call object is used. # [methodname] the method name as string # [callin] the call object def set_callin(methodname, callin) classcallins = Callins[self] #ensure validness, because of lazy init if (classcallins.nil?) classcallins = Hash.new Callins[self] = classcallins Callins.rehash end #set classcallins[methodname] = callin classcallins.rehash end # Deactivate all callins, that are active for the given connector. # Only the first level of callins must be searched, because # activation/deactivation happens in a ordered manner. def deactivate(connector) classcallins = Callins[self] if (!classcallins.nil?) classcallins.each { |name, callin| basecallin = callin.get_base_of(connector) if (basecallin.nil? or !basecallin.is_a?(RoleCall)) classcallins.delete(name) else #normal behaviour: actual callin is bound by the connector => basecallin!=actualcallin actualcallin = classcallins[name] if (actualcallin!=basecallin) #set basecall as callin for name classcallins[name] = basecallin else #walk through queue - ensure all call objects are deleted flag = true while(actualcallin.is_a?(CallWeaved) and flag) savecallin = actualcallin actualcallin = actualcallin.basecall if (actualcallin.is_a?(RoleCall) and actualcallin.connector==connector) if (actualcallin.instance_of?(RoleCall)) #set null operation savecallin.basecall = NoCall.new else #CallWeaved #found callobject of connector -> connect savecallin with actualcallinbase savecallin.basecall=actualcallin.get_base_of(connector) end flag = false end end end end } classcallins.rehash end end # Get all callins, that are registered for this class (self). # [includeSuper] indicates, if all callins of superclass shoulb be included. # [return] hash (basemethodname -> call) def callins(includeSuper=false) result = nil if (!includeSuper) result = (Callins[self] or Hash.new) else result = Hash.new klass = self while (klass!=Object) classcallins = Callins[klass] if (!classcallins.nil?) classcallins.each { |name,call| if (!result.has_key?(name)) result[name] = call end } end klass = klass.superclass end end return result end # Decalre a given class as roleclass of this class. This is usually done # by a connector. The same role can more than once activated for the same # class. # [connector] the connector, that binds the base to this role. # [roleclass] the role that this base class plays. def add_role(connector, roleclass) #get all roles of this class classroles = Roles[self] if (classroles.nil?) classroles = Hash.new Roles[self] = classroles Roles.rehash end #get all current active connectors connectors = classroles[roleclass] if (connectors.nil?) connectors = Array.new classroles[roleclass] = connectors classroles.rehash end #add connector to list connectors.push(connector) end # Remove a given role from this baseclass. Since roles can be activated # more than once, has_role? can return true after calling remove_role (if # more than one activation is active). If the last activation is removed, # the roleclass is removed too. # [connector] the connector, that binds the base to this role. # [roleclass] the role that this base class plays. def remove_role(connector, roleclass) #get all roles of this class classroles = Roles[self] if (!classroles.nil?) #get all current active connectors connectors = classroles[roleclass] if (!connectors.nil?) if (connectors.length == 1) #last active connector -> remove classroles.delete(roleclass) classroles.rehash else (!connectors.nil?) #delete the active connector connectors.delete(connector) end end end end # Similar to instance_of? for objects, a class can be asked, if it is # playing a role inside a team. # [roleclass] the roleclass to ask for. # [return] true, if this class plays the given role - otherwise false. def has_role?(roleclass) #get all roles of this class classroles = Roles[self] result = false if (!classroles.nil?) if (!classroles[roleclass].nil?) result = true else classroles.each { |role, connectors| #is roleclass a superclass of role? result = (role array of binding connectors def roles() return (Roles[self] or Hash.new) end # Extended version of Class.name. This returns the name with all rolenames # appended as "as" clauses. Eg: "Window as ObserverPattern::Observer" def full_name() result = self.name classroles = Roles[self] if (!classroles.nil?) classroles.each { |role, connectors| result += " as "+ role.name + (connectors.length>0 ? "(#{connectors.length})" : "") } end return result end # Write a little status report to stdout. This is most useful for debugging purposes. # The status displays all callin bindings for this class and all super classes. def status(formatted=false) klass = self stack = Array.new while (klass!=Object) stack.push(klass) klass = klass.superclass end puts "\nCallin definition of #{self.name}: "+("="*(57-klass.name.length))+"\n" stack.reverse.each { |klass| puts "+#{klass.name}:" klass.callins.sort.each { |method, call| if (formatted) puts " -#{method}:\n"+call.to_string else puts " -#{method} -> #{call.to_s} (#{call.connector.class.name})" end } } puts "================================================================================\n" end end # This module just handles all calls, that are not known to the class. # All such methods will be handled by the dispatcher-method. # A missing method could be a callin binding - or a really missing method. module Missing # This method is called, whenever a method is not found def method_missing(method, *params, &block) methodname = method.id2name retval = nil begin #do we have a callin registered? acallin = self.class.get_callin(methodname) if (!acallin.nil?) #simply call retval = acallin.call(self, *params, &block) else raise(NoMethodError, "undefined method `#{methodname}' for #{self.inspect}", caller) end rescue Exception => ex if ($DEBUG) #throw original backtrace raise ex.class, ex.message, ex.backtrace else #set new backtrace, without annoying weaved - trace newtrace = Array.new ex.backtrace.each { |trace| if (!ObjectTeam::Caller_RE.match(trace)) newtrace.push(trace) end } ex.set_backtrace(newtrace) raise ex.class, ex.message, newtrace end end return retval end end # This class represents an externalized Role. # All methods on an externalized role must be involved into the # lifting/lowering process. class ExternalizedRole # Instance of this class are created from the class Team, if an # externalized role is requested. def initialize(connector, role) @connector = connector @role = role end # This method handles all method to the role: # All attributes get lifted to their roles. # The result is lowered to the base object. def method_missing(method, *params, &block) newparms = @connector.lift(params) result = @role.send(method.id2name, *newparms, &block) result = @connector.lower(result) return result end # Return the encapsulated connector. def __connector() return @connector end # Return the encapsulated role. def __role() return @role end # Return the encapsulated base. def __base() return @role.__baseobject end end end # Enrich each object for a method_missing functionality. # If a method-call is not found, this method is called and forwarded # to the dispatch method inside class. If no callin is registered, # an exceptionis thrown. class Object include ObjectTeam::Missing end # Enrich each Class for the possibility to register and unregister # call-objects for a specific name. This mechanism emulates a dynamic # method call under a certain name. class Class include ObjectTeam::Compound end # Enrich Kernel for an assert method. def ot_assert(bool, faildescription=nil) if (!bool) raise ObjectTeam::TeamException, (faildescription or "Assertion failed!"), caller end end # ObjectTeam.rb April 2002 # # Copyright (c) 2002 by Matthias Veit # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2, or (at your option) # any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA.