—package
Util::Underscore;
#ABSTRACT: Common helper functions without having to import them
#CONTRIBUTOR: Lukas Atkinson (cpan: AMON) <amon@cpan.org>
#CONTRIBUTOR: Olivier Mengué (cpan: DOLMEN) <dolmen@cpan.org>
use
strict;
use
warnings;
use
overload ();
use
Carp ();
use
Const::Fast 0.011 ();
use
Data::Alias 1.18 ();
use
Data::Dump 1.10 ();
use
List::MoreUtils 0.07 ();
use
List::Util 1.35 ();
use
POSIX ();
use
Scalar::Util 1.36 ();
use
Try::Tiny 0.03 ();
BEGIN {
# check if a competing "_" exists
if
(
keys
%{_::}) {
Carp::confess
qq(The package "_" has already been defined)
;
}
}
BEGIN {
# Load the dummy "_.pm" module.
# This will set up various booby traps so that "_" isn't used directly.
# In order to prevent the traps from triggering when *we* go there, we have
# to declare our peaceful intentions:
local
our
$_WE_COME_IN_PEACE
=
'pinky swear'
;
}
our
$_ASSIGN_ALIASES
;
BEGIN {
$_ASSIGN_ALIASES
=
sub
{
my
(
$pkg
,
%aliases
) =
@_
;
no
strict
'refs'
;
## no critic (ProhibitNoStrict)
while
(
my
(
$this
,
$that
) =
each
%aliases
) {
my
$target
=
"_::${this}"
;
my
$source
=
"${pkg}::${that}"
;
*{
$target
} = *{
$source
}{CODE}
// Carp::croak
"Unknown subroutine $source in _ASSIGN_ALIASES"
;
}
};
}
# From now, every function is in the "_" package
## no critic (ProhibitMultiplePackages)
package
# Hide from PAUSE
_;
## no critic (RequireArgUnpacking, RequireFinalReturn, ProhibitSubroutinePrototypes)
# Why this "no critic"? In an util module, efficiency is crucial because we
# have no idea about the context where these function are being used. Therefore,
# no arg unpacking, and no explicit return. Most functions are so trivial anyway
# that this isn't much of a legibility concern.
# Subroutine prototypes are used to offer a convenient and natural interface.
# I fully understand why they shouldn't be used in ordinary code, but this
# module puts them to mostly good use.
# Predeclare a few things so that we can use them in the sub definitions below.
sub
blessed(_);
sub
ref_type(_);
# load the actual function collections
use
Util::Underscore::Scalars ();
use
Util::Underscore::Numbers ();
use
Util::Underscore::References ();
use
Util::Underscore::Objects ();
use
Util::Underscore::ListUtils ();
BEGIN {
$_ASSIGN_ALIASES
->(
'Carp'
,
carp
=>
'carp'
,
cluck
=>
'cluck'
,
croak
=>
'croak'
,
confess
=>
'confess'
,
);
}
$_ASSIGN_ALIASES
->(
'Try::Tiny'
,
try
=>
'try'
,
catch
=>
'catch'
,
finally
=>
'finally'
,
);
sub
carpf($@) {
my
$pattern
=
shift
;
@_
=
sprintf
$pattern
,
@_
;
goto
&carp
;
}
sub
cluckf($@) {
my
$pattern
=
shift
;
@_
=
sprintf
$pattern
,
@_
;
goto
&cluck
;
}
sub
croakf($@) {
my
$pattern
=
shift
;
@_
=
sprintf
$pattern
,
@_
;
goto
&croak
;
}
sub
confessf($@) {
my
$pattern
=
shift
;
@_
=
sprintf
$pattern
,
@_
;
goto
&confess
;
}
$_ASSIGN_ALIASES
->(
'Scalar::Util'
,
is_open
=>
'openhandle'
);
sub
_::
prototype
($;$) {
if
(
@_
== 2) {
goto
&Scalar::Util::set_prototype
if
@_
== 2;
}
if
(
@_
== 1) {
my
(
$coderef
) =
@_
;
return
prototype
$coderef
;
# Calls CORE::prototype
}
else
{
## no critic (RequireInterpolationOfMetachars)
Carp::confess
'_::prototype($;$) takes exactly one or two arguments'
;
}
}
# This sub uses CamelCase because it's a factory function
sub
Dir(@) {
## no critic (NamingConventions::Capitalization)
Path::Class::Dir->new(
@_
);
}
# This sub uses CamelCase because it's a factory function
sub
File(@) {
## no critic (NamingConventions::Capitalization)
Path::Class::File->new(
@_
);
}
$_ASSIGN_ALIASES
->(
'Data::Dump'
,
pp
=>
'pp'
,
dd
=>
'dd'
,
);
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
Util::Underscore - Common helper functions without having to import them
=head1 VERSION
version v1.2.0_1
=head1 SYNOPSIS
use Util::Underscore;
_::croak "$foo must do Some::Role" if not _::does($foo, 'Some::Role');
=head1 DESCRIPTION
This module contains various utility functions, and makes them accessible through the C<_> package.
This allows the use of these utilities (a) without much per-usage overhead and (b) without namespace pollution.
It contains selected functions from the following modules:
L<Carp|Carp>,
L<Const::Fast|Const::Fast>,
L<Data::Alias|Data::Alias>,
L<Data::Dump|Data::Dump>,
L<List::MoreUtils|List::MoreUtils>,
L<List::Util|List::Util>,
L<POSIX|POSIX>,
L<Scalar::Util|Scalar::Util>,
L<Try::Tiny|Try::Tiny>.
Not all functions from those are available, some have been renamed, and some functions of our own have been added.
=head1 FUNCTION REFERENCE
The function reference is split into separate topics which each have their own documentation:
=over 4
=item Scalars
see L<Util::Underscore::Scalars|Util::Underscore::Scalars>
C<alias>,
C<const>,
C<is_dual>,
C<is_identifier>,
C<is_package>,
C<is_plain>,
C<is_readonly>,
C<is_string>,
C<is_bool>,
C<is_tainted>,
C<is_vstring>,
C<new_dual>
=item Numbers
see L<Util::Underscore::Numbers|Util::Underscore::Numbers>
C<ceil>,
C<floor>,
C<is_int>,
C<is_numeric>,
C<is_uint>
=item References
see L<Util::Underscore::References|Util::Underscore::References>
C<ref_addr>,
C<ref_is_weak>,
C<ref_type>,
C<ref_unweaken>,
C<ref_weaken>
=item Objects
see L<Util::Underscore::Objects|Util::Underscore::Objects>
C<blessed>,
C<can>,
C<class>,
C<class_does>,
C<class_isa>,
C<does>,
C<is_instance>,
C<is_object>,
C<isa>,
C<safecall>
=item List Utils
see L<Util::Underscore::ListUtils|Util::Underscore::ListUtils>
C<all>,
C<any>,
C<each_array>,
C<first>,
C<first_index>,
C<last>,
C<last_index>,
C<max>,
C<max_str>,
C<min>,
C<min_str>,
C<natatime>,
C<none>,
C<pairfirst>,
C<pairgrep>,
C<pairmap>,
C<part>,
C<product>,
C<reduce>,
C<shuffle>,
C<sum>,
C<uniq>,
C<zip>
=item Exception handling
see below
C<carp>,
C<carpf>,
C<catch>,
C<cluck>,
C<cluckf>,
C<confess>,
C<confessf>,
C<croak>,
C<croakf>,
C<finally>,
C<try>
=item Miscellaneous Functions
see below
C<dd>,
C<Dir>,
C<File>,
C<is_open>,
C<pp>,
C<prototype>
=back
=head2 Exception handling
The functions C<_::carp>, C<_::cluck>, C<_::croak>, and C<_::confess> from the C<Carp> module are available.
They all take a list of strings as argument.
How do they differ from each other?
Stack Trace || Fatal | Warning
------------##====================
No || croak | carp
Yes || confess | cluck
How do they differ from Perl's builtin C<die> and C<warn>?
The error messages of C<die> and C<warn> are located on the line where the exception is raised.
This makes debugging hard when the error points to some internal function of a module you are using,
as this provides no information on where your client code made a mistake.
The C<Carp> family of error functions report the error from the point of usage, and optionally provide stack traces.
If you write a module, please use the C<Carp> functions instead of plain C<die>.
Additionally, the variants C<_::carpf>, C<_::cluckf>, C<_::croakf>, and C<_::confessf> are provided.
These take a C<sprintf> patterns as first argument: C<_::carpf "pattern", @arguments>.
To handle errors, the following keywords from C<Try::Tiny> are available:
=over 4
=item *
C<_::try>
=item *
C<_::catch>
=item *
C<_::finally>
=back
They are all direct aliases for their namesakes in C<Try::Tiny>.
=head2 Miscellaneous Functions
=over 4
=item C<$fh = _::is_open $fh>
wrapper for C<Scalar::Util::openhandle>
=item C<$str = _::prototype \&code>
=item C<_::prototype \&code, $new_proto>
gets or sets the prototype, wrapping either C<CORE::prototype> or C<Scalar::Util::set_prototype>
=item C<$dir = _::Dir "foo/bar", "baz">
Creates a new L<Path::Class::Dir|Path::Class::Dir> instance.
=item C<$dir = _::File "foo/bar", "baz.txt">
Creates a new L<Path::Class::File|Path::Class::File> instance.
=back
C<Data::Dump> is an alternative to C<Data::Dumper>.
The main difference is the output format: C<Data::Dump> output tends to be easier to read.
=over 4
=item C<$str = _::pp @values>
wrapper for C<Data::Dump::pp>
=item C<_::dd @values>
wrapper for C<Data::Dump::dd>.
=back
=head1 RATIONALE
=head4 Context and Package Name
There are a variety of good utility modules like C<Carp> or C<Scalar::Util>.
I noticed I don't import these (in order to avoid namespace pollution), but rather refer to these functions via their fully qualified names (e.g. C<Carp::carp>).
This is ultimately annoying and repetitive.
This module populates the C<_> package (a nod to JavaScript's Underscore.js library) with various helpers so that they can be used without having to import them, with a per-usage overhead of only three characters C<_::>.
The large number of dependencies makes this module somewhat heavyweight, but it avoids the “is C<any> in List::Util or List::MoreUtils”-problem.
In retrospect, choosing the C<_> package name was a mistake:
A certain part of Perl's infrastructure doesn't recognize C<_> as a valid package name (although Perl itself does).
More importantly, Perl's filetest operators can use the magic C<_> filehandle which would interfere with this module if it were intended for anything else than fully qualified access to its functions.
Still, a single underscore is less intrusive than some jumbled letters like C<Ut::any>.
=head4 Scope and Function Naming
This module collects various utility functions that – in my humble opinion – should be part of the Perl language, if the main namespace wouldn't become too crowded as a result.
Because everything is safely hedged into the C<_> namespace, we can go wild without fearing name collisions.
However, a few naming conventions were adhered to:
=over 4
=item *
Functions with a boolean return value start with C<is_>.
=item *
If the source module already provided a sensible name, it is kept to reduce confusion.
=item *
Factory functions that return an object use CamelCase.
=back
=head1 RELATED MODULES
The following modules were once considered for inclusion or were otherwise influental in the design of this collection:
L<Data::Types|Data::Types>,
L<Data::Util|Data::Util>,
L<Params::Util|Params::Util>,
L<Safe::Isa|Safe::Isa>.
=head1 BUGS
Please report any bugs or feature requests on the bugtracker website
When submitting a bug or request, please include a test-file or a
patch to an existing test-file that illustrates the bug or desired
feature.
=head1 AUTHOR
Lukas Atkinson <amon@cpan.org>
=head2 CONTRIBUTORS
=over 4
=item *
Lukas Atkinson (cpan: AMON) <amon@cpan.org>
=item *
Olivier Mengué (cpan: DOLMEN) <dolmen@cpan.org>
=back
=head1 COPYRIGHT AND LICENSE
This software is Copyright (c) 2014 by Lukas Atkinson.
This is free software, licensed under:
The GNU General Public License, Version 3, June 2007
=cut