Completely rewritten and refactored for 5.0 with updated documentation.