ProgressMonitor
view release on metacpan or search on metacpan
lib/ProgressMonitor.pm view on Meta::CPAN
how feedback should be shown. This code should instantiate a monitor of the correct
type and pass it in to the method that reports progress.
There is a middle ground however: a progresse may have need to use lower level functions
and they might also be able to use a monitor. In such a case, the higher level progressee
should instantiate a SubTask monitor. This special monitor type will pass on information
to the parent monitor and cause feedback to be correctly scaled. It is important
to never pass on the monitor you have been given to someone else!
=head2 The ProgressMonitor 'contract'
So, being a progressee, you get a monitor instance. What do you do?
=over 2
=item PREPARING YOURSELF
The first call you should do as soon as possible is to call the 'prepare' method.
This tells the monitor that you are in prepare mode, and this means that you now spend
your time figuring out how many 'things' you will need to do. While doing this you should
as regularly as possible call the 'tick' method (any arguments to tick will be ignored in prepare
mode). This will, depending on feedback mechanism, trigger some visible indication of
'work in progress'.
This step is actually
optional - maybe you already know how much work you need to do. Or, also common, you really don't know, and
it may be either impossible to figure out, or it may be prohibitive to calculate. In this case, you can go
straight to calling 'begin'.
=item BEING ACTIVE
Having called the begin method, you're saying "I'm now actively working with the task".
The begin method takes an optional parameter, an integer. The significance is that
if you don't pass anything, you're saying 'the extent of this work is unknown, you'll just have to wait...I will
call back once in a while to ensure you see me working'. Some feedback presentations
are better at portraying this situation than others - for example, a character changing
shape for each call will give a good view of this.
Passing a number however, constitutes
a promise; 'I will call back exactly this number of times' (there's no implication of
time between calls though). Again, some presentations look better than others in this
situation - specifically, presentations that can show "I'm now here, and so much remains"
will then give a fairly clear picture. A typical presentation is the percentage - '85 %'
clearly shows that it's almost finished.
So, in either of the above cases you should call the 'tick' method. If the total is
unknown, each tick simply increments the internal counter by one (i.e. any argument to
tick is ignored). If the total is unknown however, you should call with an integer argument.
Actually, the integer may be 0 in which case presentations sees it as an 'idle' tick
but still may do something visually interesting. Any other integer is simply added to
the current count - but beware of calling tick with a number that causes the total to
be greater than your promise; this is an error. In any event, this gives the clearest
signal to presentations, one which they typically use to calculate amount done vs amount
remaining, and then render this in the best way.
While 'ticking' is the primary way of informing the user, sometimes it makes sense
not only saying "I'm active", but also saying 'I'm currently doing this', i.e. a straightforward
message. Messages is a sort of out-of-band communication in regards to ticks. Depending
on how the monitor was set up, they may be ignored altogether, written using newlines 'beside'
the tick, or perhaps overlaying the tick field(s) (all or in part) - and then automatically
time out, restoring the tick fields. Anyway, feel free to give informational messages as
needed (but don't assume they'll be seen - just as with ticks, as the monitor in total
may be just a black hole).
=item FINISHING
When all your tasks are complete, you should call the 'end' method. Ideally, you should
by now have called tick the right number of times, and thus the final presentation should
show (the equivalent to) 100% complete.
The monitor is now unusuable for further calls and should be discarded.
=item CANCELLATION
Preferably, you should intermittently also call 'isCanceled' on the monitor. A true value
signals that you should cancel your work at the earliest convenience - if at all possible.
I.e. it is legal to not care about the cancellation status. Only you can decide, but it's very
nice to allow users to cancel a task.
=item SUBTASKS
During your prepare or active phase, you may utilize a lower level method/function to
do the overall task. In order to still report progress, you need to wrap your monitor inside
a SubTask monitor and pass that on.
It is illegal to pass on your own monitor - this will break as the lower level method should
follow the same pattern detailed here - thus, the first thing called will be 'prepare' and
since your own monitor is already in the active phase, it could get very confusing indeed!
The pattern here is that you allocate the subtask a certain amount of 'your' progress. Regardless
of how many iterations the subtask will do, the progress will be scaled to the parent so that
by the time the subtask is 100% complete, it has used up only the allotment it was given.
If you think about it, it's clear that this will work for arbitrarily deeply nested
subtasks - as long as all methods accept a monitor and use the pattern described here,
they can call each other in any order.
=back
=head1 Available monitor and field types
This package provides 4 concrete types that can be used. Two of them are special purpose
monitors, and the other two are variations on how to present the feedback as strings.
The last two uses 'field' objects to display in different ways (spinner, percentage, bar etc).
If this is not adequate for you, it's fairly easy to derive new specializations of either
complete monitors or field variations.
For each of these, see their respective documentation for details.
=head2 MONITOR TYPES
=over 2
=item ProgressMonitor::Null
If you wish to skip feedback, you may instantiate a null monitor. It implements
the interface and contract but doesn't do anything with the information.
( run in 1.930 second using v1.01-cache-2.11-cpan-39bf76dae61 )