如何在Perl Mojolicious中用state变量维护可更新的users哈希
Mojolicious中实现CSV更新后缓存哈希的解决方案
问题核心
在Mojolicious应用里,需要让返回用户哈希的模型助手避免每次调用都读取文件,同时在下载新CSV后能更新这个哈希缓存。
Lite版实现方案
直接在Lite应用中把users设为应用级属性,搭配更新方法实现缓存刷新:
use Mojolicious::Lite -base, -signatures; # 定义应用级的users哈希,初始化时自动加载最新CSV has 'users' => sub($self) { my $users = {}; my $newest; # 按文件名降序找最新的CSV foreach my $filename (sort {$b cmp $a} glob("*.csv")) { $newest = $filename; last; } $self->_load_csv($newest, $users) if $newest; return $users; }; # 私有方法:清空哈希并加载CSV内容 helper _load_csv => sub($self, $filename, $users) { delete @$users{keys %$users}; open my $fh, "<:encoding(utf8)", $filename or die $!; while (<$fh>) { next if $. == 1; # 跳过表头 chomp; my ($name, $email, $phone) = split(','); $users->{$email} = $name if $email; } close($fh); }; # 下载新CSV并刷新哈希 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); # 直接更新已有的users哈希 $self->_load_csv($file->to_string, $self->users); return 'ok'; }; get '/' => sub ($self) { my @names = sort values %{ $self->users }; $self->render(json => {names => \@names} ); }; get '/update' => sub ($self) { $self->render(text => $self->update ); }; app->start;
关键改动
- 用
has 'users'定义应用级属性,初始化时仅加载一次CSV - 新增
_load_csv方法负责清空哈希并重新加载内容,避免重复代码 update方法下载成功后,直接调用_load_csv更新已有的users哈希,无需重新初始化
全量应用实现方案
针对带Model模块的全量应用,改造模型并添加更新方法:
1. 改造MyApp::Model::Users模块
给模型添加update_users方法,用于刷新哈希:
package MyApp::Model::Users; use Mojo::Base -base, -signatures; use Text::CSV_XS; has 'app'; has 'users' => sub($self) { my $users = {}; $self->_load_csv($users); return $users; }; # 私有方法:加载CSV到哈希 sub _load_csv ($self, $users) { delete @$users{keys %$users}; # 清空原有数据 my $filename = $self->app->config->{'filename'}; open my $fh, "<:encoding(utf8)", $filename or die $!; my $csv = Text::CSV_XS->new({ diag_verbose=>1, auto_diag=>1, binary=>1, sep_char=>";" }); $csv->getline($fh); # 跳过表头 while (my $row = $csv->getline($fh)) { $users->{ $row->[0] } = $row->[1] if $row->[0]; } close($fh); } # 新增:刷新users哈希(重新加载当前配置的CSV) sub update_users ($self) { $self->_load_csv($self->users); return $self->users; } 1;
2. 应用中配置助手与路由
确保模型实例唯一,并在更新时触发哈希刷新:
# 定义users助手,用state保证模型实例只创建一次 $self->helper(users => sub { state $model = MyApp::Model::Users->new(app => $self); return $model; }); # 定义更新CSV的助手 $self->helper(update_users_csv => sub ($self) { my $tx = $self->ua->get('https://test-backend.lambdatest.com/api/dev-tools/csv-generator'); my $res = eval { $tx->result }; return $@ if $@; # 保存新CSV并更新配置中的文件名 my $epoch = Mojo::Date->new()->epoch; my $new_file = Mojo::File->new("${epoch}.csv")->spurt($res->text); $self->config->{filename} = $new_file->to_string; # 调用模型方法刷新哈希 $self->users->update_users; return 'ok'; }); # 路由示例 $self->routes->get('/' => sub ($c) { my @names = sort values %{ $c->users->users }; $c->render(json => {names => \@names}); }); $self->routes->get('/update' => sub ($c) { $c->render(text => $c->update_users_csv); });
关键改动
- 模型的
users属性延迟加载,初始化仅执行一次 update_users方法直接操作已有的哈希,清空后重新加载CSV- 应用中的
users助手用state确保模型实例唯一,避免重复创建 - 更新CSV后,先修改配置的文件名,再调用模型的
update_users刷新缓存
内容的提问来源于stack exchange,提问作者h q
相关产品推荐
相关产品推荐

