Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Perl course (4), mod

Perl course (4), mod

Perl modules and packages

Vadim Pushtaev

September 28, 2016
Tweet

More Decks by Vadim Pushtaev

Other Decks in Programming

Transcript

  1. Модули: разделяй и властвуй Структурирование кода читабельность надежность Повторное использование

    кода в рамках проекта за пределами проекта Совместная работа над проектом 3 / 104
  2. Модули: как разделять Низкий уровень Работа с внешней подсистемой база

    данных сеть xml-файлы Специфические вычисления работа с большими числами работа датами и временем шифрование Высокий уровень функционал для регистрации и авторизации пользователей функционал для загрузки, отображения и обработки фото на сайте 4 / 104
  3. Модули: как властвовать Расположение кода в отдельных файлах eval do

    require use Пространства имен и области видимости пакеты и таблицы символов области видимости фазы компиляции и выполнения Модули версионность pragma CPAN 5 / 104
  4. Функция eval видит переменные, объявленные снаружи имеет свою область видимости

    возвращает последнее вычисленное значение возвращает undef и устанавливает $@ в случае ошибки компиляции/ выполнения 6 / 104
  5. Функция eval my $E = 2.72; print "eval=", # eval=3.14159265

    eval(' warn "Loading...\n"; sub pow { $_[0] ** $_[1]; } $E = 2.71828183; my $PI = 3.14159265; '), "\n"; print "pow(2,8)=",pow(2,8),"\n"; # pow(2,8)=256 print "E=$E\n"; # E=2.71828183 print "PI=$PI\n"; # PI=undef 7 / 104
  6. Функция do do = open + eval? видит переменные, объявленные

    снаружи имеет свою область видимости возвращает последнее вычисленное значение возвращает undef и устанавливает $! в случае ошибки открытия файла возвращает undef и устанавливает $@ в случае ошибки компиляции/ выполнения 8 / 104
  7. Функция do do = open + eval? # Local/Math.pm warn

    "Loading...\n"; sub pow { $_[0] ** $_[1]; } $E = 2.71828183; my $PI = 3.14159265; 9 / 104
  8. Функция do do = open + eval? sub my_do {

    my ($file) = @_; $! = $@ = undef; open my $fd, '<', $file or return; my $code = join '', <$fd>; close $fd; eval $code; } print 'my_do=', # Loading... my_do("Local/Math.pm"),"\n"; # do=3.14159265 print '$@=', $@, "\n"; # $@=undef print '$!=', $!, "\n"; # $!=undef print 'pow(2,8)=', pow(2,8), "\n"; # pow(2,8)=256 10 / 104
  9. Функция do do = open + eval? $ perl -e

    'print join "\n", @INC' | head –3 /usr/local/lib64/perl5 /usr/local/share/perl5 /usr/lib64/perl5/vendor_perl $ PERL5LIB=/home/www/lib \ > perl -e 'print join "\n", @INC' | head -2 /home/www/lib /usr/local/lib64/perl5 $ perl -I/home/www/lib \ > -e 'print join "\n", @INC' | head -2 /home/www/lib /usr/local/lib64/perl5 11 / 104
  10. Функция do do = open + eval + @INC! #

    simplified implementation sub find_in_inc { my ($file) = @_; return $file if $file =~ m/^\//; foreach my $path (@INC) { return "$path/$file" if -f "$path/$file"; } die "Can't find file $file in \@INC"; } sub my_do { my ($file) = @_; $file = find_in_inc($file); # ... } 12 / 104
  11. Функция require require = do + die + %INC? останавливает

    выполнение в случае ошибки открытия файла, компиляции или выполнения не пытается загрузить и выполнить файл, если он уже был загружен ранее проверяет последнее вычисленное значение, останавливает выполнение, если оно ложно при первой загрузке файла возвращает последнее вычисленное значение, при последующих попытках его загрузить возвращает 1 13 / 104
  12. Функция require require = do + die + %INC? #

    simplified implementation sub my_require { my ($file) = @_; return 1 if $INC{$file}; my $filepath = find_in_inc($file) or die "Can't find $file in \@INC"; my $result = do $filepath or die "$file did not return true value"; die $@ if $@; $INC{$file} = $filepath; return $result; } 14 / 104
  13. Функция require require = do + die + %INC? $E

    = 2.72; # Loading... require "Local/Math.pm"; # 3.14159265 print "E=$E\n"; # E=2.71828183 $E = 2.72; require "Local/Math.pm"; # 1 print "E=$E\n"; # E=2.72 15 / 104
  14. Пакеты package Local::Math; # ... package Local::Math::Integer; # ... #

    ... # end of file { package Local::Math; # ... } package Local::Math { # ... } 18 / 104
  15. Пакеты package Local::Math; $PI = 3.14159265; sub pow { $_[0]

    ** $_[1] } sub sqr { pow($_[0], 0.5) } # the same # sub sqr { Local::Math::pow($_[0], 0.5) } package main; print "sqr(4)=", Local::Math::sqr(4), "\n"; # sqr(4)=2 print "fqn PI=", $Local::Math::PI, "\n"; # fqn PI=3.14159265 print "PI=$PI\n"; # PI=undef 19 / 104
  16. Пакеты package Local::Math; sub pow { $_[0] ** $_[1] }

    package main; $Local::Math::PI = 3.14159265; sub Local::Math::sqr { Local::Math::pow($_[0], 0.5) } print "sqr(4)=", Local::Math::sqr(4), "\n"; # sqr(4)=2 print "fqn PI=", $Local::Math::PI, "\n"; # fqn PI=3.14159265 print "PI=$PI\n"; # PI=undef 20 / 104
  17. Пакеты # Local/Math.pm package Local::Math; sub pow { $_[0] **

    $_[1] } printf "Pkg %s, file %s, line %d\n", __PACKAGE__, __FILE__, __LINE__; # Pkg Local::Math, file Local/Math.pm, line 4 # :-) 1; 21 / 104
  18. Пакеты # script.pl require "Local/Math.pm"; printf "Pkg %s, file %s,

    line %d\n", __PACKAGE__, __FILE__, __LINE__; # Pkg main, file script.pl, line 3 22 / 104
  19. Пакеты # script.pl { package Local::Math; sub sqr {pow($_[0], 0.5)

    } printf "Pkg %s, file %s, line %d\n", __PACKAGE__, __FILE__, __LINE__; # Pkg Local::Math, file script.pl, line 5 } printf "Pkg %s, file %s, line %d\n", __PACKAGE__, __FILE__, __LINE__; # Pkg main, file script.pl, line 11 23 / 104
  20. Таблицы символов package Some::Package { $var = 500; @var =

    (1,2,3); %func = (1 => 2, 3 => 4); sub func { return 400 } open F, "<", "/dev/null"; } package Some::Package::Deeper {} printf "%-10s => %s\n", $_, $Some::Package::{$_} foreach keys %Some::Package::; F => *Some::Package::F Deeper:: => *Some::Package::Deeper:: var => *Some::Package::var func => *Some::Package::func 24 / 104
  21. Таблицы символов TYPEGLOB: *foo SCALAR: $foo ARRAY: @foo HASH: %foo

    CODE: &foo IO: foo NAME: "foo" PACKAGE: "main" 25 / 104
  22. Таблицы символов package Local::Math { $PI = 3.14159265; sub PI

    { print 3.14 } } *Other::Math::PI = *Local::Math::PI; print $Other::Math::PI, "\n"; # 3.14159265 Other::Math::PI(); # 3.14 26 / 104
  23. Таблицы символов package Local::Math { $PI = 3.14159265; sub PI

    { print 3.14 } } *Other::Math::PI = \$Local::Math::PI; print $Other::Math::PI, "\n"; # 3.14159265 Other::Math::PI(); # Undefined subroutine &Other::Math::PI called 27 / 104
  24. Таблицы символов package Local::Math { $PI = 3.14159265; sub PI

    { print 3.14 } } *Other::Math::PI = \&Local::Math::PI; print $Other::Math::PI, "\n"; # undef Other::Math::PI(); # 3.14 28 / 104
  25. Таблицы символов *PI = \3.14159265; print $PI, "\n"; # 3.14159265

    $PI = 4; # Modification of a read-only value attempted $glob = *FH; print *{$glob}{PACKAGE}, "::", *{$glob}{NAME}; # main::FH 29 / 104
  26. Области видимости package Local::Math; our $PI = 3.14159265; package main;

    print $PI, "\n"; # 3.14159265 print $::PI, "\n"; # undef print $Local::Math::PI, "\n"; # 3.14159265 30 / 104
  27. Области видимости package Local::Math; my $PI = 3.14159265; package main;

    print $PI, "\n"; # 3.14159265 print $::PI, "\n"; # undef print $Local::Math::PI, "\n"; # undef 31 / 104
  28. Области видимости my $x = 4; { my $x =

    5; print $x, "\n"; # 5 } print $x, "\n"; # 4 32 / 104
  29. Области видимости use feature 'state'; sub test { state $x

    = 42; return $x++; } printf( # 42 43 44 45 46 '%d %d %d %d %d', test(),test(),test(),test(),test() ); print $x; # undef 33 / 104
  30. Области видимости our $x = 10; our %y = (x

    => 20, y => 30); { local $x = -10; local $y{x} = -20; print $x, "\n"; # -10 print $y{x}, "\n"; # -20 } print $x, "\n"; # 10 print $y{x}, "\n"; # 20 34 / 104
  31. Области видимости # 1,2,3 { local $/ = ","; $x=<>;

    $y = <>; $z=<>; # "1," "2," "3\n" chomp $y; # "2" } chomp $z; # "3" # 1\n2\n3\n { local $/; $all = <>; # "1\n2\n3\n" } 35 / 104
  32. Функция require require =do+die+%INC+namespace! sub pkg_to_filename { my ($pkg) =

    @_; $pkg =~ s!::!/!g; return "${pkg}.pm"; } sub my_require { my ($file) = @_; # simplified implementation $file = pkg_to_filename($file) unless $file =~ m![./]!; # ... } 37 / 104
  33. Фазы работы интерпретатора warn "[${^GLOBAL_PHASE}] Runtime 1\n"; END { warn

    "[${^GLOBAL_PHASE}] End 1\n" } CHECK { warn "[${^GLOBAL_PHASE}] Check 1\n" } UNITCHECK { warn "[${^GLOBAL_PHASE}] UnitCheck 1\n" } INIT { warn "[${^GLOBAL_PHASE}] Init 1\n" } BEGIN { warn "[${^GLOBAL_PHASE}] Begin 1\n" } END { warn "[${^GLOBAL_PHASE}] End 2\n" } CHECK { warn "[${^GLOBAL_PHASE}] Check 2\n" } UNITCHECK { warn "[${^GLOBAL_PHASE}] UnitCheck 2\n" } INIT { warn "[${^GLOBAL_PHASE}] Init 2\n" } BEGIN { warn "[${^GLOBAL_PHASE}] Begin 2\n" } warn "[${^GLOBAL_PHASE}] Runtime 2\n"; 39 / 104
  34. Фазы работы интерпретатора [START] Begin 1 [START] Begin 2 [START]

    UnitCheck 2 [START] UnitCheck 1 [CHECK] Check 2 [CHECK] Check 1 [INIT] Init 1 [INIT] Init 2 [RUN] Runtime 1 [RUN] Runtime 2 [END] End 2 [END] End 1 40 / 104
  35. Фазы работы интерпретатора warn "[${^GLOBAL_PHASE}] --- BEFORE EVAL\n"; eval '

    warn "[${^GLOBAL_PHASE}] Runtime\n"; END { warn "[${^GLOBAL_PHASE}] End\n" } CHECK { warn "[${^GLOBAL_PHASE}] Check\n" } UNITCHECK { warn "[${^GLOBAL_PHASE}] UnitCheck\n" } INIT { warn "[${^GLOBAL_PHASE}] Init\n" } BEGIN { warn "[${^GLOBAL_PHASE}] Begin\n" } '; warn "[${^GLOBAL_PHASE}] --- AFTER EVAL\n"; 42 / 104
  36. Фазы работы интерпретатора [RUN] --- BEFORE EVAL [RUN] Begin [RUN]

    UnitCheck [RUN] Runtime [RUN] --- AFTER EVAL [END] End # RUN? # CHECK?! # INIT?!! # END?!!! 43 / 104
  37. Фазы работы интерпретатора # Local/Phases.pm package Local::Phases; BEGIN { warn

    __PACKAGE__, " compile start\n" } UNITCHECK { warn __PACKAGE__, " UNITCHECK\n" } CHECK { warn __PACKAGE__, " CHECK\n" } INIT { warn __PACKAGE__, " INIT\n" } warn __PACKAGE__, " runtime\n"; BEGIN { warn __PACKAGE__, " compile end\n" } # phases.pl BEGIN { warn __PACKAGE__, " compile start\n" } UNITCHECK { warn __PACKAGE__, " UNITCHECK\n" } CHECK { warn __PACKAGE__, " CHECK\n" } INIT { warn __PACKAGE__, " INIT\n" } warn __PACKAGE__, " runtime\n"; require Local::Phases; BEGIN { warn __PACKAGE__, " compile end\n" } 44 / 104
  38. Фазы работы интерпретатора main compile start main compile end main

    UNITCHECK main CHECK main INIT main runtime Local::Phases compile start Local::Phases compile end Local::Phases UNITCHECK Local::Phases runtime # Local::Phases CHECK ?!! # Local::Phases INIT ?!! 45 / 104
  39. Фазы работы интерпретатора # Local/Phases.pm package Local::Phases; BEGIN { warn

    __PACKAGE__, " compile start\n" } UNITCHECK { warn __PACKAGE__, " UNITCHECK\n" } CHECK { warn __PACKAGE__, " CHECK\n" } INIT { warn __PACKAGE__, " INIT\n" } warn __PACKAGE__, " runtime\n"; BEGIN { warn __PACKAGE__, " compile end\n" } # phases.pl BEGIN { warn __PACKAGE__, " compile start\n" } UNITCHECK { warn __PACKAGE__, " UNITCHECK\n" } CHECK { warn __PACKAGE__, " CHECK\n" } INIT { warn __PACKAGE__, " INIT\n" } warn __PACKAGE__, " runtime\n"; BEGIN { require Local::Phases; } BEGIN { warn __PACKAGE__, " compile end\n" } 46 / 104
  40. Фазы работы интерпретатора main compile start Local::Phases compile start Local::Phases

    compile end Local::Phases UNITCHECK Local::Phases runtime main compile end main UNITCHECK Local::Phases CHECK main CHECK main INIT Local::Phases INIT main runtime 47 / 104
  41. Директива use use = BEGIN + require? use Local::Math; #

    BAREWORD # BEGIN { require Local::Math; } BEGIN { print Local::Math::pow(2, 8) } # 256 require Local::Math; BEGIN { print Local::Math::pow(2, 8) } # Undefined subroutine &Local::Math::pow 48 / 104
  42. Директива use use = BEGIN + require + import? use

    Some::Module (); BEGIN { require Some::Module; } # import? 49 / 104
  43. Директива use use = BEGIN + require + import? use

    Some::Module; BEGIN { require Some::Module; Some::Module->import(); # if can # Some::Module::import('Some::Module'); } use Some::Module ('arg1', 'arg2'); BEGIN { require Some::Module; Some::Module->import('arg1', 'arg2'); } 50 / 104
  44. Директива use use = BEGIN + require + import? #

    Local/Math.pm sub import { my $self = shift; my ($pkg) = caller(0); foreach my $func (@_) { *{"${pkg}::$func"} = \&{$func}; } } # main.pl use Local::Math qw/pow sqr/; print pow(2,8), "\n"; # 256 51 / 104
  45. Директива use use = BEGIN + require + import? #

    Local/Math.pm sub unimport { my $self = shift; my ($pkg) = caller(0); delete ${"${pkg}::"}{$_} foreach @_; } # main.pl use Local::Math qw/pow sqr/; print pow(2,8), "\n"; # 256 no Local::Math qw/pow/; print pow(2,8), "\n"; # Undefined subroutine &main::pow called 52 / 104
  46. Директива use use = BEGIN + require + import +

    version! # Local/Math.pm package Local::Math; our $VERSION = 1.25; # package Local::Math 1.25; print $Local::Math::VERSION; # 1.25 # main.pl use Local::Math 1.26; # Local::Math version 1.26 required--this is # only version 1.25 53 / 104
  47. Директива use use = BEGIN + require + import +

    version! package Local::Math; our $VERSION = v1.25.01; print $Local::Math::VERSION; # ??? print v67.97.109.101.108.33; # Camel! package Local::Math v1.25.01; print $Local::Math::VERSION; # v1.25.01 use Local::Math v1.26.2; # Local::Math version v1.26.2 required--this is # only version v1.25.1 54 / 104
  48. Директива use use = BEGIN + require + import +

    version! use 5.20; # Perl v5.200.0 required (did you mean v5.20.0?)-- # this is only v5.16.3, stopped use v5.20; # Perl v5.20.0 required--this # is only v5.16.3, stopped use 5.020_000; # Perl v5.20.0 required--this # is only v5.16.3, stopped 55 / 104
  49. Стандартные модули Модуль Exporter package Local::Math; use Exporter; *import =

    \&Exporter::import; our @EXPORT_OK = qw/$PI pow sqr/; our %EXPORT_TAGS = ( func => [ qw/pow sqr/ ], const => [ qw/$PI $E/ ], ); our @EXPORT = qw/$PI/; our $PI = 3.14159265; our $E = 2.71828183; sub pow { $_[0] ** $_[1] } sub sqr { pow($_[0], 0.5) } 56 / 104
  50. Стандартные модули Модуль Exporter use Local::Math (); # nothing use

    Local::Math; # $PI use Local::Math qw/$PI/; # $PI use Local::Math qw/pow/; # pow use Local::Math qw/:const pow/; # $PI $E pow use Local::Math qw/:const &sqr/; # $PI $E sqr 57 / 104
  51. Стандартные модули Модуль Data::Dumper use Data::Dumper; $u = \\3.14; $v

    = *STDIN; $w = "qwerty"; $x = sub { 1 }; @y = (1, "2", 3, "x"); %z = (x => 1, y => 2); warn Dumper($u, $v, $w, $x, \@y, \%z); 58 / 104
  52. Стандартные модули Модуль Data::Dumper $VAR1 = \\'3.14'; $VAR2 = *::STDIN;

    $VAR3 = 'qwerty'; $VAR4 = sub { "DUMMY" }; $VAR5 = [ 1, '2', 3, 'x' ]; $VAR6 = { 'y' => 2, 'x' => 1 }; 59 / 104
  53. Стандартные модули Модуль Getopt::Long use Getopt::Long; use Data::Dumper; GetOptions( 'format=s'

    => \$format, verbose => \$verbose, ); print Dumper [$format, $verbose]; # ./script.pl --format xml --verbose $VAR1 = [ 'xml', 1 ]; 60 / 104
  54. Стандартные модули Модуль POSIX use POSIX (); print POSIX::ceil(2.72), "\n";

    # 3 print POSIX::floor(2.72), "\n"; # 2 print POSIX::strftime("%Y-%m-%d %T / %B", localtime(0)), "\n"; # 1970-01-01 03:00:00 / January use POSIX qw(setlocale LC_ALL); setlocale(LC_ALL, "ru_RU.UTF-8"); print POSIX::strftime("%Y-%m-%d %T / %B", localtime(0)), "\n"; # 1970-01-01 03:00:00 / Январь 61 / 104
  55. Прагмы Модуль strict use strict 'vars'; our $foo = 1;

    # ok my $bar = 2; # ok $baz = 3; # Global symbol "$baz" requires # explicit package name 62 / 104
  56. Прагмы Модуль strict $foo = "foo"; $ref = \$foo; print

    $$ref; # foo use strict 'refs'; $ref = "foo"; print $$ref; # Can't use string ("foo") as a SCALAR ref # while "strict refs" in use 63 / 104
  57. Прагмы Модуль strict $x = \foo; print $$x, "\n"; #

    foo use strict 'subs'; $x = \"foo"; # ok $x = \foo; # Bareword "foo" not allowed # while "strict subs" in use 64 / 104
  58. Прагмы Модуль strict use strict; # qw/vars refs subs/ our

    $var = "foo"; my $ref = "var"; { no strict 'refs'; print $$ref; # foo } print $$ref; # Can't use string ("var") as a SCALAR ref # while "strict refs" in use 65 / 104
  59. Прагмы Модуль warnings use warnings; use warnings 'deprecated'; print "foo"

    . undef; # foo # Use of uninitialized value in concatenation (.) # or string no warnings 'uninitialized'; print "foo" . undef; # foo $ perl -we 'print 5 + "a"' # 5 # Argument "a" isn't numeric in addition (+) 66 / 104
  60. Прагмы Модуль diagnostics use diagnostics; print 5+"a"; # 5 #

    Argument "a" isn't numeric in addition (+) # (W numeric) The indicated string was fed as # an argument to an operator that expected # a numeric value instead. If you're fortunate # the message will identify which operator was # so unfortunate. 67 / 104
  61. Прагмы Модуль feature use feature qw/say state/; # a lot

    of features use feature ":5.10"; # features for 5.10 say 'New line follows this'; state $x = 10; 69 / 104
  62. Прагмы Модуль constant use constant PI => 3.14159265; # sub

    PI () { 3.14159265 } print PI; # 3.14159265 # print 3.14159265; use constant { PI => 3.14159265, E => 2.71828183, }; 70 / 104
  63. CPAN Comprehensive Perl Archive Network DBI, DBD::mysql, DBD::Pg, DBD::SQLite Digest::SHA,

    Digest::MD5, Digest::SipHash Crypt::RSA, Crypt::Rijndael XML::Parser, XML::LibXML, YAML, JSON::XS LWP , Net::T witter, Net::SMTP Devel::StackTrace, Devel::NYTProf Archive::Zip, MP3::Info, Image::ExifT ool, GD См. perlmodlib. 71 / 104
  64. CPAN $ perl –MCPAN -eshell $ cpan cpan shell --

    CPAN exploration and modules installation ( ReadLine support available (try 'install Bundle::CPAN' cpan> install Crypt::Rijndael ... cpan> ? 72 / 104
  65. CPAN $ module-starter --module Local::Math \ > --author Vadim --email

    [email protected] Local-Math/ ├── Changes ├── ignore.txt ├── lib │ └── Local │ └── Math.pm ├── Makefile.PL ├── MANIFEST ├── README └── t ├── 00-load.t ├── boilerplate.t ├── manifest.t ├── pod-coverage.t └── pod.t 74 / 104
  66. CPAN # Makefile.PL use 5.006; use strict; use warnings FATAL

    => 'all'; use ExtUtils::MakeMaker; WriteMakefile( NAME => 'Local::Math', AUTHOR => 'Vadim <[email protected]>', VERSION_FROM => 'lib/Local/Math.pm', ABSTRACT_FROM => 'lib/Local/Math.pm', LICENSE => 'Artistic_2_0', PL_FILES => {}, BUILD_REQUIRES => { 'Test::More' => 0, }, # ... ); 75 / 104
  67. CPAN $ apt-cache search libjson-perl libjson-perl - module for manipulating

    JSON-formatted data libjson-pp-perl - module for manipulating JSON-formatted data (Pure Perl) libjson-xs-perl - module for manipulating JSON-formatted data (C/XS-accelerated) $ yum search perl-JSON =============== Matched: perl-json =============== perl-JSON-XS.x86_64 : JSON serialising/ deserialising, done correctly and fast perl-JSON.noarch : Parse and convert to JSON (JavaScript Object Notation) perl-JSON-PP.noarch : JSON::XS compatible pure-Perl module 76 / 104
  68. Пишем модуль Задача: написать скрипт, выполняющий аутентификацию пользователя по его

    email и паролю для аутентифицированных пользователей должно выводиться персонифицированное приветствие, код возврата скрипта - 0 в остальных случаях выдавать сообщение об ошибке, код возврата скрипта - любой, кроме 0 всю обработку, связанную с пользовательскими данными, выделить в отдельный модуль база данных пользователей должна храниться в том же модуле (мы еще не умеем ходить в настоящие базы данных) 78 / 104
  69. Пишем модуль #!/usr/bin/perl use strict; use feature 'say'; use Local::User;

    my ($email, $passwd) = @ARGV; die "USAGE: $0 <email> <password>\n“ unless length $email && length $passwd; my $user = get_by_email($email); die "Пользователь с адресом '$email' не найден\n" unless $user; die "Введен неправильный пароль\n" unless $user->{passwd} eq $passwd; say welcome_string($user); say 'Добро пожаловать'; 79 / 104
  70. Пишем модуль package Local::User 1.1; use strict; use warnings; use

    List::Util qw/first/; use Exporter 'import'; our @EXPORT = qw/get_by_email name welcome_string/; 80 / 104
  71. Пишем модуль my @USERS = ( { first_name => 'Василий',

    last_name => 'Пупкин', gender => 'm', email => '[email protected]', passwd => '12345', }, { first_name => 'Николай', middle_name => 'Петрович', last_name => 'Табуреткин', gender => 'm', email => '[email protected]', passwd => 'admin', }, ); 81 / 104
  72. Пишем модуль # first imported from List::Util sub get_by_email {

    my $email = shift; my $user = first { $_->{email} eq $email } @USERS; return $user; } 82 / 104
  73. Пишем модуль sub name { my $user = shift; return

    join ' ', grep { length $_ } map { $user->{$_} } qw/first_name middle_name last_name/; } 83 / 104
  74. Пишем модуль sub welcome_string { my $user = shift; return

    ( $user->{gender} eq 'm' ? "Уважаемый " : "Уважаемая " ) . name($user) . "!"; } 84 / 104
  75. Пишем модуль $ perl auth USAGE: auth <email> <password> $

    perl auth [email protected] 123 Пользователь с адресом '[email protected]' не найден $ perl auth [email protected] 12345 Введен неправильный пароль $ perl auth [email protected] admin Уважаемый Николай Петрович Табуреткин! Добро пожаловать 86 / 104
  76. Пишем модуль Задача: изменить скрипт и модуль так, чтобы пароли

    не хранились в открытом виде использовать хеширование паролей использовать секретный ключ ("соль") 87 / 104
  77. Пишем модуль package Local::User 1.2; # ... # not strong

    enough :-( use Digest::MD5 'md5_hex'; # ... my @USERS = ( { first_name => 'Василий', last_name => 'Пупкин', gender => 'm', email => '[email protected]', passwd => 'd19f77fefeae0fabdfc75f17abc47c96', }, # ... ); 88 / 104
  78. Пишем модуль our @EXPORT = qw/ get_by_email name welcome_string is_password_valid

    /; # ... { my $SALT = "perl rulez!"; sub is_password_valid { my ($user, $passwd) = @_; return $user->{passwd} eq md5_hex($passwd.$SALT); } } 89 / 104
  79. Пишем модуль #!/usr/bin/perl use Local::User 1.2; # ... die "Введен

    неправильный пароль\n" unless is_password_valid($user, $passwd); # ... 90 / 104
  80. Пишем модуль Задача: усовершенствовать механизм хранения и проверки паролей использовать

    дополнительный случайный ключ для усложнения подбора пароля по хешу предусмотреть возможное изменение механизма проверки пароля в будущем оставить обратную совместимость с форматом хранения паролей из версии 1.2 91 / 104
  81. Пишем модуль package Local::User 1.3; # ... my @USERS =

    ( { first_name => 'Василий', last_name => 'Пупкин', gender => 'm', email => '[email protected]', passwd => '$1$f^34d*$24cc1e0d198dbf6bbfd812a30f1b4460', }, # ... ); 92 / 104
  82. Пишем модуль sub is_password_valid { my ($user, $passwd) = @_;

    my ($version, $data) = (0, $user->{passwd}); if ($user->{passwd} =~ /^\$(\d+)\$(.+)$/) { # new scheme ($version, $data) = ($1, $2); die "Don't know passwd version $version" unless $CHECKERS{$version}; } return $CHECKERS{$version}->($data, $passwd); } 93 / 104
  83. Пишем модуль # (password_hashed_data, password_to_check) my %CHECKERS = ( 0

    => sub { $_[0] eq md5_hex($_[1] . $SALT) }, 1 => sub { my ($rand, $hash) = split '$', $_[0]; return $hash eq md5_hex($_[1] . $SALT . $rand); }, ); 94 / 104
  84. Пишем модуль Что дальше? заменяем устаревший MD5 на другой алгоритм

    хеширования (SHA512, bcrypt, scrypt) переносим информацию о пользователях в базу данных или другое внешнее хранилище добавляем функционал для регистрации новых пользователей и изменения существующих данных интегрируем модуль в веб-приложение ... 95 / 104
  85. Домашнее задание Требуется написать скрипт , который на вход принимает

    список файлов музыкальной библиотеки, а на выходе рисует таблицу всех композиций согласно параметрам. Функционал должен быть разумно распределен по модулям, в самом скрипте должен остаться абслютно необходимый минимум. Дополнительно предлагается к получившимся модулям добавить автотесты. Это полностью опциональный пункт , на количество баллов его исполнение не повлияет . https://github.com/Nikolo/T echnosfera-perl/tree/master/homeworks/music_library 96 / 104
  86. Домашнее задание Формат входных данных На вход подается список относительных

    путей (с ведущими ./) всех файлов музыкальной библиотеки, начиная с ее корня. $ find -type f ./Dreams Of Sanity/1999 - Masquerade/The Phantom of the Opera.mp3 ./Dreams Of Sanity/1999 - Masquerade/Masquerade Act 1.mp3 ./Dreams Of Sanity/1999 - Masquerade/Opera.mp3 ./Dreams Of Sanity/1999 - Masquerade/Lost Paradise '99.mp3 ./Dreams Of Sanity/1999 - Masquerade/Masquerade - Interlude.mp3 ./Dreams Of Sanity/1999 - Masquerade/Within (The Dragon).mp3 ./Midas Fall/2015 - The Menagerie Inside/Push.ogg ./Midas Fall/2015 - The Menagerie Inside/Half a Mile Outside.ogg ./Midas Fall/2015 - The Menagerie Inside/Circus Performer.ogg Путь к каждому файлу стандартизирован: ./группа/год - альбом/ трек.формат 97 / 104
  87. Домашнее задание Формат выходных данных На выходе должна быть изображена

    таблица. По умолчанию она содержит следующие колонки (слева-направо): группа, год, альбом, трек, формат . Вид таблицы строго определен: /------------------------------------------------------------------------------------\ | Midas Fall | 2015 | The Menagerie Inside | Low | ogg | |------------+------+----------------------+-----------------------------------+-----| | Midas Fall | 2015 | The Menagerie Inside | Push | ogg | |------------+------+----------------------+-----------------------------------+-----| | Midas Fall | 2015 | The Menagerie Inside | The Morning Asked and I Said 'No' | ogg | |------------+------+----------------------+-----------------------------------+-----| | Midas Fall | 2015 | The Menagerie Inside | Afterthought | ogg | |------------+------+----------------------+-----------------------------------+-----| | Midas Fall | 2015 | The Menagerie Inside | A Song Built From Scraps of Paper | ogg | |------------+------+----------------------+-----------------------------------+-----| | Midas Fall | 2015 | The Menagerie Inside | Counting Colours | ogg | \------------------------------------------------------------------------------------/ 98 / 104
  88. Домашнее задание Формат выходных данных Для пустот используются только пробелы.

    Ширина колонки задается самым длинным значением, оно должно отступать от краев на один пробел (слева и справа). Остальные значения выравниваются по правому краю ячейки (отступая от него на один пробел). Для границ используются только символы /\-+|. В случае, если в таблице нет ни одной строки, ничего выводить не надо. 99 / 104
  89. Домашнее задание Параметры Параметры задаются скрипту ключами запуска. Параметр Смысл

    Параметр Смысл --band BAND Оставить только композиции группы BAND --year YEAR Оставить только композиции с альбомов года YEAR --album ALBUM Оставить только композиции с альбомов с именем ALBUM --track TRACK Оставить только композиции с именем TRACK --format FORMAT Оставить только композиции в формате FORMAT --sort FIELD Сортировать по возрастанию значения указанного параметра. FIELD может принимать значения band, year, album, track и format --columns COL_1,...,COL_N Список колонок через запятую, которые должны появиться в таблице (с учетом порядка). COL_I может принимать значения band, year, album, track и format. Дублирование допускается. Опциональный параметр, при отсутствии — использовать значение по умолчанию. 100 / 104
  90. Домашнее задание Параметры Необходимо учесть, что фильтрация и сортировка по

    году должна выполняться с учетом того, что год — это целое число, а не строка. (Однако выводить год в таблице следует без изменений: так, как он указан в имени файла.) 101 / 104
  91. Домашнее задание Пример $ find . -type f | ~/.../music_library.pl

    \ --band 'Midas Fall' --sort track --columns year,band,album,track,year /-------------------------------------------------------------------------------------\ | 2015 | Midas Fall | The Menagerie Inside | A Song Built From Scraps of Paper | 2015 | |------+------------+----------------------+-----------------------------------+------| | 2015 | Midas Fall | The Menagerie Inside | Afterthought | 2015 | |------+------------+----------------------+-----------------------------------+------| | 2015 | Midas Fall | The Menagerie Inside | Holes | 2015 | |------+------------+----------------------+-----------------------------------+------| | 2015 | Midas Fall | The Menagerie Inside | Low | 2015 | |------+------------+----------------------+-----------------------------------+------| | 2015 | Midas Fall | The Menagerie Inside | Push | 2015 | |------+------------+----------------------+-----------------------------------+------| | 2015 | Midas Fall | The Menagerie Inside | The Morning Asked and I Said 'No' | 2015 | |------+------------+----------------------+-----------------------------------+------| | 2015 | Midas Fall | The Menagerie Inside | Tramadol Baby | 2015 | \-------------------------------------------------------------------------------------/ 102 / 104