Skip to content

Cute web framework for Erlang built on top of Cowboy 2

License

Notifications You must be signed in to change notification settings

tapsters/smoothie

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

77 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

smoothie

Cute web framework for Erlang built on top of Cowboy 2.

Installing

In rebar.config:

{smoothie, ".*", {git, "git://github.com/tapsters/smoothie", {tag, "master"}}}

Starting up

Starting http server:

sm:start_http([
  {ranch, [{port, 3000}]},
  {cowboy, [
    {nb_acceptors, 100},
    {protocol, [{compress, true}]}
  ]},
  {routes, [
    {"/",         {priv_file, my_app, "static/index.html"}},
    {"/js/[...]", {priv_dir, "static/js"}},
    {"/user/:id", {request,   my_api,       get_user}},
    {"/ws",       {websocket, my_websocket, sm_protocol_bert}}
  ]}
]).

More about configuring Ranch TCP transport and Cowboy protocol: ranch_tcp, cowboy_protocol.

In the above example request and websocket are special route types provided by Smoothie to make handling HTTP requests and WebSockets easier.

More about Cowboy's routing: Routing, Constraints, Static files.

Handling HTTP requests

To handle HTTP request you should specify route for it:

sm:start_http([
  {routes, [
    {"/user",     {request, my_api, get_all_users}},
    {"/user/:id", {request, my_api, get_user}}
  ]}
]).

In the above example my_api is module name and get_all_user/get_user are function names.

Example code for my_api module:

-module(my_api).

-include_lib("smoothie/include/sm.hrl").

-export([get_all_users/1, get_user/1]).

get_all_users(_Req) ->
  Cookie = #sm_cookie{name    = <<"some_cookie">>, 
                      value   = <<"some_value">>, 
                      domain  = <<"example.com">>,
                      path    = <<"/some/path">>,
                      max_age = 3600},

  {ok, #sm_response{status  = 200, 
                    headers = [{<<"content-type">>, <<"text/plain">>}], 
                    body    = <<"1, 2, 3">>,
                    cookies = [Cookie]}}.

get_user(Req) ->
  Id = cowboy_req:binding(id, Req),

  {ok, #sm_response{status  = 200, 
                    headers = [{<<"content-type">>, <<"text/plain">>}], 
                    body    = Id}}.

More about accessing request's information: cowboy_req.

Handling WebSockets

To handle WebSocket connections you should implement sm_websocket behaviour:

-module(my_websocket).
-behaviour(sm_websocket).

-export([handle/2]).

-record(state, {
  user
}).

handle(init, {_, WebSocketState}) ->
  Id = sm:qs_val(id, WebSocketState),
  User = ctail:get(user, Id),
  {ok, #state{user=User}};

handle({stream, {text, <<"get_name">>}}, {State=#state{user=User}, _}) ->
  {reply, User#user.username, State};

handle({stream, {_Format, Data}}, {State, _}) -> 
  io:format("Got new data: ~p~n", [Data]),
  {ok, State};

handle(terminate, _) -> ok;
handle(_, _)         -> ok.

And also you should define route for your handler:

sm:start_http([
  {routes, [
    {"/ws", {websocket, my_websocket, sm_protocol_relay}}
  ]}
]).

In the above example third element of the tuple is data encoding/decondig protocol. Smoothie implements following protocols:

  • relay - passes data as is
  • bert - uses BIFs to encode/decode data BERT which is compatible with Erlang's ETF
  • json - uses yaws_json2 to encode/decode data

If you want add your own protocol, you should implement sm_protocol behaviour.

WebSocket File Upload

Smoothie has builtin JavaScript frontend for file uploading through WebSocket.

See example in examples/file_upload.

Working with JSON

Smoothie uses yaws_json2 taked from Yaws webserver to encode/decode JSON.

Use sm:json_enc/1 and sm:json_dec/1 to encode/decode json.

Decode example (encode works in a same way):

{
  first_name: "John",
  last_name: "Smith",
  enabled: true,
  phone_number: 937600131,
  avatar: {
    origin: "default.png",
    thumbnails: [
      "default_min.png"
    ]
  }
}

Result:

{struct, [
  {"first_name", "John"},
  {"last_name", "Smith"},
  {"enabled", true},
  {"phone_number", 937600131},
  {"avatar", {struct, [
    {"origin", "default.png"},
    {"thumbnails", {array, [
      "default_min.png"
    ]}}
  ]}}
]}

Client-side

To use Smoothie in browser include following dependencies:

<script type="text/javascript" src="/sm/erlb.js"></script>
<script type="text/javascript" src="/sm/smoothie.js"></script>

Now you can esteblish WebSocket connection, send and receive data from server:

var ws = Smoothie.connect({
  protocol: "bert",
  onMessage: function(data) {
    console.log("Got message:", data);
  },
});

ws.send(Erl.tuple(Erl.atom("ok"), "Hello", 123));

Detailed example of the Smoothie.connect options:

{
  http: {
    protocol:     "ws://",
    host:         "example.com",
    port:         3000,
    path:         "/ws",
  },
  
  queryParams:    {token: 987654321},
  
  heartBeatDelay: 20000,
  protocol:       "relay",

  onOpen:         function() {},
  onMessage:      function(data) {},
  onDisconnect:   function() {},
  onClose:        function() {},

  onBeforeSend:   function(data) {}
}

When you send or receive data from server, it is encoded/decoded by choosen protocol. Smoothie implements following protocols:

  • relay - passes data as is
  • bert - uses Erlb.js to encode/decode data to BERT which is compatible with Erlang's ETF
  • json - uses JSON to encode/decode data

About

Cute web framework for Erlang built on top of Cowboy 2

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 3

  •  
  •  
  •