A piggy bank of commands, fixes, succinct reviews, some mini articles and technical opinions from a (mostly) Perl developer.
Jump to
Showing posts with label modules. Show all posts
Showing posts with label modules. Show all posts
How to work with dist.ini as a user
You are a developer who needs to work with a Perl package. Instead of a cpanfile it has a dist.ini. Pop quiz, hot shot: What do you do?
Answer:
Answer:
- cpm install Dist::Zilla
- dzil build
- Follow instructions to install dependencies using cpanm
- Or if you prefer cpm (and you should), then try this: dzil listdeps | xargs cpm install
- Or: dzil authordeps --missing | xargs cpm install
Perl module preferences
Preferences:
- Never use Switch, it has some truly awful bugs.
- Use Try::Tiny instead of TryCatch, it's less magical/scary.
- Instead of JSON, use Cpanel::JSON::XS or JSON::XS
- It's safer to explicitly name the package being used, because JSON picks one depending on what's already installed.
Use cpm to manage your Perl dependencies
cpm is much faster than cpanm because it makes HTTP requests in parallel and caches modules between runs. The output is also infinitely more clear than cpanm.
# Define the local lib directory
unset PERL5LIB; unset PERL_LOCAL_LIB_ROOT; eval $(perl -Mlocal::lib=./local)
# Install cpm using cpanm
cpanm -nf -L local App::cpm # 35 modules
# Then manually install any problematic modules (as necessary) that have been moved in/out of core
cpm install Module::Build
cpm install Module::Pluggable
cpm install Archive::Extract
cpm install CGI::Cookie
# Finally fetch all the CPAN and other dependencies
export DARKPAN=https://username:passwork@mydarkpan.example.com
cpm install --resolver 02packages,$DARKPAN --resolver metadb
Note: --resolver is experimental. The standard argument is --mirror
Thanks HypnoMark
# Define the local lib directory
unset PERL5LIB; unset PERL_LOCAL_LIB_ROOT; eval $(perl -Mlocal::lib=./local)
cpanm -nf -L local App::cpm # 35 modules
# Then manually install any problematic modules (as necessary) that have been moved in/out of core
cpm install Module::Build
cpm install Module::Pluggable
cpm install Archive::Extract
cpm install CGI::Cookie
cpm install CGI
export DARKPAN=https://username:passwork@mydarkpan.example.com
cpm install --resolver 02packages,$DARKPAN --resolver metadb
Note: --resolver is experimental. The standard argument is --mirror
# Another form
cpm install --show-build-log-on-failure --resolver metadb --resolver 02packages,$PINTO_MIRROR --feature=client
# s/PINTO_MIRROR/DARKPAN/
How to use local::lib to install Perl modules locally
First:
cd project_dir
eval $( perl -Mlocal::lib=local )
Install dependencies:
cpanm -L local --installdeps .
cd project_dir
eval $( perl -Mlocal::lib=local )
Install dependencies:
cpanm -L local --installdeps .
Or individual modules:
cpanm -L local Some::Module
Perl modules for SOAP
Options/notes:
- W3C::Soap - not good
- SOAP::Lite - not as good as XML::Compile
- XML::Compile - recommended
- XML::Compile::SOAP
- XML::Compile::SOAP11
- XML::Compile::WSDL11
...but generally SOAP isn't much fun.
Expect the WSDL to be wrong, you may need to store a "fixed" copy locally.
Expect the WSDL to be wrong, you may need to store a "fixed" copy locally.
Labels:
comparisons,
modules,
review,
soap,
xml
Mock modules & Rules of mocking for Perl tests
Mock modules
- Test2::V0 - I really must start using this soon. See Test2::Mock and possibly Test2::Tools::Mock
- Test::MockObject - Works. Has set_isa() to pass Moose constraints. Start with an empty object and add methods as needed. This one.
- see also Test::MockObject::Extends
- Test::MockModule - "Override subroutines in a module for unit testing".
- Does not fool Moose (modules appear as Test::MockModule instead of what they are).
- Only overrides the methods you say.
- Has strict mode (can't accidentally mock non-existent methods)
- Has an 'original()' function for wrapping methods (source):
my$mock= Test::MockModule->new("MyModule");$mock->redefine("something",sub{ my ($self, $args) = @_; push @record_for_testing_later, $args;return$mock->original(@_);});- DBD::Mock - don't use this, use Test::DBIx::Class (with DBIx::Class::Fixtures) or a real test database.
- Test::Mock::Class - looks clunky
- Mock::Quick - another one, syntax may be better. "less side effects", doesn't reload modules.
- To call original un-mocked method, use (source):
my $mock = qtakeover 'Some::Module' => ( id => sub { my ( $mock_self, @args ) = @_; $counter->{id}++ if scalar @args; return $mock_self->MQ_CONTROL->original('id')->( $mock_self, @args ); }, );
Reasons to use Mock::Quick
- The syntax is more terse
- That's all. Test::MockModule also allows wrapping (i.e. calling the original module)
- The syntax is more terse
- That's all. Test::MockModule also allows wrapping (i.e. calling the original module)
Monkey patching modules
- Manually overriding subroutines AKA monkey patching - risky. What if you miss a method? Same as what Test::MockModule does.
- example: { package X; *method_name = sub { return 'foo'; } }
- Class::Monkey
- Monkey::Patch
- Mojo::Util::monkey_patch
- Sub::Override
- Mock::Sub - another one
- Mock::MonkeyPatch
- Can easily call original sub with Mock::MonkeyPatch::ORIGINAL
Rules of mocking
Don't monkey patch. Don't modify the symbol table directly like this:
*Acme::Foo::method = sub { return "mock you"; }
Instead use Test::MockModule or Sub::Override or similar. Reasons:
*Acme::Foo::method = sub { return "mock you"; }
Instead use Test::MockModule or Sub::Override or similar. Reasons:
- If you type the method name wrong, or if it gets moved, it becomes so obvious that it can't be missed
- You won't continue to test code that doesn't exist anymore
The first rule of mocking is: Don't mock.
Instead use dependency injection. If you think you want to use a mock:- instead expose the object as an attribute,
- and pass in one mock object once
- this is easier to develop & maintain than mocking different things in every test
- and less prone to leakage between tests
- create a test class like Foo::Test that inherits from Foo
- override the dangerous methods
- add test code: my $test_foo = Foo::Test->(@test_args)
- and then: $t = Test::Mojo->new("App", { foo => $test_foo });
- instantiate like this: $class = $config->foo || Foo->new(@args)
- if necessary, the main library and test library should share an abstract interface (implemented via a role)
Only if all the above fail, use MockModule. Note that scope can matter when using set_isa()
Which Perl modules are installed?
To run: perl script.pl $(cat list_of_modules.txt)
#!/usr/bin/perl
use strict;
use warnings;
foreach my $mod (@ARGV) {
(my $fn="$mod.pm")=~s|::|/|g; # Foo::Bar::Baz => Foo/Bar/Baz.pm
if (eval { require $fn; 1; }) {
print "Module $mod loaded ok\n";
} else {
print "Could not load $mod. Error Message: $@\n";
}
}
# Thanks Perl Monks
Also try typing perldoc perllocal to see a list of all installed modules.
#!/usr/bin/perl
use strict;
use warnings;
foreach my $mod (@ARGV) {
(my $fn="$mod.pm")=~s|::|/|g; # Foo::Bar::Baz => Foo/Bar/Baz.pm
if (eval { require $fn; 1; }) {
print "Module $mod loaded ok\n";
} else {
print "Could not load $mod. Error Message: $@\n";
}
}
# Thanks Perl Monks
Also try typing perldoc perllocal to see a list of all installed modules.
Date/Time perl modules
- Date::Manip - Big. Needs several step sto convert to/from eopch seconds. But you could just subtract 1 week's worth of seconds.
- Date::Calc - Less big, simpler. Has Add_Delta_Days. Doesn't generate BST from epoch seconds (on Mac).
- POSIX - has strftime. Can easily generate epoch seconds from the current time. Lacking in conversion to date?
- DateTime - The best and easiest.
How to use Catalyst to automatically create Perl DBIx::Class modules
UNTESTED:
1. install Catalyst
2. pretend like you're going to create a catalyst app
catalyst.pl MyFakeApp
3.
cd MyFakeApp
./script/myfakeapp_create.pl model MyModelName DBIC::Schema \
MyApp::SchemaClass create=static dbi:mysql:... user password
http://search.cpan.org/~rkitover/Catalyst-Model-DBIC-Schema-0.29/lib/Catalys
t/Helper/Model/DBIC/Schema.pm
one I did earlier
perl ./script/tagindexer_create.pl model RequestDB DBIC::Schema
TagIndexer::Schema create=static
'dbi:mysql:database=request;host=[ip address];port=3306' tagindexer
tagindexer
1. install Catalyst
2. pretend like you're going to create a catalyst app
catalyst.pl MyFakeApp
3.
cd MyFakeApp
./script/myfakeapp_create.pl model MyModelName DBIC::Schema \
MyApp::SchemaClass create=static dbi:mysql:... user password
http://search.cpan.org/~rkitover/Catalyst-Model-DBIC-Schema-0.29/lib/Catalys
t/Helper/Model/DBIC/Schema.pm
one I did earlier
perl ./script/tagindexer_create.pl model RequestDB DBIC::Schema
TagIndexer::Schema create=static
'dbi:mysql:database=request;host=[ip address];port=3306' tagindexer
tagindexer
Perl test comparison methods
use Test::More tests => 23;
# or
use Test::More qw(no_plan);
ok($got eq $expected, $test_name);
like ($got, qr/expected/, $test_name);
is_deeply($got_complex_structure, $expected_complex_structure, $test_name);
Compare data structures
eq_or_diff $got, $expected, "description"Test::Lazy
No descriptions required
# Will evaluate the code and check it:
try('qw/a/' => eq => 'a');
# Don't evaluate, but still compare:
check(1 => is => 1);
Labels:
comparisons,
complex-data-structures,
data,
modules,
perl,
review,
testing
Everybody do the DBI
use DBI;
@driver_names = DBI->available_drivers;
%drivers = DBI->installed_drivers;
@data_sources = DBI->data_sources($driver_name, \%attr);
$dbh = DBI->connect($data_source, $username, $auth, \%attr);
$rv = $dbh->do($statement);
$rv = $dbh->do($statement, \%attr);
$rv = $dbh->do($statement, \%attr, @bind_values);
$ary_ref = $dbh->selectall_arrayref($statement);
$hash_ref = $dbh->selectall_hashref($statement, $key_field);
$ary_ref = $dbh->selectcol_arrayref($statement);
$ary_ref = $dbh->selectcol_arrayref($statement, \%attr);
@row_ary = $dbh->selectrow_array($statement);
$ary_ref = $dbh->selectrow_arrayref($statement);
$hash_ref = $dbh->selectrow_hashref($statement);
$sth = $dbh->prepare($statement);
$sth = $dbh->prepare_cached($statement);
$rc = $sth->bind_param($p_num, $bind_value);
$rc = $sth->bind_param($p_num, $bind_value, $bind_type);
$rc = $sth->bind_param($p_num, $bind_value, \%attr);
$rv = $sth->execute;
$rv = $sth->execute(@bind_values);
$rv = $sth->execute_array(\%attr, ...);
$rc = $sth->bind_col($col_num, \$col_variable);
$rc = $sth->bind_columns(@list_of_refs_to_vars_to_bind);
@row_ary = $sth->fetchrow_array;
$ary_ref = $sth->fetchrow_arrayref;
$hash_ref = $sth->fetchrow_hashref;
$ary_ref = $sth->fetchall_arrayref;
$ary_ref = $sth->fetchall_arrayref( $slice, $max_rows );
$hash_ref = $sth->fetchall_hashref( $key_field );
$rv = $sth->rows;
$rc = $dbh->begin_work;
$rc = $dbh->commit;
$rc = $dbh->rollback;
$quoted_string = $dbh->quote($string);
$rc = $h->err;
$str = $h->errstr;
$rv = $h->state;
$rc = $dbh->disconnect;Test scripts
Even a set of test scripts often benefit from modularised separate libraries, configuration files and dedicated data files. And logging.
Importing a module's namespace in Perl
Ever get this error for a module you've written?
Use of inherited AUTOLOAD for non-method X() is deprecated
Put this at the top of your module:
package My::Module;
use Exporter;
our @ISA = 'Exporter';
our @EXPORT = qw(put the methods here);
And then in the main program:
use My::Module qw( the methods to import );
keywords: export import exporting
Labels:
code,
export,
import,
modules,
namespaces,
perl,
productivity,
programming
Subscribe to:
Posts (Atom)