Package: navmazing
navmazingIntroductionSimple navigation framework supporting complex designsDesignnavmazing is based around a number of navigation steps which subclass the NavigateStep object. These step objects have a relationship by defining a prerequisite method which can go to the previous step if it is determined that we are not current on the requested step. This chaining of steps starting at a first and visiting all the prerequisites in the chain until they are resolved constitutes a complete navigation.UsageAn example is below. This creates a simple class modelling some Provider object that has three destinations or pages in the UI, aNewpage, aDetailspage and anAllpage. TheNewpage is where a new Provider would be added, and theAllpage is where you could view a list of all of them. To get to theNewpage, you must first visit theAllpage. TheDetailspage is where a specific provider would be displayed. In the example below you can see that we model some very simple steps and register them to the class.fromnavmazingimportNavigate,NavigateStep,NavigateToSiblingnavigator=Navigate()classProvider(object):def__init__(self,name):self.name=name@navigator.register(Provider,'Details')classProviderDetails(NavigateStep):prerequisite=NavigateToSibling('All')defstep(self):click(self.obj.name)@navigator.register(Provider,'New')classAddANewProvider(NavigateStep):prerequisite=NavigateToSibling('All')defstep(self):click('Add New Button')@navigator.register(Provider,'All')classShowAllProviders(NavigateStep):defam_i_here(self):returncheck_if_i_am_already_on_page()defstep(self):click('All button')The classShowAllProvidersrepresents the step of getting to theAllpage. This class has two methods,am_i_here()andstep(). Thestep()method describes how we should perform the step of getting to theAllpage. It is assumed in this particular UI that theAll buttonis always visible. Theam_i_here()method is a way to query to see if we are already on this page. This is used to shortcut navigation if it can be determined that we are already on a page that we have requested to visit.The classAddANewProviderrepresents the step of getting to theNewpage. This class has an attribute and a method. Theprerequisiteis actually a python descriptor. It uses theNavigateToSiblinghelper to navigate to theAlldestination on the same object that it has been requested to visit theNewpage on. One could simply makeprerequisitea function that callsnavigate_towithself.objand theAlldestination. However the helper is much nicer and makes defining prerequisite steps a little more cleaner.The classProviderDetailsrepresents the step of getting to a specific providers page, itsDetailspage. This class is the same as theAddANewProviderclass in that it first requires the All button to be pressed. Notice in thestep()method, that there is a reference toself.obj. This object is the object that is passed to navigation step as can be seen below. In the example below this object is calledprov.# Non helper methoddefprerequisite(self):navigator.navigate(self.obj,'All')# Using helper methodprerequisite=NavigateToSibling('All')To use the code above, one would instantiate a Provider object and then use the navigate method like so:prov=Provider('name')navigator.navigate(prov,'Details')Navigation Helpersnavmazing has a few helpers to make prerequisites easier to defineNavigateToSibling- Navigates to a destination that is registered to the same object as the current request.NavigateToAttribute- Navigates to a destination that is registered to an attribute of the same object as the current request. This is useful if there is some kind of hierarchy to your objects and you wish to navigate to a step that is registered against an attribute rather than the object itselfNavigateToObject- Navigates to a destination that is registered to the supplied object, it could be argued that NavigateToAttribute could be implemented with a NavigateToObject call, by simply offering the attribute object as the object. It feels more dynamic and cleaner to use NavigateToAttribute in these cases, there the attribute is a string that is looked up, rather than an object which is static.FlowchartThe diagram below shows the flowchart of the process of resolving a navigation using the stock navmazing library. As the methods are overridable, the flow can be altered, but should suffice for most usages.Advanced Usagenavmazing also allows parameters to be passed at the navigate call. This means you can allow for some level of dynamic nature or choice in your navigation destinations. Let’s say for example you have a step that usually works one way, but there are certain times when you need to override that behaviour and make it work a different way.@navigator.register(Provider,'Details')classProviderDetails(NavigateStep)prerequisite=NavigateToSibling('All')defstep(self,active=True):ifchoice:click(self.obj.name)else:click(self.obj.name+" - inactive")# Call by defaultnavigate(obj,'Details')# Call, passing in a parameter for choicenavigate(obj,'Details',active=False)In the example above, passing theactiveparameter changes the behaviour of the step.