Skip to content

Instantly share code, notes, and snippets.

@bbkr
Last active October 13, 2015 20:48
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save bbkr/4254320 to your computer and use it in GitHub Desktop.
JSON::RPC blogpost

During Polish Perl Workshop 2014 Carl Mäsak showed us how to model Feline Hotel application.

But he forgot one thing - that cats own the Internet and they want to browse and reserve rooms online!

I will pick up where he left off and show you how to publish API and go live in a blink of an eye.

So let's create modern Feline Hotel in Perl 6!

    class FelineHotel;

	has %!rooms =
	    403 => {
	        'type'      => 'Standard',
	        'equipment' => [ 'bed', 'bowl' ],
	        'price'     => 64,
	        'available' => True,
	    },
	    406 => {
	        'type'      => 'Purrific',
	        'equipment' => [ 'bed', 'bowl', 'toys', 'jacuzzi' ],
	        'price'     => 128,
	        'available' => True
	    };

	method browse_rooms ( ) {
	    return %!rooms.grep( { .value{ 'available' } } ).hash;
	}

	method reserve_room ( Str $name!, Int $number! ) {
	    self!check_room( $number );

	    return not %!rooms{ $number }{ 'available' } = False;
	}

	method !check_room ( Int $number ) {
	    die 'No such room' unless %!rooms{ $number }:exists;
	    die 'Room not available' unless %!rooms{ $number }{ 'available' };
	}

Application lives in FelineHotel.pm file and has very simple interface - browse_rooms method returns all available rooms and reserve_room method allows to make reservation by giving cats name and room number. Reservation calls private method check_room and fails if room does not exist or is not available. But how to write an API that allows online clients to connect and use those functions? Just create server.pl file.

    use FelineHotel;
    use JSON::RPC::Server;

    JSON::RPC::Server.new( application => FelineHotel.new ).run;

Then run it in your interpreter.

    $ perl6 -I. server.pl

It should print Started HTTP server and hang waiting for connections on port 8080. That's ALL, Your Feline Hotel just went live.

To see whole picture let's create client application.

	use JSON::RPC::Client;

	my $feline_hotel = JSON::RPC::Client.new( url => 'http://localhost:8080' );

	say 'Hotel has following rooms available:';
	say $feline_hotel.browse_rooms( );

	say 'Nyan cat makes reservation of room 403:';
	say $feline_hotel.reserve_room( 'Nyan', 403 );

	say 'Hotel has following rooms available:';
	say $feline_hotel.browse_rooms( );

You can run it on the same machine as server. Or on any remote machine if you have port 8080 forwarded - in that case change url param in third line. And you will see that Nyan cat just reserved room online and this room is not available anymore. Meooow!

Because our Feline Hotel is working we have time for little code dissection. Whole functionality is wrapped into FelineHotel class. Module JSON::RPC::Server takes instance of this class and exposes its public methods for outside world to use. On the other side of Internet cable JSON::RPC::Client invokes those methods just as if they were declared in local code. This technique is called 'Remote Procedure Call' and uses JavaScript Object Notation format to exchange data in a way formalized by JSON-RPC 2.0 protocol.

Go ahead and try to create your own services or improve this Feline Hotel if you want. You will quickly realize that JSON::RPC module not only hides networking stuff that happens between client and server but it tries to make your life easier in "Do What I Mean" way. For example you can overload methods on server. And catch exceptions in client.

To demonstrate this let's say that we want to refuse reservation for Grumpy cat because he always gives bad reviews online. It is as simple as overloading reserve_room method in server:

	multi method reserve_room ( Str $name!, Int $number! ) {
	    self!check_room( $number );

	    return not %!rooms{ $number }{ 'available' } = False;
	}

	multi method reserve_room ( "Grumpy", Int $number! ) {
	    die 'No!';
	}

Now when Grumpy tries to make reservation from client:

	say 'Grumpy cat makes reservation of room 406:';
	try {
	    $feline_hotel.reserve_room( 'Grumpy', 406 );
	    CATCH { default { .say } }
	}

It will fail with following error:

	Internal error (-32603): "No!"

You can also use named params on both client and server side, advanced methods signatures, batches of requests, notifications and much more. Moreover - all this stuff is not language dependent. You can connect to Perl 6 server using JSON-RPC 2.0 client in PHP/Ruby/Java/etc or use Perl 6 client to call JSON-RPC 2.0 based APIs written in any languages.

Have fun and cheers from APIcon San Francisco 2014!

Note: JSON::RPC module is included in Rakudo Star distribution. If you don't have it you can install it using panda manager or get it directly from GitHub.

use JSON::RPC::Client;
my $feline_hotel = JSON::RPC::Client.new( url => 'http://localhost:8080' );
say 'Hotel has following rooms available:';
say $feline_hotel.browse_rooms( );
say 'Nyan cat makes reservation of room 403:';
say $feline_hotel.reserve_room( 'Nyan', 403 );
say 'Hotel has following rooms available:';
say $feline_hotel.browse_rooms( );
say 'Grumpy cat makes reservation of room 406:';
try {
$feline_hotel.reserve_room( 'Grumpy', 406 );
CATCH { default { .say } }
}
class FelineHotel;
has %!rooms =
403 => {
'type' => 'Standard',
'equipment' => [ 'bed', 'bowl' ],
'price' => 64,
'available' => True,
},
406 => {
'type' => 'Purrific',
'equipment' => [ 'bed', 'bowl', 'toys', 'jacuzzi' ],
'price' => 128,
'available' => True
};
method browse_rooms ( ) {
return %!rooms.grep( { .value{ 'available' } } ).hash;
}
multi method reserve_room ( Str $name!, Int $number! ) {
self!check_room( $number );
return not %!rooms{ $number }{ 'available' } = False;
}
multi method reserve_room ( "Grumpy", Int $number! ) {
die 'No!';
}
method !check_room ( Int $number ) {
die 'No such room' unless %!rooms{ $number }:exists;
die 'Room not available' unless %!rooms{ $number }{ 'available' };
}
use lib '.';
use FelineHotel;
use JSON::RPC::Server;
JSON::RPC::Server.new( application => FelineHotel.new ).run;
@timo
Copy link

timo commented May 28, 2014

please use "use lib '.'" instead of @*INC.unshift in modern perl6 code :)

@bbkr
Copy link
Author

bbkr commented May 29, 2014

old habits die hard :) fixed, thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment