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<roleclass)
break if result
}
end
end
return result
end
# Get all roles, this class has.
# roleclass -> 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 <matthias_veit@yahoo.de>
#
# 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.
syntax highlighted by Code2HTML, v. 0.9.1