Perl Mojolicious的状态变量

gzszwxb4  于 2023-08-06  发布在  Perl
关注(0)|答案(1)|浏览(122)

在一个Mojolicous的完整应用程序中,我有一个模型帮助器,它返回一个哈希值。我想定期更新这个哈希的内容(从下载的CSV)。

我的问题:如何使users成为状态变量(和/或使用Mojo::Base has?)这样我就不会每次通话时都从文件中读取?也就是说,在调用updatedelete的过程中,users哈希值并重新创建它,如果下载成功?

我举了一个Lite示例来演示我想要实现的目标:

  • GET /update-下载更新的CSV,并将其存储为${epoch}.json(如果下载成功,我需要它来更新哈希值)
  • GET /-通过阅读最新的${epoch}.json文件并返回所需的散列来列出用户。(这应该只返回哈希值,而不是每次调用时读取文件。
use Mojolicious::Lite -signatures;

helper users => sub($self) {
    my $users;
    my $newest;
    foreach my $filename (sort {$b cmp $a} glob("1*.csv")) {
        $newest = $filename;
        last;
    }
    open my $fh, "<:encoding(utf8)", $newest or die $!;
    while (<$fh>) {
        next if $. == 1; # ignore CSV header
        my ($name,$email,$phone) = split(',');
        $users->{$email} = $name;
    }
    close($fh);
    $users;
};

helper update => sub ($self) {
    my $tx = $self->ua->get( 'https://test-backend.lambdatest.com/api/dev-tools/csv-generator' );
    my $res = eval { $tx->result };
    return $@ if $@;
    my $epoch = Mojo::Date->new()->epoch;
    Mojo::File->new( "${epoch}.csv" )->spurt( $res->text );
    return 'ok';
};

get '/' => sub ($self) {
    my @names = sort values %{ $self->users };
    $self->render(json => {emails=>[@names]} );
    
};

get '/update' => sub ($self) {
    $self->render(text => $self->update );
};

app->start;

字符串
在我的完整应用程序中,我的代码看起来像:

$self->helper(users => sub {state $data = MyApp::Model::Users->new(app => $self)});


然后在Users.pm中:

package MyApp::Model::Users;
use Mojo::Base -base, -signatures;
use Text::CSV_XS;

has 'app';
has 'users' => sub($self) {
  my $users;
    open my $fh, "<:encoding(utf8)", $self->app->config->{'filename'} or die $!;
    my $csv = Text::CSV_XS->new ({ diag_verbose=>1, auto_diag=>1, binary=>1, sep_char=>";" });
    $csv->getline($fh); # Ignore Header
    while (my $row = $csv->getline ($fh)) {        
        $users->{ $row->[0] } = $row->[1];
    }
    close($fh);
    $users;
};


我对这段代码的问题是,我不知道如何在初始设置后更新users

编辑

我知道如何在一个完整的应用程序中实现它,但我不知道如何访问$self->users。在一个完整的应用程序中,我会这样做:$self->helper(users => sub {state $data = MyApp::Model::Users->new(app => $self)});

use Mojolicious::Lite -base, -signatures;

has 'users' => sub($self) {
    my $users = {};
    my $newest;
    foreach my $filename (sort {$b cmp $a} glob("1*.csv")) {
        $newest = $filename;
        last;
    }
    $self->updateUsers($newest, $users);
    $users;
};

helper updateUsers => sub($self, $filename, $users=undef) {
    say STDERR "### updateUsers($filename)";
    $users //= $self->users;
    delete @$users{keys %$users}; # delete existing hash
    open my $fh, "<:encoding(utf8)", $filename or die $!;
    while (<$fh>) {
        next if $. == 1; # ignore CSV header
        my ($name,$email,$phone) = split(',');
        $users->{$email} = $name;
    }
    close($fh);
    $users;
};

helper update => sub ($self) {
    my $tx = $self->ua->get( 'https://test-backend.lambdatest.com/api/dev-tools/csv-generator' );
    my $res = eval { $tx->result };
    return $@ if $@;
    my $epoch = Mojo::Date->new()->epoch;
    my $file = Mojo::File->new( "${epoch}.csv" )->spurt( $res->text );
    $self->update( $file->to_string );    
    return 'ok';
};

get '/' => sub ($self) {
    my @names = sort values %{ $self->users };
    $self->render(json => {emails=>[@names]} );
    
};

get '/update' => sub ($self) {
    $self->render(text => $self->update );
};

app->start;

xam8gpfp

xam8gpfp1#

我认为有一个更好的方法来完成你的任务,但让我们先谈谈具体的语言问题。
但是,一种通用的方法是设置一个持久性变量(state)。当您第一次进入子例程时,请检查该变量值。如果它有值,则传回该值。如果不是,请计算出它的值并赋值:

sub get_data {
    state $data;
    return $data if defined $data;

    $data = ...
    }

字符串
但我认为你的建筑问题是不同的。您的Web应用程序不需要是负责更新数据的部分。请在带外执行。某些程序会定期检查来源、下载适当的新数据,然后更新数据存放区。您的Web应用程序仅查看数据存储。

sub get_data {
    state $data;
    state $serial;

    my $lastest_serial = ...; # however you want to tag the data
    undef $data unless $latest_serial eq $serial; # invalidate somehow
    return $data if defined $data;

    $serial = $latest_serial;
    $data = ...
    }


但是,有时您希望使该数据无效。您可以将该变量与另一个可以实现该技巧的子例程共享:

{
my $data;

sub get_data { ... }

sub invalidate_data { undef $data }
}


}
我们将在Intermediate Perl中介绍这种持久的私有变量共享。有很多方法可以达到同一个目标。
不过,如果你的Web应用程序从来没有考虑过这一点,那就更好了。你的控制器向模型请求它所需要的数据,模型就得到了。控制器不会告诉模型何时更新。一些外部的东西,如cron更新,而不是等待命中一个URL。可能是SQLite或其他更重的数据库。有多种方法可以处理此问题。

相关问题