Tag Archives: epiphany

Broken Rules Pattern with Epiphany

broken rulesThe Broken Rules pattern* provides an encapsulated way for objects to indicate whether or not their internal state is valid. Here, “valid” usually means that the data contained in the object is ready to be persisted to a database. An object following this pattern will have two properties: isvalid and brokenrules. isvalid returns True or False to indicate whether or not the object has any broken rules. brokenrules will return a collection of validation problems if there are any.

Broken Rules for Entity Classes

To demonstrate, let’s create some entity classes.


class deities(entities):
    pass

class deity(entity):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    def __str__(self):
        return self.name

pantheon = deities()
zeus = deity('Zeus', 'male')

assert pantheon.isvalid
assert pantheon.brokenrules.count == 0

assert zeus.isvalid
assert zeus.brokenrules.count == 0

Here, we define an entities collection class called deities and an entity class called deity. We instantiate one of each and we can immediately assert that each has an isvalid proprety that is True and a brokenrules collection property which is empty. These properties are available as a consequence of the classes inheriting from entities and entity. Both asserts mean the same thing since by definition, an entity with no broken rules is valid.

Now let’s redefine the deity class to contain validation logic so we can ensure it knows when it is invalid.


class deity(entity):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

    
    @property
    def brokenrules(self):
        brs = brokenrules()
        if self.gender not in ('female', 'male'):
            brs += '{} is an invalid gender.'.format(self.gender)

        if type(self.name) != str:
            brs += 'Deity names must be strings.'

        return brs
    
    def __str__(self):
        return self.name

zeus = deity(42, None)

assert not zeus.isvalid

assert str(zeus.brokenrules) == 'None is an invalid gender.\nDeity names must be strings.'

The first step is to override the brokenrules property. It creates and return its own brokenrules collection class each time it is called. We establish two validation rules: the first is that a deity must have a gender that is either “female” or “male”, and the second is that the deity’s name must be a string. Later, we instantiate a deity called zeus and deliberately brake both of these rules upon instantiation by making its name 42 and giving it a gender of None. Now, we can assert that zeus is not valid. We can also convert the brokenrules property to a string to get a \n delimited list of human readable broken rules.

Persistence

Epiphany entities don’t currently perform persistence routines (this will likely change in the future), but if we wanted to ensure that our invalid zeus is never saved to the database, we could write our save (INSERT/UPDATE) method like so.


class deity(entity):
    ⋮ 
    save(self):
        if self.isvalid:
            # INSERT/UPDATE state date into database
            pass
         else:
              raise Exception("Can't save to database. Object is in an invalid state.")
    ⋮ 

Here, isvalid is tested to ensure that invalid data is never persisted. However, if this were for a typical data entry application, the UI code should first test if the object is valid, and if is not, inform the user so the data can be corrected.

Broken Rules for Collections

Let’s see what happens when we add our invalid zeus to a deities collection.


pantheon = deities()
pantheon += zeus

assert not pantheon.isvalid
assert str(pantheon.brokenrules) == 'None is an invalid gender.\nDeity names must be strings.'

If an entities collection object contains one or more invalid entity objects, it will become invalid. Thus, having added zeus to the pantheon collection, we have now made pantheon invalid. Converting the entities collection’s brokenrules property to a string will list each of the broken rules in each of the invalid entity objects within the collection.

Recursive Broken Rules

When an entity has a collection of child entities, it is easy to create a validation rule that states that the entity is only valid if all of it’s child objects are valid.

Let’s say that our deity object has a collection called children(appropriately enough):


class deity(entity):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender
        self.children = deities()

    @property
    def brokenrules(self):
        brs = brokenrules()
        if self.gender not in ('female', 'male'):
            brs += '{} is an invalid gender for "{}".'.format(self.gender, self.name)

        if type(self.name) != str:
            brs += 'Deity names must be strings.'

        brs += self.children.brokenrules

        return brs

    def __str__(self):
        return self.name


zeus = deity('Zeus', 'male')
zeus.children += deity('Aeacus', 'm')
zeus.children += deity('Angelos', 'female')
zeus.children += deity('Aphrodite', 'female')
zeus.children += deity('Apollo', 'male')
zeus.children += deity('Ares', 'male')
zeus.children += deity('Artemis', 'f')
zeus.children += deity('Athena', 'female')


assert not zeus.isvalid
assert str(zeus.brokenrules) == 'm is an invalid gender for "Aeacus".\nf is an invalid gender for "Artemis".'

Here we’ve redefined the deity entity to have a children property to contain all of the deity’s children. (Note that it’s a coincidence that this property is called children. Obviously, an object can (and usually would) have child collections for things other then literal children. deity and children have a one-to-many relationship with each other and that is the concept that is being illustrated here.)

We’ve also added a line in the brokenrules property to collect all the broken rules of the deity’s children objects. We’ve additionally made a minor adjustment to the broken rule text to indicate which deity the broken rule pertains to.

Now we use the new class by re-instantiating zeus and adding his children to his children collection. (We will stick to those of his children whose name started with “A” for brevity.)

Note that for Aeacus and Artemis, we’ve abbreviated the gender. This breaks a rule because gender must be either of the exact strings ‘male’ or ‘female’. Thus, zeus becomes invalid – not because the zeus itself is invalid, but because two of his children are in an invalid state. The brokenrule collection tells us which deity objects are invalid.

Conclusion

The Broken Rule pattern is a powerful way to centralize the validation rules of the object, and ensure that bad data is never persisted to the data store. Additionally, it is easy to capture any broken rules in an object graph by adding a single line of code in the brokenrule property.

Ideally, all validation could could happen in this property since it’s centralized and has the full power and expressiveness of the Python language to test values for validity. However, for reasons of optimization, it will still be necessary to duplicate many of these rules on the browser using JavaScript to improve the user’s experience. Also, some validation rules are so data-intensive that they should be implemented in the database itself (e.g., using check constraints, unique constraints, etc.). Regardless, I’ve found this pattern so useful that I use it for virtually all of the code I author.

* The Broken Rules pattern was likely first described in Rocky Lhotka’s book Professional Visual Basic 5.0 Business Objects published in 1997

Appending to and retrieving objects from Epiphany Py entities

This tutorial will get you started subclassing the ‘entities’ class in Epiphany Py to make array-like classes. Checkout version 1.1 to follow along.

First, let’s get some subclasses defined.


from entities import *
class gods(entities):
    pass

class god(entity):
    def __init__(self, name):
        self.name = name

    def __str__(self):
        return self.name

Here, our array-like class is gods since it inherits from entities. The second class, god inherits from entity. Instances of this class will will be elements of the gods. It doesn’t have to inherit from entity but it helps and one operations is impossible unless it does (we’ll see which one below).

Let’s create some gods and append them to the gods collection.


norse_pantheon = gods()
assert norse_pantheon.count == 0

thor = god('Thor')
odin = god('Odin')

norse_pantheon.append(thor)
norse_pantheon.append(odin)
assert norse_pantheon.count == 2

After instantiating our norse_pantheon collection, we can assert that its count property is 0 since we haven’t added any elements yet. Next we create thor and odin objects and append them to the norse_pantheon. Now the count property is 2.

The append method is only one way to add objects to our collection. Let’s clear the collection and try some other ways.


norse_pantheon.clear()
assert norse_pantheon.count == 0
assert norse_pantheon.isempty

norse_pantheon += thor
norse_pantheon += odin
assert norse_pantheon.count == 2

The clear method method empties the collection. Now we can assert that the count proprety is 0 and norse_pantheon.isempty is True.

Next we append thor and oden to the collection again, this time using the += operator.

The append method and the += operator do the same thing here. I prefer += since it reduces the number of characters on the line. Once you get used to it, it’s easier to read.

There is one more method to append object to entities collection which is very useful in some circumstances.


norse_pantheon.clear()
norse_pantheon += thor + odin
assert norse_pantheon.count == 2

Here, thor and odin are added together to make an entities collection which is then append to the norse_pantheon collection. Convenient, but for this to work, thor and odin had to inherit from the entity class.

So far, we’ve added to the collection. Now, let’s explore ways to get the objects out of the collection. Here are three ways to do the same thing.


assert norse_pantheon[0] is thor
assert norse_pantheon[1] is odin

assert norse_pantheon['Thor'] is thor
assert norse_pantheon['Odin'] is odin

assert norse_pantheon.first is thor
assert norse_pantheon.second is odin

The first uses the collection indexer to to get the object out by it’s index number. The first object added to a collection will be indexed at 0, the second will be index at 1 and so on.

The second way is to use the name property as the index. Since we instantiated the god objects so their name properties would be ‘Thor’ and ‘Odin’ (see the constructor), we can obtain them by their name value.

The third way is to use the first and second properties. (There are also third, fourth and fifth properties. At the moment, there is no onehundredandseventyseventh property but I am looking in to a way to do that)

We can also iterate over the collection.


for g in norse_pantheon:
    print(g.name)

Since we added the __str__ method to the god class, we can do this and it will do the same as the above.


print(norse_pantheon)

Epiphany Py released

I’ve released version 1.0 of Epiphany Py today. Epiphany Py is a collection of libraries that would prove useful in any Python project. Currently it contains a base-class similar to the .NET CollectionBase class. You can use it to make your Python objects behave like super powerful arrays. It also comes with a useful unit testing framework called Tester.

I’ve enjoyed using these base classes in my other projects and have achieved a high degree of reusability and terseness from them. In future posts, I will provide demonstrations on how to use them.