Midas

A web framework for Gleam, Midas makes shiny things.

Hex pm Build Status IRC: #gleam-lang on chat.freenode.net

Overview

Midas is a framework to help you get started started building web applications in Gleam as quickly as possible, while focusing on simplicity in the long run.

The quickest way to get started is to run the template project

Gleam is a young language so midas comes with postgresql client library, configuration support and module for implementing procsses and supervisors.

Web Functionality

import midas/http
import midas/http.{Request, Response, Get, Post}

pub fn handle_request(request) {
  let Request(method: method, path: path, query: query, body: body ..) = request
  let segments = http.split_segments(path)
  let params = http.parse_query(result.unwrap(query, ""))

  case method, segments {
    Get, [] -> {
      let name = params
        |> list.key_find("name")
        |> result.unwrap("World")

      Response(
        status: 200,
        headers: [tuple("content-type", "text/plain")],
        body: string.concat(["Hello, ", name, "!"])
      )
    }

    Get, ["posts", id] -> {
      // Action to get a post by id
      todo
    }

    Post, ["posts"] -> {
      // Action to create a post
      todo
    }

    _, _ -> Response(
      status: 404,
      headers: [tuple("content-type", "text/plain")],
      body: "Nothing here."
    )
  }
}

Config

config.get_env

Returns the system environment variables as a map

config.required

Try to fetch and parse a value from a map, return as a result.

config.optional

Try to fetch value from a map, returns the fallback value if not in the map.

let env = get_env()

let port = optional(env, "PORT", int.parse, 8080)
let Ok(secret_key_base) = required(env, "SECRET_KEY_BASE", fn(x) { Ok(x) })

Start Midas

midas.start_link(handle_request, 8080)

Start Midas in an erlang application

Normally you would want to start midas under your applications supervision tree. Inside you application file src/my_app_app.erl add midas to your supervision tree.

Handler = my_app@web:handle_request/1,
Port = 8080,

ChildSpecs = [
  #{
    id => midas,
    start => {midas, start_link, [Handler, Port]}}
],

Tasks

rel start
rel eval "myapp:"

Development

This project is to validate the value of Gleam as a language for web development. Currently I am investigating the shape of such a project and not filling in the details, top priorities are as follows.

Thoughts on processes

These notes are not relevant to understanding Midas as a server or framework. The process code should eventually be extracted from this repo.

Supervisors should not accept start functions that fail.

One thing that can cause failures is naming processes. This is a big global state problem and probably could always happen, but at the registered process level a naming failure should be a hard crash.

Potential an Ignore response is ok, for example not starting a server if the PORT is not set. There is no reason the supervisor can't handle this but it might be a nicer grouping of logic to put this in the particular process. This is similar to the logic that lead to child_specs

Having the init and init_ack functionality causes alot of delay and the supervisor has to watch for more race conditions.

config typed properly

let Ok(pid) = postgresql.start_client(database_url: String)

// Replace that unsafe start API with two step process
// 1. outside supervision tree
let Ok(db_config) = postgresql.parse_url(database_url: String)

// 2.
let pid = postgresql.start_client(db_config)

There are some good discussion on this around, need to look for more on less things being required at startup. users of the pg_client are better of reporting the errors.

Request handlers

These are tasks not "actors", they shouldn't have an open inbox but deal in gen:replies promises.

Super general inbox setup

let tuple(receive, resolve) = promise.new()

process.spawn(fn(){
  resolve(5)
})

let Ok(value) = receive(Infinity)

A sendable trait could be really useful here.

Resources http://joeduffyblog.com/2015/11/19/asynchronous-everything/