http://qs1969.pair.com?node_id=26921
Category: Miscellaneous
Author/Contact Info Zenon Zabinski | zdog7@hotmail.com
Description: Script for managing one's collection of books.

Update: This was completely redone with a new interface and information stored in MySQL and the like.

#!/usr/bin/perl -w
#
# mybooks.pl - Script for managing one's collection of books.
#

use strict;
use DBI;
use DBIx::DataSource qw ( create_database );
use Getopt::Std;
use vars qw ( $db_name $db_user $db_pass );


### BEGINNING of user defined variables 

$db_name = 'mybooks';  # Name of mysql database. (instead of -d option
+)
$db_user = 'root';     # Username to access database. (instead of -u o
+ption)
$db_pass = '';         # Password to access database. (instead of -p o
+ption)

### END of user defined variables


my %opts = ();
getopts ('Id:hp:u:', \%opts);

$db_name = $opts{'d'} || $db_name || die "No database name specified."
+;
$db_user = $opts{'u'} || $db_user;
$db_pass = $opts{'p'} || $db_pass;

$opts{'h'} && usage () && exit ();
$opts{'I'} && init_database () && exit ();

my %cmds = ( add => [ 'Add a book to the database.', \&add_book ],
             del => [ 'Remove a book from the database.', \&del_book ]
+,
             mod => [ 'Change the status of a book.', \&mod_book ],
             view => [ 'View books currently in the db.', \&view_books
+ ] );

print "Type 'quit' to exit or 'help' for a list of other commands.\n";

for (;;)
{
    my $q = get_input ("? ");
    
    if ($q eq 'quit') 
    { 
        print "Bye.\n";
        exit ();
    }
    
    if ($q eq 'help')
    {
        for (keys %cmds) { print "$_ :  $cmds{$_}[0]\n"; }
        next;
    }

    grep ($q eq $_, keys %cmds) && $cmds{$_}[1]->() && next;

    print "Unknown command '$q'.\n";
}


### SUB definitions lie after this point

sub init_database
{
    create_database ("DBI:mysql:$db_name", $db_user, $db_pass)
        or die $DBIx::DataSource::errstr;

    do_sql ("CREATE TABLE $db_name ( 
                   id INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, 
                   title VARCHAR(100) NOT NULL, 
                   author VARCHAR(60), 
                   isbn VARCHAR(15), 
                   status CHAR(1) NOT NULL DEFAULT 'W', 
                   PRIMARY KEY(id))")
        or die "Could not create table in database '$db_name'.\n";
}

sub get_input
{
    print shift ();
    chomp ($_ = <STDIN>);
    $_;
}

sub add_book
{
    my $title  = get_input ('Enter title: ');
    my $author = get_input ('Enter author: ');
    my $isbn   = get_input ('Enter isbn: ');
    my $status = get_input ('Do you own this book? (y/n) ') eq 'y' ? '
+H' : 'W';
    
    do_sql ("INSERT INTO $db_name 
                 VALUES (NULL, '$title', '$author', '$isbn', '$status'
+)");
}

sub del_book
{
    my $id = get_input ('Enter book id: ');
    
    do_sql ("DELETE FROM $db_name WHERE id=$id");
}

sub mod_book
{
    my $id = get_input ('Enter book id: ');
    my $status = get_input ('Do you own this book? (y/n) ') eq 'y' ? '
+H' : 'W';
    
    do_sql ("UPDATE $db_name SET status='$status' WHERE id=$id");     
+  
}

sub view_books
{
    my $type;

    {
        $type = get_input ('Show all/wanted/owned? (A/W/H) ');
        grep ($type eq $_, qw ( A H W )) || redo;
    }
    
    my $dbh = DBI->connect ("DBI:mysql:$db_name", $db_user, $db_pass)
        or die $DBI::errstr;
    my $sth;

    if ($type eq 'A')
    {
        $sth = $dbh->prepare ("SELECT * FROM $db_name");
    }
    else
    {
        $sth = $dbh->prepare ("SELECT * FROM $db_name WHERE status='$t
+ype'");
    }
    
    my $rv = $sth->execute ();
    my $books = $sth->fetchall_arrayref ();
    @$books = sort { $$a[2] cmp $$b[2] } @$books;

    for my $book (@$books)
    {
        print "$$book[0]: $$book[2]. $$book[1]. $$book[3]. $$book[4]\n
+";
    }

    $dbh->disconnect ();
}

sub do_sql
{
    my $dbh = DBI->connect ("DBI:mysql:$db_name", $db_user, $db_pass)
        or return 0;
    $dbh->do (shift ());
    $dbh->disconnect ();
    1;        
}

sub usage
{
    print <<EOF;
Usage: perl mybooks.pl [options]

Options:
    -h     Prints this help message then exits.
    -I     Initializes MySQL database then exits.
    -d X   Name of the MySQL database to use.
    -u X   Username with which to connect to MySQL.
    -p X   Password with which to connect to MySQL.
EOF
}
Replies are listed 'Best First'.
RE: mybooks.pl
by chromatic (Archbishop) on Aug 10, 2000 at 05:24 UTC
    A couple of fixes and a couple of suggestions. First, the mkdir thing has a precedence problem, as tye suggests. Use parenthesis or or to fix it:
    mkdir("$dir", 0777) || die "Could not mkdir \"$dir\": $!\n";
    Second, the chop bothers me, 'cuz it's easy to grab something you don't want. (In this case, it's right, but I prefer chomp).

    Third, there are a couple of other ways for doing your menu stuff. Some people prefer this:

    ($menu eq 1) && addwant(); ($menu eq 2) && addhave();
    When I have to do something like this, I usually use a data structure of references:
    my @actions = qw( \&addwant \&addhave ); # and so forth # get input if (defined (my $action = $actions[$menu])) { $action->(); } else { print "\n\n\nGoodbye!\n"; exit; }
    Finally, if you want to clear the screen, there's a bit in perlfaq8 that might help. Not a big deal, though.
      I liked your idea of using a data stucture of references, after all, I was trying to do something similar from the start, but my lack of Perl skill got in the way. Anyway, I put it into my code and there was a problem, but I fixed it by writing $actions[$menu-1] instead of $actions[$menu] since the options in the menu number from 1 to 5 while the elements of array @actions number from 0 to 4. However, I still get the following error:

      Can't use string ("\&addwant") as a subroutine ref while "strict refs" in use at mybooks.pl line 50, <STDIN> chunk 1.

      Is there any way to fix this other than by not using strict?

      Thanx.

      Zenon Zabinski | zdog | zdog7@hotmail.com

        Yes, the array of code references should not have been created using qw. That string-ifies the contents of the array, which means that you end up with an array containing ("\&addwant"), etc.

        Which isn't what you want. You want:

        my @actions = (\&addwant, \&addhave);
(zdog) RE: mybooks.pl
by zdog (Priest) on Aug 09, 2000 at 05:43 UTC
    Wups! I didn't know I had to put the code into code tage. Well, here it is:

    Update: Nevermind, I just realized I could edit my code. Oh, look, another --.

    Zenon Zabinski | zdog | zdog7@hotmail.com

      Actually, you make a good point. If you get a box that says "Enter your code here" you would think that the en-coding (pun intended) would be automatic, but it is not. I've been bit by that before too. At the very least, it should have <TEXTAREA><CODE>
      </CODE>
      </TEXTAREA>
      like some other parts of the site do. So no -- for you as you pointed out an important er...sub-optimal feature of the site. :)

RE: mybooks.pl (or or | | )
by ybiC (Prior) on Aug 09, 2000 at 06:15 UTC
    I have an honest question reqarding zdog's <code>code</code>.

    Exactly 31 days ago, this thread led me to the conclusion that or is generally better than || when used with die.   Does this conclusion apply here as well?
        cheers,
        ybiC

    Update: escaping all of <code>code</code> was meant as a wry joke on zdog, who is known to have keen sense of humor.
    ybiC scans horizon for personal drummer...

      Well, yes and no. If you use open as a list operator like this:
      open FOO || die "Argh: $!\n";

      then you will not get what you want. However, this:
      open (FOO) || die "Argh: $!\n";

      will do what you want it to (which is what zdog used).

      So in general, or is a safer bet, but || is not necessarily wrong. And perl will let you know that there is a precedence problem if you use -w.

      BlueLines

      Disclaimer: This post may contain inaccurate information, be habit forming, cause atomic warfare between peaceful countries, speed up male pattern baldness, interfere with your cable reception, exile you from certain third world countries, ruin your marriage, and generally spoil your day. No batteries included, no strings attached, your mileage may vary.

        And in the case we are discussing here, || is very much wrong. Consider the first example of || die in the posted code:

        mkdir "$dir", 0777 || die "Could not mkdir \"$dir\": $!\n";

        Here is a demonstration of why this is wrong:

        my $dir= "source"; # A directory that I know exists. warn "Using or...\n"; mkdir "$dir", 0777 or die "Could not mkdir \"$dir\": $!\n"; warn "Using ||...\n"; mkdir "$dir", 0777 || die "Could not mkdir \"$dir\": $!\n"; warn "Done.\n";

        This produces the following output:

        E:\etm\Work>perl -w mkdir.pl Using or... Could not mkdir "source": File exists Using ||... Done.

        Note that -w was silent as well, so I'm not sure when it will warn me of a precedence problem (I'm using Perl 5.6.0).

                - tye (but my friends call me "Tye")
        i'm using 5.005_03, and this is what happens when i try to use || in a naughty way:
        #!/usr/bin/perl $FOO="/etc/passwd"; open FOO || die "Argh: $!\n";

        results in:
        nooky:~$ ./foo.pl Precedence problem: open FOO should be open(FOO) at ./foo.pl line 3.
        Running with -w like a good boy produces even more useful output:
        nooky:~$ ./foo.pl Precedence problem: open FOO should be open(FOO) at ./foo.pl line 3. Probable precedence problem on logical or at ./foo.pl line 3.

        I have not played with 5.6 yet, but I'd be a little upset if this feature was removed....

        BlueLines

        Disclaimer: This post may contain inaccurate information, be habit forming, cause atomic warfare between peaceful countries, speed up male pattern baldness, interfere with your cable reception, exile you from certain third world countries, ruin your marriage, and generally spoil your day. No batteries included, no strings attached, your mileage may vary.