在一个Mojolicous的完整应用程序中,我有一个模型帮助器,它返回一个哈希值。我想定期更新这个哈希的内容(从下载的CSV)。
我的问题:如何使users
成为状态变量(和/或使用Mojo::Base has
?)这样我就不会每次通话时都从文件中读取?也就是说,在调用update
、delete
的过程中,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;
型
1条答案
按热度按时间xam8gpfp1#
我认为有一个更好的方法来完成你的任务,但让我们先谈谈具体的语言问题。
但是,一种通用的方法是设置一个持久性变量(
state
)。当您第一次进入子例程时,请检查该变量值。如果它有值,则传回该值。如果不是,请计算出它的值并赋值:字符串
但我认为你的建筑问题是不同的。您的Web应用程序不需要是负责更新数据的部分。请在带外执行。某些程序会定期检查来源、下载适当的新数据,然后更新数据存放区。您的Web应用程序仅查看数据存储。
型
但是,有时您希望使该数据无效。您可以将该变量与另一个可以实现该技巧的子例程共享:
型
}
我们将在Intermediate Perl中介绍这种持久的私有变量共享。有很多方法可以达到同一个目标。
不过,如果你的Web应用程序从来没有考虑过这一点,那就更好了。你的控制器向模型请求它所需要的数据,模型就得到了。控制器不会告诉模型何时更新。一些外部的东西,如cron更新,而不是等待命中一个URL。可能是SQLite或其他更重的数据库。有多种方法可以处理此问题。