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(); }