曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.かつては雑草やヨモギと共に雨や露を分かち合っていたが、今では松やヒノキと共に霜や雪に耐えている。曾与蒿藜同雨露,Once sharing rain and dew with weeds and wormwood, now enduring frost and snow with pines and cypresses.终随松柏到冰霜.曾与蒿藜同雨露한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.,终随松柏到冰霜.譖セ荳手珍阯懷酔髮ィ髴イ�檎サ磯囂譚セ譟丞芦蜀ー髴�曾与蒿藜同雨露,鏇句笌钂胯棞鍚岄洦闇诧紝缁堥殢鏉炬煆鍒板啺闇�终随松柏到冰霜.曾与蒿藜同雨露,한때 잡초와 쑥과 함께 비와 이슬을 나누던 곳이 이제는 소나무와 삼나무와 함께 서리와 눈을 견뎌내고 있다.终随松柏到冰霜.曾与蒿藜同雨露,终随松柏到冰霜.
#============================================================= -*-Perl-*-
#
# Template::VMethods
#
# DESCRIPTION
# Module defining virtual methods for the Template Toolkit
#
# AUTHOR
# Andy Wardley <abw@wardley.org>
#
# COPYRIGHT
# Copyright (C) 1996-2015 Andy Wardley. All Rights Reserved.
#
# This module is free software; you can redistribute it and/or
# modify it under the same terms as Perl itself.
#
# REVISION
# $Id$
#
#============================================================================
package Template::VMethods;
use strict;
use warnings;
use Scalar::Util qw( blessed looks_like_number );
use Template::Filters;
our $VERSION = '3.100';
our $DEBUG = 0 unless defined $DEBUG;
our $ROOT_VMETHODS = {
inc => \&root_inc,
dec => \&root_dec,
};
our $TEXT_VMETHODS = {
item => \&text_item,
list => \&text_list,
hash => \&text_hash,
length => \&text_length,
size => \&text_size,
empty => \&text_empty,
defined => \&text_defined,
upper => \&text_upper,
lower => \&text_lower,
ucfirst => \&text_ucfirst,
lcfirst => \&text_lcfirst,
match => \&text_match,
search => \&text_search,
repeat => \&text_repeat,
replace => \&text_replace,
remove => \&text_remove,
split => \&text_split,
chunk => \&text_chunk,
substr => \&text_substr,
trim => \&text_trim,
collapse => \&text_collapse,
squote => \&text_squote,
dquote => \&text_dquote,
html => \&Template::Filters::html_filter,
xml => \&Template::Filters::xml_filter,
};
our $HASH_VMETHODS = {
item => \&hash_item,
hash => \&hash_hash,
size => \&hash_size,
empty => \&hash_empty,
each => \&hash_each,
keys => \&hash_keys,
values => \&hash_values,
items => \&hash_items,
pairs => \&hash_pairs,
list => \&hash_list,
exists => \&hash_exists,
defined => \&hash_defined,
delete => \&hash_delete,
import => \&hash_import,
sort => \&hash_sort,
nsort => \&hash_nsort,
};
our $LIST_VMETHODS = {
item => \&list_item,
list => \&list_list,
hash => \&list_hash,
push => \&list_push,
pop => \&list_pop,
unshift => \&list_unshift,
shift => \&list_shift,
max => \&list_max,
size => \&list_size,
empty => \&list_empty,
defined => \&list_defined,
first => \&list_first,
last => \&list_last,
reverse => \&list_reverse,
grep => \&list_grep,
join => \&list_join,
sort => \&list_sort,
nsort => \&list_nsort,
unique => \&list_unique,
import => \&list_import,
merge => \&list_merge,
slice => \&list_slice,
splice => \&list_splice,
};
# Template::Stash needs the above, so defer loading this module
# until they are defined.
require Template::Stash;
our $PRIVATE = $Template::Stash::PRIVATE;
#========================================================================
# root virtual methods
#========================================================================
sub root_inc {
no warnings;
my $item = shift;
++$item;
}
sub root_dec {
no warnings;
my $item = shift;
--$item;
}
#========================================================================
# text virtual methods
#========================================================================
sub text_item {
$_[0];
}
sub text_list {
[ $_[0] ];
}
sub text_hash {
{ value => $_[0] };
}
sub text_length {
length $_[0];
}
sub text_size {
return 1;
}
sub text_empty {
return 0 == text_length($_[0]) ? 1 : 0;
}
sub text_defined {
return 1;
}
sub text_upper {
return uc $_[0];
}
sub text_lower {
return lc $_[0];
}
sub text_ucfirst {
return ucfirst $_[0];
}
sub text_lcfirst {
return lcfirst $_[0];
}
sub text_trim {
for ($_[0]) {
s/^\s+//;
s/\s+$//;
}
return $_[0];
}
sub text_collapse {
for ($_[0]) {
s/^\s+//;
s/\s+$//;
s/\s+/ /g
}
return $_[0];
}
sub text_match {
my ($str, $search, $global) = @_;
return $str unless defined $str and defined $search;
my @matches = $global ? ($str =~ /$search/g)
: ($str =~ /$search/);
return @matches ? \@matches : '';
}
sub text_search {
my ($str, $pattern) = @_;
return $str unless defined $str and defined $pattern;
return $str =~ /$pattern/;
}
sub text_repeat {
my ($str, $count) = @_;
$str = '' unless defined $str;
return '' unless $count;
$count ||= 1;
return $str x $count;
}
sub text_replace {
my ($text, $pattern, $replace, $global) = @_;
$text = '' unless defined $text;
$pattern = '' unless defined $pattern;
$replace = '' unless defined $replace;
$global = 1 unless defined $global;
if ($replace =~ /\$\d+/) {
# replacement string may contain backrefs
my $expand = sub {
my ($chunk, $start, $end) = @_;
$chunk =~ s{ \\(\\|\$) | \$ (\d+) }{
$1 ? $1
: ($2 > $#$start || $2 == 0 || !defined $start->[$2]) ? ''
: substr($text, $start->[$2], $end->[$2] - $start->[$2]);
}exg;
$chunk;
};
if ($global) {
$text =~ s{$pattern}{ &$expand($replace, [@-], [@+]) }eg;
}
else {
$text =~ s{$pattern}{ &$expand($replace, [@-], [@+]) }e;
}
}
else {
if ($global) {
$text =~ s/$pattern/$replace/g;
}
else {
$text =~ s/$pattern/$replace/;
}
}
return $text;
}
sub text_remove {
my ($str, $search) = @_;
return $str unless defined $str and defined $search;
$str =~ s/$search//g;
return $str;
}
sub text_split {
my ($str, $split, $limit) = @_;
$str = '' unless defined $str;
# For versions of Perl prior to 5.18 we have to be very careful about
# spelling out each possible combination of arguments because split()
# is very sensitive to them, for example C<split(' ', ...)> behaves
# differently to C<$space=' '; split($space, ...)>. Test 33 of
# vmethods/text.t depends on this behaviour.
if ($] < 5.018) {
if (defined $limit) {
return [ defined $split
? split($split, $str, $limit)
: split(' ', $str, $limit) ];
}
else {
return [ defined $split
? split($split, $str)
: split(' ', $str) ];
}
}
# split's behavior changed in Perl 5.18.0 making this:
# C<$space=' '; split($space, ...)>
# behave the same as this:
# C<split(' ', ...)>
# qr// behaves the same, so use that for user-defined split.
my $split_re;
if (defined $split) {
eval {
$split_re = qr/$split/;
};
}
$split_re = ' ' unless defined $split_re;
$limit ||= 0;
return [split($split_re, $str, $limit)];
}
sub text_chunk {
my ($string, $size) = @_;
my @list;
$size ||= 1;
if ($size < 0) {
# sexeger! It's faster to reverse the string, search
# it from the front and then reverse the output than to
# search it from the end, believe it nor not!
$string = reverse $string;
$size = -$size;
unshift(@list, scalar reverse $1)
while ($string =~ /((.{$size})|(.+))/g);
}
else {
push(@list, $1) while ($string =~ /((.{$size})|(.+))/g);
}
return \@list;
}
sub text_substr {
my ($text, $offset, $length, $replacement) = @_;
$offset ||= 0;
if(defined $length) {
if (defined $replacement) {
substr( $text, $offset, $length, $replacement );
return $text;
}
else {
return substr( $text, $offset, $length );
}
}
else {
return substr( $text, $offset );
}
}
sub text_squote {
my $text = shift;
for ($text) {
s/(['\\])/\\$1/g;
}
return $text;
}
sub text_dquote {
my $text = shift;
for ($text) {
s/(["\\])/\\$1/g;
s/\n/\\n/g;
}
return $text;
}
#========================================================================
# hash virtual methods
#========================================================================
sub hash_item {
my ($hash, $item) = @_;
$item = '' unless defined $item;
return if $PRIVATE && $item =~ /$PRIVATE/;
$hash->{ $item };
}
sub hash_hash {
$_[0];
}
sub hash_size {
scalar keys %{$_[0]};
}
sub hash_empty {
return 0 == hash_size($_[0]) ? 1 : 0;
}
sub hash_each {
# this will be changed in TT3 to do what hash_pairs() does
[ %{ $_[0] } ];
}
sub hash_keys {
[ keys %{ $_[0] } ];
}
sub hash_values {
[ values %{ $_[0] } ];
}
sub hash_items {
[ %{ $_[0] } ];
}
sub hash_pairs {
[ map {
{ key => $_ , value => $_[0]->{ $_ } }
}
sort keys %{ $_[0] }
];
}
sub hash_list {
my ($hash, $what) = @_;
$what ||= '';
return ($what eq 'keys') ? [ keys %$hash ]
: ($what eq 'values') ? [ values %$hash ]
: ($what eq 'each') ? [ %$hash ]
: # for now we do what pairs does but this will be changed
# in TT3 to return [ $hash ] by default
[ map { { key => $_ , value => $hash->{ $_ } } }
sort keys %$hash
];
}
sub hash_exists {
exists $_[0]->{ $_[1] };
}
sub hash_defined {
# return the item requested, or 1 if no argument
# to indicate that the hash itself is defined
my $hash = shift;
return @_ ? defined $hash->{ $_[0] } : 1;
}
sub hash_delete {
my $hash = shift;
delete $hash->{ $_ } for @_;
}
sub hash_import {
my ($hash, $imp) = @_;
$imp = {} unless ref $imp eq 'HASH';
@$hash{ keys %$imp } = values %$imp;
return '';
}
sub hash_sort {
my ($hash) = @_;
[ sort { lc $hash->{$a} cmp lc $hash->{$b} } (keys %$hash) ];
}
sub hash_nsort {
my ($hash) = @_;
[ sort { $hash->{$a} <=> $hash->{$b} } (keys %$hash) ];
}
#========================================================================
# list virtual methods
#========================================================================
sub list_item {
$_[0]->[ $_[1] || 0 ];
}
sub list_list {
$_[0];
}
sub list_hash {
my $list = shift;
if (@_) {
my $n = shift || 0;
return { map { ($n++, $_) } @$list };
}
no warnings;
return { @$list };
}
sub list_push {
my $list = shift;
push(@$list, @_);
return '';
}
sub list_pop {
my $list = shift;
pop(@$list);
}
sub list_unshift {
my $list = shift;
unshift(@$list, @_);
return '';
}
sub list_shift {
my $list = shift;
shift(@$list);
}
sub list_max {
no warnings;
my $list = shift;
$#$list;
}
sub list_size {
no warnings;
my $list = shift;
$#$list + 1;
}
sub list_empty {
return 0 == list_size($_[0]) ? 1 : 0;
}
sub list_defined {
# return the item requested, or 1 if no argument to
# indicate that the hash itself is defined
my $list = shift;
return 1 unless @_; # list.defined is always true
return unless looks_like_number $_[0]; # list.defined('bah') is always false
return defined $list->[$_[0]]; # list.defined(n)
}
sub list_first {
my $list = shift;
return $list->[0] unless @_;
return [ @$list[0..$_[0]-1] ];
}
sub list_last {
my $list = shift;
return $list->[-1] unless @_;
return [ @$list[-$_[0]..-1] ];
}
sub list_reverse {
my $list = shift;
[ reverse @$list ];
}
sub list_grep {
my ($list, $pattern) = @_;
$pattern ||= '';
return [ grep /$pattern/, @$list ];
}
sub list_join {
my ($list, $joint) = @_;
join(defined $joint ? $joint : ' ',
map { defined $_ ? $_ : '' } @$list);
}
sub _list_sort_make_key {
my ($item, $fields) = @_;
my @keys;
if (ref($item) eq 'HASH') {
@keys = map { $item->{ $_ } } @$fields;
}
elsif (blessed $item) {
@keys = map { $item->can($_) ? $item->$_() : $item } @$fields;
}
else {
@keys = $item;
}
# ugly hack to generate a single string using a delimiter that is
# unlikely (but not impossible) to be found in the wild.
return lc join('/*^UNLIKELY^*/', map { defined $_ ? $_ : '' } @keys);
}
sub list_sort {
my ($list, @fields) = @_;
return $list unless @$list > 1; # no need to sort 1 item lists
return [
@fields # Schwartzian Transform
? map { $_->[0] } # for case insensitivity
sort { $a->[1] cmp $b->[1] }
map { [ $_, _list_sort_make_key($_, \@fields) ] }
@$list
: map { $_->[0] }
sort { $a->[1] cmp $b->[1] }
map { [ $_, lc $_ ] }
@$list,
];
}
sub list_nsort {
my ($list, @fields) = @_;
return $list unless @$list > 1; # no need to sort 1 item lists
my $sort = sub {
my $cmp;
if(@fields) {
# compare each field individually
for my $field (@fields) {
my $A = _list_sort_make_key($a, [ $field ]);
my $B = _list_sort_make_key($b, [ $field ]);
($cmp = $A <=> $B) and last;
}
}
else {
my $A = _list_sort_make_key($a);
my $B = _list_sort_make_key($b);
$cmp = $A <=> $B;
}
$cmp;
};
return [ sort $sort @{ $list } ];
}
sub list_unique {
my %u;
[ grep { ++$u{$_} == 1 } @{$_[0]} ];
}
sub list_import {
my $list = shift;
push(@$list, grep defined, map ref eq 'ARRAY' ? @$_ : undef, @_);
return $list;
}
sub list_merge {
my $list = shift;
return [ @$list, grep defined, map ref eq 'ARRAY' ? @$_ : undef, @_ ];
}
sub list_slice {
my ($list, $from, $to) = @_;
$from ||= 0;
$to = $#$list unless defined $to;
$from += @$list if $from < 0;
$to += @$list if $to < 0;
return [ @$list[$from..$to] ];
}
sub list_splice {
my ($list, $offset, $length, @replace) = @_;
if (@replace) {
# @replace can contain a list of multiple replace items, or
# be a single reference to a list
@replace = @{ $replace[0] }
if @replace == 1 && ref $replace[0] eq 'ARRAY';
return [ splice @$list, $offset, $length, @replace ];
}
elsif (defined $length) {
return [ splice @$list, $offset, $length ];
}
elsif (defined $offset) {
return [ splice @$list, $offset ];
}
else {
return [ splice(@$list) ];
}
}
1;
__END__
=head1 NAME
Template::VMethods - Virtual methods for variables
=head1 DESCRIPTION
The C<Template::VMethods> module implements the virtual methods
that can be applied to variables.
Please see L<Template::Manual::VMethods> for further information.
=head1 AUTHOR
Andy Wardley E<lt>abw@wardley.orgE<gt> L<http://wardley.org/>
=head1 COPYRIGHT
Copyright (C) 1996-2007 Andy Wardley. All Rights Reserved.
This module is free software; you can redistribute it and/or
modify it under the same terms as Perl itself.
=head1 SEE ALSO
L<Template::Stash>, L<Template::Manual::VMethods>
=cut
# Local Variables:
# mode: perl
# perl-indent-level: 4
# indent-tabs-mode: nil
# End:
#
# vim: expandtab shiftwidth=4: