SlideShare a Scribd company logo
1 of 106
Download to read offline
Climbing a Tree:
Refactoring FindBin::libs for Raku
Steven Lembark
Workhorse Computing
lembark@wrkhors.com
FindBin::libs is a programatic “-I”
FindBin
Scan up the tree.
Use non-root ./lib dirs.
Why?
Test or execute project without installing.
In-house modules without CPAN or zef.
Tests use ./t/lib or ./t/etc
Avoid destructive tests in production.
Resolve path.
Use modules relative to ‘real’ path.
Describing Raku logic
Not Perl.
Coding & testing utilities.
Command line tools.
zef Installer.
Releasing with zef.
First Step: FindBin
You are here ---> *
Interactive vs. program.
Absolute path.
As-is vs. resolved.
Native filesystem.
&Bin &Script
Thinking Raku
FindBin used to export $Bin along with subs.
Now exports Bin & Script subs only.
“unit”
Raku uses “compilation units”.
This unit is a single module.
use v6.d;
unit module FindBin:ver<0.4.0>:auth<CPAN:lembark>;
“unit”
Raku uses “compilation units”.
Raku supports multiple versions & authors.
The version & author are part of the definition.
use v6.d;
unit module FindBin:ver<0.4.0>:auth<CPAN:lembark>;
Constant’s don’t change
Neither does your interactive status.
Constant’s don’t change
Neither does your interactive status:
constant IS‑INTERACTIVE
= $*PROGRAM‑NAME eq '-e'| '-' | 'interactive'
;
Twigil
`tween a sigil and a name.
‘*’ is for dynamic.
AUTOGENERATED
constant IS‑INTERACTIVE
= $*PROGRAM‑NAME eq '-e'| '-' | 'interactive'
;
Module args
‘:’ prefix is part of the language:
use FindBin; # :DEFAULT
use FindBin :resolve;
use FindBin :verbose;
use FindBin :Bin;
use FindBin :Script :resolve;
Module args
‘:’ prefix is part of the language:
use FindBin; # :DEFAULT
use FindBin :resolve;
use FindBin :verbose;
use FindBin :Bin;
use FindBin :Script :resolve;
constant _FindBin_RESOLVE-DEF is export( :resolve ) = True;
constant _FindBin_VERBOSE-DEF is export( :verbose ) = True;
Slip
|( ) converts a list into a Slip.
No context: Raku lists don't flatten automatically.
Slips do.
Good examples at doc.raku.org (see references).
constant OPTION-TAGS = |( :resolve , :verbose );
Pseudo-Packages
CALLER::
LEXCAL::
CALLER::LEXICAL::YourNameHere
Signature
An object.
Defines input, output.
sub Bin
(
Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF,
Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF
--> IO
)
Signature
:$resolve stores “:resolve” or “:!resolve” argument.
Defaults to caller’s lexical.
sub Bin
(
Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF,
Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF
--> IO
)
Signature
Bool() coerces the values to type Bool.
Avoids undef.
sub Bin
(
Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF,
Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF
--> IO
)
export
Explicit “:Bin”, no args.
Either of “:verbose” or “:resolve” via flattening.
sub Bin
(
Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF,
Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF
--> IO
)
is export( :Bin, :DEFAULT, OPTION-TAGS )
Using a constant
No sigil.
“?? !!” Ternary.
my $bin_from
= IS-INTERACTIVE
?? $*CWD
!! $*PROGRAM.IO
;
Using a constant
$*CWD is an IO. my $bin_from
= IS-INTERACTIVE
?? $*CWD
!! $*PROGRAM.IO
;
Using a constant
$*PROGRAM is Str.
.IO calls constructor:
Converts the string to
an IO object.
my $bin_from
= IS-INTERACTIVE
?? $*CWD
!! $*PROGRAM.IO
;
Using an IO
.resolve --> IO
.absolute --> Str.
my $path
= $resolve
?? $bin_from.resolve
!! $bin_from.absolute.IO
;
Returning a directory
dirname works on
*NIX, fails on
MS, VMS.
Parent includes
the volume.
IS-INTERACTIVE
?? $path
!! $path.parent
Bin
sub Bin
(
Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF,
Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF
--> IO
)
is export( :Bin, :DEFAULT, OPTION-TAGS )
{
my $bin_from
= IS-INTERACTIVE
?? $*CWD
!! $*PROGRAM.IO
;
my $path
= $resolve
?? $bin_from.resolve
!! $bin_from.absolute.IO
;
if $verbose { ... }
IS-INTERACTIVE
?? $path
!! $path.parent
}
Viola!
Viola!
Script
note is
“say” to stderr.
our sub Script
(
Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF,
Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF
--> Str
)
is export( :Script, :DEFAULT, OPTION-TAGS )
{
if $verbose
{
note '# Script()';
note "# Interactive: '{IS-INTERACTIVE}'";
note "# Resolve: $resolve";
note "# Path: '$*PROGRAM-NAME'";
}
IS-INTERACTIVE
?? $*PROGRAM-NAME
!! $resolve
?? $*PROGRAM.resolve.basename
!! $*PROGRAM.basename
}
Oops...
Bug in v6.d:
Signature
cannot handle
Caller::Lexical
our sub Script
(
Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF,
Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF
--> Str
)
is export( :Script, :DEFAULT, OPTION-TAGS )
{
if $verbose
{
note '# Script()';
note "# Interactive: '{IS-INTERACTIVE}'";
note "# Resolve: $resolve";
note "# Path: '$*PROGRAM-NAME'";
}
IS-INTERACTIVE
?? $*PROGRAM-NAME
!! $resolve
?? $*PROGRAM.resolve.basename
!! $*PROGRAM.basename
}
Viola!
Bug in v6.d:
Signature
cannot handle
Caller::Lexical
Fix: Assign
defaults.
our sub Script
(
Bool() :$resolve is copy,
Bool() :$verbose is copy
--> Str
)
is export( :Script, :DEFAULT, OPTION-TAGS )
{
$resolve //= ? CALLER::LEXICAL::_FindBin_RESOLVE-DEF;
$verbose //= ? CALLER::LEXICAL::_FindBin_VERBOSE-DEF;
if $verbose
{
note '# Script()';
note "# Interactive: '{IS-INTERACTIVE}'";
note "# Resolve: $resolve";
note "# Path: '$*PROGRAM-NAME'";
}
IS-INTERACTIVE
?? $*PROGRAM-NAME
!! $resolve
?? $*PROGRAM.resolve.basename
!! $*PROGRAM.basename
}
Viola!
‘?’ coerces
value to Bool.
Avoids undef.
our sub Script
(
Bool() :$resolve is copy,
Bool() :$verbose is copy
--> Str
)
is export( :Script, :DEFAULT, OPTION-TAGS )
{
$resolve //= ? CALLER::LEXICAL::_FindBin_RESOLVE-DEF;
$verbose //= ? CALLER::LEXICAL::_FindBin_VERBOSE-DEF;
if $verbose
{
note '# Script()';
note "# Interactive: '{IS-INTERACTIVE}'";
note "# Resolve: $resolve";
note "# Path: '$*PROGRAM-NAME'";
}
IS-INTERACTIVE
?? $*PROGRAM-NAME
!! $resolve
?? $*PROGRAM.resolve.basename
!! $*PROGRAM.basename
}
How to test it.
Test
ok
use_ok
require_ok
is
Generic sanity test
use v6.d;
use Test;
use lib $*PROGRAM.IO.parent(2).add( 'lib' );
( $*PROGRAM-NAME.IO.basename ~~ m{^ (d+ '-')? (.+) '.'t $} )
or bail-out 'Unable to parse test name: ' ~ $*PROGRAM-NAME;
my $madness = $1.subst( '-', '::', :g );
use-ok $madness;
my $found = ::( $madness ).^name;
is $found, $madness, "Package '$found' ($madness).";
done-testing;
Checking exports
Pseudo-class MY:: with hardwired name.
use v6.d;
use Test;
ok ! MY::<&Bin> , 'Bin not installed before use.';
ok ! MY::<&Script> , 'Script not installed before use.';
ok ! MY::<_FindBin_RESOLVE-DEF>, '! _FindBin_RESOLVE-DEF’;
ok ! MY::<_FindBin_VERBOSE-DEF>, '! _FindBin_VERBOSE-DEF’;
Checking exports
do
{
use FindBin;
ok MY::<&Bin> , 'Bin installed by default.';
ok MY::<&Script> , 'Script installed by default.';
ok ! MY::<_FindBin_RESOLVE-DEF>, '! _FindBin_RESOLVE-DEF';
ok ! MY::<_FindBin_VERBOSE-DEF>, '! _FindBin_VERBOSE-DEF';
note '# Bin is: ' ~ Bin;
note '# Script is: ' ~ Script;
};
Checking exports
do
{
use FindBin;
...
};
ok ! MY::<&Bin> , 'Bin is lexicaly scoped.';
ok ! MY::<&Script> , 'Script is lexicaly scoped.';
ok ! MY::<_FindBin_RESOLVE-DEF>, '! _FindBin_RESOLVE-DEF';
ok ! MY::<_FindBin_VERBOSE-DEF>, '! _FindBin_RESOLVE-DEF';
Script test program is easy
use v6.d;
use Test;
use lib $*PROGRAM.IO.parent(2).add( 'lib' );
use FindBin;
my $found = Script;
my $expect = $*PROGRAM.basename;
ok $found, 'Script returns ' ~ $found;
ok $expect, '$*PROGRAM.basename is ' ~ $expect;
is $found, $expect, 'Script matches basename.';
done-testing;
Testing -e takes more syntax
use v6.d;
use Test;
my $expect = '-e';
my ( $found )
= qx{ raku -I./lib -M'FindBin' -e 'say Script' }.chomp;
is $found, $expect, "'raku -e' returns '$found' ($expect)";
done-testing;
Now you got all of the tests...
How do you run them?
prove6; what you are used to
mi6 test; command line assistants
assixt test;
Releasing a module
“mi6 release;”
Changes entry
META6.json
Content pushed.
Assign a version tag.
Upload to CPAN.
Getting mi6
$ zef install App::Mi6
===> Searching for: App::Mi6
===> Testing: App::Mi6:ver<0.2.6>:auth<cpan:SKAJI>
===> Testing [OK] for App::Mi6:ver<0.2.6>:auth<cpan:SKAJI>
===> Installing: App::Mi6:ver<0.2.6>:auth<cpan:SKAJI>
1 bin/ script [mi6] installed to:
/opt/raku/share/raku/site/bin
Note the install dir.
Symlinks are your friends...
Changes
Revision history for FindBin
{{$NEXT}}
- Return IO for simplicity
0.3.4 2019-05-25T23:02:06-05:00
- use generic 01 test for use/require.
- POD, whitespace, comments.
0.3.3 2019-05-20T11:42:45-05:00
...
META6.json
{
"auth" : "CPAN:lembark",
"authors" : [
"lembark"
],
"build-depends" : [ ],
"depends" : [ ],
"description" : "Locate execution directory and basename.",
"license" : "Artistic-2.0",
"name" : "FindBin",
"perl" : "6.d",
"provides" : {
"FindBin" : "lib/FindBin.pm6"
},
"resources" : [ ],
"source-url" : "https://gitlab.com/lembark/raku-FindBin.git",
"tags" : [ ],
"test-depends" : [ ],
"version" : "0.3.4"
}
POD is still largely POD
=begin pod
=head1 SYNOPSIS
# export Bin() and Script() by default.
use FindBin;
my $dir_from = Bin(); # $dir_from is an IO object
my $script_base = Script(); # $script_base is a string.
# explicitly export only Bin() or Script().
use FindBin :Bin;
Releasing a module
$ mi6 release
==> Release distribution to CPAN
There are 12 steps: # Rakuholics Anonymous???
<snip>
==> Step 1. CheckChanges
==> Step 2. CheckOrigin
==> Step 3. CheckUntrackedFiles
==> Step 4. BumpVersion
Next release version? [0.3.5]: 0.4.0
==> Step 5. RegenerateFiles
==> Step 6. DistTest
<snip>
==> Step 8. UploadToCPAN
Are you sure you want to upload FindBin-0.4.0.tar.gz to CPAN? (y/N) y
Releasing a module
Housekeeping includes version tag.
==> Step 9. RewriteChanges
==> Step10. GitCommit
[master 6b2a3e2] 0.4.0
4 files changed, 5 insertions(+), 3 deletions(-)
<snip
To gitlab.com:lembark/Raku-FindBin.git
607929e..6b2a3e2 master -> master
==> Step11. CreateGitTag
Total 0 (delta 0), reused 0 (delta 0)
To gitlab.com:lembark/Raku-FindBin.git
* [new tag] 0.4.0 -> 0.4.0
==> Step12. CleanDist
Instant gratification
zef can install from a directory.
$ zef install . ;
===> Testing: FindBin:ver<0.4.0>:auth<CPAN:lembark>
# Madness: 'FindBin' (01-)
# FindBin
# Bin is: /sandbox/lembark/Modules/Raku/FindBin/t
<snip>
===> Testing [OK] for FindBin:ver<0.4.0>:auth<CPAN:lembark>
===> Installing: FindBin:ver<0.4.0>:auth<CPAN:lembark>
Bin is the root of all evil...
Next Step: Root out the rest of it.
Originally part of FindBin::libs:
Scan up directory tree.
Append relative paths.
Filter them.
FileSystem::Parent
Exports “scan-up”.
Stringy standard filters.
Roll your own: block, regex...
Even more than more than one way to do it
multi scan-up
(
Stringy :$filter = 'all', *%argz
--> Seq
)
{
state %filterz =
(
all => True ,
dir => :d ,
file => :f ,
exist => :e ,
);
my $value = %filterz{ $filter }
or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' ');
samewith filter => $value, |%argz
}
Single named arg “:filter”
multi scan-up
(
Stringy :$filter = 'all', *%argz
--> Seq
)
{
state %filterz =
(
all => True ,
dir => :d ,
file => :f ,
exist => :e ,
);
my $value = %filterz{ $filter }
or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' ');
samewith filter => $value, |%argz
}
*%argz slurps remaining args
multi scan-up
(
Stringy :$filter = 'all', *%argz
--> Seq
)
{
stage %filterz =
(
all => True ,
dir => :d ,
file => :f ,
exist => :e ,
);
my $value = %filterz{ $filter }
or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' ');
samewith filter => $value, |%argz
}
|%argz flattens them back out
multi scan-up
(
Stringy :$filter = 'all', *%argz
--> Seq
)
{
state %filterz =
(
all => True ,
dir => :d ,
file => :f ,
exist => :e ,
);
my $value = %filterz{ $filter }
or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' ');
samewith filter => $value, |%argz
}
same name, new filter
multi scan-up
(
Stringy :$filter = 'all', *%argz
--> Seq
)
{
state %filterz =
(
all => True ,
dir => :d ,
file => :f ,
exist => :e ,
);
my $value = %filterz{ $filter }
or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' ');
samewith filter => $value, |%argz
}
The other way to do it
multi scan-up
(
Any :$filter,
Str :$append = '',
Bool() :$resolve = False,
Bool() :$verbose = False,
Bool() :$skip-root = False,
IO() :$from = Bin( :$resolve, :$verbose )
--> Seq
)
is export( :DEFAULT )
$filter here is anything.
Args are defined in the parameters.
multi scan-up
(
Any :$filter,
Str :$append = '',
Bool() :$resolve = False,
Bool() :$verbose = False,
Bool() :$skip-root = False,
IO() :$from = Bin( :$resolve, :$verbose )
--> Seq
)
is export( :DEFAULT )
Re-cycled from Stringy multi via %args.
Telling them apart
multi Foo ( Stringy $filter = ‘all’, … )
multi Foo ( Any $filter, … )
Foo() Pick default no viable alternative.
Foo(‘X’) Pick Stringy derived from Any.
Foo( blah ) Pick Any.
Pick a filter, Any filter...
Perl5 used a code block, passed as a closure.
$filter->( $path ) or next...
Pick a filter, Any filter...
Smart matches are better way.
Pass in a file test, regex, block...
$path ~~ $filter or next;
Pick a filter, Any filter...
Smart matches are better way.
Pass in a file test, regex, block...
$path ~~ $filter or next;
Anything but a string.
Which was used up in the other multi.
Finding your path
absolute, resolve, parent should be familiar by now...
my $start =
(
$resolve
?? $from.resolve( completely => True )
!! $from
).absolute.IO;
my $path
= $start ~~ :d
?? $start
!! $start.parent
;
Finding your path
Fail if the path is not resolvable.
my $start =
(
$resolve
?? $from.resolve( completely => True )
!! $from
).absolute.IO;
my $path
= $start ~~ :d
?? $start
!! $start.parent
;
Gathering for a loop
return gather loop
{
…
take … if … ;
…
last if … ;
}
gather returns a Seq from take.
loop runs until a break.
Replaces for(;;){ ... push @result... }
Gathering for a loop
return gather loop
{
…
take … if … ;
…
last if … ;
}
gather returns a Seq from take.
loop runs until a break.
Replaces for(;;){ ... push @result... }
Gathering for a loop
return gather loop
{
…
take … if … ;
…
last if … ;
}
gather returns a Seq from take.
loop runs until a break.
Replaces for(;;){ ... push @result... }
Find your roots
return gather loop
{
my $next = $path.parent;
my $at-root = $path ~~ $next;
...
$path ~~ $filter
and take $path;
...
last if $at-root;
$path = $next;
}
What to take
if( $skip-root && $at-root )
{
say “Root: ‘$path’ (skip)” if $verbose;
}
else
{
my $take
= $append
?? $path.add( $append ) # add a relative path.
!! $path
;
$take ~~ $filter # $filter controls the match.
and take $take;
}
return gather loop
{
my $next = $path.parent;
my $at-root = $path ~~ $next;
if $at-root && $skip-root
{
note ‘Skip root’ if $verbose;
}
else
{
$take = $append ?? ... ;
$take ~~ $filter
and take $take;
}
$at-root
and last;
$path = $next;
}
Skip root before append.
Supply path to $filter.
Exit at root.
Working logic
Q: What’s in a Seq from gather?
Q: What’s in a Seq from gather?
$((IO::Path.new("/sandbox/lembark/Modules/Raku/FileSystem-Parent/
t/lib", :SPEC(IO::Spec::Unix),
:CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")),
IO::Path.new("/sandbox/lembark/Modules/Raku/FileSystem-Parent/lib"
, :SPEC(IO::Spec::Unix),
:CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")),
IO::Path.new("/sandbox/lembark/Modules/Raku/lib", :SPEC(IO::Spec::
Unix), :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")),
IO::Path.new("/sandbox/lembark/Modules/lib", :SPEC(IO::Spec::Unix)
, :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")),
IO::Path.new("/sandbox/lembark/lib", :SPEC(IO::Spec::Unix), :CWD("
/sandbox/lembark/Modules/Raku/FileSystem-Parent")),
IO::Path.new("/sandbox/lib", :SPEC(IO::Spec::Unix),
:CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")),
IO::Path.new("/lib", :SPEC(IO::Spec::Unix),
A: Lazyness
(
IO::Path.new
(
"/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib"
, :SPEC(IO::Spec::Unix)
, :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")
),
...
).Seq
A: Lazyness
(
IO::Path.new
(
"/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib"
, :SPEC(IO::Spec::Unix)
, :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")
),
...
).Seq
Untyped until the value is extracted.
A: Lazyness
(
IO::Path.new
(
"/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib"
, :SPEC(IO::Spec::Unix)
, :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")
),
...
).Seq
It may fail:
Filesystem is [usually] stable.
A: Lazyness
(
IO::Path.new
(
"/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib"
, :SPEC(IO::Spec::Unix)
, :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")
),
...
).Seq
It may fail:
Database or network queries may not be.
(
IO::Path.new
(
"/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib"
, :SPEC(IO::Spec::Unix)
, :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")
),
...
).Seq
Caveat: hoc comedit seriem!
A: Lazyness
Testing lazyness
One test: Compare it to an array.
my @found = scan-up.eager;
for ( 0 ..^ +@found ) -> $i { … }
Another is smart-match it with a seq:
my $found = scan-up;
ok $found ~~ $expect, ‘Found it.’;
Compare Bin dir with a file
Path and Bin results should match.
my $pass0 = scan-up;
ok $pass0, "scan-up = $pass0";
my $pass1 = scan-up :from( $*PROGRAM-NAME );
ok $pass1, "scan-up :from( $*PROGRAM-NAME ) = $pass1";
ok $pass0 ~~ $pass1, 'Bin matches $*Program-Name';
File and dir path work
$ Raku t/03-no-subdir.rakutest
ok 1 - scan-up = /sandbox/lembark/Modules/Raku/FileSystem-
Parent/t /sandbox/lembark/Modules/Raku/FileSystem-Parent
/sandbox/lembark/Modules/Raku /sandbox/lembark/Modules
/sandbox/lembark /sandbox /
ok 2 - scan-up :from( t/03-no-subdir.t ) =
/sandbox/lembark/Modules/Raku/FileSystem-Parent/t
/sandbox/lembark/Modules/Raku/FileSystem-Parent
/sandbox/lembark/Modules/Raku /sandbox/lembark/Modules
/sandbox/lembark /sandbox /
ok 3 - Bin and $*Program-Name return same list.
1..3
Adding a lib
:$append with variable name.
my $append = 'lib';
my $pass0 = scan-up :$append;
ok $pass0, 'scan-up $append';
my $pass1 = scan-up :$append, from => $*PROGRAM-NAME;
ok $pass1, 'scan-up $append :from => $*PROGRAM-NAME';
ok $pass0.eager ~~ $pass1.eager, ‘They match’;
Testing :$append
ok 1 - scan-up $append
ok 2 - scan-up $append :from => $*PROGRAM-NAME
# Pass0:/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib
/sandbox/lembark/Modules/Raku/FileSystem-Parent/lib
/sandbox/lembark/Modules/Raku/lib
/sandbox/lembark/Modules/lib /sandbox/lembark/lib /sandbox/lib
/lib
# Pass1:/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib
/sandbox/lembark/Modules/Raku/FileSystem-Parent/lib
/sandbox/lembark/Modules/Raku/lib
/sandbox/lembark/Modules/lib /sandbox/lembark/lib /sandbox/lib
/lib
ok 3 - Bin and $*Program-Name return same list.
Test verbose overrides
There is, of course, more than one way.
Shouldn’t affect Bin’s return value.
use FindBin :verbose;
my $false_1 = Bin( verbose => False ).Str;
my $false_2 = Bin( :verbose( False ) ).Str;
my $false_3 = Bin( :!verbose ).Str;
is $false_1, $false_2, "Match with false.";
is $false_2, $false_3, "Match with false.";
pragmata
Finally!
Given libs, use them.
pragmata
Finally!
Given libs, use them.
Runs at compile time.
Calling a sub is too late.
“EXPORT” does the deed
Outside of the package.
Handles positionals passed via use.
use v6.d;
sub EXPORT
(
*@args –-> Map
)
{
my $resolve = ? @args.first( 'resolve' );
my $verbose = ? @args.first( 'verbose' );
# do things...
}
unit module FindBin::libs:ver<0.0.1>:auth<CPAN:lembark>
# ain’t nobody here but us chickens
“EXPORT” does the deed
Outside of the package.
Handles positionals passed via use.
use v6.d;
sub EXPORT
(
*@args –-> Map
)
{
my $resolve = ? @args.first( 'resolve' );
my $verbose = ? @args.first( 'verbose' );
# do things...
}
unit module FindBin::libs:ver<0.0.1>:auth<CPAN:lembark>
# ain’t nobody here at all
Raku Colon Cleanser
Ever loose track of parens in an expression?
Raku method calls can replace
$foo.some_method( $bar );
with
$foo.some_method: $bar;
Raku Colon Cleanser
Ever loose track of parens in an expression?
Convert:
$foo.frob($bar.nicate($thing.new($blort)));
into:
$foo.frob: $bar.nicate: $thing.new: $blort
Raku Colon Cleanser
Ever loose track of parens in an expression?
$foo.frob:
$bar.nicate:
$thing.new: $blort
;
Find non-root ./lib dirs & add them.
use FileSystem::Parent;
my %fixed =
(
append => 'lib',
filter => 'dir',
skip-root => True
);
my @found = scan-up( :$resolve, :$verbose, |%fixed );
for @found.reverse -> $dir
{
for CompUnit::Repository::FileSystem.new( prefix => $dir ) -> $lib
{
CompUnit::RepositoryRegistry.use-repository( $lib );
}
}
Find non-root ./lib dirs & add them.
use FileSystem::Parent;
my %fixed =
(
append => 'lib',
filter => 'dir',
skip-root => True
);
my @found = scan-up( :$resolve, :$verbose, |%fixed );
for @found.reverse -> $dir
{
given CompUnit::Repository::FileSystem.new( prefix => $dir ) -> $lib
{
CompUnit::RepositoryRegistry.use-repository( $lib );
}
}
Find non-root ./lib dirs & add them.
use FileSystem::Parent;
my %fixed =
(
append => 'lib',
filter => 'dir',
skip-root => True
);
my @found = scan-up( :$resolve, :$verbose, |%fixed );
for @found.reverse -> $dir
{
given CompUnit::Repository::FileSystem.new( prefix => $dir ) -> $lib
{
CompUnit::RepositoryRegistry.use-repository( $lib );
}
}
Find non-root ./lib dirs & add them.
use FileSystem::Parent;
my %fixed =
(
append => 'lib',
filter => 'dir',
skip-root => True
);
my @found = scan-up( :$resolve, :$verbose, |%fixed );
for @found.reverse -> $prefix
{
given CompUnit::Repository::FileSystem.new( :$prefix ) -> $lib
{
CompUnit::RepositoryRegistry.use-repository( $lib );
}
}
Find non-root ./lib dirs & add them.
use FileSystem::Parent;
my %fixed =
(
append => 'lib',
filter => 'dir',
skip-root => True
);
my @found = scan-up( :$resolve, :$verbose, |%fixed );
for @found.reverse -> $prefix
{
CompUnit::RepositoryRegistry.use-repository(
CompUnit::Repository::FileSystem.new(
:$prefix
)
)
}
Find non-root ./lib dirs & add them.
use FileSystem::Parent;
my %fixed =
(
append => 'lib',
filter => 'dir',
skip-root => True
);
my @found = scan-up( :$resolve, :$verbose, |%fixed );
for @found.reverse -> $prefix
{
CompUnit::RepositoryRegistry.use-repository:
CompUnit::Repository::FileSystem.new:
:$prefix
}
Named Parameters & Variables
Egads! Using “:$prefix” ties your code to an API!
Named Parameters & Variables
Egads! Using “:$prefix” ties your code to my API!
Signatures allow arbitrary variable names.
:$prefix is a shortcut for
sub foo( :prefix( $prefix ) )
You can also have:
sub foo ( :prefix( $dir_pref ) )
Change internal variable names.
Named Parameters & Variables
Egads! Using “:$prefix” ties your code to my API!
Signatures allow arbitrary variable names.
:$prefix is a shortcut for
sub foo( :prefix( $prefix ) )
You can also have:
sub foo ( :add( $prefix ) )
Change external parameter name.
--> Map
sub EXPORT
(
*@args --> Map
)
{
# return a Map
%( '@FindBin-libs-dirs' => @found )
}
Map of ( Name => Value )
--> Map
Map of ( Name => Value )
‘@’ is part of the name.
sub EXPORT
(
*@args --> Map
)
{
# return a Map
%( '@FindBin-libs-dirs' => @found )
}
sub EXPORT
(
*@args --> Map
)
{
use FileSystem::Parent;
my $resolve = ? @_.first( 'resolve' );
my $verbose = ? @_.first( 'verbose' );
my %fixed = ( ... );
my @found
= scan-up( :$verbose, :$resolve, |%fixed );
for @found.reverse -> $prefix
{
CompUnit::RepositoryRegistry.use-repository:
CompUnit::Repository::FileSystem.new:
:$prefix
}
%( '@FindBin-libs-dirs' => @found )
}
Exporter
“unit” is a placeholder.
Export does all the work.
unit module FindBin::libs has no subs.
Pass1: Check for installed dirs.
EXPORT installs the variable.
Flatten it for printing.
use FindBin::libs;
ok @FindBin-libs-dirs, 'Exported FindBin-libs-dirs';
note join( "n#t", '# libs:', |@FindBin-libs-dirs );
done-testing;
Pass2: Validate directory order.
Frobnicate.rakumod in ./t/lib/FindBin & ./lib/FindBin.
“FrobPath” will be ‘t/lib/...’ or ‘lib/...’.
unit module FindBin::Frobnicate:ver<0.1.1>:auth<CPAN:lembark>;
constant FrobPath is export( :DEFAULT )
= 't/lib/FindBin/Frobnicate.pm6';
constant FrobPath is export( :DEFAULT )
= 'lib/FindBin/Frobnicate.pm6';
Pass2: Validate directory order.
Test in ./t should prefer t/lib.
use FindBin::libs;
use FindBin::Frobnicate;
ok MY::<FrobPath>, 'FrobPath exported.';
my $expect = 't/lib/FindBin/Frobnicate.pm6';
my $found := MY::FrobPath;
is $found, $expect, "Found: ‘$found’”;
prove6
Looks familiar.
$ prove6 -v t/03-use-frobnicate.t;
ok 1 - FrobPath exported.
ok 2 - Found 't/lib/FindBin/Frobnicate.pm6'
1..2
t/03-use-frobnicate.t .. ok
All tests successful.
Files=1, Tests=2, 0 wallclock secs
Result: PASS
Summary
Raku is quite doable.
mi6 & assixt make it manageable.
Raku has gotten much better.
Raku is quite doable.
mi6 & assixt make it more manageable.
The doc’s website has gotten much better:
Raku
Try it... you’ll like it.
References
https://gitlab.com/lembark
https://github.com/skaji/mi6
https://www.tyil.nl/post/2018/03/20/Raku-
introduction-to-application-programming/
https://docs.Raku.org/
routine/|
type/Slip
type/Signature#Coercion_type
type/Stringy
language/variables#index-entry-Twigil
language/variables#The_*_twigil
language/testing
language/modules#EXPORT
syntax/state#The_$_variable

More Related Content

What's hot

Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.Workhorse Computing
 
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning CBuilding and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning CDavid Wheeler
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Workhorse Computing
 
Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Kang-min Liu
 
I, For One, Welcome Our New Perl6 Overlords
I, For One, Welcome Our New Perl6 OverlordsI, For One, Welcome Our New Perl6 Overlords
I, For One, Welcome Our New Perl6 Overlordsheumann
 
Melhorando sua API com DSLs
Melhorando sua API com DSLsMelhorando sua API com DSLs
Melhorando sua API com DSLsAugusto Pascutti
 
Perforce Object and Record Model
Perforce Object and Record Model  Perforce Object and Record Model
Perforce Object and Record Model Perforce
 
Utility Modules That You Should Know About
Utility Modules That You Should Know AboutUtility Modules That You Should Know About
Utility Modules That You Should Know Aboutjoshua.mcadams
 
Barely Legal Xxx Perl Presentation
Barely Legal Xxx Perl PresentationBarely Legal Xxx Perl Presentation
Barely Legal Xxx Perl PresentationAttila Balazs
 
Webrtc mojo
Webrtc mojoWebrtc mojo
Webrtc mojobpmedley
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworksdiego_k
 
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner) Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner) Puppet
 

What's hot (20)

Short Introduction To "perl -d"
Short Introduction To "perl -d"Short Introduction To "perl -d"
Short Introduction To "perl -d"
 
Get your teeth into Plack
Get your teeth into PlackGet your teeth into Plack
Get your teeth into Plack
 
Smoking docker
Smoking dockerSmoking docker
Smoking docker
 
Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.Perl6 Regexen: Reduce the line noise in your code.
Perl6 Regexen: Reduce the line noise in your code.
 
Unit Testing Lots of Perl
Unit Testing Lots of PerlUnit Testing Lots of Perl
Unit Testing Lots of Perl
 
Getting testy with Perl
Getting testy with PerlGetting testy with Perl
Getting testy with Perl
 
Building and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning CBuilding and Distributing PostgreSQL Extensions Without Learning C
Building and Distributing PostgreSQL Extensions Without Learning C
 
Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6Ethiopian multiplication in Perl6
Ethiopian multiplication in Perl6
 
Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)Good Evils In Perl (Yapc Asia)
Good Evils In Perl (Yapc Asia)
 
I, For One, Welcome Our New Perl6 Overlords
I, For One, Welcome Our New Perl6 OverlordsI, For One, Welcome Our New Perl6 Overlords
I, For One, Welcome Our New Perl6 Overlords
 
Melhorando sua API com DSLs
Melhorando sua API com DSLsMelhorando sua API com DSLs
Melhorando sua API com DSLs
 
Perl6 in-production
Perl6 in-productionPerl6 in-production
Perl6 in-production
 
Perforce Object and Record Model
Perforce Object and Record Model  Perforce Object and Record Model
Perforce Object and Record Model
 
Utility Modules That You Should Know About
Utility Modules That You Should Know AboutUtility Modules That You Should Know About
Utility Modules That You Should Know About
 
Perl6 grammars
Perl6 grammarsPerl6 grammars
Perl6 grammars
 
Barely Legal Xxx Perl Presentation
Barely Legal Xxx Perl PresentationBarely Legal Xxx Perl Presentation
Barely Legal Xxx Perl Presentation
 
Ruby 2.0
Ruby 2.0Ruby 2.0
Ruby 2.0
 
Webrtc mojo
Webrtc mojoWebrtc mojo
Webrtc mojo
 
Perl web frameworks
Perl web frameworksPerl web frameworks
Perl web frameworks
 
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner) Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
Puppet Camp Paris 2015: Power of Puppet 4 (Beginner)
 

Similar to Findbin libs

Complet vector.dep.inc# This code depends on make tool being .docx
Complet vector.dep.inc# This code depends on make tool being .docxComplet vector.dep.inc# This code depends on make tool being .docx
Complet vector.dep.inc# This code depends on make tool being .docxardhowp
 
Buildr In Action @devoxx france 2012
Buildr In Action @devoxx france 2012Buildr In Action @devoxx france 2012
Buildr In Action @devoxx france 2012alexismidon
 
ZFConf 2012: Capistrano для деплоймента PHP-приложений (Роман Лапин)
ZFConf 2012: Capistrano для деплоймента PHP-приложений (Роман Лапин)ZFConf 2012: Capistrano для деплоймента PHP-приложений (Роман Лапин)
ZFConf 2012: Capistrano для деплоймента PHP-приложений (Роман Лапин)ZFConf Conference
 
Isolated development in python
Isolated development in pythonIsolated development in python
Isolated development in pythonAndrés J. Díaz
 
From Java to Kotlin - The first month in practice
From Java to Kotlin - The first month in practiceFrom Java to Kotlin - The first month in practice
From Java to Kotlin - The first month in practiceStefanTomm
 
Test Driven Development with Chef
Test Driven Development with ChefTest Driven Development with Chef
Test Driven Development with ChefSimone Soldateschi
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Railsrstankov
 
Lets make better scripts
Lets make better scriptsLets make better scripts
Lets make better scriptsMichael Boelen
 
Advanced debugging  techniques in different environments
Advanced debugging  techniques in different environmentsAdvanced debugging  techniques in different environments
Advanced debugging  techniques in different environmentsAndrii Soldatenko
 
Useful Kafka tools
Useful Kafka toolsUseful Kafka tools
Useful Kafka toolsDale Lane
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hopeMarcus Ramberg
 
Lab Zero Lunchdown: Deploying Elixir and Phoenix Applications
Lab Zero Lunchdown: Deploying Elixir and Phoenix ApplicationsLab Zero Lunchdown: Deploying Elixir and Phoenix Applications
Lab Zero Lunchdown: Deploying Elixir and Phoenix Applicationsbrien_wankel
 
Using the Groovy Ecosystem for Rapid JVM Development
Using the Groovy Ecosystem for Rapid JVM DevelopmentUsing the Groovy Ecosystem for Rapid JVM Development
Using the Groovy Ecosystem for Rapid JVM DevelopmentSchalk Cronjé
 
7.Compiling And Cataloguing Routines
7.Compiling And Cataloguing Routines7.Compiling And Cataloguing Routines
7.Compiling And Cataloguing RoutinesMadhu mp
 
The road to continuous deployment (DomCode September 2016)
The road to continuous deployment (DomCode September 2016)The road to continuous deployment (DomCode September 2016)
The road to continuous deployment (DomCode September 2016)Michiel Rook
 
Node.js basics
Node.js basicsNode.js basics
Node.js basicsBen Lin
 
From Zero to Application Delivery with NixOS
From Zero to Application Delivery with NixOSFrom Zero to Application Delivery with NixOS
From Zero to Application Delivery with NixOSSusan Potter
 
Less-Dumb Fuzzing and Ruby Metaprogramming
Less-Dumb Fuzzing and Ruby MetaprogrammingLess-Dumb Fuzzing and Ruby Metaprogramming
Less-Dumb Fuzzing and Ruby MetaprogrammingNephi Johnson
 

Similar to Findbin libs (20)

Complet vector.dep.inc# This code depends on make tool being .docx
Complet vector.dep.inc# This code depends on make tool being .docxComplet vector.dep.inc# This code depends on make tool being .docx
Complet vector.dep.inc# This code depends on make tool being .docx
 
Buildr In Action @devoxx france 2012
Buildr In Action @devoxx france 2012Buildr In Action @devoxx france 2012
Buildr In Action @devoxx france 2012
 
ZFConf 2012: Capistrano для деплоймента PHP-приложений (Роман Лапин)
ZFConf 2012: Capistrano для деплоймента PHP-приложений (Роман Лапин)ZFConf 2012: Capistrano для деплоймента PHP-приложений (Роман Лапин)
ZFConf 2012: Capistrano для деплоймента PHP-приложений (Роман Лапин)
 
New in php 7
New in php 7New in php 7
New in php 7
 
Isolated development in python
Isolated development in pythonIsolated development in python
Isolated development in python
 
RxSwift to Combine
RxSwift to CombineRxSwift to Combine
RxSwift to Combine
 
From Java to Kotlin - The first month in practice
From Java to Kotlin - The first month in practiceFrom Java to Kotlin - The first month in practice
From Java to Kotlin - The first month in practice
 
Test Driven Development with Chef
Test Driven Development with ChefTest Driven Development with Chef
Test Driven Development with Chef
 
Ruby/Rails
Ruby/RailsRuby/Rails
Ruby/Rails
 
Lets make better scripts
Lets make better scriptsLets make better scripts
Lets make better scripts
 
Advanced debugging  techniques in different environments
Advanced debugging  techniques in different environmentsAdvanced debugging  techniques in different environments
Advanced debugging  techniques in different environments
 
Useful Kafka tools
Useful Kafka toolsUseful Kafka tools
Useful Kafka tools
 
Mojolicious - A new hope
Mojolicious - A new hopeMojolicious - A new hope
Mojolicious - A new hope
 
Lab Zero Lunchdown: Deploying Elixir and Phoenix Applications
Lab Zero Lunchdown: Deploying Elixir and Phoenix ApplicationsLab Zero Lunchdown: Deploying Elixir and Phoenix Applications
Lab Zero Lunchdown: Deploying Elixir and Phoenix Applications
 
Using the Groovy Ecosystem for Rapid JVM Development
Using the Groovy Ecosystem for Rapid JVM DevelopmentUsing the Groovy Ecosystem for Rapid JVM Development
Using the Groovy Ecosystem for Rapid JVM Development
 
7.Compiling And Cataloguing Routines
7.Compiling And Cataloguing Routines7.Compiling And Cataloguing Routines
7.Compiling And Cataloguing Routines
 
The road to continuous deployment (DomCode September 2016)
The road to continuous deployment (DomCode September 2016)The road to continuous deployment (DomCode September 2016)
The road to continuous deployment (DomCode September 2016)
 
Node.js basics
Node.js basicsNode.js basics
Node.js basics
 
From Zero to Application Delivery with NixOS
From Zero to Application Delivery with NixOSFrom Zero to Application Delivery with NixOS
From Zero to Application Delivery with NixOS
 
Less-Dumb Fuzzing and Ruby Metaprogramming
Less-Dumb Fuzzing and Ruby MetaprogrammingLess-Dumb Fuzzing and Ruby Metaprogramming
Less-Dumb Fuzzing and Ruby Metaprogramming
 

More from Workhorse Computing

Wheels we didn't re-invent: Perl's Utility Modules
Wheels we didn't re-invent: Perl's Utility ModulesWheels we didn't re-invent: Perl's Utility Modules
Wheels we didn't re-invent: Perl's Utility ModulesWorkhorse Computing
 
Paranormal statistics: Counting What Doesn't Add Up
Paranormal statistics: Counting What Doesn't Add UpParanormal statistics: Counting What Doesn't Add Up
Paranormal statistics: Counting What Doesn't Add UpWorkhorse Computing
 
Generating & Querying Calendar Tables in Posgresql
Generating & Querying Calendar Tables in PosgresqlGenerating & Querying Calendar Tables in Posgresql
Generating & Querying Calendar Tables in PosgresqlWorkhorse Computing
 
The W-curve and its application.
The W-curve and its application.The W-curve and its application.
The W-curve and its application.Workhorse Computing
 
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6Workhorse Computing
 
Shared Object images in Docker: What you need is what you want.
Shared Object images in Docker: What you need is what you want.Shared Object images in Docker: What you need is what you want.
Shared Object images in Docker: What you need is what you want.Workhorse Computing
 
Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.Workhorse Computing
 
Selenium Sandwich Part 1: Data driven Selenium
Selenium Sandwich Part 1: Data driven Selenium Selenium Sandwich Part 1: Data driven Selenium
Selenium Sandwich Part 1: Data driven Selenium Workhorse Computing
 

More from Workhorse Computing (19)

Wheels we didn't re-invent: Perl's Utility Modules
Wheels we didn't re-invent: Perl's Utility ModulesWheels we didn't re-invent: Perl's Utility Modules
Wheels we didn't re-invent: Perl's Utility Modules
 
mro-every.pdf
mro-every.pdfmro-every.pdf
mro-every.pdf
 
Paranormal statistics: Counting What Doesn't Add Up
Paranormal statistics: Counting What Doesn't Add UpParanormal statistics: Counting What Doesn't Add Up
Paranormal statistics: Counting What Doesn't Add Up
 
Generating & Querying Calendar Tables in Posgresql
Generating & Querying Calendar Tables in PosgresqlGenerating & Querying Calendar Tables in Posgresql
Generating & Querying Calendar Tables in Posgresql
 
Effective Benchmarks
Effective BenchmarksEffective Benchmarks
Effective Benchmarks
 
The W-curve and its application.
The W-curve and its application.The W-curve and its application.
The W-curve and its application.
 
Getting Testy With Perl6
Getting Testy With Perl6Getting Testy With Perl6
Getting Testy With Perl6
 
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
Neatly Hashing a Tree: FP tree-fold in Perl5 & Perl6
 
Neatly folding-a-tree
Neatly folding-a-treeNeatly folding-a-tree
Neatly folding-a-tree
 
Light my-fuse
Light my-fuseLight my-fuse
Light my-fuse
 
Paranormal stats
Paranormal statsParanormal stats
Paranormal stats
 
Shared Object images in Docker: What you need is what you want.
Shared Object images in Docker: What you need is what you want.Shared Object images in Docker: What you need is what you want.
Shared Object images in Docker: What you need is what you want.
 
Putting some "logic" in LVM.
Putting some "logic" in LVM.Putting some "logic" in LVM.
Putting some "logic" in LVM.
 
Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.Selenium sandwich-3: Being where you aren't.
Selenium sandwich-3: Being where you aren't.
 
Selenium sandwich-2
Selenium sandwich-2Selenium sandwich-2
Selenium sandwich-2
 
Selenium Sandwich Part 1: Data driven Selenium
Selenium Sandwich Part 1: Data driven Selenium Selenium Sandwich Part 1: Data driven Selenium
Selenium Sandwich Part 1: Data driven Selenium
 
Docker perl build
Docker perl buildDocker perl build
Docker perl build
 
Designing net-aws-glacier
Designing net-aws-glacierDesigning net-aws-glacier
Designing net-aws-glacier
 
Lies, Damn Lies, and Benchmarks
Lies, Damn Lies, and BenchmarksLies, Damn Lies, and Benchmarks
Lies, Damn Lies, and Benchmarks
 

Recently uploaded

From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .Alan Dix
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsPixlogix Infotech
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...Fwdays
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteDianaGray10
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsRizwan Syed
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyAlfredo García Lavilla
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Manik S Magar
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DaySri Ambati
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxLoriGlavin3
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionDilum Bandara
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningLars Bell
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr BaganFwdays
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity PlanDatabarracks
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek SchlawackFwdays
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 

Recently uploaded (20)

From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .From Family Reminiscence to Scholarly Archive .
From Family Reminiscence to Scholarly Archive .
 
The Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and ConsThe Ultimate Guide to Choosing WordPress Pros and Cons
The Ultimate Guide to Choosing WordPress Pros and Cons
 
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks..."LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
"LLMs for Python Engineers: Advanced Data Analysis and Semantic Kernel",Oleks...
 
Take control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test SuiteTake control of your SAP testing with UiPath Test Suite
Take control of your SAP testing with UiPath Test Suite
 
Scanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL CertsScanning the Internet for External Cloud Exposures via SSL Certs
Scanning the Internet for External Cloud Exposures via SSL Certs
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Commit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easyCommit 2024 - Secret Management made easy
Commit 2024 - Secret Management made easy
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!Anypoint Exchange: It’s Not Just a Repo!
Anypoint Exchange: It’s Not Just a Repo!
 
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo DayH2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
H2O.ai CEO/Founder: Sri Ambati Keynote at Wells Fargo Day
 
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptxMerck Moving Beyond Passwords: FIDO Paris Seminar.pptx
Merck Moving Beyond Passwords: FIDO Paris Seminar.pptx
 
Advanced Computer Architecture – An Introduction
Advanced Computer Architecture – An IntroductionAdvanced Computer Architecture – An Introduction
Advanced Computer Architecture – An Introduction
 
DSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine TuningDSPy a system for AI to Write Prompts and Do Fine Tuning
DSPy a system for AI to Write Prompts and Do Fine Tuning
 
"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan"ML in Production",Oleksandr Bagan
"ML in Production",Oleksandr Bagan
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
How to write a Business Continuity Plan
How to write a Business Continuity PlanHow to write a Business Continuity Plan
How to write a Business Continuity Plan
 
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
"Subclassing and Composition – A Pythonic Tour of Trade-Offs", Hynek Schlawack
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 

Findbin libs

  • 1. Climbing a Tree: Refactoring FindBin::libs for Raku Steven Lembark Workhorse Computing lembark@wrkhors.com
  • 2. FindBin::libs is a programatic “-I” FindBin Scan up the tree. Use non-root ./lib dirs.
  • 3. Why? Test or execute project without installing. In-house modules without CPAN or zef. Tests use ./t/lib or ./t/etc Avoid destructive tests in production. Resolve path. Use modules relative to ‘real’ path.
  • 4. Describing Raku logic Not Perl. Coding & testing utilities. Command line tools. zef Installer. Releasing with zef.
  • 5. First Step: FindBin You are here ---> * Interactive vs. program. Absolute path. As-is vs. resolved. Native filesystem. &Bin &Script
  • 6. Thinking Raku FindBin used to export $Bin along with subs. Now exports Bin & Script subs only.
  • 7. “unit” Raku uses “compilation units”. This unit is a single module. use v6.d; unit module FindBin:ver<0.4.0>:auth<CPAN:lembark>;
  • 8. “unit” Raku uses “compilation units”. Raku supports multiple versions & authors. The version & author are part of the definition. use v6.d; unit module FindBin:ver<0.4.0>:auth<CPAN:lembark>;
  • 9. Constant’s don’t change Neither does your interactive status.
  • 10. Constant’s don’t change Neither does your interactive status: constant IS‑INTERACTIVE = $*PROGRAM‑NAME eq '-e'| '-' | 'interactive' ;
  • 11. Twigil `tween a sigil and a name. ‘*’ is for dynamic. AUTOGENERATED constant IS‑INTERACTIVE = $*PROGRAM‑NAME eq '-e'| '-' | 'interactive' ;
  • 12. Module args ‘:’ prefix is part of the language: use FindBin; # :DEFAULT use FindBin :resolve; use FindBin :verbose; use FindBin :Bin; use FindBin :Script :resolve;
  • 13. Module args ‘:’ prefix is part of the language: use FindBin; # :DEFAULT use FindBin :resolve; use FindBin :verbose; use FindBin :Bin; use FindBin :Script :resolve; constant _FindBin_RESOLVE-DEF is export( :resolve ) = True; constant _FindBin_VERBOSE-DEF is export( :verbose ) = True;
  • 14. Slip |( ) converts a list into a Slip. No context: Raku lists don't flatten automatically. Slips do. Good examples at doc.raku.org (see references). constant OPTION-TAGS = |( :resolve , :verbose );
  • 16. Signature An object. Defines input, output. sub Bin ( Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF, Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF --> IO )
  • 17. Signature :$resolve stores “:resolve” or “:!resolve” argument. Defaults to caller’s lexical. sub Bin ( Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF, Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF --> IO )
  • 18. Signature Bool() coerces the values to type Bool. Avoids undef. sub Bin ( Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF, Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF --> IO )
  • 19. export Explicit “:Bin”, no args. Either of “:verbose” or “:resolve” via flattening. sub Bin ( Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF, Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF --> IO ) is export( :Bin, :DEFAULT, OPTION-TAGS )
  • 20. Using a constant No sigil. “?? !!” Ternary. my $bin_from = IS-INTERACTIVE ?? $*CWD !! $*PROGRAM.IO ;
  • 21. Using a constant $*CWD is an IO. my $bin_from = IS-INTERACTIVE ?? $*CWD !! $*PROGRAM.IO ;
  • 22. Using a constant $*PROGRAM is Str. .IO calls constructor: Converts the string to an IO object. my $bin_from = IS-INTERACTIVE ?? $*CWD !! $*PROGRAM.IO ;
  • 23. Using an IO .resolve --> IO .absolute --> Str. my $path = $resolve ?? $bin_from.resolve !! $bin_from.absolute.IO ;
  • 24. Returning a directory dirname works on *NIX, fails on MS, VMS. Parent includes the volume. IS-INTERACTIVE ?? $path !! $path.parent
  • 25. Bin sub Bin ( Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF, Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF --> IO ) is export( :Bin, :DEFAULT, OPTION-TAGS ) { my $bin_from = IS-INTERACTIVE ?? $*CWD !! $*PROGRAM.IO ; my $path = $resolve ?? $bin_from.resolve !! $bin_from.absolute.IO ; if $verbose { ... } IS-INTERACTIVE ?? $path !! $path.parent } Viola!
  • 26. Viola! Script note is “say” to stderr. our sub Script ( Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF, Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF --> Str ) is export( :Script, :DEFAULT, OPTION-TAGS ) { if $verbose { note '# Script()'; note "# Interactive: '{IS-INTERACTIVE}'"; note "# Resolve: $resolve"; note "# Path: '$*PROGRAM-NAME'"; } IS-INTERACTIVE ?? $*PROGRAM-NAME !! $resolve ?? $*PROGRAM.resolve.basename !! $*PROGRAM.basename }
  • 27. Oops... Bug in v6.d: Signature cannot handle Caller::Lexical our sub Script ( Bool() :$resolve = CALLER::LEXICAL::_FindBin_RESOLVE-DEF, Bool() :$verbose = CALLER::LEXICAL::_FindBin_VERBOSE-DEF --> Str ) is export( :Script, :DEFAULT, OPTION-TAGS ) { if $verbose { note '# Script()'; note "# Interactive: '{IS-INTERACTIVE}'"; note "# Resolve: $resolve"; note "# Path: '$*PROGRAM-NAME'"; } IS-INTERACTIVE ?? $*PROGRAM-NAME !! $resolve ?? $*PROGRAM.resolve.basename !! $*PROGRAM.basename }
  • 28. Viola! Bug in v6.d: Signature cannot handle Caller::Lexical Fix: Assign defaults. our sub Script ( Bool() :$resolve is copy, Bool() :$verbose is copy --> Str ) is export( :Script, :DEFAULT, OPTION-TAGS ) { $resolve //= ? CALLER::LEXICAL::_FindBin_RESOLVE-DEF; $verbose //= ? CALLER::LEXICAL::_FindBin_VERBOSE-DEF; if $verbose { note '# Script()'; note "# Interactive: '{IS-INTERACTIVE}'"; note "# Resolve: $resolve"; note "# Path: '$*PROGRAM-NAME'"; } IS-INTERACTIVE ?? $*PROGRAM-NAME !! $resolve ?? $*PROGRAM.resolve.basename !! $*PROGRAM.basename }
  • 29. Viola! ‘?’ coerces value to Bool. Avoids undef. our sub Script ( Bool() :$resolve is copy, Bool() :$verbose is copy --> Str ) is export( :Script, :DEFAULT, OPTION-TAGS ) { $resolve //= ? CALLER::LEXICAL::_FindBin_RESOLVE-DEF; $verbose //= ? CALLER::LEXICAL::_FindBin_VERBOSE-DEF; if $verbose { note '# Script()'; note "# Interactive: '{IS-INTERACTIVE}'"; note "# Resolve: $resolve"; note "# Path: '$*PROGRAM-NAME'"; } IS-INTERACTIVE ?? $*PROGRAM-NAME !! $resolve ?? $*PROGRAM.resolve.basename !! $*PROGRAM.basename }
  • 30. How to test it. Test ok use_ok require_ok is
  • 31. Generic sanity test use v6.d; use Test; use lib $*PROGRAM.IO.parent(2).add( 'lib' ); ( $*PROGRAM-NAME.IO.basename ~~ m{^ (d+ '-')? (.+) '.'t $} ) or bail-out 'Unable to parse test name: ' ~ $*PROGRAM-NAME; my $madness = $1.subst( '-', '::', :g ); use-ok $madness; my $found = ::( $madness ).^name; is $found, $madness, "Package '$found' ($madness)."; done-testing;
  • 32. Checking exports Pseudo-class MY:: with hardwired name. use v6.d; use Test; ok ! MY::<&Bin> , 'Bin not installed before use.'; ok ! MY::<&Script> , 'Script not installed before use.'; ok ! MY::<_FindBin_RESOLVE-DEF>, '! _FindBin_RESOLVE-DEF’; ok ! MY::<_FindBin_VERBOSE-DEF>, '! _FindBin_VERBOSE-DEF’;
  • 33. Checking exports do { use FindBin; ok MY::<&Bin> , 'Bin installed by default.'; ok MY::<&Script> , 'Script installed by default.'; ok ! MY::<_FindBin_RESOLVE-DEF>, '! _FindBin_RESOLVE-DEF'; ok ! MY::<_FindBin_VERBOSE-DEF>, '! _FindBin_VERBOSE-DEF'; note '# Bin is: ' ~ Bin; note '# Script is: ' ~ Script; };
  • 34. Checking exports do { use FindBin; ... }; ok ! MY::<&Bin> , 'Bin is lexicaly scoped.'; ok ! MY::<&Script> , 'Script is lexicaly scoped.'; ok ! MY::<_FindBin_RESOLVE-DEF>, '! _FindBin_RESOLVE-DEF'; ok ! MY::<_FindBin_VERBOSE-DEF>, '! _FindBin_RESOLVE-DEF';
  • 35. Script test program is easy use v6.d; use Test; use lib $*PROGRAM.IO.parent(2).add( 'lib' ); use FindBin; my $found = Script; my $expect = $*PROGRAM.basename; ok $found, 'Script returns ' ~ $found; ok $expect, '$*PROGRAM.basename is ' ~ $expect; is $found, $expect, 'Script matches basename.'; done-testing;
  • 36. Testing -e takes more syntax use v6.d; use Test; my $expect = '-e'; my ( $found ) = qx{ raku -I./lib -M'FindBin' -e 'say Script' }.chomp; is $found, $expect, "'raku -e' returns '$found' ($expect)"; done-testing;
  • 37. Now you got all of the tests... How do you run them? prove6; what you are used to mi6 test; command line assistants assixt test;
  • 38. Releasing a module “mi6 release;” Changes entry META6.json Content pushed. Assign a version tag. Upload to CPAN.
  • 39. Getting mi6 $ zef install App::Mi6 ===> Searching for: App::Mi6 ===> Testing: App::Mi6:ver<0.2.6>:auth<cpan:SKAJI> ===> Testing [OK] for App::Mi6:ver<0.2.6>:auth<cpan:SKAJI> ===> Installing: App::Mi6:ver<0.2.6>:auth<cpan:SKAJI> 1 bin/ script [mi6] installed to: /opt/raku/share/raku/site/bin Note the install dir. Symlinks are your friends...
  • 40. Changes Revision history for FindBin {{$NEXT}} - Return IO for simplicity 0.3.4 2019-05-25T23:02:06-05:00 - use generic 01 test for use/require. - POD, whitespace, comments. 0.3.3 2019-05-20T11:42:45-05:00 ...
  • 41. META6.json { "auth" : "CPAN:lembark", "authors" : [ "lembark" ], "build-depends" : [ ], "depends" : [ ], "description" : "Locate execution directory and basename.", "license" : "Artistic-2.0", "name" : "FindBin", "perl" : "6.d", "provides" : { "FindBin" : "lib/FindBin.pm6" }, "resources" : [ ], "source-url" : "https://gitlab.com/lembark/raku-FindBin.git", "tags" : [ ], "test-depends" : [ ], "version" : "0.3.4" }
  • 42. POD is still largely POD =begin pod =head1 SYNOPSIS # export Bin() and Script() by default. use FindBin; my $dir_from = Bin(); # $dir_from is an IO object my $script_base = Script(); # $script_base is a string. # explicitly export only Bin() or Script(). use FindBin :Bin;
  • 43. Releasing a module $ mi6 release ==> Release distribution to CPAN There are 12 steps: # Rakuholics Anonymous??? <snip> ==> Step 1. CheckChanges ==> Step 2. CheckOrigin ==> Step 3. CheckUntrackedFiles ==> Step 4. BumpVersion Next release version? [0.3.5]: 0.4.0 ==> Step 5. RegenerateFiles ==> Step 6. DistTest <snip> ==> Step 8. UploadToCPAN Are you sure you want to upload FindBin-0.4.0.tar.gz to CPAN? (y/N) y
  • 44. Releasing a module Housekeeping includes version tag. ==> Step 9. RewriteChanges ==> Step10. GitCommit [master 6b2a3e2] 0.4.0 4 files changed, 5 insertions(+), 3 deletions(-) <snip To gitlab.com:lembark/Raku-FindBin.git 607929e..6b2a3e2 master -> master ==> Step11. CreateGitTag Total 0 (delta 0), reused 0 (delta 0) To gitlab.com:lembark/Raku-FindBin.git * [new tag] 0.4.0 -> 0.4.0 ==> Step12. CleanDist
  • 45. Instant gratification zef can install from a directory. $ zef install . ; ===> Testing: FindBin:ver<0.4.0>:auth<CPAN:lembark> # Madness: 'FindBin' (01-) # FindBin # Bin is: /sandbox/lembark/Modules/Raku/FindBin/t <snip> ===> Testing [OK] for FindBin:ver<0.4.0>:auth<CPAN:lembark> ===> Installing: FindBin:ver<0.4.0>:auth<CPAN:lembark>
  • 46. Bin is the root of all evil... Next Step: Root out the rest of it. Originally part of FindBin::libs: Scan up directory tree. Append relative paths. Filter them.
  • 47. FileSystem::Parent Exports “scan-up”. Stringy standard filters. Roll your own: block, regex...
  • 48. Even more than more than one way to do it multi scan-up ( Stringy :$filter = 'all', *%argz --> Seq ) { state %filterz = ( all => True , dir => :d , file => :f , exist => :e , ); my $value = %filterz{ $filter } or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' '); samewith filter => $value, |%argz }
  • 49. Single named arg “:filter” multi scan-up ( Stringy :$filter = 'all', *%argz --> Seq ) { state %filterz = ( all => True , dir => :d , file => :f , exist => :e , ); my $value = %filterz{ $filter } or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' '); samewith filter => $value, |%argz }
  • 50. *%argz slurps remaining args multi scan-up ( Stringy :$filter = 'all', *%argz --> Seq ) { stage %filterz = ( all => True , dir => :d , file => :f , exist => :e , ); my $value = %filterz{ $filter } or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' '); samewith filter => $value, |%argz }
  • 51. |%argz flattens them back out multi scan-up ( Stringy :$filter = 'all', *%argz --> Seq ) { state %filterz = ( all => True , dir => :d , file => :f , exist => :e , ); my $value = %filterz{ $filter } or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' '); samewith filter => $value, |%argz }
  • 52. same name, new filter multi scan-up ( Stringy :$filter = 'all', *%argz --> Seq ) { state %filterz = ( all => True , dir => :d , file => :f , exist => :e , ); my $value = %filterz{ $filter } or fail "Unknown: '$filter'. not "~ %filterz.keys.join(' '); samewith filter => $value, |%argz }
  • 53. The other way to do it multi scan-up ( Any :$filter, Str :$append = '', Bool() :$resolve = False, Bool() :$verbose = False, Bool() :$skip-root = False, IO() :$from = Bin( :$resolve, :$verbose ) --> Seq ) is export( :DEFAULT ) $filter here is anything.
  • 54. Args are defined in the parameters. multi scan-up ( Any :$filter, Str :$append = '', Bool() :$resolve = False, Bool() :$verbose = False, Bool() :$skip-root = False, IO() :$from = Bin( :$resolve, :$verbose ) --> Seq ) is export( :DEFAULT ) Re-cycled from Stringy multi via %args.
  • 55. Telling them apart multi Foo ( Stringy $filter = ‘all’, … ) multi Foo ( Any $filter, … ) Foo() Pick default no viable alternative. Foo(‘X’) Pick Stringy derived from Any. Foo( blah ) Pick Any.
  • 56. Pick a filter, Any filter... Perl5 used a code block, passed as a closure. $filter->( $path ) or next...
  • 57. Pick a filter, Any filter... Smart matches are better way. Pass in a file test, regex, block... $path ~~ $filter or next;
  • 58. Pick a filter, Any filter... Smart matches are better way. Pass in a file test, regex, block... $path ~~ $filter or next; Anything but a string. Which was used up in the other multi.
  • 59. Finding your path absolute, resolve, parent should be familiar by now... my $start = ( $resolve ?? $from.resolve( completely => True ) !! $from ).absolute.IO; my $path = $start ~~ :d ?? $start !! $start.parent ;
  • 60. Finding your path Fail if the path is not resolvable. my $start = ( $resolve ?? $from.resolve( completely => True ) !! $from ).absolute.IO; my $path = $start ~~ :d ?? $start !! $start.parent ;
  • 61. Gathering for a loop return gather loop { … take … if … ; … last if … ; } gather returns a Seq from take. loop runs until a break. Replaces for(;;){ ... push @result... }
  • 62. Gathering for a loop return gather loop { … take … if … ; … last if … ; } gather returns a Seq from take. loop runs until a break. Replaces for(;;){ ... push @result... }
  • 63. Gathering for a loop return gather loop { … take … if … ; … last if … ; } gather returns a Seq from take. loop runs until a break. Replaces for(;;){ ... push @result... }
  • 64. Find your roots return gather loop { my $next = $path.parent; my $at-root = $path ~~ $next; ... $path ~~ $filter and take $path; ... last if $at-root; $path = $next; }
  • 65. What to take if( $skip-root && $at-root ) { say “Root: ‘$path’ (skip)” if $verbose; } else { my $take = $append ?? $path.add( $append ) # add a relative path. !! $path ; $take ~~ $filter # $filter controls the match. and take $take; }
  • 66. return gather loop { my $next = $path.parent; my $at-root = $path ~~ $next; if $at-root && $skip-root { note ‘Skip root’ if $verbose; } else { $take = $append ?? ... ; $take ~~ $filter and take $take; } $at-root and last; $path = $next; } Skip root before append. Supply path to $filter. Exit at root. Working logic
  • 67. Q: What’s in a Seq from gather?
  • 68. Q: What’s in a Seq from gather? $((IO::Path.new("/sandbox/lembark/Modules/Raku/FileSystem-Parent/ t/lib", :SPEC(IO::Spec::Unix), :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")), IO::Path.new("/sandbox/lembark/Modules/Raku/FileSystem-Parent/lib" , :SPEC(IO::Spec::Unix), :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")), IO::Path.new("/sandbox/lembark/Modules/Raku/lib", :SPEC(IO::Spec:: Unix), :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")), IO::Path.new("/sandbox/lembark/Modules/lib", :SPEC(IO::Spec::Unix) , :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")), IO::Path.new("/sandbox/lembark/lib", :SPEC(IO::Spec::Unix), :CWD(" /sandbox/lembark/Modules/Raku/FileSystem-Parent")), IO::Path.new("/sandbox/lib", :SPEC(IO::Spec::Unix), :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent")), IO::Path.new("/lib", :SPEC(IO::Spec::Unix),
  • 70. A: Lazyness ( IO::Path.new ( "/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib" , :SPEC(IO::Spec::Unix) , :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent") ), ... ).Seq Untyped until the value is extracted.
  • 71. A: Lazyness ( IO::Path.new ( "/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib" , :SPEC(IO::Spec::Unix) , :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent") ), ... ).Seq It may fail: Filesystem is [usually] stable.
  • 72. A: Lazyness ( IO::Path.new ( "/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib" , :SPEC(IO::Spec::Unix) , :CWD("/sandbox/lembark/Modules/Raku/FileSystem-Parent") ), ... ).Seq It may fail: Database or network queries may not be.
  • 74. Testing lazyness One test: Compare it to an array. my @found = scan-up.eager; for ( 0 ..^ +@found ) -> $i { … } Another is smart-match it with a seq: my $found = scan-up; ok $found ~~ $expect, ‘Found it.’;
  • 75. Compare Bin dir with a file Path and Bin results should match. my $pass0 = scan-up; ok $pass0, "scan-up = $pass0"; my $pass1 = scan-up :from( $*PROGRAM-NAME ); ok $pass1, "scan-up :from( $*PROGRAM-NAME ) = $pass1"; ok $pass0 ~~ $pass1, 'Bin matches $*Program-Name';
  • 76. File and dir path work $ Raku t/03-no-subdir.rakutest ok 1 - scan-up = /sandbox/lembark/Modules/Raku/FileSystem- Parent/t /sandbox/lembark/Modules/Raku/FileSystem-Parent /sandbox/lembark/Modules/Raku /sandbox/lembark/Modules /sandbox/lembark /sandbox / ok 2 - scan-up :from( t/03-no-subdir.t ) = /sandbox/lembark/Modules/Raku/FileSystem-Parent/t /sandbox/lembark/Modules/Raku/FileSystem-Parent /sandbox/lembark/Modules/Raku /sandbox/lembark/Modules /sandbox/lembark /sandbox / ok 3 - Bin and $*Program-Name return same list. 1..3
  • 77. Adding a lib :$append with variable name. my $append = 'lib'; my $pass0 = scan-up :$append; ok $pass0, 'scan-up $append'; my $pass1 = scan-up :$append, from => $*PROGRAM-NAME; ok $pass1, 'scan-up $append :from => $*PROGRAM-NAME'; ok $pass0.eager ~~ $pass1.eager, ‘They match’;
  • 78. Testing :$append ok 1 - scan-up $append ok 2 - scan-up $append :from => $*PROGRAM-NAME # Pass0:/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib /sandbox/lembark/Modules/Raku/FileSystem-Parent/lib /sandbox/lembark/Modules/Raku/lib /sandbox/lembark/Modules/lib /sandbox/lembark/lib /sandbox/lib /lib # Pass1:/sandbox/lembark/Modules/Raku/FileSystem-Parent/t/lib /sandbox/lembark/Modules/Raku/FileSystem-Parent/lib /sandbox/lembark/Modules/Raku/lib /sandbox/lembark/Modules/lib /sandbox/lembark/lib /sandbox/lib /lib ok 3 - Bin and $*Program-Name return same list.
  • 79. Test verbose overrides There is, of course, more than one way. Shouldn’t affect Bin’s return value. use FindBin :verbose; my $false_1 = Bin( verbose => False ).Str; my $false_2 = Bin( :verbose( False ) ).Str; my $false_3 = Bin( :!verbose ).Str; is $false_1, $false_2, "Match with false."; is $false_2, $false_3, "Match with false.";
  • 81. pragmata Finally! Given libs, use them. Runs at compile time. Calling a sub is too late.
  • 82. “EXPORT” does the deed Outside of the package. Handles positionals passed via use. use v6.d; sub EXPORT ( *@args –-> Map ) { my $resolve = ? @args.first( 'resolve' ); my $verbose = ? @args.first( 'verbose' ); # do things... } unit module FindBin::libs:ver<0.0.1>:auth<CPAN:lembark> # ain’t nobody here but us chickens
  • 83. “EXPORT” does the deed Outside of the package. Handles positionals passed via use. use v6.d; sub EXPORT ( *@args –-> Map ) { my $resolve = ? @args.first( 'resolve' ); my $verbose = ? @args.first( 'verbose' ); # do things... } unit module FindBin::libs:ver<0.0.1>:auth<CPAN:lembark> # ain’t nobody here at all
  • 84. Raku Colon Cleanser Ever loose track of parens in an expression? Raku method calls can replace $foo.some_method( $bar ); with $foo.some_method: $bar;
  • 85. Raku Colon Cleanser Ever loose track of parens in an expression? Convert: $foo.frob($bar.nicate($thing.new($blort))); into: $foo.frob: $bar.nicate: $thing.new: $blort
  • 86. Raku Colon Cleanser Ever loose track of parens in an expression? $foo.frob: $bar.nicate: $thing.new: $blort ;
  • 87. Find non-root ./lib dirs & add them. use FileSystem::Parent; my %fixed = ( append => 'lib', filter => 'dir', skip-root => True ); my @found = scan-up( :$resolve, :$verbose, |%fixed ); for @found.reverse -> $dir { for CompUnit::Repository::FileSystem.new( prefix => $dir ) -> $lib { CompUnit::RepositoryRegistry.use-repository( $lib ); } }
  • 88. Find non-root ./lib dirs & add them. use FileSystem::Parent; my %fixed = ( append => 'lib', filter => 'dir', skip-root => True ); my @found = scan-up( :$resolve, :$verbose, |%fixed ); for @found.reverse -> $dir { given CompUnit::Repository::FileSystem.new( prefix => $dir ) -> $lib { CompUnit::RepositoryRegistry.use-repository( $lib ); } }
  • 89. Find non-root ./lib dirs & add them. use FileSystem::Parent; my %fixed = ( append => 'lib', filter => 'dir', skip-root => True ); my @found = scan-up( :$resolve, :$verbose, |%fixed ); for @found.reverse -> $dir { given CompUnit::Repository::FileSystem.new( prefix => $dir ) -> $lib { CompUnit::RepositoryRegistry.use-repository( $lib ); } }
  • 90. Find non-root ./lib dirs & add them. use FileSystem::Parent; my %fixed = ( append => 'lib', filter => 'dir', skip-root => True ); my @found = scan-up( :$resolve, :$verbose, |%fixed ); for @found.reverse -> $prefix { given CompUnit::Repository::FileSystem.new( :$prefix ) -> $lib { CompUnit::RepositoryRegistry.use-repository( $lib ); } }
  • 91. Find non-root ./lib dirs & add them. use FileSystem::Parent; my %fixed = ( append => 'lib', filter => 'dir', skip-root => True ); my @found = scan-up( :$resolve, :$verbose, |%fixed ); for @found.reverse -> $prefix { CompUnit::RepositoryRegistry.use-repository( CompUnit::Repository::FileSystem.new( :$prefix ) ) }
  • 92. Find non-root ./lib dirs & add them. use FileSystem::Parent; my %fixed = ( append => 'lib', filter => 'dir', skip-root => True ); my @found = scan-up( :$resolve, :$verbose, |%fixed ); for @found.reverse -> $prefix { CompUnit::RepositoryRegistry.use-repository: CompUnit::Repository::FileSystem.new: :$prefix }
  • 93. Named Parameters & Variables Egads! Using “:$prefix” ties your code to an API!
  • 94. Named Parameters & Variables Egads! Using “:$prefix” ties your code to my API! Signatures allow arbitrary variable names. :$prefix is a shortcut for sub foo( :prefix( $prefix ) ) You can also have: sub foo ( :prefix( $dir_pref ) ) Change internal variable names.
  • 95. Named Parameters & Variables Egads! Using “:$prefix” ties your code to my API! Signatures allow arbitrary variable names. :$prefix is a shortcut for sub foo( :prefix( $prefix ) ) You can also have: sub foo ( :add( $prefix ) ) Change external parameter name.
  • 96. --> Map sub EXPORT ( *@args --> Map ) { # return a Map %( '@FindBin-libs-dirs' => @found ) } Map of ( Name => Value )
  • 97. --> Map Map of ( Name => Value ) ‘@’ is part of the name. sub EXPORT ( *@args --> Map ) { # return a Map %( '@FindBin-libs-dirs' => @found ) }
  • 98. sub EXPORT ( *@args --> Map ) { use FileSystem::Parent; my $resolve = ? @_.first( 'resolve' ); my $verbose = ? @_.first( 'verbose' ); my %fixed = ( ... ); my @found = scan-up( :$verbose, :$resolve, |%fixed ); for @found.reverse -> $prefix { CompUnit::RepositoryRegistry.use-repository: CompUnit::Repository::FileSystem.new: :$prefix } %( '@FindBin-libs-dirs' => @found ) } Exporter
  • 99. “unit” is a placeholder. Export does all the work. unit module FindBin::libs has no subs.
  • 100. Pass1: Check for installed dirs. EXPORT installs the variable. Flatten it for printing. use FindBin::libs; ok @FindBin-libs-dirs, 'Exported FindBin-libs-dirs'; note join( "n#t", '# libs:', |@FindBin-libs-dirs ); done-testing;
  • 101. Pass2: Validate directory order. Frobnicate.rakumod in ./t/lib/FindBin & ./lib/FindBin. “FrobPath” will be ‘t/lib/...’ or ‘lib/...’. unit module FindBin::Frobnicate:ver<0.1.1>:auth<CPAN:lembark>; constant FrobPath is export( :DEFAULT ) = 't/lib/FindBin/Frobnicate.pm6'; constant FrobPath is export( :DEFAULT ) = 'lib/FindBin/Frobnicate.pm6';
  • 102. Pass2: Validate directory order. Test in ./t should prefer t/lib. use FindBin::libs; use FindBin::Frobnicate; ok MY::<FrobPath>, 'FrobPath exported.'; my $expect = 't/lib/FindBin/Frobnicate.pm6'; my $found := MY::FrobPath; is $found, $expect, "Found: ‘$found’”;
  • 103. prove6 Looks familiar. $ prove6 -v t/03-use-frobnicate.t; ok 1 - FrobPath exported. ok 2 - Found 't/lib/FindBin/Frobnicate.pm6' 1..2 t/03-use-frobnicate.t .. ok All tests successful. Files=1, Tests=2, 0 wallclock secs Result: PASS
  • 104. Summary Raku is quite doable. mi6 & assixt make it manageable. Raku has gotten much better. Raku is quite doable. mi6 & assixt make it more manageable. The doc’s website has gotten much better: Raku Try it... you’ll like it.