Name

counter — manipulate a persistent, named counter

ATTRIBUTES

Attribute Pos. Req. Default Description
name | file Yes CATROOT/etc/counter Counter file to use. Taken relatively to CATROOT unless absolute pathname is specified.
start Counter start value
sql A table:field specification, if [counter] is to increment a field in an SQL database.
inc_routine Routine to use to increase the counter. The routine should be an existing Perl function, catalog subroutine, or global subroutine
bypass 0 Bypass the existing database connection, and instead connect to the database anew, if sql attribute is used.
dsn DBI_DSN DSN information to connect to the SQL database, if sql attribute is used
user User to connect to the database as, if sql attribute is used
pass Password to provide during connection to the database, if sql attribute is used
attr Extra content for the DBI->connect call
date Date-based counter? Set to any true value, or gmt to also signify GMT date
dec_routine Routine to use to decrease the counter The routine should be an existing Perl function, catalog subroutine, or global subroutine
value Only show the counter value, without incrementing or decrementing it? (This option is not applicable to SQL counters).
decrement 0 Decrement instead of incrementing the counter?
interpolate     0 interpolate output?
hide     0 Hide the tag return value?

DESCRIPTION

The tag provides an interface to the counter functionality within Interchange. The counters are usually kept as text files, but can also be sequences in SQL tables.

[counter] can increase and decrease counters, or set them to specific values. In addition, custom increment or decrement functions can be used.

BEHAVIOR

This tag does not appear to be affected by, or affect, the rest of Interchange.

EXAMPLES

Example: Basic counter file

The following creates a counter file, counter.basic in your catalog root directory. The counter starts at 10.

[counter file=counter.basic start=10]

Example: Basic date-based counter file

The following creates two date-based counter files, counter.loc and counter.gmt in your catalog root directory.

[counter file=counter.loc date=1]
[counter file=counter.gmt date=gmt]

Example: Counter using steps of +2 and -2, with in-place subroutine specification

The following creates two counter files, counter.p2 and counter.m2 in your catalog root directory. Counters initially start at 20; one adds 2 and one subtracts 2 each time they're called.

[counter
  file=counter.p2
  start=20
  inc-routine=`sub { shift(@_) + 2 }`
]
[counter
  file=counter.m2
  start=20
  decrement=1
  dec-routine=`sub { shift(@_) - 2 }`
]

Example: Counter using steps of +3 and -3, with Sub or GlobalSub routine specification

The following creates two counter files, counter.p3g and counter.m3g in your catalog root directory. Counters initially start at 20; one adds 3 and one subtracts 3 each time they're called.

You need the following in catalog.cfg or interchange.cfg:

Sub three_steps_forward <<EOR
sub {
  my $val = shift; $val += 3; return $val;
}
EOR

Sub three_steps_back <<EOR
sub {
  my $val = shift; $val -= 3; return $val;
}
EOR

And the following on an Interchange page:

[counter file=counter.p3 start=20 inc-routine=three_steps_forward ]
[counter file=counter.m3 start=20 decrement=1 dec-routine=three_steps_back]

Example: PostgreSQL database counter

Create sequence counter1 in the database:

CREATE SEQUENCE "counter1" start 1 increment 1 maxvalue 2147483647 minvalue 1 cache 1;

And use the counter on your pages:

[counter sql="table1:counter1"]

Example: MySQL database counter

Create table table2 and a sequence counter2 in that database:

create table table2(counter2 int NOT NULL AUTO_INCREMENT PRIMARY KEY);

And use the counter on your pages:

[counter sql="table2:counter2"]

Example: Oracle database counter

Create a sequence counter3 in the database:

CREATE SEQUENCE counter3 START WITH 1 INCREMENT BY 1 MAXVALUE 2147483647 MINVALUE 1 CACHE 2;

And use the counter on your pages:

[counter sql="table3:counter3"]

NOTES

The SQL field-updating routine is database-dependent; please see the tag source for exact behavior.

Date-based counters cannot be decremented.

AVAILABILITY

counter is available in Interchange versions:

4.6.0-5.9.0 (git-head)

SOURCE

Interchange 5.9.0:

Source: code/SystemTag/counter.coretag
Lines: 17


# Copyright 2002-2007 Interchange Development Group and others
# 
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.  See the LICENSE file for details.
# 
# $Id: counter.coretag,v 1.6 2007-03-30 23:40:49 pajamian Exp $

UserTag counter             Order        file
UserTag counter             addAttr
UserTag counter             attrAlias    name file
UserTag counter             PosNumber    1
UserTag counter             Version      $Revision: 1.6 $
UserTag counter             MapRoutine   Vend::Interpolate::tag_counter

UserTag fcounter            Alias        counter

Source: lib/Vend/Interpolate.pm
Lines: 2250

sub tag_counter {
  my $file = shift || 'etc/counter';
my $opt = shift;
#::logDebug("counter: file=$file start=$opt->{start} sql=$opt->{sql} routine=$opt->{inc_routine} \
 caller=" . scalar(caller()) );
if($opt->{sql}) {
  my ($tab, $seq) = split /:+/, $opt->{sql}, 2;
  my $db = database_exists_ref($tab);
  my $dbh;
  my $dsn;
  if($opt->{bypass}) {
    $dsn = $opt->{dsn} || $ENV{DBI_DSN};
    $dbh = DBI->connect(
          $dsn,
          $opt->{user},
          $opt->{pass},
          $opt->{attr},
        );
  }
  elsif($db) {
    $dbh = $db->dbh();
    $dsn = $db->config('DSN');
  }

  my $val;

  eval {
    my $diemsg = errmsg(
            "Counter sequence '%s' failed, using file.\n",
            $opt->{sql},
          );
    if(! $dbh) {
      die errmsg(
          "No database handle for counter sequence '%s', using file.",
          $opt->{sql},
        );
    } 
    elsif($seq =~ /^\s*SELECT\W/i) {
#::logDebug("found custom SQL SELECT for sequence: $seq");
      my $sth = $dbh->prepare($seq) or die $diemsg;
      $sth->execute or die $diemsg;
      ($val) = $sth->fetchrow_array;
    }
    elsif($dsn =~ /^dbi:mysql:/i) {
      $seq ||= $tab;
      $dbh->do("INSERT INTO $seq VALUES (0)")    or die $diemsg;
      my $sth = $dbh->prepare("select LAST_INSERT_ID()")
        or die $diemsg;
      $sth->execute()                or die $diemsg;
      ($val) = $sth->fetchrow_array;
    }
    elsif($dsn =~ /^dbi:Pg:/i) {
      my $sth = $dbh->prepare("select nextval('$seq')")
        or die $diemsg;
      $sth->execute()
        or die $diemsg;
      ($val) = $sth->fetchrow_array;
    }
    elsif($dsn =~ /^dbi:Oracle:/i) {
      my $sth = $dbh->prepare("select $seq.nextval from dual")
        or die $diemsg;
      $sth->execute()
        or die $diemsg;
      ($val) = $sth->fetchrow_array;
    }

  };

  logOnce('error', $@) if $@;

  return $val if defined $val;
}

unless (allowed_file($file)) {
  log_file_violation ($file, 'counter');
  return undef;
}

  $file = $Vend::Cfg->{VendRoot} . "/$file"
      unless Vend::Util::file_name_is_absolute($file);

for(qw/inc_routine dec_routine/) {
  my $routine = $opt->{$_}
    or next;

  if( ! ref($routine) ) {
    $opt->{$_}   = $Vend::Cfg->{Sub}{$routine};
    $opt->{$_} ||= $Global::GlobalSub->{$routine};
  }
}

  my $ctr = new Vend::CounterFile
        $file,
        $opt->{start} || undef,
        $opt->{date},
        $opt->{inc_routine},
        $opt->{dec_routine};
  return $ctr->value() if $opt->{value};
  return $ctr->dec() if $opt->{decrement};
  return $ctr->inc();
}

Source: lib/Vend/Interpolate.pm
Lines: 2250

sub tag_counter {
  my $file = shift || 'etc/counter';
my $opt = shift;
#::logDebug("counter: file=$file start=$opt->{start} sql=$opt->{sql} routine=$opt->{inc_routine} \
 caller=" . scalar(caller()) );
if($opt->{sql}) {
  my ($tab, $seq) = split /:+/, $opt->{sql}, 2;
  my $db = database_exists_ref($tab);
  my $dbh;
  my $dsn;
  if($opt->{bypass}) {
    $dsn = $opt->{dsn} || $ENV{DBI_DSN};
    $dbh = DBI->connect(
          $dsn,
          $opt->{user},
          $opt->{pass},
          $opt->{attr},
        );
  }
  elsif($db) {
    $dbh = $db->dbh();
    $dsn = $db->config('DSN');
  }

  my $val;

  eval {
    my $diemsg = errmsg(
            "Counter sequence '%s' failed, using file.\n",
            $opt->{sql},
          );
    if(! $dbh) {
      die errmsg(
          "No database handle for counter sequence '%s', using file.",
          $opt->{sql},
        );
    } 
    elsif($seq =~ /^\s*SELECT\W/i) {
#::logDebug("found custom SQL SELECT for sequence: $seq");
      my $sth = $dbh->prepare($seq) or die $diemsg;
      $sth->execute or die $diemsg;
      ($val) = $sth->fetchrow_array;
    }
    elsif($dsn =~ /^dbi:mysql:/i) {
      $seq ||= $tab;
      $dbh->do("INSERT INTO $seq VALUES (0)")    or die $diemsg;
      my $sth = $dbh->prepare("select LAST_INSERT_ID()")
        or die $diemsg;
      $sth->execute()                or die $diemsg;
      ($val) = $sth->fetchrow_array;
    }
    elsif($dsn =~ /^dbi:Pg:/i) {
      my $sth = $dbh->prepare("select nextval('$seq')")
        or die $diemsg;
      $sth->execute()
        or die $diemsg;
      ($val) = $sth->fetchrow_array;
    }
    elsif($dsn =~ /^dbi:Oracle:/i) {
      my $sth = $dbh->prepare("select $seq.nextval from dual")
        or die $diemsg;
      $sth->execute()
        or die $diemsg;
      ($val) = $sth->fetchrow_array;
    }

  };

  logOnce('error', $@) if $@;

  return $val if defined $val;
}

unless (allowed_file($file)) {
  log_file_violation ($file, 'counter');
  return undef;
}

  $file = $Vend::Cfg->{VendRoot} . "/$file"
      unless Vend::Util::file_name_is_absolute($file);

for(qw/inc_routine dec_routine/) {
  my $routine = $opt->{$_}
    or next;

  if( ! ref($routine) ) {
    $opt->{$_}   = $Vend::Cfg->{Sub}{$routine};
    $opt->{$_} ||= $Global::GlobalSub->{$routine};
  }
}

  my $ctr = new Vend::CounterFile
        $file,
        $opt->{start} || undef,
        $opt->{date},
        $opt->{inc_routine},
        $opt->{dec_routine};
  return $ctr->value() if $opt->{value};
  return $ctr->dec() if $opt->{decrement};
  return $ctr->inc();
}

AUTHORS

Interchange Development Group

SEE ALSO

DocBook! Interchange!