Skip to content

Commit

Permalink
First nice version of RuleChecker
Browse files Browse the repository at this point in the history
  • Loading branch information
ratem committed Apr 9, 2011
1 parent a0dfb99 commit 357c271
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 38 deletions.
3 changes: 2 additions & 1 deletion bank_system/decorators/bank_account_decorator.py
Expand Up @@ -20,11 +20,12 @@ def __init__(self, number):

def decorate(self, decorated):
try:
self.rule_should_be_machine_instance(decorated)
BankAccountDecorator.rule_should_be_machine_instance(decorated)
except:
raise AssociationError('Machine instance expected, instead %s passed' % type(decorated))
self.decorated = decorated

@classmethod
@rule('association')
def rule_should_be_machine_instance(self, decorated):
decorated |should| be_instance_of(Machine)
Expand Down
3 changes: 2 additions & 1 deletion bank_system/decorators/credit_analyst_decorator.py
Expand Up @@ -19,11 +19,12 @@ def __init__(self, register):

def decorate(self, decorated):
try:
self.rule_should_be_person_instance(decorated)
CreditAnalystDecorator.rule_should_be_person_instance(decorated)
except:
raise AssociationError('Person instance expected, instead %s passed' % type(decorated))
self.decorated = decorated

@classmethod
@rule('association')
def rule_should_be_person_instance(self, decorated):
decorated |should| be_instance_of(Person)
Expand Down
55 changes: 24 additions & 31 deletions configurator/rule_checker.py
@@ -1,3 +1,15 @@
'''
@rules must be class methods because creating an instance for each decorator is
a problem, given that the different arguments required by different decorator
constructors. The solution is to use @rules as Class Methods, with no arguments
besides decorated.
If decorators instance attributes are needed they should be used at decorate(),
making that some object can pass the @rule tests but get an extra error while
trying to decorate() it. Anyway, checking decorators instance attributes for
decoration maybe is a bad practice, given that decorators are created for
decorating an object (having setted only very basic attributes), and not the
opposite.
'''
import sys
import inspect
from bank_system.decorators.credit_analyst_decorator import CreditAnalystDecorator
Expand All @@ -13,6 +25,7 @@ def __init__(self):
self.rules = []

def find_decorators(self, module):
''' finds decorator classes in a module '''
for name in dir(module):
obj = getattr(module, name)
if inspect.isclass(obj):
Expand All @@ -29,41 +42,21 @@ def find_decorators(self, module):
if new_decorator: self.decorators.append(obj)

def find_rules(self, decorator):
''' finds @rules in a decorator '''
for name, method in inspect.getmembers(decorator, inspect.ismethod):
if hasattr(method,'rule_category'):
self.rules.append(method)

def check_rules(self, node):
self.checker = Decorator()
#import pdb;pdb.set_trace()
''' runs, for each decorator, each rule separately '''
for cls in self.decorators:
#creating an instance is a problem, because of the creation arguments
#the solution is to use Class Methods, with standard arguments
#if we need instance attributes for checking associations, one of
#the default arguments must be and instance of the given class
#this also can cause problems, thus the solution is to use getsource
checker = cls('x')
try:
checker.decorate(node)
except:
pass
else:
self.allowable_decorators.append(cls.__doc__)

def stupid_check(self,node):
#What is done below must be done dinamically, by iterating through all decorators
credit_analyst = CreditAnalystDecorator('x')
try:
credit_analyst.decorate(node)
except:
pass
else:
self.allowable_decorators.append(credit_analyst.__class__.__doc__)
bank_account = BankAccountDecorator('x')
try:
bank_account.decorate(node)
except:
pass
else:
self.allowable_decorators.append(credit_analyst.__class__.__doc__)
for rule in self.rules:
if rule.im_self == cls: #it is safer than im_class
allowable = True
#check http://stackoverflow.com/questions/114214/class-method-differences-in-python-bound-unbound-and-static
try: cls.__dict__[rule.__name__].__get__(None, cls)(node)
except: allowable = False #should register "why"
else: allowable = False
if allowable:
self.allowable_decorators.append(cls)

7 changes: 2 additions & 5 deletions configurator/spec/rule_checker_spec.py
Expand Up @@ -38,11 +38,8 @@ def test_it_check_rules(self):
self.rule_checker.find_rules(decorator)
self.rule_checker.check_rules(self.a_person)
self.rule_checker.allowable_decorators |should| have(1).decorator


def test_it_stupidly_checks_which_decorators_can_decorate_a_node(self):
self.rule_checker.stupid_check(self.a_person)
self.rule_checker.allowable_decorators |should| contain('Credit Analyst')
#non_allowable should contain tuples (class, reason), where reason is
#the __doc__ of a @rule == fine grained reasons for not decorating!

'''
if __name__ == '__main__':
Expand Down

0 comments on commit 357c271

Please sign in to comment.