in reply to Re: Conditonal insert into Array? (Tie::File)
in thread Conditonal insert into Array? (Tie::File)

Test Script

As requested, i've dummied up a short test script to show the functionality of the above snippet...

The instructions are embedded as comments (i know, should be POD..) in the script, and it needs no inputs beyond fiddling with the indicated 3 variables between runs.

$msg_id should be incremented with each running of the script.
$thread should be the last $msg_id, plus one to make a new top-level post, or it should inherit the $thread of the parent if a reply
$parent can be set to the $thread_$msg_id of any arbitrary existing post to create a reply.

When replying, the $thread should always match the parent's $thread, but the $msg_id is the current $msg_id, in the incremental sequence. In real life all of this is accomplished by simply writing a number to a file, and incrementing it each time the script is called.

It will produce it's own output on first running, and should be allowed to continue to work on that file for subsequent runs. The output is best understood if viewed with a web browser.

In real life, this output file is inserted into another .html file via SSI, and is wrapped in <ol> </ol> tags, which will number all of the top-level posts. Try it manually and you'll see what i mean.

#!/usr/bin/perl -w use Tie::File; use strict; ### ### This is a complete, self-contained dummy script to ### test the insert functionality of this sub... ### ###################################################################### +#### ### ### These vars should be changed to demonstrate different behaviours ### my $thread = "208"; # is the thread identifier my $msg_id = "211"; # is this message's unique number my $parent = "208_208"; # is the message_id of the parent post ## To create new top-level post set $thread=$msg_id, $parent is ununse +d ## and should be undef ## All replies to given top-level post should carry same $thread value +, ## whereas $msg_id is incremented ## Set $parent to: ($thread . "_" . $msg_id) to make a reply to the ## post which carries that message_id string in the html comment ## $msg_id should be unique for each post ($msg_id++) ## $thread should be unique for each thread ###################################################################### +### ### ### These vars are (relatively) static ### my @threads; # the array we will use my $main_threads_file = "threads.html"; # the threads file we will +work on my $link = "<li><!-- message_id=$thread" . "_" . "$msg_id --><a href= +\"http://link_to_post\">dummy link to message $thread - $msg_id</a>"; ### Update the main threads file tie @threads, 'Tie::File', $main_threads_file or syslog('err', "update +_threads couldn't open file: $main_threads_file"); if ($thread == $msg_id) { # put it right at the top, it's a new thread unshift @threads, $link; } else { for my $i (0 .. $#threads) { if ($threads[$i] =~ $parent) { if ($threads[$i+1] eq "<ul type=\"disc\">") { # insert $link after the next element (after the <ul>) $threads[$i+1] .= "\n$link"; } else { # insert three new lines/records after the parent $threads[$i] .= "\n<ul type=\"disc\">\n$link\n</ul>\n" +; } last; } } } untie @threads;
This isn't really the final state of the production code for this, as it also needs to understand that sometimes replies should not be listed on the main threads file, but rather only the total *number* of replies, and users must click on the parent link to expand the thread. If anyone's interested i can post the complete code including the flat-threads-file aware stuff...

Replies are listed 'Best First'.
(jeffa) 3Re: Conditonal insert into Array? (Tie::File)
by jeffa (Bishop) on May 29, 2003 at 06:30 UTC

    Well, that is not exactly how i would have solved the problem, but if it works for you, then good. If you are curious as to how i would solve the problem, then read on.

    Use A Database!

    That's right. Store your data in place that is used to store data. Display your data in a place that is used to display data. Don't mix the two. Repeat. Don't mix content and presentation. You say don't have access to a database? Get one! Between SQLite, MySQL, and PostgreSQL you have no excuses not to "upgrade" your skills. ;)

    Of course, seeing as how you have come along this far with your solution, switching now probably seems impossible. I understand that completely, but here is some code that keeps track of "threads" via DBI.pm and CGI.pm. I used the MySQL database with the following table creation syntax:

    CREATE TABLE node ( id int(10) NOT NULL auto_increment, name varchar(64) NOT NULL default '', parent int(10) default '0', PRIMARY KEY (id) );
    The following CGI script will print a form allowing the user to enter a title for the new "message" and an optional "parent id" to link to. It's id is auto-generated by the database and the user can't change it. Any messages are printed out via a recursive subroutine, but first they are fetched from the database and placed into a datastructure whose structure was concocted by me in a rather quick and dirty fashion. Improvements are always welcome.
    use strict; use warnings; use DBI; use Data::Dumper; use CGI::Pretty qw(:standard *ul); my %node; my $dbh = DBI->connect( qw(DBI:vendor:database:host user pass), {RaiseError=>1} ); print header(),start_html('messages'), h1('messages'),start_form, 'New thread title: ', textfield('name'),br, 'Parent thread: ', textfield('parent'),br, submit('go'),end_form, ; insert_message(param('name'),param('parent')) if param('go'); display_messages(); print end_html; $dbh->disconnect; sub display_messages { my $sth = $dbh->prepare('select id,name,parent from node'); $sth->execute; while (my $row = $sth->fetchrow_hashref()) { $node{$row->{id}} = { name => $row->{name} }; push @{$node{$row->{parent}}->{children}}, $row->{id} if $row->{parent}; } # uncomment this to see the datastructure #print pre(Dumper \%node),hr; print start_ul; display_thread($_) for sort {$a<=>$b} keys %node; print end_ul; } sub display_thread { my $id = shift; my $node = delete $node{$id}; return unless $node; print li("$id: " . $node->{name}); return unless $node->{children}; print start_ul; display_thread($_) for @{$node->{children}}; print end_ul; } sub insert_message { my ($name,$parent) = @_; my $sth = $dbh->prepare(' insert into node(name,parent) values(?,?) '); $sth->execute($name,$parent); } # this sub is not used, but i'll leave it anyway ;) sub get_last_id { return $dbh->selectcol_arrayref('SELECT last_insert_id()')->[0]; }

    jeffa

    L-LL-L--L-LL-L--L-LL-L--
    -R--R-RR-R--R-RR-R--R-RR
    B--B--B--B--B--B--B--B--
    H---H---H---H---H---H---
    (the triplet paradiddle with high-hat)
    
Re: Re: Re: Conditonal insert into Array? (Tie::File)
by BrowserUk (Patriarch) on May 28, 2003 at 15:12 UTC

    If i'm interpreting your code and comments correctly, you are expecting that

    $threads[$i] .= "\n<ul type=\"disc\">\n$link\n</ul>\n";

    will insert 3 (or 4?) new lines after line $i. Whilst this may work, at least some of the time, it is TWWTDI (the wrong way to do it:).

    From the Tie::File pod:

    Inserting records that contain the record separator string is not supported by this module. It will probably produce a reasonable result, but what this result will be may change in a future version. Use 'splice' to insert records or to replace one record with several.

    You should replace that line with

    splice @threads, $i, 0, '<ul type=\"disc\">', "$link",'</ul>';

    Which says 'insert the list (second line above) at position $i, whilst deleting nothing (0)'.

    See perlfunc:splice for more details.


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


      Ah! you are indeed correct!

      The way i'm doing it now *does* work (it's the last \n that's optional, not the first), but i'll try as the POD suggests..

      The important thing is that it's possible for the "parent" match to actually be the last line in the file (last element in the array) and so the array will have to be extended by n elements.

      thanks!