Git Product home page Git Product logo

axiom's Introduction

Axiom Build Status

Axiom is a micro-framework for building web applications in Erlang. It is inspired by Sinatra and built on top of Cowboy.

Getting Started

Axiom is built to make creating web applications fast and easy. A minimal application would look like this:

-module(my_app).
-export([start/0, handle/3]).

start() ->
	axiom:start(?MODULE).

handle(<<"GET">>, [<<"hi">>], _Request) ->
	<<"Hello world!">>.

This handles requests for GET /hi and returns "Hello world!".

The third argument given to the handler is of type cowboy_req:req(). Use the cowboy_req module, if you need anything out of the request.

The return value can be a binary string or iolist. So, this also works:

handle(<<"GET">>, [<<"hello">>, Who], _Request) ->
	[<<"Hello ">>, Who, <<"!">>].

If you want to specify a response status code and/or headers, use a tuple with either the status code and body or status code, headers and body, in these respective orders.

Examples:

{418, <<"<h1>I'm a teapot!</h1>">>}

or

{418, [{<<"X-Foo">>, <<"bar">>}], <<"<h1>I'm a teapot!</h1>">>}

As a third option a cowboy_req:req() can be returned. In this case, to set the response headers and body, use the cowboy_req:set_resp_header/3 and cowboy_req:set_resp_body/2 functions. To set the status code, use axiom:set_resp_status/2. These functions return a new cowboy_req:req() to be used further and to be returned from YourHandler:handle/3.

The full spec of YourHandler:handle/3 is expected to look like this:

handle(Method, Path, Req) -> Body | Req | {Status, Body} | {Status, Headers, Body}.

  Types:
    Method = binary(),
    Path = [PathSegment]
    PathSegment = binary()
    Req = cowboy_req:req()
    Body = iodata()
    Status = non_neg_integer()
    Headers = [Header]
    Header = {binary(), binary()}

Request parameters

To get the request parameters out of the request, you can use the two handy functions axiom:params(Req) and axiom:param(Name, Req). The first returns a proplist of all parameters, the second one returns the named parameter's value. Keys and values are binary strings.

Configuration

axiom:start/1 has a bigger brother called axiom:start/2, taking a proplist as the second argument. Possible properties and their defaults are as follows:

[
	{nb_acceptors: 100},		% acceptor pool size
	{host, '_'},				% host IP
	{port, 7654},				% host port
	{public, "public"}			% custom path for static files
]

Static Files

Static files are served via the cowboy_static handler. By default, every file in the ./public directory and all its subdirectories will be made accessible via URL path the same as file's relative path. E.g. the file ./public/about.html can be accessed via GET /about.html. Note: Currently, if the contents of the ./public subtree change, Axiom needs to be restarted to reflect the change.

You can specify a custom directory via the public option.

When you use this feature, it is advisable to start Erlang with the +A n flag. This will start n async threads. Rule of thumb is to use your machine's number of CPU cores.

Redirects

You can redirect requests with redirect/2:

handle(<<"GET">>, [<<"foo">>], Req) ->
  Req1 = axiom:redirect("/bar", Req),
  Req;

handle(<<"GET">>, [<<"bar">>], Request) ->
	<<"<h1>Welcome back!</h1>">>.

Templates

Axiom comes with Django template support via erlydtl. To make use of it in your application, create a directory named templates and in it, create a template, e.g. my_template.dtl:

<h1>Hello {{who}}</h1>

In your handler, specify the template to be rendered:

handle(<<"GET">>, [<<"hello">>], _Request) ->
	axiom:dtl(my_template, [{who, "you"}]).

For convenience, the second argument, a proplist of parameters, can have atoms, lists or binaries as keys. That way request parameters can be put in there, without you having to convert them first.

The templates are compiled into modules when rebar compile is called.

To see what else erlydtl can do for you, take a look at its project page.

Sessions

Axiom comes with a basic session handler and ets based session store. To use it, add this tuple to the configuration proplist:

{sessions, []}

In your handler you can then use axiom_session:set(Key, Value, Request) and axiom_session:get(Key, Request).

To set attributes for the cookie, storing the session ID, add some parameters to the session configuration in a tuple with the key cookies:

{sessions, [{cookies, [param()]}]}

Possible parameters are:

param() = {max_age, integer()} |
		  {local_time, calendar:datetime()} |
		  {domain, binary()} |
		  {path, binary()} |
		  {secure, true | false} |
		  {http_only, true | false}

The default session store is the axiom_session_ets module. You can use your own by adding a store tuple to the sessions tuple:

{sessions, [{store, my_session_store, []}]}

For implementation details take a look into the axiom_session_ets module.

Filters

The functions before_filter/1 and after_filter/1 can be implemented to deal with the cowboy_req:req() before and after YourHandler:handle/3. When implemented, these are called no matter which handle function matches the request.

In your handler module:

before_filter(Req) ->
	% do stuff
	Req.

after_filter(Req) ->
	% do more stuff
	Req.

Errors

Not Found

To overwrite Axiom's response to 404 errors, just create a catch-all handler:

handle(_Method, _Path, _Req) ->
	{404, <<"nope.">>}.

Note that you have to take care of the status code yourself, as otherwise the default of 200 is sent back to the client.

Internal Server Error

To handle these yourself, you can implement a function named error/1. The argument is the cowboy_req:req() object, otherwise it works like your Handler:handle/3 function.

Streaming

To send a chunked reply, call axiom:chunk/2 for each chunk:

chunk(Data::iodata(), Req::cowboy_req:req()) -> {ok, Req1::cowboy_req:req()}.

The returned cowboy_req:req() object has to be given as an argument to subsequent calls to chunk and as the return value of your Handler:handle/3 function.

To stream data with a Content-Type other than text/html, use chunk/3, which has an additional parameter, to be set to the type you want:

chunk(Data::iodata(), Req::cowboy_req:req(), Type::binary()) -> {ok, Req1::cowboy_req:req()}.

Example

handle(<<"GET">>, [<<"stream">>], Req) ->
	{ok, Req1} = axiom:chunk(<<"Hello">>, Req, <<"text/plain">>),
	{ok, Req2} = axiom:chunk(<<" world">>, Req1),
	{ok, Req3} = axiom:chunk(<<"!">>, Req2),
	Req3.

Installation

To use it in your OTP application, add this to your rebar.config:

{lib_dirs, ["deps"]}.
{deps, [
	{'axiom', "0.1.0", {git, "git://github.com/tsujigiri/axiom.git", {tag, "v0.1.0"}}}
]}.

then, as usual:

rebar get-deps
rebar compile

License

Please take a look at the LICENSE file! (tl;dr: it's the MIT License)

axiom's People

Contributors

bneil avatar couchemar avatar erszcz avatar mtornwall avatar piotr-sobczyk avatar swsch avatar tsujigiri avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

axiom's Issues

Upgrade Cowboy

After Cowboy 0.6.1 Cowboy's http_req record was made opaque and can not be used directly anymore. I didn't really have the time yet to look into it, but it looks like we just need to remove all references to the record from the documentation and add directions on how to access cowboy_req:req() via the cowboy_req functions.

See ninenines/cowboy#266 and ninenines/cowboy#267.

Require newer mimetypes (at least d915b9f441) in dependencies

Axiom project doesn't rebar compile in my Windows environment, I got the following error:

=INFO REPORT==== 22-Feb-2013::23:06:09 ===
application: mimetypes
exited: {shutdown,{mimetypes_app,start,[normal,[]]}}
type: temporary
escript: exception error: no match of right hand side value
{error,{shutdown,{mimetypes_app,start,[normal,[]]}}}

The problem is described here: erlangpack/mimetypes#10 and here: ChicagoBoss/ChicagoBoss#129 .

It's fixed here: erlangpack/mimetypes@d915b9f .

Raw URL

I really like Axiom. However I think there should be a way to specify that I do not want to split the URL:

handle(<<"POST">>, _, Req) ->
    {<<"/", Url/binary>>, _} = cowboy_req:path(Req),

For example check how Elli works if I use raw_instead of path:

 handle(Req#req.method, elli_request:raw_path(Req), Req).
handle('GET', <<"/", RandomUrl/binary>>, _Req) ->

Check these real world examples that I implemented with Axiom and With elli. I am creating this examples to show devs that they should not fear Erlang and that they will learn a lot:

Correct way of running Axiom application as a OTP application

How can I convert "hello world' application from "Getting started" section to OTP project that is generated by rebar?

When I create invoke rebar create-app I end up with some skeleton files in src directory, like myapp.app.src, myapp_app.erl and myapp_sup.erl. Where is the correct place to add axiom code. Should "Hello world" application be implemented inside myapp_app.erl, like:

...

%% ===================================================================
%% Application callbacks
%% ===================================================================

start(_StartType, _StartArgs) ->
    myapp_sup:start_link(),
    axiom:start(?MODULE).

handle(<<"GET">>, [<<"hi">>], _Request) ->
    <<"Hello world!">>.

Is this the correct place? If not, what's the correct place to add axiom code and how to invoke it?

How to run my application in this approach? It was extremely hard for my to find so easy examples in web :/.

Race condition in streaming test

There is a test randomly failing on Travis. On my machine I can fix it by adding some sleep. But that is ugly and doesn't fix it on Travis.

The test is here.

Remove response record

Cowboy has its own set_resp_body and set_resp_header functions. Additionally, arbitrary data can be stored under the meta key of cowboy_req:req(). I think it is better to make use of that and only have one data structure representing the request, including the response.

cookie sessions

Hi!

I recently spawned cookie session projects and wonder would it be feasible to have either inside axiom or as a external helper?

In either event, feedback is welcome.

TIA,
--Vladimir Dronnikov

Maintaner Wanted!

As some of you may have noticed, there hasn't been much going on in this repository for a while. At the same time there are a bunch of forks and dependencies to update. If anyone would be interested in maintaining the project in the future, let me know!

Development Reload

Hi,

I've been playing with Cowboy for a while. One of the annoying things is I have to reload Cowboy server after each code change.

Is it the same in axiom?

Thanks,
-- buriwoy

Error with rebar axiom deps

Hello on MS Windows I get the following error:

c:\erl_apps\myaxion>rebar get-deps
==> myaxion (get-deps)
ERROR: Dependency dir c:/erl_apps/myaxion/deps/axiom failed application validation with reason:
{missing_app_file,"c:/erl_apps/myaxion/deps/axiom"}.

c:\erl_apps\myaxion>rebar get-deps
==> myaxion (get-deps)
Pulling axiom from {git,"git://github.com/tsujigiri/axiom.git",{tag,"0.0.17"}}
Cloning into axiom...
error: pathspec '0.0.17' did not match any file(s) known to git.
ERROR: cmd /q /c git checkout -q 0.0.17 failed with error: 1 and output:
error: pathspec '0.0.17' did not match any file(s) known to git.

after removing the axiom version tag in the rebar.config file like this:

{lib_dirs, ["deps"]}.
{deps, [
    {'axiom', "0.0.17", {git, "git://github.com/tsujigiri/axiom.git"}}
]}.

then everything works fine:

c:\erl_apps\myaxion>rebar get-deps
==> myaxion (get-deps)
Pulling axiom from {git,"git://github.com/tsujigiri/axiom.git"}
Cloning into axiom...
==> Entering directory `c:/erl_apps/myaxion/deps/axiom'
==> axiom (get-deps)
Pulling cowboy from {git,"git://github.com/extend/cowboy.git",{tag,"0.6.1"}}
Cloning into cowboy...
Pulling erlydtl from {git,"git://github.com/evanmiller/erlydtl.git","dda4db0"}
Cloning into erlydtl...
Pulling mimetypes from {git,"git://github.com/spawngrid/mimetypes.git",
                            {tag,"1.0"}}
Cloning into mimetypes...
==> Entering directory `c:/erl_apps/myaxion/deps/cowboy'
==> cowboy (get-deps)
==> Leaving directory `c:/erl_apps/myaxion/deps/cowboy'
==> Entering directory `c:/erl_apps/myaxion/deps/erlydtl'
==> erlydtl (get-deps)
==> Leaving directory `c:/erl_apps/myaxion/deps/erlydtl'
==> Entering directory `c:/erl_apps/myaxion/deps/mimetypes'
==> mimetypes (get-deps)
==> Leaving directory `c:/erl_apps/myaxion/deps/mimetypes'
==> Leaving directory `c:/erl_apps/myaxion/deps/axiom'
c:\erl_apps\myaxion>

No way to expose static resources directly under ./public

Both documentation and code prove that only files in subdirectories of ./public directory can be accessed as a static resources. I really need to be able to expose files directly under ./public directory (or at least access file in subdirectory of public but using url like host:port/static_filename).

Is it on purpose/by design that files under ./public cannot be accessed or is it a bug?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.