I have been working on a rather complex system recently, which lead me to talking to a lot of my collegues. Here is a piece that I got from those talks. :)

Problem: generate and display a complex interface/menu, which varies from user to user and depends on a number of options. There are plenty of conditions to check and there are multiple combinations of conditions that can produce the same result. The number of conditions and their combinations can grow and change over time. Heh, even describing this is complex. :)

Example:

if ($not_in_shift) { if ($current || !$past) { print "Register for shift\n"; } } else { if ($current) { print "Confirm in shift\n"; print "Remove from shift\n"; } if ($past) { print "Confirm in shift\n"; } }

Even as it is, it already looks ugly. Adding few more conditions and combinations brings code to a real mess and makes it unmaintainable.

Solution: first the code, then explanation.

#!/usr/bin/perl -w use strict; # These can be filled by some function my %conditions = ( Current => 1, Not_in_shift => 1, Past => 0, ); # Menu item to print if any set of conditions satisfies my %menu = ( 'Add Self' => [ "Current,Not_in_shift", "!Past,Not_in_shift", ], ); # No need to touch this stuff ever for my $item ( keys %menu ) { for my $set ( @{$menu{$item}} ) { my $result = 1; for my $condition ( split(/,/,$set) ) { # Negating condition is a convenient thing to have if (substr($condition,0,1) eq '!') { substr($condition,0,1,''); ($result &&= !$conditions{$condition}) || last; } else { ($result &&= $conditions{$condition}) || last; } } if ($result) { add_menu_item($item); last; } } } # Whatever we want to do with the menu item sub add_menu_item { my $item = shift; print "$item\n"; }

Basically, here we have a %conditions hash that contains the current status. It can be updated by a separate function. %menu contains all menu items that can be displayed. For each item, there is a list of combinations (strings). Each combination lists conditions that must true (or false, with negation) to satisfy. Any combination that satisfies all conditions causes the menu item to appear.

The good side of this solution is that it is obvious under which conditions which menu item will get displayed. It is easy to add more items, edit or delete existing ones, or temporarily hide menu items. Chances of making a mistake are lower and it is also possible to store the whole thing outside the code (like in the database or config file) and provide GUI menu editing functionlity. The possibilities are endless. :)

Update: One picture is a thousand words. Here is a screenshot from the running system.


In reply to Checking lots of conditions...ORed and ANDed by TVSET

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.