Skip to content

Deal of the Day – Half off Erlang and OTP in Action

Here is your chance to get our book Erlang and OTP in Action for half price on April 16th. Use code dotd0416au at www.manning.com/logan/

Running Opa Applications on Heroku

TL;DR

As I’ve mentioned before, Opa is a new web framework that introduces not only the framework itself but a whole new language. A lot has changed in Opa since I last posted about it. Now Opa has a Javascript-esque look and runs on Node.js. But it still has the amazing typing system that makes Opa a joy to code in.

The currently available Heroku buildpack for Opa only supported the old, pre-Node, support. So I’ve created an all new buildpack and here I will show both a bit of how I created that buildpack and how to use it to run your Opa apps on Heroku.

The first step was creating a tarball of Opa that would work on Heroku. For this I used the build tool vulcan. Vulcan is able to build software on Heroku in order to assure what is built will work on Heroku through your buildpacks.

vulcan build -v -s ./opalang/ -c "mkdir /app/mlstate-opa && yes '' | ./opa-1.0.7.x64.run" -p /app/mlstate-opa

This command is telling vulcan to build what is in the directory opalang with a command that creates the directory /app/mlstate-opa and then runs the Opa provided install script to unpack the system. This is much simpler than building Opa from source, but it is still necessary to still use vulcan to create the tarball from the output of the install script to ensure paths are correct in the Opa generated scripts.

After this run, by vulcan’s default, we will have /tmp/opalang.tgz. I upload this to S3, so that our buildpack is able to retrieve it.

Since Opa now relies on Node.js, the new buildpack must install both Node.js and the opalang.tgz that was generated. To do this I simply copied from the Node.js buildpack.

If you look at the Opa buildpack you’ll see, as with any buildpack, it consists of three main scripts under ./bin/: compile, detect and release. There are three important parts for understanding how your Opa app must be changed to be supported by the buildpack.

First, the detect script relies on there being a opa.conf to detect this being an Opa application. This for now is less important since we will be specifying the buildpack to use to the heroku script. Second, in the compile script we rely on there being a Makefile in your application for building. There is no support for simply running opa to compile the code in your tree at this time. Third, since Opa relies on Node.js and Node modules from npm you must provide a package.json file that the compile script uses to install the necessary modules.

To demostrate this I converted Opa’s hello_chat example to work on Heroku, see it on Github here.

There are two necessary changes. One, add the Procfile. A Procfile define the processes required for your application and how to run them. For hello_chat we have:

web: ./hello_chat.exe --http-port $PORT

This tell Heroku that our web process is run from the binary hello_chat.exe. We must pass the $PORT variable to the Opa binary so that it binds to the proper port that Heroku expects it to be listening on to route our traffic.

Lastly, a package.json file is added so that our buildpack’s compile script installs the necessary Node.js modules:

{
  "name": "hello_chat",
  "version": "0.0.1",
  "dependencies": {
      "mongodb" : "*",
      "formidable" : "*",
      "nodemailer" : "*",
      "simplesmtp" : "*",
      "imap" : "*"
  },
  "engines": {
    "node": "0.8.7",
    "npm": "1.1.x"
  }
}

With these additions to hello_chat we are ready to create an Opa app on Heroku and push the code!

$ heroku create --stack cedar --buildpack https://github.com/tsloughter/heroku-buildpack-opa.git
$ git push heroku master

The output from the push will show Node.js and npm being install, followed by Opa being unpacked and finally make being run against hello_chat. The web process in Procfile will then be run and the output will provide a link to go to our new application. I have the example running at http://mighty-garden-9304.herokuapp.com

Next time I’ll delve into database and other addon support in Heroku with Opa applications.

Projmake-mode: Flymake Replacement

There is a great new Emacs plugin from Eric Merritt that like FlyMake builds your code and highlights within Emacs any errors or warnings, but unlike FlyMake builds across the whole project. You can clone the mode from here projmake-mode

After cloning the repo to your desired location add this bit to your dot emacs file, replacing <PATH> with the path to where you cloned the repo.

This Emacs code also uses add-hook to set projmake-mode to start for erlang-mode is loaded. Projmake by default knows how to handle rebar and Make based builds so there is no setup after this, assuming your project is built this way.

Here is my Makefile for building Erlang projects with rebar, replace PROJECT with the name of your project:

Now you can load Emacs and a file from your project and if it is an Erlang file due to the add-hook function in our dot emacs file it will automatically load projmake-mode. You can add hooks for other modes or simply run M-x projmake-mode.

For more documentation and how to extend to other types of projects check out the documentation.

Maru Models: JSON to Erlang Record with Custom Types

Working with Erlang for writing RESTful interfaces JSON is the communication “language” of choice. For simplifying the process of JSON to a model the backend could work with efficiently I’ve created maru_models. This app decodes the JSON with jiffy and uses functions generated by a modified version of Ulf’s exprecs to create an Erlang record. The generated functions are created with type information from the record definition and when a property is set for the record through these functions it is first passed to the convert function of maru_model_types to do any necessary processing.

I separated this application into a separate repo to simplify people trying the examples. But the real development will be done in the Maru main repo.

Getting Flymake and Rebar to Play Nice

TLDR;
Copy and paste the following into your elisp erlang-mode configuration to get flymake working with Rebar projects.

 (defun ebm-find-rebar-top-recr (dirname)
      (let* ((project-dir (locate-dominating-file dirname "rebar.config")))
        (if project-dir
            (let* ((parent-dir (file-name-directory (directory-file-name project-dir)))
                   (top-project-dir (if (and parent-dir (not (string= parent-dir "/")))
                                       (ebm-find-rebar-top-recr parent-dir)
                                      nil)))
              (if top-project-dir
                  top-project-dir
                project-dir))
              project-dir)))

    (defun ebm-find-rebar-top ()
      (interactive)
      (let* ((dirname (file-name-directory (buffer-file-name)))
             (project-dir (ebm-find-rebar-top-recr dirname)))
        (if project-dir
            project-dir
          (erlang-flymake-get-app-dir))))

     (defun ebm-directory-dirs (dir name)
        "Find all directories in DIR."
        (unless (file-directory-p dir)
          (error "Not a directory `%s'" dir))
        (let ((dir (directory-file-name dir))
              (dirs '())
              (files (directory-files dir nil nil t)))
            (dolist (file files)
              (unless (member file '("." ".."))
                (let ((absolute-path (expand-file-name (concat dir "/" file))))
                  (when (file-directory-p absolute-path)
                    (if (string= file name)
                        (setq dirs (append (cons absolute-path
                                                 (ebm-directory-dirs absolute-path name))
                                           dirs))
                        (setq dirs (append
                                    (ebm-directory-dirs absolute-path name)
                                    dirs)))))))
              dirs))

    (defun ebm-get-deps-code-path-dirs ()
        (ebm-directory-dirs (ebm-find-rebar-top) "ebin"))

    (defun ebm-get-deps-include-dirs ()
       (ebm-directory-dirs (ebm-find-rebar-top) "include"))

    (fset 'erlang-flymake-get-code-path-dirs 'ebm-get-deps-code-path-dirs)
    (fset 'erlang-flymake-get-include-dirs-function 'ebm-get-deps-include-dirs)

Intro

Its probably no great surprise to anyone that I dislike Rebar a lot. That said there are times when I have no choice but to use it. This is always either because a company I am contracting for uses it, or an open source project I am contributing to uses it. When I am forced to use it there are a few things I don’t want to give up. Most important among these is Flymake for Erlang. The default setup for Flymake doesn’t work for Rebar projects because Flymake does not know where the code and include paths for dependencies are. Fortunately, we can fix this with a few lines of elisp.

Flymake For Erlang

First make sure you have Flymake for Erlang installed. It is easiest just to follow the directions available on the Erlang Website.

The Elisp Additions for Erlang Flymake

There are two defvars that point to functions that are used to search for the correct code paths and include paths respectively. We are going to replace those functions with our own functions. Both these functions search upwards from the directory that contains the file pointed to by the current buffer, looking for the top most ‘rebar.config’ in the directory path. It then uses that for a base and searches down the directory structure looking for either ‘ebin’ files or ‘include’ files.

There are two things to note here. The first is that you must have already run `get-deps` for rebar for this to work and the second is that if your project is truly huge or you have way more dependencies then you probably need this search could take a second or two. That is a second or two too long in an interactive compiler like Flymake. That said, the likelihood that you will run into this second problem is quite low.

Getting Started

The very thing you want to do is ensure that you have required the erlang-flymake module. Most of what we do below depends on this.

(require 'erlang-flymake)

Finding the Top rebar.config

The second thing we want to do is look for the top rebar.config in the project. If a rebar project contains more then one OTP application its quite likely that it will contain more then one rebar.config. The very topmost `rebar`config` is the right one to serve as root of our search. So we introduce a set of recursive functions to look for that top level dir.

    (defun ebm-find-rebar-top-recr (dirname)
      (let* ((project-dir (locate-dominating-file dirname "rebar.config")))
        (if project-dir
            (let* ((parent-dir (file-name-directory (directory-file-name project-dir)))
                   (top-project-dir (if (and parent-dir (not (string= parent-dir "/")))
                                       (ebm-find-rebar-top-recr parent-dir)
                                      nil)))
              (if top-project-dir
                  top-project-dir
                project-dir))
              project-dir)))

ebm-find-rebar-top-recr will return either the top most directory or nil. Our next function takes that result and does something useful with.

    (defun ebm-find-rebar-top ()
      (interactive)
      (let* ((dirname (file-name-directory (buffer-file-name)))
             (project-dir (ebm-find-rebar-top-recr dirname)))
        (if project-dir
            project-dir
          (erlang-flymake-get-app-dir))))

In this function, we get the directory containing the file pointed at by the current buffer. We then call our recr function. If it returns a directory we return that, if it returns nil however, we call the original erlang-flymake-get-app-dir function.

At this point we should have our project root. Now its a simple matter of recursively searching down the directory tree looking for files of a certain name. So we create a function that does just that, given a directory and a name will return a list of absolute paths for each subdirectory that matches the specified name.

(defun ebm-directory-dirs (dir name)
        "Find all directories in DIR."
        (unless (file-directory-p dir)
          (error "Not a directory `%s'" dir))
        (let ((dir (directory-file-name dir))
              (dirs '())
              (files (directory-files dir nil nil t)))
            (dolist (file files)
              (unless (member file '("." ".."))
                (let ((absolute-path (expand-file-name (concat dir "/" file))))
                  (when (file-directory-p absolute-path)
                    (if (string= file name)
                        (setq dirs (append (cons absolute-path
                                                 (ebm-directory-dirs absolute-path name))
                                           dirs))
                        (setq dirs (append
                                    (ebm-directory-dirs absolute-path name)
                                    dirs)))))))
              dirs))

Now we write a couple of functions to replace the corresponding functions in `erlang-flymake`. The first looks for all `ebin` directories while the second looks for all `include` directories.

    (defun ebm-get-deps-code-path-dirs ()
        (ebm-directory-dirs (ebm-find-rebar-top) "ebin"))

    (defun ebm-get-deps-include-dirs ()
       (ebm-directory-dirs (ebm-find-rebar-top) "include"))

Finally we replace the `erlang-flymake` versions of those functions with our implementations.

(fset 'erlang-flymake-get-code-path-dirs 'ebm-get-deps-code-path-dirs)
(fset 'erlang-flymake-get-include-dirs-function 'ebm-get-deps-include-dirs)

Conclusion

This approach is a bit of a hack, we basically use some heuristics to find a root and then just grab everything under that that looks remotely like a code or include directory. While its a bit hacky it has the valuable upside that its flexible and robust.

Erlang Common Test Continuous Integration

Common Test is a well thought out integration testing framework for Erlang. If you
are not using it you probably should be. However, it has one fault. It
does not return non-negative exit status’ to the caller when the tests
fail. This is a major oversight, and it makes it difficult to use as
part of a continuous integration scheme or in a make based build
system.

The long term fix is for the OTP folks to resolve the issue in the
ct_run command. To that end I have filed a bug report with the
Erlang folks. In the short term, though, we need this behaving
correctly. After much twiddling around with different solutions and
conversions on the erlang-questions list. This solution finally popped
out of a conversation with Lukas Larsson. Basically, we use the old
unix standby of awk.


    ct_run -dir tests  ... | awk "/FAILED/{exit 1;}/failed/{exit 1;}/SKIPPED/{exit 1;}"

Where ... is replaced with your additional options. Its not the best
solution on the planet, but it is the simplest one that I found that
works consistently.

Sinan Releases and Being Right

Fred, of Learn You Some Erlang for Great Good, today posted on his blog about the problems around how rebar handles releases, Rebar Releases and Being Wrong. The problems he mentions and a few others are why, despite giving it a legitimate shot, I have found rebar unusable for my workflow to be efficient and stable while adhering to OTP standards at the same time.

I suggest first reading his post, if you already use rebar, and then continuing on with the rest of this.

I’ll start with an example on the generation of a project containing two applications and a dependency from one of those applications of cowboy. Next, I’ll create a release (and in the process a deployable target system) to show the difference in how sinan handles this process.

TL;DR Sinan does OTP the right way, rebar does not.

First, you can download the latest version sinan from this link, it is simply an executable escript, so ‘chmod +x sinan‘ and put it in your PATH and you are good to go.

Sinan provides a ‘gen’ command to create your project. I include the output of the steps I took to build this project. Sinan assumes this is a multiple application project, but if you give “y” instead it will create a directory structure similar to rebars default structure with a src/ directory instead of a lib/ directory.

$ sinan gen
Please specify your name 
your name> Tristan Sloughter
Please specify your email address 
your email> tristan@mashape.com
Please specify the copyright holder 
copyright holder ("Tristan Sloughter")> 
Please specify name of your project
project name> rel_example
Please specify version of your project
project version> 0.0.1
Please specify the ERTS version ("5.9")> 
Is this a single application project ("n")> 
Please specify the names of the OTP apps that will be developed under this project. One application to a line. Finish with a blank line.
app> app_1
app ("")> app_2
app ("")> 
Would you like a build config? ("y")> y
Project was created, you should be good to go!

We now have a project named rel_example and can see the generated contents.

$ cd rel_example/
$ ls
config  doc  lib  sinan.config

Before going further I add the line {include_erts, true}. to sinan.config so that a generated tarball of the release contains erts and can be booted on a machine without Erlang installed.

$ cat sinan.config
{project_name, rel_example}.
{project_vsn, "0.0.1"}.

{build_dir,  "_build"}.

{ignore_dirs, ["_", "."]}.

{ignore_apps, []}.

{include_erts, true}.

A tree structure view of the generated project is below:

.
├── config
│   └── sys.config
├── doc
├── lib
│   ├── app_1
│   │   ├── doc
│   │   ├── ebin
│   │   │   └── overview.edoc
│   │   ├── include
│   │   └── src
│   │       ├── app_1_app.erl
│   │       ├── app_1.app.src
│   │       └── app_1_sup.erl
│   └── app_2
│       ├── doc
│       ├── ebin
│       │   └── overview.edoc
│       ├── include
│       └── src
│           ├── app_2_app.erl
│           ├── app_2.app.src
│           └── app_2_sup.erl
└── sinan.config

You’ll see we have a lib directory with two applications containing their source files under a src directory. Now in order to boot the release we’ll create, we need to remove a couple tings from each supervisor. Instead of creating something for them to supervise just remove the variable AChild and replace [AChild] with [].

Next, so we have a third party dependency in the example, add cowboy to the applications in nano lib/app_1/src/app_1.app.src:

{applications, [kernel, stdlib, cowboy]},

Sinan provides a depends command to show the depenedencies of the project and where they are located:

$ sinan depends -v
starting: depends
Using the following lib directories to show resolved dependencies and where it found them:

    /home/tristan/.kerl/installs/r15b/lib
    /home/tristan/Devel/rel_example/_build/rel_example/lib

compile time dependencies:

runtime dependencies:

    kernel                    2.15       : /home/tristan/.kerl/installs/r15b/lib/kernel-2.15
    stdlib                    1.18       : /home/tristan/.kerl/installs/r15b/lib/stdlib-1.18
    cowboy                    0.5.0      : /home/tristan/.kerl/installs/r15b/lib/cowboy-0.5.0

project applications:

    app_1                     0.1.0      : /home/tristan/Devel/rel_example/_build/rel_example/lib/app_1-0.1.0
    app_2                     0.1.0      : /home/tristan/Devel/rel_example/_build/rel_example/lib/app_2-0.1.0

Now lets build a release and target system.

$ sinan dist

After running the dist command we have a _build directory that we find the following structure. I removed the files/dirs under each app to shorten the listing.

_build/
├── rel_example
│   ├── bin
│   │   ├── rel_example
│   │   └── rel_example-0.0.1
│   ├── erts-5.9
│   │   ├── 
│   ├── lib
│   │   ├── app_1-0.1.0
│   │   │   ├── 
│   │   ├── app_2-0.1.0
│   │   │   ├── 
│   │   ├── cowboy-0.5.0
│   │   │   ├── 
│   │   ├── kernel-2.15
│   │   │   ├── 
│   │   └── stdlib-1.18
│   │       ├── 
│   └── releases
│       └── 0.0.1
│           ├── rel_example.boot
│           ├── rel_example.rel
│           ├── rel_example.script
│           └── sys.config
└── tar
    └── rel_example-0.0.1.tar.gz

Sinan has created a lib directory containing all necessary applications for our release as well as the needed files for booting the release. Additionally the dist command creates a tar.gz for easy deployment. But if we simply want to run our release where we are we can:

$ _build/rel_example/bin/rel_example
Erlang R15B (erts-5.9)

[64-bit] [smp:4:4] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9  (abort with ^G)
1>

This is only the tip of the iceberg of what sinan is capable of. I can’t go into all of it here but I’ll mention that you are able to define multiple releases for a project to generate and which of your project apps to include in each. Additionally you are able to provide a custom rel file if you require tweaks.

The important part to take away from this post is the structure of what you are working with when using sinan and how it is based on OTP standards, both for the source you work on and the results of the build process under _build/.

Follow

Get every new post delivered to your Inbox.