README.md

Weber
========

Weber - is a MVC Web framework for [Elixir](http://elixir-lang.org/).

[![Build Status](https://travis-ci.org/elixir-web/weber.svg?branch=master)](https://travis-ci.org/elixir-web/weber)

## Join the Community

[`#WeberMVC` on freenode IRC](http://webchat.freenode.net/?channels=%23webermvc&uio=d4)

[Mail listing](https://groups.google.com/forum/#!forum/webermvc)

[![Build Status](https://travis-ci.org/0xAX/weber.png)](https://travis-ci.org/0xAX/weber)

## Features

 * MVC web framework;
 * Project generation;
 * Json generation with exjson;
 * Websocket support;
 * HTML helpers;
 * Web controller Helpers.
 * i18n support;
 * Live code/templates update
 * Sessions support;
 * [weber-contrib](https://github.com/0xAX/weber-contrib)

## Quick start

 1. Get and install Elixir from master.
 2. Clone this repository.
 3. Execute `make && make test` in the weber directory.
 6. Create new project with: `mix weber.new /home/user/testWebApp`.

Now go to the `/home/user/testWebApp` and execute there: `mix deps.get && mix compile --all --force`. Then you can try to run your testWeberApplication with:

```
./start.sh
```

or run it in daemon mode:

```
./start.sh --no-shell
```

and go to the [http://localhost:8080/](http://localhost:8080/)

For more details see in `examples` directory and Weber's [API](http://0xax.github.io/weber/public/docs/index.html).

## Directory structure

| Dir/File              | Description                                               |
| --------------------- |:---------------------------------------------------------:|
|    ./start.sh         | Startup script                                            |
|    ./lib/controllers  | Directory with web controllers                            |
|    ./lib/helpers      | Helper functions                                          |
|    ./lib/models       | Directory for models (ecto)                               |
|    ./lib/views        | Directory with EEx views                                  |
|    ./lib/app.ex       | Application startup settings                              |
|    ./lib/config.ex    | Configuration file.                                       |
|    ./lib/route.ex     | File with routes declaration                              |
|    ./public           | Directory for static files (css, js ....)                 |

## Routing

Routing declaration is in `route.ex` files:

```elixir
    route on("GET", "/", :Simpletodo.Main, :action)
       |> on("POST", "/add/:note", :Simpletodo.Main, :add)
       |> redirect("GET", "/redirect", "/weber")
       |> on("ANY", %r{/hello/([\w]+)}, :Simpletodo.Main, :action)
```

Also `on` supports following syntax:

```elixir
    route on("GET", "/", "Simpletodo.Main#action")
       |> on("POST", "/add/:note", "Simpletodo.Main#add")
```

It is `route` macro which value is chain of `on` functions with 3 parametes:

  * Http method
  * Route path, can be binding (starts with ':' symbol);
  * Module name of controller;
  * Function name from this controller.

Http method can be:

  * `"GET"`
  * `"POST"`
  * `"PUT"`
  * `"DELETE"`
  * `"PATCH"`
  * `"ANY"`

You can set up resource in routing:

```elixir
    route resources(:Controller.Photos)
```

It will be the same as

```elxir
route on("GET",    "/controller/photos",            :Controller.Photos, :index)
   |> on("GET",    "/controller/photos/new",        :Controller.Photos, :new)
   |> on("POST",   "/controller/photos",            :Controller.Photos, :create)
   |> on("GET",    "/controller/photos/:id,         :Controller.Photos, :show)
   |> on("GET",    "/controller/photos/:id/edit,    :Controller.Photos, :edit)
   |> on("PUT",    "/controller/photos/:id,         :Controller.Photos, :update)
   |> on("DELETE", "/controller/photos/:id,         :Controller.Photos, :destroy)
```

### Build url from code

You can build url from your `elixir` code with:

```elixir
import Weber.Route

route on("GET", "/", "Simpletodo.Main#action")
   |> on("POST", "/add/:note", "Simpletodo.Main#add")

# generates: /add/1
link(:Elixir.Simpletodo.Main, :add, [note: 1])
```

## Controllers

Every Weber's controller is just an elixir module, like:

```elixir
defmodule Simpletodo.Main do

  import Simplemodel

  use Weber.Controller

  layout false

  def action(_, conn) do
    {:render, [project: "simpleTodo"], []}
  end

  def add([body: body], conn) do
    new(body)
    {:json, [response: "ok"], [{"Content-Type", "application/json"}]}
  end

end
```

Every controller's action passes 2 parameters:

  * List of URL bindings
  * [Plug.Conn](https://github.com/elixir-lang/plug) record

Controller can return:

  * `{:render, [project: "simpleTodo"], [{"HttpHeaderName", "HttpHeaderValheaderVal"}]}` - Renders views from `views/controller/action.html` and sends it to response;
  * `{:render, [project: "simpleTodo"]}` - the same without headers;
  * `{:render_inline, "foo <%= bar %>", [bar: "baz"]}}` - Renders inline template;
  * `{:file, path, headers}` - Sends file in response; 
  * `{:file, path}` - the same without headers;
  * `{:json, [response: "ok"], [{"HttpHeaderName", "HttpHeaderValheaderVal"}]}` - Weber converts keyword to json and sends it to response;
  * `{:json, 200, [response: "ok"], [{"HttpHeaderName", "HttpHeaderValheaderVal"}]}` - Allows a custom status;
  * `{:json, [response: "ok"]}` - the same without headers;
  * `{:redirect, "/main"}` - Redirects to other resource;
  * `{:text, data, headers}` - Sends plain text; 
  * `{:text, data}` - the same without headers;
  * `{:nothing, ["Cache-Control", "no-cache"]}` - Sends empty response with status `200` and headers;
  * `{:nothing, ["Cache-Control", "no-cache"], http_status :: integer}` - Sends empty response with custom status.

## Request params

Sometimes it is necessary for the request parameters in the controller. For this point can be used `Weber.Http.Params` [API](https://github.com/0xAX/weber/wiki/Weber.Http.Params-API).

```elixir
defmodule Simplechat.Main.Login do

  import Weber.Http.Params

  use Weber.Controller

  layout false

  def render_login([], conn) do
    # get body request
    body = get_body(conn)
    #
    # Do something with param
    #
    {:render, [project: "SimpleChat"]}
  end

end
```

If you need to get parameters from query string, it is easy to do with `param/1` API. For example you got request for: `/user?name=0xAX`, you can get `name` parameter's value with:

```elixir
defmodule Simplechat.Main.Login do

  import Weber.Http.Params

  use Weber.Controller

  def render_login([], conn) do
    name = param(:name, conn)
    #
    # Do something with param
    #
    {:render, [project: "SimpleChat", name: name]}
  end

end
```

You can find the full API at the [wiki](https://github.com/0xAX/weber/wiki/Weber.Http.Params-API).

## Before/After request hooks

You can define `__before__` or after `__after__` hooks in your controller. It will pass two parameters:

  * `:action` - action name
  * `conn` - connection parameter

```elixir
defmodule Simplechat.Main.Login do

  def render_login([], conn) do
    {:render, [project: "SimpleChat", name: "WeberChat"]}
  end

  #
  # Executes before request
  #
  def __before__(:render_login, conn) do
    conn
  end

  #
  # Execute after response
  #
  def __after__(:render_login, conn) do
    conn
  end

end
```

## Helper

### Html Helper
Html helpers helps to generate html templates from elixir:

```elixir
defmodule Simpletodo.Helper.MyHelper
  import Weber.Helper.Html

  # Generates <p>test</p>
  def do_something do
    tag(:p, "test")
  end

  # Generates <p class="class_test">test</p>
  def do_something do
    tag(:p, "test", [class: "class_test"])
  end

  # Generates <img src="path/to/file">
  def do_something do
    tag(:img, [src: "path/to/file"])
  end
end
```

Tags with blocks

```elixir
defmodule Simpletodo.Helper.MyHelper
  import Weber.Helper.Html

  # Generates <div id="test"><p>test</p></div>
  def do_something do
    tag(:div, [id: "test"]) do
      tag(:p, "test")
    end
  end
end
```

### Partials

Include html partials to the your template with:

```html
<%= render "Partial", [test: "Hello"] %>
```

You must have `"your_project_name/lib/views/partials/Partial.html"` with:

```html
<%= @test %>
```

### Resource Helpers

You can include your static resources like `javascript`, `css`, `favicon` or `image` files with resource helpers:

```elixir
#
# Generates: <script type="text/javascript" src="/static/test.js"></script>
script("/static/test.js")
# If no value is passed for src it defaults to "/public/js/application.js"
script()

#
# Generates: <link href="/static/test.css" rel="stylesheet" media="screen">
#
style("/static/test.css")
# If no value is passed for href it defaults to "/public/css/application.css"
style()

#
# Generates: <link href="/public/img/favicon.ico" rel="shortcut icon" type="image/png">
favicon("/public/img/favicon.ico")
# If no value is passed for href it defaults to "/public/img/favicon.ico"
favicon()

#
# Generates: <img src="/public/img/example.jpg" alt="Image" class="some-class" height="100" width="100">"
image("/public/img/example.jpg", [alt: "Image", class: "some-class", height: 100, width: 100])

#
# Generates: <audio src="/public/audio/sound">
audio("/public/audio/sound")

#
# Generates <link href="my.rss" type="application/atom+xml" title="My feed">
atom("my.atom", "My feed")

#
# Generates <link href="my.rss" type="application/rss+xml" title="My feed">
rss("my.rss", "My feed")

#
# Generates:
#  <audio autoplay="autoplay">
#    <souce src="/public/audio/sound1"></souce>
#    <souce src="/public/audio/sound2"></souce>
#  </audio>
#
audio(["/public/audio/sound1", "/public/audio/sound2"], [autoplay: true])

#
# Generates: <video src="public/videos/trailer">
video("public/videos/trailer")

#
# Generates:
#  <video height="48" width="48">
#    <souce src="/public/videos/video1"></souce>
#    <souce src="/public/videos/video2"></souce>
#  </video>
video(["/public/videos/video1", "/public/videos/video2"], [height: 48, width: 48])
```

## Controller Helpers

#### `content_for_layout` and `layout`

**NOTE: Now all `views` and `layout` files must start with capital letter.**

All controllers got `main.html` by default for views, but you'd might change it.

You can create custom `layout` for you controller:

Create `Layout.html` in the `lib/views/layouts` directory and put there:

```HTML
<!DOCTYPE html>
<html>
  <head>
    <title>
      My Project
    </title>
    <meta http-equiv="content-type" content="text/html;charset=utf-8" />
  </head>
  <body>
    <div id="container">
    <%= @content_for_layout %>
    </div>
  </body>
</html>
```

Than declare `layout` helper in your controller:

```elixir
defmodule TestController.Main do

  use Weber.Controller

  layout "Layout.html"

  #
  # Here are some actions
  #

end

```

And you have `lib/views/Main.html` with:

```
Hello World!
```

Weber puts `lib/views/Main.html` content inside `<%= content_for_layout %> ` and renders
it in the response.

## Logging

Weber uses [exlager](https://github.com/khia/exlager) for the logging. For using it just set up:

```elixir
log: true
```

in your config and use it:

```elixir
defmodule LogTest.Main do

  require Lager

  def action([], _conn) do
    Lager.info "New request"
    {:render, []}
  end

end
```

## Internationalization

**Important** Experemental now

See - [Weber Internationalization](https://github.com/0xAX/weber/tree/master/lib/weber/i18n#weber-i18n)

```
{
  "HELLO_STR" : "Hello, It is weber framework!",
  "FRAMEWORK_DESCRIPTION" : "Weber - is a MVC Web framework for Elixir."
}
```

and you can use it like:

```html
<span><%= t(@conn, "HELLO_STR") %></span>
```

in your html template.

## Websocket

You can handle websocket connection and incoming/outcoming websocket message in your controllers.

First of all you need to designate websocket controller in your `config.ex` file in `webserver:` section, like:

```elixir
ws:
  [
   ws_mod: :Handler
  ]
```

After it you must implement 3 callbacks in your controller like this:

```elixir
defmodule Simplechat.Main.Chat do

  def websocket_init(pid, conn) do
    #
    # new websocket connection init
    #
  end

  def websocket_message(pid, message, conn) do
    #
    # handle incoming message here
    #
  end

  def websocket_terminate(pid, conn) do
    #
    # connection terminated
    #
  end

end
```

All websocket connections are must start with prefix `/_ws/`.

## Session

[Session API](https://github.com/0xAX/weber/wiki/Weber.Session-API)

## Testing requests

Currently, one way to test requests is using `exunit` and the `hackney` http client as we do in [our own tests.] (https://github.com/0xAX/weber/blob/master/templates/default/test/response_test.exs)

This is not as convenient and expressive as more established frameworks like rspec for rails offer but we are planning to improve this in the future.

## Mix tasks

### Create new project

```
mix weber.new /home/user/projectName
```

### Version

```
mix weber --version
```

### Help

```
mix weber --help
```

### Print all current routes

```
mix weber.routes
```

## Dependencies

  * [cowboy](https://github.com/extend/cowboy)
  * [ecto](https://github.com/elixir-lang/ecto)
  * [postgrex](https://github.com/ericmj/postgrex)
  * [exjson](https://github.com/guedes/exjson)
  * [plug](https://github.com/elixir-lang/plug)
  * [exlager](https://github.com/khia/exlager)

## Contributing

See [Contributing.md](https://github.com/0xAX/weber/blob/master/Contributing.md)

## Additional info

  * Introduction to the Weber - [Weber](http://0xax.blogspot.com/2013/12/weber-high-performance-web-framework.html)
  * Weber example for Heroku - [heroku_weber_example](https://github.com/tsloughter/heroku_weber_example)
  * A template for using Vagrant for developing Elixir applications with Weber - [vagrant-weber](https://github.com/slogsdon/vagrant-weber)
  * [ElixirSips. Episode 035: Weber](http://elixirsips.com/episodes/035_weber.html)
  * [ElixirSips. Weber, Part 2 - Performance](http://elixirsips.com/episodes/036_weber_part_2.html)

## Author

[@0xAX](https://twitter.com/0xAX).