aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLoïc Hoguin <[email protected]>2013-09-07 12:54:55 +0200
committerLoïc Hoguin <[email protected]>2013-09-07 12:54:55 +0200
commitc7f7e4456e2fa54d53482ac309011433d6c48d21 (patch)
tree6a914964bc09d4cc2911500bff54498780f3c6b3
parent4a30198f9068cc989616c8088e4b890bc1de259d (diff)
downloadcowboy-c7f7e4456e2fa54d53482ac309011433d6c48d21.tar.gz
cowboy-c7f7e4456e2fa54d53482ac309011433d6c48d21.tar.bz2
cowboy-c7f7e4456e2fa54d53482ac309011433d6c48d21.zip
Much improved Getting started chapter
We now describe how to build a hello world application from start to finish, including setting up erlang.mk for building, and using relx for generating the release. All concepts are not explained in details of course, but we don't need to at this point, we just want things to be working.
-rw-r--r--guide/getting_started.md284
1 files changed, 254 insertions, 30 deletions
diff --git a/guide/getting_started.md b/guide/getting_started.md
index 2ad67db..3143a7f 100644
--- a/guide/getting_started.md
+++ b/guide/getting_started.md
@@ -1,18 +1,136 @@
Getting started
===============
-Cowboy does nothing by default.
+Setting up a working Erlang application is a little more complex than
+for most other languages. The reason is that Erlang is designed to
+build systems and not just simple applications.
+
+An Erlang system is typically comprised of many different nodes,
+each containing many different OTP applications, each containing
+many different modules and running many different processes.
+Nodes may or may not be identical depending on the nature of the
+system.
+
+To get started though, we only need one node that contains your own
+HTTP application, plus the dependencies that it needs, like Cowboy.
+To create our node, we need to build what is called a release. A
+release is a set of files that contain the Erlang VM plus all the
+applications required to run our node.
+
+Let's start by creating this application. We will simply call it
+`hello_erlang`. This application will have the following directory
+structure:
+
+```
+hello_erlang/
+ src/
+ hello_erlang.app.src
+ hello_erlang_app.erl
+ hello_erlang_sup.erl
+ hello_handler.erl
+ erlang.mk
+ Makefile
+ relx.config
+```
+
+Once the release is generated, we will also have the following
+files added:
+
+```
+hello_erlang/
+ ebin/
+ hello_erlang.app
+ hello_erlang_app.beam
+ hello_erlang_sup.beam
+ hello_handler.beam
+ _rel/
+```
+
+As you can probably guess, the `.app.src` file end up becoming
+the `.app` file, and the `.erl` files are compiled into `.beam`.
+Then, the whole release will be copied into the `_rel/` directory.
+
+The `.app` file contains various informations about the application.
+It contains its name, a description, a version, a list of modules,
+default configuration and more.
+
+Using a build system like [erlang.mk](https://github.com/extend/erlang.mk),
+the list of modules will be included automatically in the `.app` file,
+so you don't need to manually put them in your `.app.src` file.
+
+For generating the release, we will use [relx](https://github.com/erlware/relx)
+as it is a much simpler alternative to the tool coming with Erlang.
+
+First, create the `hello_erlang` directory. It should have the same name
+as the application within it. Then we create the `src` directory inside
+it, which will contain the source code for our application.
+
+``` bash
+$ mkdir hello_erlang
+$ cd hello_erlang
+$ mkdir src
+```
-Cowboy requires the `crypto`, `cowlib` and `ranch` applications to be
-started.
+Let's first create the `hello_erlang.app.src` file. It should be pretty
+straightforward for the most part. You can use the following template
+and change what you like in it.
``` erlang
-ok = application:start(crypto).
-ok = application:start(cowlib).
-ok = application:start(ranch).
-ok = application:start(cowboy).
+{application, hello_erlang, [
+ {description, "Hello world with Cowboy!"},
+ {vsn, "0.1.0"},
+ {modules, []},
+ {registered, [hello_erlang_sup]},
+ {applications, [
+ kernel,
+ stdlib,
+ cowboy
+ ]},
+ {mod, {hello_erlang_app, []}},
+ {env, []}
+]}.
```
+The `modules` line will be replaced with the list of modules during
+compilation. Make sure to leave this line even if you do not use it
+directly.
+
+The `registered` value indicates which processes are registered by this
+application. You will often only register the top-level supervisor
+of the application.
+
+The `applications` value lists the applications that must be started
+for this application to work. The Erlang release will start all the
+applications listed here automatically.
+
+The `mod` value defines how the application should be started. Erlang
+will use the `hello_erlang_app` module for starting the application.
+
+The `hello_erlang_app` module is what we call an application behavior.
+The application behavior must define two functions: `start/2` and
+`stop/1`, for starting and stopping the application. A typical
+application module would look like this:
+
+``` erlang
+-module(hello_erlang_app).
+-behavior(application).
+
+-export([start/2]).
+-export([stop/1]).
+
+start(_Type, _Args) ->
+ hello_erlang_sup:start_link().
+
+stop(_State) ->
+ ok.
+```
+
+That's not enough however. Since we are building a Cowboy based
+application, we also need to initialize Cowboy when we start our
+application.
+
+Cowboy does nothing by default.
+
Cowboy uses Ranch for handling the connections and provides convenience
functions to start Ranch listeners.
@@ -33,50 +151,156 @@ options to the connection processes. The protocol options must include
the dispatch list for routing requests to handlers.
The dispatch list is explained in greater details in the
-[Routing](routing.md) chapter.
+[Routing](routing.md) chapter. For the purpose of this example
+we will simply map all URLs to our handler `hello_handler`,
+using the wildcard `_` for both the hostname and path parts
+of the URL.
+
+This is how the `hello_erlang_app:start/2` function looks like
+with Cowboy initialized.
``` erlang
-Dispatch = cowboy_router:compile([
- %% {URIHost, list({URIPath, Handler, Opts})}
- {'_', [{'_', my_handler, []}]}
-]),
-%% Name, NbAcceptors, TransOpts, ProtoOpts
-cowboy:start_http(my_http_listener, 100,
- [{port, 8080}],
- [{env, [{dispatch, Dispatch}]}]
-).
+start(_Type, _Args) ->
+ Dispatch = cowboy_router:compile([
+ %% {URIHost, list({URIPath, Handler, Opts})}
+ {'_', [{'_', hello_handler, []}]}
+ ]),
+ %% Name, NbAcceptors, TransOpts, ProtoOpts
+ cowboy:start_http(my_http_listener, 100,
+ [{port, 8080}],
+ [{env, [{dispatch, Dispatch}]}]
+ ),
+ hello_erlang_sup:start_link().
```
+Do note that we told Cowboy to start listening on port 8080.
+You can change this value if needed.
+
+Our application doesn't need to start any process, as Cowboy
+will automatically start processes for every incoming
+connections. We are still required to have a top-level supervisor
+however, albeit a fairly small one.
+
+``` erlang
+-module(hello_erlang_sup).
+-behavior(supervisor).
+
+-export([start_link/0]).
+-export([init/1]).
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+init([]) ->
+ {ok, {{one_for_one, 10, 10}, []}}.
+```
+
+Finally, we need to write the code for handling incoming requests.
+
Cowboy features many kinds of handlers. For this simple example,
we will just use the plain HTTP handler, which has three callback
-functions: init/3, handle/2 and terminate/3. You can find more information
-about the arguments and possible return values of these callbacks in the
+functions: `init/3`, `handle/2` and `terminate/3`. You can find more
+information about the arguments and possible return values of these
+callbacks in the
[cowboy_http_handler function reference](http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_http_handler).
-Here is an example of a simple HTTP handler module.
+
+Our handler will only send a friendly hello back to the client.
``` erlang
--module(my_handler).
--behaviour(cowboy_http_handler).
+-module(hello_handler).
+-behavior(cowboy_http_handler).
-export([init/3]).
-export([handle/2]).
-export([terminate/3]).
-init({tcp, http}, Req, Opts) ->
+init(_Type, Req, _Opts) ->
{ok, Req, undefined_state}.
handle(Req, State) ->
- {ok, Req2} = cowboy_req:reply(200, [], <<"Hello World!">>, Req),
+ {ok, Req2} = cowboy_req:reply(200, [
+ {<<"content-type">>, <<"text/plain">>}
+ ], <<"Hello World!">>, Req),
{ok, Req2, State}.
-terminate(Reason, Req, State) ->
+terminate(_Reason, _Req, _State) ->
ok.
```
The `Req` variable above is the Req object, which allows the developer
-to obtain information about the request and to perform a reply. Its usage
-is explained in the [cowboy_req function reference](http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_req).
+to obtain information about the request and to perform a reply.
+Its usage is documented in the
+[cowboy_req function reference](http://ninenines.eu/docs/en/cowboy/HEAD/manual/cowboy_req).
+
+The code for our application is ready, so let's build a release!
+
+First we need to download `erlang.mk`.
+
+``` bash
+$ wget https://raw.github.com/extend/erlang.mk/master/erlang.mk
+$ ls
+src/
+erlang.mk
+```
+
+Then we need to create a Makefile that will include `erlang.mk`
+for building our application. We need to define the Cowboy
+dependency in the Makefile. Thankfully `erlang.mk` already
+knows where to find Cowboy as it features a package index,
+so we can just tell it to look there.
+
+``` Makefile
+PROJECT = hello_erlang
+
+DEPS = cowboy
+dep_cowboy = pkg://cowboy master
+
+include erlang.mk
+```
+
+Note that when creating production nodes you will most likely
+want to use a specific version of Cowboy instead of `master`,
+and properly test your release every time you update Cowboy.
+
+If you type `make` in a shell now, your application should build
+as expected. If you get compilation errors, double check that you
+haven't made any typo when creating the previous files.
+
+``` bash
+$ make
+```
+
+That's not all however, as we want to create a working release.
+For that purpose we will need `relx`. You can download it directly
+[from Github](https://github.com/erlware/relx). After downloading
+it, you will need to build it using `make`, which should give
+you a `relx` executable that you can then put in your `$PATH`.
+You only need to do this once.
+
+We are almost ready to build the release. All that's left is
+the `relx.config` file! In it, we only need to tell `relx` that
+we want the release to include the `hello_erlang` application,
+and that we want an extended start script for convenience.
+`relx` will figure out which other applications are required
+by looking into the `.app` files for dependencies.
+
+``` erlang
+{release, {hello_erlang, "1"}, [hello_erlang]}.
+{extended_start_script, true}.
+```
+
+The `release` value is used to specify the release name, its
+version, and the applications to be included.
+
+We can now build and start the release.
+
+``` bash
+$ relx
+$ ./_rel/bin/hello_erlang console
+```
+
+If you then access `http://localhost:8080` using your browser,
+you should receive a nice greet!
-You can find many examples in the `examples/` directory of the
-Cowboy repository. A more complete "Hello world" example can be
-found in the `examples/hello_world/` directory.
+You can find many more examples in the `examples/` directory
+of the Cowboy repository.