Perl Advent Calendar 2008-12-18

The Toy-O-Matic (3000X!)

by Steve Scaffidi & Jerrad Pierce

Elves are a creative bunch. Really, they're constantly tweaking, updating, and adding features and options to the Toy-O-Matic (3000X!). What, you've never heard of the Toy-O-Matic? (3000X!) Well let me tell you about it! It slices, it dices, it can build just about any toy you can dream up! Sound complicated? You betcha! But elves are good engineers and the whole thing is driven by simple, elf-readable configuration files. In the beginning though, was the Doll-O-Matic. It accepted only a few parameters:

  gender=male
  jacket=red
  pants=green # just like an elf!

But that was enough that the elves could offer dozens of custom dolls to suit most sensible requests. Better yet, because they were using Config::General, only a few changes to the code were necessary to allow each elf to edit the configs as they wanted—within reason—and actually expect it to work!

  -my $config = Config::General->new( $cfg_file );
  +my $config = Config::General->new( 
  +   -ConfigFile     => $cfg_file,
  +   -SplitDelimiter => '\s*(?:\:|=|->)\s*|\s+',
  +   -SplitPolicy    => 'custom');

Now, even after several elves had commited edits of differing styles, Config::General could DWIM

  gender:  female
  #We're out of green dresses, blue is sort of like green, right? -Murphy
  #dress=green
  dress blue
  hairbow->pink #Arrows rule!

Elves love Perl because, like them, it serves to please… and Config::General is very Perlish!

Before long, this level of customization simply wasn't enough. Seriously, when have you ever heard (grand)parents complain about anything today being simpler? Therefore, the Doll-O-Matic was retooled and renamed Toy-O-Matic (3000X!) to match. Now it could make more than dolls, but it's configuration remained straight-forward and flexible, even though a single file could contain parameters for a number of toys. An Army Dude with a Deluxe Rocket Launcher for Jimmy, a Modern Career Woman with the Malibu Accessory Set for Lisa and an Elf! for Tim.

  <toy elf_doll>
      Include elf.cfg
  </toy>

  <toy army_dude>
      nationality = antarctica
      accessories = rocket launcher
      accessories = jeep
      accessories = backpack
      speaks      = Viper commander is over the hill! Cover me! GO! GO! GO!
  </toy>

  <toy career_woman>
      phone = Raspberry 200
      <car>
          make = BMVV
          model = M3
          color = pink
      </car>
      hair = perfect
      speaks =<<END_PHRASE
Buy! Sell! Buy! Sell! 
Call my lawyer, check on the babysitter, 
book me a flight to london!
END_PHRASE
  </toy>

That's right, Config::General supports heredocs, (globbing) of includes, and Apache-style structured files! Many elves work as webmasters in the off-season, so these latter features were second nature to them. Plus, the resulting data structures are easily inspected and understood:

% perl -MConfig::General -MData::Dumper -e "print Dumper{Config::General->new(-ConfigFile=>'@ARGV',-UseApacheInclude=>1)->getall()}" toys.conf
$VAR1 = {
           'toy' => {
                      'jimmy_doll' => {
                                        'pants' => 'green',
                                        'jacket' => 'red',
                                        'gender' => 'male'
                                      },
                      'career_woman' => {
                                          'car' => {
                                                     'color' => 'pink',
                                                     'make' => 'BMVV',
                                                     'model' => 'M3'
                                                   },
                                          'speaks' => 'Buy! Sell! Buy! Sell!
Call my lawyer, check on the babysitter, 
book me a flight to london!',
                                          'hair' => 'perfect',
                                          'phone' => 'Raspberry 200'
                                        },
                      'army_dude' => {
                                       'speaks' => 'Viper commander is over the hill! Cover me! GO! GO! GO!',
                                       'accessories' => [
                                                          'rocket launcher',
                                                          'jeep',
                                                          'backpack'
                                                        ],
                                       'nationality' => 'antarctica'
                                     }
                    }
         };

The hacker elves were also quite happy to learn that Config::General also write configuration files from raw data structures. They even created a GUI for designing new toys, and the results were written out to disk. Productivity soared, creativity blossomed, and kids were happy everywhere!

mod18.pl

   1 #!/usr/bin/env perl
   2 use Config::General;
   3 use Doll::O::Matic;
   4 my $cfg_file = shift @ARGV || die "Please specify a config file!\n";
   5 
   6 
   7 my $config = Config::General->new( 
   8 				  -ConfigFile     => $cfg_file,
   9 				  -SplitDelimiter => '\s*(?:\:|=|->)\s*|\s+',
  10 				  -SplitPolicy    => 'custom',
  11 );
  12 
  13 my %doll_params = $config->getall();
  14 
  15 
  16 my $doll_o_matic = Doll::O::Matic->new( \%doll_params );
  17 my $doll = $doll_o_matic->make_doll() or die($doll_o_matic->error . "\n");
  18 
  19 print "$doll is ready!\nPlease send to the giftwrapping station.\n";

mod18b.pl

   1 #!/usr/bin/env perl
   2 use Config::General;
   3 
   4 my $toy_data = {
   5 		toy => {
   6 			game_sphere_360 => {
   7 					    accessories => [
   8 							    'duck zapper',
   9 							    'balance board',
  10 							    'gyro-bot',
  11 							   ],
  12 					    controllers => 2,
  13 					    output      => 'HDTV',
  14 					    games       => {
  15 							    rpg    => "The Legend of Esmerelda",
  16 							    sports => "Madder Foosball, '09",
  17 							    racing => "North Pole Position",
  18 							    action => "Zombie Reindeer Hunter 2",
  19 							    puzzle => "Lonely Elf Solitaire",
  20 							   },
  21 					   },
  22 		       },
  23 	       };
  24 
  25 my $config = Config::General->new( 
  26 				  -StoreDelimiter => ' = ',
  27 				  -LowerCaseNames => 1,
  28 				  -SaveSorted     => 1,
  29 				 );
  30 
  31 my $cfg_file = "game_sphere.conf";
  32 
  33 # This method always returns undef unless it croaks.
  34 # That is the only thing I don't like about this module!
  35 eval { ! $config->save_file( $cfg_file, $toy_data ) } or die $@;
  36 
  37 print "Toy config saved to file $cfg_file\n";
View Source (POD)