날짜 처리 모듈화

perl weekly newsletter에 대한 내 첫 번째 게시물을 언급한 덕분에 많은 사람들이 이 기사 시리즈의 첫 번째 부분을 읽었습니다. (아직 몇 파트가 될지는 모르겠습니다.) 그래서 후속편을 쓰기로 했습니다.

무엇을 기대할 수 있습니까?
  • 기능을 모듈로 이동
  • 기능을 자동으로 테스트
  • 모듈을 클래스로 변경
  • 기본 클래스에서 클래스를 상속합니다.

  • 1부에서 작은 스크립트를 남긴 위치를 요약하면 다음과 같습니다.

    use strict;
    
    use Getopt::Long;
    use DateTime;
    use DateTime::Duration;
    
    my $date;   ## the starting date argument
    my $dt;     ## DateTime Object of starting date
    
    
    GetOptions(
            "date=s"         => \$date,
            );
    
    $dt = get_dt($date);
    my $today = DateTime->today();
    
    
    
    if(!$dt) {
        print "submitted value $date is no date\n";
    }
    if(is_last_week($dt)) {
        print "submitted date was in the last week\n";
    }
    
    sub is_last_week {
        my $dt = shift;
        return unless ref $dt eq "DateTime";
        return if($dt > $today) ;
    
        my $dur = DateTime::Duration->new(days => 7);
        $today->subtract_duration($dur);
        return 1 if $today < $dt;
    }
    
    
    sub get_dt {
        my $date = shift;
    
        my($y, $m, $d) = $date =~ /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
        my $dt;
        eval {
            $dt = DateTime->new(year => $y, month => $m, day => $d);
        }; 
        if ($@) {
            print STDERR "Error while generating DateTime: $@\n";
            return 0;
        }
        else {
            return $dt;
        }
    }
    
    


    스크립트는 매개변수 -d가 유효한 날짜이고 날짜가 지난 주인지 확인합니다.

    매개변수 없이 스크립트를 호출하면 좋지 않은 오류로 끝납니다.

    perl skript.pl
    Error while generating DateTime: Validation failed for type named DayOfMonth declared in package DateTime::Types (/home/kroll/.plenv/versions/5.32.1/lib/perl5/site_perl/5.32.1/x86_64-linux/DateTime/Types.pm) at line 29 in sub named (eval) with value undef

    Trace begun at Specio::Exception->new line 57
    Specio::Exception::throw('Specio::Exception', 'message', 'Validation failed for type named DayOfMonth declared in package DateTime::Types (/home/kroll/.plenv/versions/5.32.1/lib/perl5/site_perl/5.32.1/x86_64-linux/DateTime/Types.pm) at line 29 in sub named (eval) with value undef', 'type', 'Specio::Constraint::Simple=HASH(0x55a2fa53c0a8)', 'value', undef) called at (eval 201) line 91
    DateTime::_check_new_params('year', undef, 'month', undef, 'day', undef) called at /home/kroll/.plenv/versions/5.32.1/lib/perl5/site_perl/5.32.1/x86_64-linux/DateTime.pm line 176
    DateTime::new('DateTime', 'year', undef, 'month', undef, 'day', undef) called at skript.pl line 45
    eval {...} at skript.pl line 44
    main::get_dt(undef) called at skript.pl line 15

    submitted value is no date



    우리는 이것을 나중에 할 일 목록에 쓸 것입니다.

    그러나 오늘은 모듈의 스크립트에서 날짜 처리를 제거하여 나중에 스크립트가 무거운 작업을 수행할 수 있지만 날짜 처리는 다른 스크립트에서 재사용할 수 있도록 합니다.

    비어 있는 새 모듈 생성



    perl 사람들의 경우 디렉토리 lib를 생성하고 모듈을 넣고 일반적인 perl 상용구를 수행하는 것이 매우 분명합니다.

    package Date;
    
    use strict;
    
    use DateTime;
    use DateTime::Duration;
    
    
    1;
    


    그리고 나는 평소에 테스트 주도형으로서 첫 번째 테스트도 추가했습니다. t/date.t에서 다음을 찾을 수 있습니다.

    use Test::More;
    
    use_ok("Date");
    
    done_testing();
    


    물론 이것은 녹색으로 나타납니다.

    $ prove -lv
    t/date.t ..
    ok 1 - use Date;
    1..1
    ok



    그래서 우리는 그것을 커밋할 수 있습니다:

    Author: Volker Kroll
    Date: Thu May 6 11:59:53 2021 +0200

    new Module Date

    • Date is empty
    • t/date.t has a test, that it loads correctly


    Date.pm에서 get_dt 이동



    스크립트에서 코드를 가져와서 모듈에 붙여넣으면 됩니다. 그러나 나는 모듈이 좀 더 객체 지향적인 것을 선호하기 때문에 - 조금 이상하고 오래된 oo-perl 스타일의 생성자를 추가했습니다.

    sub new {
        my $class = shift;
        my $datestring = shift|| undef;
        if ($datestring) {
            bless get_dt($datestring), $class;
        }
    }
    


    그리고 물론 조금 더 테스트:

    use_ok("Date");
    
    my $d = Date::get_dt('2020-01-02');
    isa_ok($d, DateTime);
    my $date = Date->new('2020-01-02');
    isa_ok($date, "Date");
    


    를 야기하는:

    $ prove -lv
    t/date.t ..
    ok 1 - use Date;
    ok 2 - An object of class 'DateTime' isa 'DateTime'
    ok 3 - An object of class 'Date' isa 'Date'
    1..3
    ok



    커밋하기에 충분한 코드:

    Author: Volker Kroll [email protected]
    Date: Thu May 6 12:11:15 2021 +0200

    get_dt now in Date

    • get_dt is called like in skript.pl Date::get_dt(yyyy-mm-dd)
    • additionally you can do a Date->new(yyyy-mm-dd) returned is an object of class Date (that is a DateTime)


    왜 Date를 DateTime의 자식으로 만들지 않습니까?



    내 모듈에서 작업하는 동안 Date->new(...)를 수행하면 DateTime 개체를 다시 얻을 수 있도록 DateTime에서 상속하는 것이 좋습니다. 그러나 해당 생성자를 따르기 위해 DateTime의 많은 코드를 추가해야 하기 때문에 즉시 작동하지 않았습니다. (솔직히 말하면 생성자에 약간만 추가하고 "실제 생성자"가 DateTime의 무거운 작업을 수행하도록 하는 방법을 잊어버렸습니다. 그래서 이 프로젝트에서 생성자의 이름을 변경하여 생성하기로 결정했습니다(펄에서는 쉽습니다. 원하는 대로 생성자의 이름을 지정할 수 있습니다.

    package Date;
    
    use strict;
    
    use base qw(DateTime);
    use DateTime;
    use DateTime::Duration;
    
    
    sub create {
        my $class = shift;
        my $datestring = shift|| undef;
        if ($datestring) {
            return bless get_dt($datestring), $class;
        }
    }
    
    


    또한 모듈에서도 내 기능인 is_last_week를 원했습니다. 이전에 우리의 작은 스크립트에서 이 기능을 사용했습니다. 테스트를 보다 쉽게 ​​하기 위해 A씨가 언급한 "오늘 치팅"가능성을 추가했습니다. 오늘 호출에 매개변수를 추가하면 "진짜 오늘"이 아닌 매개변수를 사용합니다.

    sub is_last_week {
        my $dt = shift;
        return unless ref $dt;
        my $today = shift ||  DateTime->today();
        return if($dt > $today) ;
    
        my $dur = DateTime::Duration->new(days => 7);
        $dt->add_duration($dur);
        return 1 if $today < $dt;
        return 0;
    }
    


    이제 몇 가지 테스트를 더 추가할 수 있었습니다.

    use Test::More;
    use Data::Dumper;
    use DateTime;
    
    use_ok("Date");
    test_new();
    test_is_last_week();
    done_testing();
    
    sub test_new {
        my $d = Date::get_dt('2020-01-02');
        isa_ok($d, DateTime);
        my $date = Date->create('2020-01-02');
        isa_ok($date, Date);
        isa_ok($date, DateTime);
    }
    
    sub test_is_last_week {
        my $date = Date->create('2020-01-02');
        my $today = DateTime->new(year => 2020, month => 01, day => 05);
        can_ok($date, "is_last_week");
        isa_ok($date, "Date");
        isa_ok($today, "DateTime");
        is($date->is_last_week($today), 1, "2020-01-02 was in the week before 2020-01-05 ");
        is($date->is_last_week(), 0, "2020-01-02 was not last week from today ");
    
    }
    
    


    다시 예상되는 출력 결과:

    $ prove -lv
    t/date.t ..
    ok 1 - use Date;
    ok 2 - An object of class 'DateTime' isa 'DateTime'
    ok 3 - An object of class 'Date' isa 'Date'
    ok 4 - An object of class 'Date' isa 'DateTime'
    ok 5 - Date->can('is_last_week')
    ok 6 - An object of class 'Date' isa 'Date'
    ok 7 - An object of class 'DateTime' isa 'DateTime'
    ok 8 - 2020-01-02 was in the week before 2020-01-05
    ok 9 - 2020-01-02 was not last week from today
    1..9
    ok



    다음 단계는 물론 평소와 같이 커밋입니다.

    Author: Volker Kroll
    Date: Fri May 7 11:38:11 2021 +0200

    added Tests for date was in last week

    • changed new to create to not interfere with DateTime new
    • add function for is_last_week
    • for testing purposes it is possible to call is_last_week with a given "today" so tests don't become red


    몇 가지 테스트만 더 하면




    sub test_is_last_week {
        my $date = Date->create('2020-01-02');
        my $today = DateTime->new(year => 2020, month => 01, day => 05);
        can_ok($date, "is_last_week");
        isa_ok($date, "Date");
        isa_ok($today, "DateTime");
        is($date->is_last_week($today), 1, "2020-01-02 was in the week before 2020-01-05 ");
        is($date->is_last_week(), 0, "2020-01-02 was not last week from today ");
        is($date->is_last_week(), 0, "2020-01-02 was not last week from today ");
    
    }
    
    


    이제 전체 모듈을 사용할 수 있을 만큼 충분히 테스트되었습니다.

    t/date.t ..
    ok 1 - use Date;
    ok 2 - An object of class 'DateTime' isa 'DateTime'
    ok 3 - An object of class 'Date' isa 'Date'
    ok 4 - An object of class 'Date' isa 'DateTime'
    ok 5 - Date->can('is_last_week')
    ok 6 - An object of class 'Date' isa 'Date'
    ok 7 - An object of class 'DateTime' isa 'DateTime'
    ok 8 - 2020-01-02 was in the week before 2020-01-05
    ok 9 - 2020-01-02 was not last week from today
    ok 10 - 2020-01-02 was not last week from today
    1..10
    ok



    최종 커밋만

    Author: Volker Kroll
    Date: Fri May 7 14:21:09 2021 +0200

    some more testing in date.t



    결론



    몇 가지 변경 사항만 많이 변경되었습니다. 이제 우리가 개발한 기능을 쉽게 테스트할 수 있습니다. 또한 나중에 (현재 DateTime) 객체를 재사용할 수 있습니다. 그러나 다음 작업의 가장 큰 이점은 다른 작업을 수행해야 하는 스크립트에서 날짜 처리를 제거하는 것입니다. 그래서 우리는 수정하고 사용하기 쉽도록 다른 부분을 분리했습니다.

    다음 논리적 단계는 무엇입니까? 이는 요구 사항과 소프트웨어 사용자에 따라 다릅니다. 하지만 매개변수 없이 호출될 때 "잘못된 오류 메시지"를 수정하겠습니다. 일반적으로 필수 매개변수가 누락된 경우 도움말 기능(여전히 작성해야 함)을 호출합니다. 그러나 그것은 세 번째 부분에서 이루어질 것입니다. 아마도 -- 계속 지켜봐 주시기 바랍니다.

    좋은 웹페이지 즐겨찾기