You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 23:00:54