|
| 1 | +=begin pod :tag<perl6> |
| 2 | +
|
| 3 | +=TITLE Input/Output The Definitive Guide |
| 4 | +
|
| 5 | +=SUBTITLE The dos and don'ts of Perl 6 IO |
| 6 | +
|
| 7 | +
|
| 8 | +=head1 The Wrong Way To Do Things |
| 9 | +
|
| 10 | +This section describes how NOT to do Perl 6 IO. |
| 11 | +
|
| 12 | +=head2 Leave $*SPEC Alone |
| 13 | +
|
| 14 | +You may have heard of L«C<$*SPEC>|/language/variables#Dynamic_variables» and |
| 15 | +seen some code or books show its usage for splitting and joining path fragments. |
| 16 | +Some of the routine names it provides may even look familiar to what you've |
| 17 | +used in other languages. |
| 18 | +
|
| 19 | +However, unless you're writing your own IO framework, |
| 20 | +you don't ever need to use L«C<$*SPEC>|/language/variables#Dynamic_variables»! |
| 21 | +L«C<$*SPEC>|/language/variables#Dynamic_variables» provides low-level stuff and |
| 22 | +its use will not only make your code horrible to read, you'll likely introduce |
| 23 | +security issues (e.g. nul bytes)! |
| 24 | +
|
| 25 | +The L«C<IO::Path>|/type/IO::Path» type is the workhorse of Perl 6 worl. It |
| 26 | +caters to all the path manipulation needs as well as provides shortcut routines |
| 27 | +that let you avoid dealing with file handles. Use that instead of the |
| 28 | +L«C<$*SPEC>|/language/variables#Dynamic_variables» stuff. |
| 29 | +
|
| 30 | +Tip: you can join path parts with C</> and feed them to |
| 31 | +L«C<IO::Path>|/type/IO::Path»'s routines; they'll still do The Right Thing™ with |
| 32 | +them, regardless of the operating system. |
| 33 | +
|
| 34 | +=for code :skip-test |
| 35 | +# WRONG!! TOO MUCH WORK! |
| 36 | +my $fh = open $*SPEC.catpath: '', 'foo/bar', $file; |
| 37 | +my $data = $fh.slurp; |
| 38 | +$fh.close; |
| 39 | +
|
| 40 | +# RIGHT! Use IO::Path to do all the dirty work |
| 41 | +my $data = 'foo/bar'.IO.add($file).slurp; |
| 42 | +
|
| 43 | +=head2 Stringifying IO::Path |
| 44 | +
|
| 45 | +Don't use C<.Str> method to stringify L«C<IO::Path>|/type/IO::Path» objects, |
| 46 | +unless you just want to display them somewhere for information purposes or |
| 47 | +something. The C<.Str> method returns whatever the basic path string the |
| 48 | +L«C<IO::Path>|/type/IO::Path» was instantiated with. It doesn't consider the |
| 49 | +value of L«C<$.CWD> attribute|/type/IO::Path#attribute_CWD». For example, this |
| 50 | +code is broken: |
| 51 | +
|
| 52 | +=for code :skip-test |
| 53 | +# WRONG!! .Str DOES NOT USE $.CWD! |
| 54 | +my $path = 'foo'.IO; |
| 55 | +chdir 'bar'; |
| 56 | +run <tar -cvvf archive.tar>, ~$path; |
| 57 | +
|
| 58 | +The L«C<chdir>|/routine/chdir» call changed the value of the current directory, |
| 59 | +but the C<$path> we created is relative to the directory before that change. |
| 60 | +
|
| 61 | +However, the L«C<IO::Path>|/type/IO::Path» object I<does> know what directory |
| 62 | +it's relative to. We just need to use L«C<.absolute>|/routine/absolute» or |
| 63 | +L«C<.relative>|/routine/relative» to stringify the object. Both routines return |
| 64 | +a L«C<Str>|/type/Str» object; they only differ in whether the result is an |
| 65 | +absolute or relative path. So, we can fix our code like this: |
| 66 | +
|
| 67 | +=for code :skip-test |
| 68 | +# RIGHT!! .absolute does consider the value of $.CWD! |
| 69 | +my $path = 'foo'.IO; |
| 70 | +chdir 'bar'; |
| 71 | +run <tar -cvvf archive.tar>, $path.absolute; |
| 72 | +# Also good: |
| 73 | +run <tar -cvvf archive.tar>, $path.relative; |
| 74 | +
|
| 75 | +=head2 Be mindful of $*CWD |
| 76 | +
|
| 77 | +While usually out of view, every L«C<IO::Path>|/type/IO::Path» object, by |
| 78 | +default, uses the current value of |
| 79 | +L«C<$*CWD>|/language/variables#Dynamic_variables» to set its |
| 80 | +L«C<$.CWD> attribute|/type/IO::Path#attribute_CWD». This means there are two |
| 81 | +things to pay attention to. |
| 82 | +
|
| 83 | +=head3 temp the $*CWD |
| 84 | +
|
| 85 | +This code is a mistake: |
| 86 | +
|
| 87 | +=for code :skip-test |
| 88 | +# WRONG!! |
| 89 | +my $*CWD = "foo".IO; |
| 90 | +
|
| 91 | +The C<my $*CWD> made L«C<$*CWD>|/language/variables#Dynamic_variables» |
| 92 | +undefined. The L«C<.IO>|/routine/IO» coercer |
| 93 | +then goes ahead and sets the L«C<$.CWD> attribute|/type/IO::Path#attribute_CWD» |
| 94 | +of the path its creating to the stringified version of the undefined C<$*CWD>; |
| 95 | +an empty string. |
| 96 | +
|
| 97 | +The correct way to perform this operation is use |
| 98 | +L«C<temp>|/routine/temp» instead of C<my>. It'll localize the effect of changes |
| 99 | +to L«C<$*CWD>|/language/variables#Dynamic_variables», just like C<my> would, |
| 100 | +but it won't make it undefined, so the L«C<.IO>|/routine/IO» coercer will still |
| 101 | +get the correct old value: |
| 102 | +
|
| 103 | +=for code :skip-test |
| 104 | +temp $*CWD = "foo".IO; |
| 105 | +
|
| 106 | +Better yet, if you want to perform some code in localized |
| 107 | +L«C<$*CWD>|/language/variables#Dynamic_variables», use the |
| 108 | +L«C<indir> routine|/routine/indir» for that purpose. |
| 109 | +
|
| 110 | +=end pod |
| 111 | + |
| 112 | +# vim: expandtab shiftwidth=4 ft=perl6 |
0 commit comments