summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGianni Ceccarelli <gianni.ceccarelli@broadbean.com>2019-07-29 16:20:29 +0100
committerGianni Ceccarelli <gianni.ceccarelli@broadbean.com>2019-07-29 16:20:29 +0100
commit0ccebc121a2c4733a2974e9e4b53f6efec229035 (patch)
treef0a863713e018ea7bbd8de32107bbaa39d99fbc9
parentperl5 implementation (diff)
downloadmixed-server-0ccebc121a2c4733a2974e9e4b53f6efec229035.tar.gz
mixed-server-0ccebc121a2c4733a2974e9e4b53f6efec229035.tar.bz2
mixed-server-0ccebc121a2c4733a2974e9e4b53f6efec229035.zip
perl6 implementation
-rw-r--r--p6/META6.json9
-rw-r--r--p6/mixed-server.pl163
2 files changed, 172 insertions, 0 deletions
diff --git a/p6/META6.json b/p6/META6.json
new file mode 100644
index 0000000..81d9eb3
--- /dev/null
+++ b/p6/META6.json
@@ -0,0 +1,9 @@
+{
+ "perl": "6.d",
+ "name": "mixed-server",
+ "version": "1",
+ "depends": [
+ "Cro::HTTP::Router",
+ "Cro::HTTP::Server",
+ ],
+}
diff --git a/p6/mixed-server.pl b/p6/mixed-server.pl
new file mode 100644
index 0000000..7f9b8a8
--- /dev/null
+++ b/p6/mixed-server.pl
@@ -0,0 +1,163 @@
+#!/usr/bin/env perl6
+use v6.d;
+
+class Model {
+ has Lock::Async $!lock .= new;
+ has BagHash $!words .= new;
+
+ method add_words(@words --> Promise) {
+ return $!lock.lock.then: {
+ LEAVE $!lock.unlock;
+ ++$!words{$_} for @words;
+ }
+ }
+
+ method get_most_common_pairs(Int() $how_many=10 --> Promise) {
+ return $!lock.lock.then: {
+ LEAVE $!lock.unlock;
+ reverse $!words.sort(*.value).head($how_many);
+ }
+ }
+}
+
+class View {
+ method words_from_input(Str() $input --> Seq) {
+ return $input.split(/\s+/,:skip-empty);
+ }
+
+ method table_from_ranked_pairs(@ranked_pairs) {
+ return @ranked_pairs.map({ sprintf '%-15s %3d',$^p.key,$^p.value }).join("\n");
+ }
+}
+
+class HTTPController {
+ use Cro::HTTP::Router;
+
+ has $.model is required;
+ has $.view is required;
+
+ sub respond($body) {
+ response.status = 200;
+ response.set-body("{$body}\n");
+ }
+
+ method routes() {
+ return route {
+ get -> *@, :$n! is query {
+ self.get_words($n);
+ }
+ get -> *@ {
+ self.get_words();
+ }
+ post -> *@ {
+ request-body-text -> $body {
+ self.post_words($body);
+ }
+ }
+ }
+ }
+
+ method get_words($n=10) {
+ my @most_common_pairs = await $!model.get_most_common_pairs($n);
+ my $table = $!view.table_from_ranked_pairs(@most_common_pairs);
+ respond($table);
+ }
+
+ method post_words($body) {
+ my @words = $!view.words_from_input($body);
+ await $!model.add_words(@words);
+ respond('ok');
+ }
+}
+
+class LineController {
+ has $.model is required;
+ has $.view is required;
+
+ method on_command(:$stream,:$command,:$args_string) {
+ if $command eq 'get' {
+ self.get_words($stream,$args_string);
+ }
+ elsif $command eq 'put' {
+ self.put_words($stream,$args_string);
+ }
+ else {
+ reply($stream,"bad command, only 'get' and 'put'");
+ }
+ }
+
+ sub reply($stream,$text) {
+ await $stream.print("$text\n");
+ }
+
+ method get_words($stream,$args_string) {
+ my $how_many = $args_string || 10;
+
+ my @most_common_pairs = await $!model.get_most_common_pairs($how_many);
+ my $table = $!view.table_from_ranked_pairs(@most_common_pairs);
+
+ reply($stream,$table);
+ }
+
+ method put_words($stream,$args_string) {
+ my @words = $!view.words_from_input($args_string);
+ await $!model.add_words(@words);
+
+ reply($stream,'ok');
+ }
+}
+
+class TextServer {
+
+ has $.controller is required;
+ has $.host is required;
+ has $.port is required;
+
+ has $!stop = Promise.new;
+
+ method start() {
+ start react {
+ whenever IO::Socket::Async.listen($!host,$!port) -> $conn {
+ whenever $conn.Supply.lines -> $line {
+ my ($command,$args_string) = $line.split(/\s+/,2);
+ $.controller.on_command(
+ stream => $conn,
+ :$command, :$args_string
+ );
+ }
+ }
+ whenever $!stop -> {
+ last;
+ }
+ };
+ }
+
+ method stop() { $!stop.keep }
+}
+
+use Cro::HTTP::Server;
+
+my Model $model .= new;
+my View $view .= new;
+
+my HTTPController $httpcontroller .= new(:$model,:$view);
+my $httpserver = Cro::HTTP::Server.new(
+ host => 'localhost',
+ port => 8080,
+ application => $httpcontroller.routes,
+);
+$httpserver.start;
+
+my LineController $linecontroller .= new(:$model,:$view);
+my TextServer $textserver .=new(
+ host => 'localhost',
+ port => 2020,
+ controller => $linecontroller,
+);
+$textserver.start;
+
+react whenever signal(SIGINT) {
+ $httpserver.stop;
+ $textserver.stop;
+ exit;
+}