Go to content Go to navigation Go to search

Dynamic Accessor Methods · 7 hours ago by Dylan Doxey

So here I am writing this module which pulls a database record that is keyed by a topic keyword. And each accessor method was just a copy of the one preceding it, over and over. To make matters worse, each time there's a new topic added to the database am I to go an add yet another accessor method? Not.


Introducing the dynamic accessor method.


...

for my $topic (@topics) {

    my $method_name = sprintf '%s::get_%s', __PACKAGE__, $topic;

    my $method_rc = sub {
        my $self = shift;

        my $page_rh = $self->{db}->select_rh( {
            from     => 'content',
            where_rh => {
                topic => $topic,
            },
        } );

        $page_rh->{text} //= $MT_STR;

        return $page_rh->{text};
    };

    {
        no strict 'refs';
        *{$method_name} = $method_rc;
    }
}

...

Anyone curious about my database interface?

Changing Case in Vim · 5 days ago by Dylan Doxey

Every once in a while I'm preparing some text for something or other and I think, "I sure would like to change all of this text to lower case."

...

50 504 gateway_timeout
51 505 http_version_not_supported
52 506 variant_also_negotiates
53 507 insufficient_storage
54 509 bandwidth_limit_exceeded
55 510 not_extended
:'<,'>!tr [A-Z] [a-z]

This vim command is composed of three parts:

  1. '<,'> -- this is the region of highlighted text.
  2. ! -- what follows is a shell command
  3. tr [A-Z] [a-z] -- translate class [A-Z] to [a-z] (same as Perl tr/[A-Z]/[a-z]/).


I'm sure to be back for this snippet soon.

HTML Formatting Your Perl · 13 days ago by Dylan Doxey

Ever wonder how to make your Perl code presentable on a website? Use perltidy.

perltidy -html rand_sort.pl

Then cut & paste the resulting HTML into your blog and delight your readers.


#!/usr/bin/perl 

use strict;
use warnings;

my @numbers = sort { -1 + int rand 3 } 1 .. 1000;

for my $x (@numbers) {

    print "$x\n";
}

However, in my opinion the best option for a blog snippet is vim. While you've got your Perl (or whatever) file open in vim use the :TOhtml command. That will open another buffer with HTML in it. The resulting HTML page is:

 1 #!/usr/bin/perl 
 2 
 3 use strict;
 3 use warnings;
 4  
 5 my @numbers = sort { -1 + int rand 3 } 1 .. 1000; 
 6  
 7 for my $x (@numbers) { 
 8  
 9     print "$x\n"; 
10 } 
11  

For more information about using perltidy see http://perltidy.sourceforge.net/perltidy.html#html_options. For the record, my example of perltidy HTML output is pretty lame. As linked from the perltidy home page (http://perltidy.sourceforge.net/) check out this example of perltidy HTML output: http://perltidy.sourceforge.net/Conf.pm.html.


For more details about using :TOhtml user command in vim see http://vimdoc.sourceforge.net/htmldoc/syntax.html#:TOhtml


Now I won't forget how to do that again!

Ejecting the CDROM · 75 days ago by Dylan Doxey

Well, the CDROM has been open for the last couple of hours.

I did a quick 'man eject' with hope that there's a way to close it without having to get out of my chair.

 eject -t

Ah, that's better.

SCP -- The World's Most Convenient File Transfer · 117 days ago by Dylan Doxey

Okay, world's most convenient might be a stretch if you don't do it very often. But if you can force yourself to remember this ...

scp -P 1234 favicon.ico yourself@example.com:/var/www/example.com/htdocs/

That's a relatively convenient way to push your brand new favicon.ico file to the server without all the drudgery of messing around with a GUI FTP client, or the rigmarole adding to your repository, committing, logging in on the server and updating.


And it goes the other way also. Need to fetch a report from your home directory?

scp -P 1234 yourself@example.com:/home/yourself/big_report.csv .

Now that's getting the job done without a lot of distraction!

Note: In your world you're likely change the port number 1234 to something else, or leave out the -P # option altogether.



Happy computing.

Just a Little Program... to be sure. · 183 days ago by Dylan Doxey

Some things are just obviously correct and you go about your business doing things the correct way until one day you realize there's a better way.

For example, you're going about your business, about to populate an array with regex matches and you might wonder, "Does it really need to be done in a loop?"

 1 #!/usr/bin/perl -Tw
 2 
 3 use strict;
 4 use warnings;
 5 use Data::Dumper;
 6 
 7 my $text = "
 8     abc123abc
 9     abc123abc
10     abc123abc
11     abc123abc
12 ";
13 
14 my $regex = qr{ ( \d+ ) }xms;
15 
16 {
17     my @matches = $text =~ $regex;
18     
19     print 'A: ' . Dumper( \@matches ) . "\n";
20 }   
21 {   
22     my @matches = $text =~ m/$regex/g;
23     
24     print 'B: ' . Dumper( \@matches ) . "\n";
25 }   
26 {   
27     my @matches;
28     while ( $text =~ m/$regex/g ) {
29     
30         push @matches, $1;
31     }
32     
33     print 'C: ' . Dumper( \@matches ) . "\n";
34 }

If you're still reading, then it's probably not obvious to you... as it now is to me.


Long story short, here's what you get:

 1 A: $VAR1 = [
 2           '123'
 3         ];
 4 
 5 B: $VAR1 = [
 6           '123',
 7           '123',
 8           '123',
 9           '123'
10         ];
11 
12 C: $VAR1 = [
13           '123',
14           '123',
15           '123',
16           '123'
17         ];


Happy computing.

Permutations · 187 days ago by Dylan Doxey

Last night was was thinking to myself, "Self, I sure would like a list of all the permutations of these characters in a string of size n."

That's the point when I set myself to work and produced this little snippet of code.

 1 sub permute {
 2     my ($letters_ra,$size) = @_;
 3 
 4     return
 5         if not $size;
 6 
 7     my $words_ra = permute( $letters_ra, $size - 1 );
 8 
 9     return $letters_ra
10         if not @{ $words_ra || [] };
11 
12     my @words;
13 
14     for my $word (@{ $words_ra }) {
15 
16         for my $letter (@{ $letters_ra }) {
17 
18             push @words, "$letter$word";
19         }
20     }
21 
22     return \@words;
23 }


Yea, that gets the job done.

27 # create a list of a-z
28 my @letters = map { chr $_ } ( 97 .. 122 );
29 
30 # create a list of all four letter words
31 my $words_ra = permute( \@letters, 4 );


Now that we've got this list laying around, let's see if we can register any of them as domains.

35 use LWP::Simple qw( get );
36 
37 WORD:
38 for my $word (@{ $words_ra }) {
39 
40     my $json = get( "http://instantdomainsearch.com/services/quick/?name=$word" );
41 
42     my ($com,$net,$org) = $json =~ m{'name':'$word','com':'(\w+)','net':'(\w+)','org':'(\w+)'}xms;
43 
44     next WORD
45         if "$com$net$org" eq 'uuu';
46 
47     print "$word: com:$com, net:$net, org:$org\n";
48 }


That's going to be 264 domain lookups which average around 1 second per. That should take about 42 hours to run.

I've found that just because instantdomainsearch.com reports it as available, doesn't mean it's available.

Happy computing.

Booting From a Thumb-Drive? · 351 days ago by Dylan Doxey

Who needs it? That's just bragging rights for nerds!

That is, unless you need it.

I bought the Shuttle SA76G2 bare bones system, AMD Pantheon II, and 4GB of memory. This should make a nice little desktop workstation.

I slapped it all together, started it up, and popped in the Ubuntu 9.04 install disk. I entered the BIOS setup and set the the book disk priority to go to CDROM first.

That should do it. Right?

Wrong.

Evidently this machine doesn't support ISOLINUX, and therefore won't boot from the Ubuntu installer disk.

Cutting a long story short -- I found the solution in using a Smart Boot Manager image to boot the machine on a thumb-drive, which in turn presents you with a menu asking what your preferred boot device is.

Yes!

The procedure goes a little something like this:

The key component here is the sbm.bin Smart Boot Manager image file which I found by poking around on the Ubuntu install disk.

/media/cdrom/install/sbm.bin


Those of you who may have worked with Chilton's automotive repair manuals might have noticed the occasional directive such as, "Next remove the exhaust manifold." But as you proceed it is immediately apparent that this operation deserves more than a casual one line mention. This would be the case above when I stated, "install the Smart Boot Manager image -- sbm.bin".

Here's a play by play.

After plugging the thumb drive in, you'll need to know what device it is. (You'll be pleased to know that it's really really hard to accidentally format your primary boot device in Linux.)

dylan@doxey.org:~/Desktop$ sudo fdisk -l

Disk /dev/sda: 500.1 GB, 500107862016 bytes
255 heads, 63 sectors/track, 60801 cylinders
Units = cylinders of 16065 * 512 = 8225280 bytes
Disk identifier: 0x000c4861

   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1       59483   477797166   83  Linux
/dev/sda2           59484       60801    10586835    5  Extended
/dev/sda5           59484       60801    10586803+  82  Linux swap / Solaris
Note: sector size is 4096 (not 512)

Disk /dev/sdc: 7952 MB, 7952142336 bytes
217 heads, 32 sectors/track, 279 cylinders
Units = cylinders of 6944 * 4096 = 28442624 bytes
Disk identifier: 0x20202020

   Device Boot      Start         End      Blocks   Id  System
/dev/sdc1               1         280     7765508    b  W95 FAT32
Partition 1 has different physical/logical beginnings (non-Linux?):
     phys=(0, 1, 1) logical=(0, 1, 32)
Partition 1 has different physical/logical endings: 
     phys=(120, 216, 32) logical=(279, 126, 32)

Disk /dev/sdb: 129 MB, 129236992 bytes
8 heads, 32 sectors/track, 986 cylinders
Units = cylinders of 256 * 512 = 131072 bytes
Disk identifier: 0xfd266c52

   Device Boot      Start         End      Blocks   Id  System
/dev/sdb1   *           1         986      126192    6  FAT16

That's the little bugger at the bottom: /dev/sdb1. (I think there is probably a better way to identify your thumb drive. But you can be pretty sure by removing the thumb drive and observing fdisk -l one more time. In my case there's just nothing else that would be FAT 16. Gosh!)
Doing fdisk -l is generally an intert action -- but you do need to do it as root to see anything useful.

Format the thumb drive.

dylan@doxey.org:~/Desktop$ sudo mkfs.ext3 /dev/sdb1
mke2fs 1.41.4 (27-Jan-2009)
Filesystem label=
OS type: Linux
Block size=4096 (log=2)
Fragment size=4096 (log=2)
251968 inodes, 1007612 blocks
50380 blocks (5.00%) reserved for the super user
First data block=0
Maximum filesystem blocks=1031798784
31 block groups
32768 blocks per group, 32768 fragments per group
8128 inodes per group
Superblock backups stored on blocks: 
	32768, 98304, 163840, 229376, 294912, 819200, 884736

Writing inode tables: done                            
Creating journal (16384 blocks): done
Writing superblocks and filesystem accounting information: done

This filesystem will be automatically checked every 36 mounts or
180 days, whichever comes first.  Use tune2fs -c or -i to override.

Obviously there's nothing inert about formatting a storage device. But you'll find it's hard to format something you didn't mean to.

Now add the SBM image.

dylan@doxey.org:~/Desktop$ sudo dd if=/media/cdrom/install/sbm.bin of=/dev/sdb1
2880+0 records in
2880+0 records out
1474560 bytes (1.5 MB) copied, 0.934536 s, 1.6 MB/s

Now your thumb drive is suitable for booting up and providing an alternate means of directing the machine which device to boot from.


Now that's something worth bragging about!

Happy computing.

Parsing HTML · 446 days ago by Dylan Doxey

Did you every think to yourself, "I wish I could split this HTML document up into an array of tokens with descriptive keys."?

Well, it's occurred to me. So here's what I came up with.

  1 package Dox::Parser; 
  2  
  3 use strict;
  4 use warnings;
  5 {
  6     use Carp;
  7     use File::Slurp qw( slurp );
  8 }
  9  
 10 my (%TYPE_REGEX_FOR,@TYPES);
 11 {
 12     use Readonly;
 13  
 14     # HTML identifiers may have : or - such as xml:lang or http-equiv. 
 15     # No recognition of mixed case HTML identifiers such as <Body> or <Title>. 
 16     my $ident_re  = qr{ (?: [a-z:-]+ | [A-Z:-]+ ) }xms;
 17  
 18     # Quoted strings may contain backlash escaped quotes 
 19     my $q_str_re  = qr{ ' (?: [\\]['] | [^'] )* ' }xms;
 20     my $qq_str_re = qr{ " (?: [\\]["] | [^"] )* " }xms;
 21  
 22     Readonly %TYPE_REGEX_FOR => (
 23         terminal_tag   => qr{ ( < \s* / \s* $ident_re [^>]* > ) }xms,
 24         begin_tag      => qr{ ( < \s* $ident_re ) }xms,
 25         end_tag        => qr{ ( /? > ) }xms,
 26         template_code  => qr{ ( \[% \s* .+? \s* %\] ) }xms,
 27         open_comment   => qr{ ( <!-- ) }xms,
 28         close_comment  => qr{ ( --> ) }xms,
 29         open_doctype   => qr{ ( <!DOCTYPE ) \s }xms,
 30         attribute      => qr{ ( $ident_re \s*=\s* (?: $q_str_re | $qq_str_re ) ) [\s/>] }xms,
 31         html_word      => qr{ ( $ident_re ) \s }xms,
 32         quoted_string  => qr{ ( $q_str_re | $qq_str_re ) }xms,
 33         whitespace     => qr{ ( \s+ ) }xms,
 34         content        => qr{ ( [^<]+? ) (?: \[ [%] | [<] ) }xms,
 35     );
 36  
 37     # The priority of types in evaluating 
 38     # the leading characters of the HTML string. 
 39     Readonly @TYPES => qw( 
 40         open_doctype 
 41         open_comment 
 42         close_comment 
 43         whitespace 
 44         terminal_tag 
 45         begin_tag 
 46         end_tag 
 47         attribute 
 48         html_word 
 49         quoted_string 
 50         template_code 
 51         content 
 52     );
 53 }
 54  
 55 sub new {
 56     my ($class,$filename) = @_;
 57  
 58     croak "can't find $filename\n" 
 59         if !stat $filename;
 60  
 61     # Memory usage concerns? Sorry. :( 
 62     my $html = slurp( $filename );
 63  
 64     my $self = bless {
 65         html       => $html,
 66         last_token => [],
 67     }, $class;
 68  
 69     return $self;
 70 }
 71  
 72 sub next_token {
 73     my $self = shift;
 74  
 75     my $html = $self->{html};
 76  
 77     for my $type (@TYPES) {
 78  
 79         my $regex = $TYPE_REGEX_FOR{$type};
 80  
 81         if ( $html =~ m/\A $regex /xms ) {
 82  
 83             my $token = $1;
 84  
 85             $self->{html} = substr $html, length $token;
 86  
 87             push @{ $self->{last_token} }, $token;
 88  
 89             return { type => $type, token => $token, };
 90         }
 91     }
 92     return;
 93 }
 94  
 95 sub push_back {
 96     my ($self,$token) = @_;
 97  
 98     $self->{html} = $token . $self->{html};
 99  
100     return length $token;
101 }
102  
103 1;

Here's a little program, which I like to call parser_tester.pl, which demonstrates this baby in action.

 1 #!/usr/bin/perl 
 2  
 3 use strict;
 4 use warnings;
 5 {
 6     use lib qw( . );
 7     use Dox::Parser;
 8     use File::Slurp qw( write_file );
 9     use Term::ANSIColor qw( :constants );
10 }
11  
12 my %filename = (
13     before => 'document.html',
14     after  => 'parsed_document.html',
15 );
16  
17 my $document_text = "";
18 my $parser = Dox::Parser->new( $filename{before} );
19  
20 while ( my $token_rh = $parser->next_token() ) {
21  
22     print "{" . $token_rh->{token} . "}";
23     print GREEN, "(" . $token_rh->{type} . ")", RESET;
24     print "\n";
25  
26     $document_text .= $token_rh->{token};
27 }
28  
29 write_file( $filename{after}, $document_text );
30  
31 print RED, BOLD, "\ncreated $filename{after}\n\n", RESET;
32  
33 1;

When you run parser_tester.pl you'll get an enumeration of the parsed HTML tokens, each in curly brackets, and the named token type in green (thanks to Term::ANSIColor). This program assumes you've got your sample HTML in document.html, and it will subsequently create parsed_document.html. The two files ought to be identical, which indicates the parser successfully identified all of the tokens and didn't forget anything.


Coming next: Dox::FSA -- a Finite State Automaton module which can be applied to make sense of the token stream so you can do correct and useful modifications to the HTML document.

Knowing Your File System · 461 days ago by Dylan Doxey

For a quick assessment of your drive space distribution and usage use the df command.

dylan@dev.doxey.org$: ~ df -h
Filesystem        Size  Used Avail Use% Mounted on
/dev/mapper/root   15G   14G  661M  96% /
varrun            2.0G   56K  2.0G   1% /var/run
varlock           2.0G     0  2.0G   0% /var/lock
udev              2.0G   40K  2.0G   1% /dev
devshm            2.0G     0  2.0G   0% /dev/shm
/dev/sda1         237M   24M  201M  11% /boot
/dev/mapper/home  440G  7.8G  410G   2% /home

The -h switch indicates human readable mode.
Gosh, looks like I ought to move some of my junk under /home.


For a more granular display of where the bulk of your stuff is, use the du command.

dylan@dev.doxey.org$: ~ du -h --max-depth=1
52K	./.subversion
7.6G	./rep
9.5M	./sandbox
45M	./.cpan
4.0K	./.gnupg
64K	./bin
128K	./.vim
7.7G	.

Again, the -h switch gives you the easier to read numeric values.
The --max-depth option let's you control the depth of the display. The default is unlimited depth.

Previous