counter — manipulate a persistent, named counter
| 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 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? |
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.
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"]
The SQL field-updating routine is database-dependent; please see the tag source for exact behavior.
Date-based counters cannot be decremented.
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();
}