diff --git a/composer/rule/README.txt b/composer/rule/README.txt index 195a8b3..bb03422 100644 --- a/composer/rule/README.txt +++ b/composer/rule/README.txt @@ -8,6 +8,10 @@ Rule-based Execution of Actions >>> from cybertools.composer.rule.base import RuleManager, Rule, Action >>> from cybertools.composer.rule.base import EventType, Event + >>> from cybertools.composer.rule.base import ActionHandler + >>> component.provideAdapter(ActionHandler, name='message') + >>> component.provideAdapter(ActionHandler, name='sendmail') + >>> manager = RuleManager() >>> loginEvent = EventType('login') @@ -18,7 +22,7 @@ Rule-based Execution of Actions >>> checkoutRule.actions.append(Action('message', ... parameters = dict(messageName='confirmation_mail'))) >>> checkoutRule.actions.append(Action('sendmail')) - >>> manager.rules.append(checkoutRule) + >>> manager.addRule(checkoutRule) >>> manager.handleEvent(Event(loginEvent)) diff --git a/composer/rule/base.py b/composer/rule/base.py index aff0ffa..4825ce9 100644 --- a/composer/rule/base.py +++ b/composer/rule/base.py @@ -22,6 +22,8 @@ Basic classes for rules and actions. $Id$ """ +from zope import component +from zope.component import adapts from zope.interface import implements from cybertools.composer.base import Component, Element, Compound @@ -42,12 +44,29 @@ class RuleManager(object): rulesFactory = Jeep rules = None - def __init__(self): - if self.rulesFactory is not None: + def addRule(self, rule): + rule.manager = self + if self.rules is None: self.rules = self.rulesFactory() + for e in rule.events: + entry = self.rules.setdefault(e.name, []) + entry.append(rule) + + def getRulesForEvent(self, event): + return self.rules.get(event.name, []) def handleEvent(self, event): - pass + rules = self.getRulesForEvent(event) + for r in rules: + for c in r.conditions: + cond = component.getAdapter(r, ICondition, name=c) + if not cond(event): + continue + data = None + for action in r.actions: + handler = component.getAdapter(action, IActionHandler, + name=action.handlerName) + data = handler(data, event) class Rule(Template): @@ -101,21 +120,13 @@ class Event(object): # conditions -class ConditionType(object): - - def __init__(self, name, title): - self.name = name - self.title = title - - class Condition(object): implements(ICondition) + adapts(IRule) - def __init__(self, conditionType): - self.conditionType = conditionType - self.name = conditionType.name - self.title = conditionType.title + def __init__(self, context): + self.context = context def __call__(self, context, params): return True @@ -123,7 +134,7 @@ class Condition(object): # actions -class Action(object): +class Action(Component): implements(IAction) @@ -133,17 +144,22 @@ class Action(object): rule = None def __init__(self, name, **kw): + self.name = name for k, v in kw.items(): setattr(self, k, v) if self.parameters is None: self.parameters = {} if not self.handlerName: - self.handlerName = self.name + self.handlerName = name class ActionHandler(object): implements(IActionHandler) + adapts(IAction) - def __call__(self, data, params): + def __init__(self, context): + self.context = context + + def __call__(self, data, event, params={}): pass diff --git a/composer/rule/interfaces.py b/composer/rule/interfaces.py index 6c36d7b..62c62a5 100644 --- a/composer/rule/interfaces.py +++ b/composer/rule/interfaces.py @@ -40,11 +40,21 @@ class IRuleManager(Interface): description=_(u'The title of the object.'), required=True,) - rules = Attribute('An ordered collection of rules managed by this object.') + #rules = Attribute('An ordered collection of rules managed by this object.') + + def addRule(rule): + """ Add the rule given to the rule manager's collection of rules, + registering it for the event types that are handled by + the rule. + """ + + def getRulesForEvent(event): + """ Retrieve the rules that may handle the event given. + """ def handleEvent(event): """ Handle the event given and apply the corresponding rules - to the client object. + to the event's context object. """ @@ -66,9 +76,10 @@ class IRule(ITemplate): required=False,) manager = Attribute('The associated rule manager.') - events = Attribute('The events to be handled by this rule.') + events = Attribute('The events (the event types, to be more precise) ' + 'to be handled by this rule.') conditions = Attribute('Conditions to be checked.' - 'This is typically a list of names of ICondition adapters.') + 'This is typically a list of names of ICondition adapters.') actions = Attribute('Sequence of actions to be carried out by this rule.') @@ -81,7 +92,7 @@ class IEvent(Interface): class ICondition(Interface): - def __call__(context, params): + def __call__(context, event, params): """ Should return True if the condition should be fulfilled; will allow the rule to call its actions. """ diff --git a/organize/service.py b/organize/service.py index ed30d9b..0f6697c 100644 --- a/organize/service.py +++ b/organize/service.py @@ -45,18 +45,16 @@ from cybertools.organize.interfaces import IRegistration, IRegistrationTemplate from cybertools.organize.interfaces import IClientRegistrations -class ServiceManager(RuleManager): +class ServiceManager(object): implements(IServiceManager, IClientManager) servicesFactory = Jeep clientSchemasFactory = Jeep clientsFactory = OOBTree - rulesFactory = Jeep services = None clients = None - rules = None allowRegWithNumber = False allowDirectRegistration = True @@ -66,8 +64,6 @@ class ServiceManager(RuleManager): self.services = self.servicesFactory() if self.clientSchemasFactory is not None: self.clientSchemas = self.clientSchemasFactory() - if self.rulesFactory is not None: - self.rules = self.rulesFactory() def getServices(self, categories=[]): return self.services @@ -94,9 +90,6 @@ class ServiceManager(RuleManager): def checkClientName(self, name): return name not in self.getClients() - def getRules(self): - return self.rules - class Registration(object): @@ -351,13 +344,21 @@ class StatefulRegistration(StatefulAdapter): statesDefinition = registrationStates -# event types for rule-based processing +# rules and events eventTypes = Jeep(( EventType('service.checkout'), )) +class RuleManagerAdapter(RuleManager): + + adapts(IServiceManager) + + def __init__(self, context): + self.context = context + + # Zope event handlers def clientRemoved(obj, event):