Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/elixir-web/weber
[WiP] Web framework for Elixir inspired by Rails [#WeberMVC at freenode]
https://github.com/elixir-web/weber
Last synced: about 2 months ago
JSON representation
[WiP] Web framework for Elixir inspired by Rails [#WeberMVC at freenode]
- Host: GitHub
- URL: https://github.com/elixir-web/weber
- Owner: elixir-web
- License: mit
- Created: 2013-09-07T19:02:36.000Z (about 11 years ago)
- Default Branch: master
- Last Pushed: 2016-05-20T12:42:09.000Z (over 8 years ago)
- Last Synced: 2024-07-29T00:02:59.098Z (about 2 months ago)
- Language: Elixir
- Homepage: http://elixir-web.github.io/weber/
- Size: 1 MB
- Stars: 369
- Watchers: 23
- Forks: 33
- Open Issues: 10
-
Metadata Files:
- Readme: README.md
- Changelog: Changelog.md
- Contributing: Contributing.md
- License: LICENSE
Awesome Lists containing this project
README
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/elixir-web/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.Routeroute 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 doimport Simplemodel
use Weber.Controller
layout false
def action(_, conn) do
{:render, [project: "simpleTodo"], []}
enddef add([body: body], conn) do
new(body)
{:json, [response: "ok"], [{"Content-Type", "application/json"}]}
endend
```Every controller's action passes 2 parameters:
* List of URL bindings
* [Plug.Conn](https://github.com/elixir-lang/plug) recordController 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, status, data, headers}` - Sends plain text;
* `{:text, data, headers}` - the same without status;
* `{: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.Controllers can also raise at any point in the action and immediately render a response:
```elixir
defmodule Simpletodo.Main do
import Simplemodel# Add :unauthorized to list of known responses
render_when_raise :unauthorized, {:text, 401, "Action prohibited.", []}def action([user_id: user_id], conn) do
if unauthorized_user_id?(user_id) do
# Immediately render the known response
raise_and_render :unauthorized
end
{:render, [project: "simpleTodo"], []}
end
end
```* `render_when_raise(value, response)` - macro that adds to the known responses to render if specific value is raised
* `raise_and_render(value)` - raises a WeberControllerException and renders a response based on the known responses## 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/elixir-web/weber/wiki/Weber.Http.Params-API).
```elixir
defmodule Simplechat.Main.Login doimport 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"]}
endend
```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 doimport Weber.Http.Params
use Weber.Controller
def render_login([], conn) do
name = param(:name, conn)
#
# Do something with param
#
{:render, [project: "SimpleChat", name: name]}
endend
```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 dodef 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
endend
```## Helper
### Html Helper
Html helpers helps to generate html templates from elixir:```elixir
defmodule Simpletodo.Helper.MyHelper
import Weber.Helper.Html# Generates
test
def do_something do
tag(:p, "test")
end# Generates
test
def do_something do
tag(:p, "test", [class: "class_test"])
end# Generates
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
test
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("/static/test.js")
# If no value is passed for src it defaults to "/public/js/application.js"
script()#
# Generates:
#
style("/static/test.css")
# If no value is passed for href it defaults to "/public/css/application.css"
style()#
# Generates:
favicon("/public/img/favicon.ico")
# If no value is passed for href it defaults to "/public/img/favicon.ico"
favicon()#
# Generates: "
image("/public/img/example.jpg", [alt: "Image", class: "some-class", height: 100, width: 100])#
# Generates:
audio("/public/audio/sound")#
# Generates
atom("my.atom", "My feed")#
# Generates
rss("my.rss", "My feed")#
# Generates:
#
#
#
#
#
audio(["/public/audio/sound1", "/public/audio/sound2"], [autoplay: true])#
# Generates:
video("public/videos/trailer")#
# Generates:
#
#
#
#
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
My Project
<%= @content_for_layout %>
```
Than declare `layout` helper in your controller:
```elixir
defmodule TestController.Main douse 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 dorequire Lager
def action([], _conn) do
Lager.info "New request"
{:render, []}
endend
```## 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
<%= t(@conn, "HELLO_STR") %>
```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 dodef websocket_init(pid, conn) do
#
# new websocket connection init
#
enddef websocket_message(pid, message, conn) do
#
# handle incoming message here
#
enddef websocket_terminate(pid, conn) do
#
# connection terminated
#
endend
```All websocket connections are must start with prefix `/_ws/`.
## Session
[Session API](https://github.com/elixir-web/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/ninenines/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).