Skip to content

Commit

Permalink
Added documentation on how to create rules.
Browse files Browse the repository at this point in the history
  • Loading branch information
austinhartzheim committed Jan 2, 2016
1 parent c78822f commit 90137b2
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 2 deletions.
83 changes: 83 additions & 0 deletions docs/creating_rules.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
Creating Rules
==============

This page details how you can create your own rules for use in Rigidity.

.. toctree:
:maxdepth: 2
:glob:
A Simple Example
----------------
Rigidty rules are all contained in classes subclassing :class:`~rigidity.Rule`. The simplest example has only an `apply()` method that validates or modifies data.

For example, a simple rule to check integers might look like this::

class Integer(rigidity.rules.Rule):
def apply(self, value):
return int(value)

When this rule is used, it attempts to convert the data passed in the `value` parameter to an integer. If it is successful, it returns the integer representation of `value`. If it fails, the `ValueError` from the cast propagates, preventing the invalid data from entering or exiting the program.

As a side effect, because the integer representation is returned, all future checks will then be operating on an integer value, regardless of the original type of `value`. This can be useful to automatically convert data as it enters the program rather than having to handle the casting logic later.

Dropping Rows
-------------
It is not always desirable to raise an error when invalid data is discovered. Sometimes the appropriate action is to ignore the offending row. Rigidity rules can cause a row to be ignored by raising the :exc:`~rigidity.errors.DropRow` exception.

The following code can be used to verify inventory data, preventing the inclusion of any products that are out of stock::

class Inventory(rigidity.rules.Rule):
def apply(self, value):
if isinstance(value, str):
value = int(str)
if not isinstance(value, int):
raise ValueError('Inventory was not an integer value.')

if value < 1:
raise rigidity.errors.DropRow()
return value

If we use this rule to validate this CSV file::
Product,Inventory
T-Shirt,12
Pants,4
Shorts,0
Shoes,-1
Gloves,3

We will get the following list of items that are in stock at the store::

Product,Inventory
T-Shirt,12
Pants,4
Gloves,3

Additionally, if any invalid data is located in the inventory column, an error will be raised to prevent other data from entering the CSV file.

Bidirectional Validation
------------------------
Sometimes it is necessary to validate data differently depending on whether it is being read or written. This is why the :class:`~rigidity.rules.Rule` class supports both the :meth:`~rigidity.rules.Rule.read` and :meth:`~rigidity.rules.Rule.write` methods. Implementing these methods in your rules can allow for greater flexibility of rulesets because the same rules can be used for both reading and writing data.

An example use of this functionality is the built-in :class:`~rigidity.rules.Bytes` rule. This rule assumes that the data being read is raw binary data that is best represented as a Python `bytes` object::

class Bytes(Rule):
'''
When reading data, encode it as a bytes object using the given
encoding. When writing data, decode it using the given encoding.
'''
def __init__(self, encoding='utf8'):
self.encoding = encoding

def read(self, value):
return value.encode(self.encoding)

def write(self, value):
return value.decode(self.encoding)

When the data is read from a CSV file, the :meth:`~rigidity.rules.Bytes.read` method is called, which encodes the data using the selected encoding type and returns it. When it is time to write the data back into a CSV file, the :meth:`~rigidity.rules.Bytes.write` method is called to decode the data using the specified encoding scheme and return the value.

This rule could not be implemented as a unidirectional rule because the `csv` module would not know how to decode the bytes object.
4 changes: 2 additions & 2 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ It would be much more readable in the following form, and could even be included
Brave New World,Aldous Huxley
Nineteen Eighty-Four,George Orwell

Rigidity's `CapitalizeWords` rule allows for slective capitalization of certain letters. By default, it capitalizes the characters following whitespace. But, we need to capitalize words following hyphens as well (in the case of Nineteen Eighty-Four). Here is how we do it::
Rigidity's `CapitalizeWords` rule allows for selective capitalization of certain letters. By default, it capitalizes the characters following whitespace. But, we need to capitalize words following hyphens as well (in the case of Nineteen Eighty-Four). Here is how we do it::

import csv
import rigidity
Expand All @@ -41,7 +41,7 @@ Rigidity's `CapitalizeWords` rule allows for slective capitalization of certain
for row in r:
print(', '.join(row))

The `CapitalizeWords` rule only performs selective capitalization. So, we need to use the `Lower` rule to convert the entire string to lower-case first. We also tell the rule to capitalize all letters immediately following a space character or a hyphen, which allows us to correctly capitalize "Ninteen Eighty-Four."
The `CapitalizeWords` rule only performs selective capitalization. So, we need to use the `Lower` rule to convert the entire string to lower-case first. We also tell the rule to capitalize all letters immediately following a space character or a hyphen, which allows us to correctly capitalize "Nineteen Eighty-Four."

UPC Validation
--------------
Expand Down
1 change: 1 addition & 0 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ With Rigidity, you can easily construct validation and correction rulesets to be
:maxdepth: 2

examples.rst
creating_rules.rst
code.rst

Indices and tables
Expand Down

0 comments on commit 90137b2

Please sign in to comment.