Examples
Simple examples: Counter , Chat app
More convoluted demos: Gaming portal
Counter
Try switching your browser to offline mode for a while, to see up to 4 missed events appear when you turn it back on again.
Vue code (client):
<template>
<div>
<div class="panel">
<div class="left pane">
<div class="header">
<strong>Events</strong>
</div>
<div ref="eventsData" class="data">
<div v-for="(event, idx) in events" :key="idx">
{{ event }}
</div>
</div>
</div>
<div class="right pane">
<div class="header">
<strong>State</strong>
</div>
<div class="data">
{{ state }}
</div>
</div>
</div>
<div class="form">
<input
v-model.number="number"
type="number"
placeholder="Enter number..."
>
<button @click="incBy(number)">
Increase by...
</button>
<button @click="setTo(number)">
Set to... (after 3 seconds)
</button>
</div>
</div>
</template>
<script>
import BS from '@/libs/BS'
export default {
data: () => ({
state: null,
events: [],
number: null
}),
created () {
this.ch = BS.joinChannel('counter')
this.ch.on('event', async (ev) => {
this.events.push(ev)
if (this.events.length > 20) { this.events.shift() }
// scroll to bottom
await this.$nextTick()
this.$refs.eventsData.scrollTo(0, this.$refs.eventsData.scrollHeight)
})
this.ch.on('state', (state) => {
this.state = state
})
},
beforeDestroy () {
this.ch.leave()
},
methods: {
incBy (number) {
this.ch.doAction('inc_counter', number)
},
async setTo (number) {
try {
await this.ch.doRequest('set_counter', number)
} catch (e) {
window.console.error(e)
}
}
}
}
</script>
<style lang="scss" scoped>
.panel {
border: 3px solid black;
display: flex;
height: 200px;
}
.pane {
flex: 1 0 0;
display: flex;
flex-direction: column;
}
.left {
border-right: 3px solid black;
}
.header {
border-bottom: 3px solid black;
padding: 3px 5px;
}
.data {
flex: 1 0 0;
overflow-y: auto;
padding: 3px 5px;
}
.form {
margin-top: 1em;
}
</style>
Perl code (server):
package MyApp::Plugin::DemoCounter;
use Mojo::Base 'Mojolicious::Plugin', -signatures, -async_await;
use Mojo::JSON 'true';
use Mojo::Promise;
use Mojo::IOLoop;
use constant CHANNEL => 'counter';
use constant PASSWORD => '...';
sub register ($self, $app, $config) {
$app->bs->create_channel(CHANNEL, 1);
$app->bs->on_join(CHANNEL, sub ($channel_name, $c, $attrs) {
my $is_reconnect = $attrs->{is_reconnect};
return { limit => $is_reconnect ? 4 : 0 };
});
$app->bs->on_action(CHANNEL, 'inc_counter', sub ($channel_name, $c, $payload) {
$c->bs->lock_state(CHANNEL, sub ($state) {
$state += $payload;
my $event = "Someone increased the counter by $payload";
return $event, $state;
});
});
$app->bs->on_request(CHANNEL, 'set_counter', sub ($channel_name, $c, $payload) {
my $p = Mojo::Promise->new;
Mojo::IOLoop->timer(3, sub {
if (1 <= $payload <= 100) {
$c->bs->lock_state(CHANNEL, sub ($state) {
$state = $payload;
my $event = "Someone set the counter to $payload";
return $event, $state;
});
$p->resolve(true);
} else {
$p->reject('out of bounds'); # could be a JSON object too
}
});
return $p;
});
$app->bs->on_action(CHANNEL, 'inc_by_1', sub ($channel_name, $c, $payload) {
my $password = $payload;
die if $password ne PASSWORD;
$c->bs->lock_state(CHANNEL, sub ($state) {
$state += 1;
$state = 1 if $state > 100;
return $state, $state;
});
});
}
1;
Perl script (server):
This script increases the counter every two seconds.
#!/usr/bin/env perl
use v5.32;
use warnings;
use BoardStreams::Client;
use Mojo::IOLoop;
use constant PASSWORD => '...';
my $BS = BoardStreams::Client->new($ENV{WEBSOCKET_URL});
my $ch = $BS->join_channel('counter');
Mojo::IOLoop->recurring(2, sub {
$ch->do_action('inc_by_1', PASSWORD);
});
Mojo::IOLoop->start;
Cron script (server):
This script deletes old events from the database.
#!/usr/bin/env perl
use v5.32;
use warnings;
use Sys::RunAlone silent => 1;
use MyApp;
my $app = MyApp->new;
$app->bs->delete_events('counter', keep_num => 10);
__END__
Simple examples: Counter , Chat app
More convoluted demos: Gaming portal