view release on metacpan or search on metacpan
examples/pagi-chat/lib/ChatApp/State.pm view on Meta::CPAN
$data->{rooms} = $data->{rooms} ? $JSON->decode($data->{rooms}) : {};
$data->{connected} = $data->{connected} ? 1 : 0;
return $data;
}
async sub get_session_by_name {
my ($name) = @_;
# Scan for session with this name (not efficient, but works for demo)
my $cursor = "0";
do {
my $result = await $redis->scan($cursor, MATCH => 'session:*', COUNT => 100);
$cursor = $result->[0];
my $keys = $result->[1] // [];
for my $key (@$keys) {
my $session = await $redis->hgetall($key);
if ($session && $session->{name} eq $name && $session->{connected}) {
$session->{rooms} = $session->{rooms} ? $JSON->decode($session->{rooms}) : {};
return $session;
}
}
} while ($cursor && $cursor ne "0");
return;
}
async sub create_session {
my ($session_id, $name, $send_cb) = @_;
my $session = {
id => $session_id,
name => $name,
examples/pagi-chat/public/css/style.css view on Meta::CPAN
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.btn-primary {
background: var(--accent-color);
color: white;
}
.btn-primary:hover {
background: var(--accent-hover);
examples/pagi-chat/public/css/style.css view on Meta::CPAN
}
.btn-icon {
width: 32px;
height: 32px;
padding: 0;
border: none;
border-radius: 6px;
background: transparent;
color: var(--text-secondary);
cursor: pointer;
font-size: 1.2rem;
transition: all 0.2s;
}
.btn-icon:hover {
background: var(--bg-tertiary);
color: var(--text-primary);
}
.pagi-info {
examples/pagi-chat/public/css/style.css view on Meta::CPAN
/* Navigation Lists */
.nav-list {
list-style: none;
max-height: 200px;
overflow-y: auto;
}
.nav-list li {
padding: 0.5rem 0.75rem;
border-radius: 6px;
cursor: pointer;
transition: background 0.2s;
display: flex;
align-items: center;
gap: 0.5rem;
}
.nav-list li:hover {
background: var(--bg-tertiary);
}
lib/Async/Redis/Iterator.pm view on Meta::CPAN
sub new {
my ($class, %args) = @_;
return bless {
redis => $args{redis},
command => $args{command} // 'SCAN',
key => $args{key}, # For HSCAN/SSCAN/ZSCAN
match => $args{match},
count => $args{count},
type => $args{type}, # For SCAN TYPE filter
cursor => 0,
done => 0,
}, $class;
}
async sub next {
my ($self) = @_;
# Already exhausted
return undef if $self->{done};
my @args;
# Build command args based on scan type
if ($self->{command} eq 'SCAN') {
@args = ($self->{cursor});
}
else {
# HSCAN, SSCAN, ZSCAN take key first, then cursor
@args = ($self->{key}, $self->{cursor});
}
# Add MATCH pattern if specified
if (defined $self->{match}) {
push @args, 'MATCH', $self->{match};
}
# Add COUNT hint if specified
if (defined $self->{count}) {
push @args, 'COUNT', $self->{count};
}
# Add TYPE filter for SCAN (Redis 6.0+)
if ($self->{command} eq 'SCAN' && defined $self->{type}) {
push @args, 'TYPE', $self->{type};
}
# Execute scan command
my $result = await $self->{redis}->command($self->{command}, @args);
# Result is [cursor, [elements...]]
my ($new_cursor, $elements) = @$result;
# Update cursor
$self->{cursor} = $new_cursor;
# Check if iteration complete (cursor returned to 0)
if ($new_cursor eq '0' || $new_cursor == 0) {
$self->{done} = 1;
}
# Return batch (may be empty)
# Return undef only when done AND no elements in final batch
return $elements && @$elements ? $elements : ($self->{done} ? undef : []);
}
sub reset {
my ($self) = @_;
$self->{cursor} = 0;
$self->{done} = 0;
}
sub cursor { shift->{cursor} }
sub done { shift->{done} }
1;
__END__
=head1 NAME
Async::Redis::Iterator - Cursor-based SCAN iterator
lib/Async/Redis/Iterator.pm view on Meta::CPAN
my $iter = $redis->scan_iter(match => 'user:*', count => 100);
while (my $batch = await $iter->next) {
for my $key (@$batch) {
say $key;
}
}
=head1 DESCRIPTION
Iterator provides async cursor-based iteration over Redis SCAN commands:
=over 4
=item * C<SCAN> - iterate keys
=item * C<HSCAN> - iterate hash field/value pairs
=item * C<SSCAN> - iterate set members
=item * C<ZSCAN> - iterate sorted set members and scores as a flat list
lib/Async/Redis/Iterator.pm view on Meta::CPAN
my $batch = await $iter->next;
Return the next batch as an arrayref, or C<undef> when the scan is complete and
there are no elements in the final batch. Redis may return empty intermediate
batches; those are returned as empty arrayrefs and are still truthy in Perl.
=head2 reset
$iter->reset;
Reset the cursor to zero so iteration can start again.
=head2 cursor
Return the current Redis cursor.
=head2 done
Return true after Redis has returned cursor C<0>.
=head1 BEHAVIOR
=over 4
=item * Returns batches of elements, not individual items
=item * Cursor managed internally
=item * C<next> returns C<undef> when iteration is complete
lib/Async/Redis/KeyExtractor.pm view on Meta::CPAN
'GEOSEARCHSTORE' => sub { (0, 1) },
# MIGRATE - special handling
'MIGRATE' => \&_keys_for_migrate,
# SORT
'SORT' => sub { (0) },
'SORT_RO' => sub { (0) },
# SCAN commands return patterns, not keys - first arg is key for HSCAN/SSCAN/ZSCAN
'SCAN' => sub { () }, # No key, cursor-based
# Pub/Sub - channels, not keys
'PUBLISH' => sub { () },
'SUBSCRIBE' => sub { () },
'UNSUBSCRIBE' => sub { () },
'PSUBSCRIBE' => sub { () },
'PUNSUBSCRIBE' => sub { () },
# Server commands - no keys
'PING' => sub { () },
script/commands.json view on Meta::CPAN
"readonly"
],
"hints": [
"nondeterministic_output"
]
},
"HSCAN": {
"summary": "Iterates over fields and values of a hash.",
"since": "2.8.0",
"group": "hash",
"complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.",
"acl_categories": [
"@read",
"@hash",
"@slow"
],
"arity": -3,
"key_specs": [
{
"begin_search": {
"type": "index",
script/commands.json view on Meta::CPAN
}
],
"arguments": [
{
"name": "key",
"type": "key",
"display_text": "key",
"key_spec_index": 0
},
{
"name": "cursor",
"type": "integer",
"display_text": "cursor"
},
{
"name": "pattern",
"type": "pattern",
"display_text": "pattern",
"token": "MATCH",
"optional": true
},
{
"name": "count",
script/commands.json view on Meta::CPAN
"admin",
"noscript",
"no_async_loading",
"no_multi"
]
},
"SCAN": {
"summary": "Iterates over the key names in the database.",
"since": "2.8.0",
"group": "generic",
"complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.",
"history": [
[
"6.0.0",
"Added the `TYPE` subcommand."
]
],
"acl_categories": [
"@keyspace",
"@read",
"@slow"
],
"arity": -2,
"arguments": [
{
"name": "cursor",
"type": "integer",
"display_text": "cursor"
},
{
"name": "pattern",
"type": "pattern",
"display_text": "pattern",
"token": "MATCH",
"optional": true
},
{
"name": "count",
script/commands.json view on Meta::CPAN
],
"command_flags": [
"write",
"fast"
]
},
"SSCAN": {
"summary": "Iterates over members of a set.",
"since": "2.8.0",
"group": "set",
"complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.",
"acl_categories": [
"@read",
"@set",
"@slow"
],
"arity": -3,
"key_specs": [
{
"begin_search": {
"type": "index",
script/commands.json view on Meta::CPAN
}
],
"arguments": [
{
"name": "key",
"type": "key",
"display_text": "key",
"key_spec_index": 0
},
{
"name": "cursor",
"type": "integer",
"display_text": "cursor"
},
{
"name": "pattern",
"type": "pattern",
"display_text": "pattern",
"token": "MATCH",
"optional": true
},
{
"name": "count",
script/commands.json view on Meta::CPAN
],
"command_flags": [
"readonly",
"fast"
]
},
"ZSCAN": {
"summary": "Iterates over members and scores of a sorted set.",
"since": "2.8.0",
"group": "sorted-set",
"complexity": "O(1) for every call. O(N) for a complete iteration, including enough command calls for the cursor to return back to 0. N is the number of elements inside the collection.",
"acl_categories": [
"@read",
"@sortedset",
"@slow"
],
"arity": -3,
"key_specs": [
{
"begin_search": {
"type": "index",
script/commands.json view on Meta::CPAN
}
],
"arguments": [
{
"name": "key",
"type": "key",
"display_text": "key",
"key_spec_index": 0
},
{
"name": "cursor",
"type": "integer",
"display_text": "cursor"
},
{
"name": "pattern",
"type": "pattern",
"display_text": "pattern",
"token": "MATCH",
"optional": true
},
{
"name": "count",