{"id":13835132,"url":"https://github.com/elixir-web/weber","last_synced_at":"2025-10-21T15:01:51.880Z","repository":{"id":62430750,"uuid":"12669962","full_name":"elixir-web/weber","owner":"elixir-web","description":"[WiP] Web framework for Elixir inspired by Rails [#WeberMVC at freenode]","archived":false,"fork":false,"pushed_at":"2016-05-20T12:42:09.000Z","size":1052,"stargazers_count":373,"open_issues_count":10,"forks_count":33,"subscribers_count":22,"default_branch":"master","last_synced_at":"2025-06-17T04:19:33.605Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://elixir-web.github.io/weber/","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/elixir-web.png","metadata":{"files":{"readme":"README.md","changelog":"Changelog.md","contributing":"Contributing.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-09-07T19:02:36.000Z","updated_at":"2025-05-13T05:44:41.000Z","dependencies_parsed_at":"2022-11-01T20:22:46.214Z","dependency_job_id":null,"html_url":"https://github.com/elixir-web/weber","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/elixir-web/weber","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-web%2Fweber","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-web%2Fweber/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-web%2Fweber/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-web%2Fweber/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/elixir-web","download_url":"https://codeload.github.com/elixir-web/weber/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/elixir-web%2Fweber/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264545167,"owners_count":23625404,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-04T14:00:57.108Z","updated_at":"2025-10-21T15:01:51.579Z","avatar_url":"https://github.com/elixir-web.png","language":"Elixir","readme":"Weber\n========\n\nWeber - is a MVC Web framework for [Elixir](http://elixir-lang.org/).\n\n[![Build Status](https://travis-ci.org/elixir-web/weber.svg?branch=master)](https://travis-ci.org/elixir-web/weber)\n\n## Join the Community\n\n[`#WeberMVC` on freenode IRC](http://webchat.freenode.net/?channels=%23webermvc\u0026uio=d4)\n\n[Mail listing](https://groups.google.com/forum/#!forum/webermvc)\n\n[![Build Status](https://travis-ci.org/0xAX/weber.png)](https://travis-ci.org/0xAX/weber)\n\n## Features\n\n * MVC web framework;\n * Project generation;\n * Json generation with exjson;\n * Websocket support;\n * HTML helpers;\n * Web controller Helpers.\n * i18n support;\n * Live code/templates update\n * Sessions support;\n * [weber-contrib](https://github.com/elixir-web/weber-contrib)\n\n## Quick start\n\n 1. Get and install Elixir from master.\n 2. Clone this repository.\n 3. Execute `make \u0026\u0026 make test` in the weber directory.\n 6. Create new project with: `mix weber.new /home/user/testWebApp`.\n\nNow go to the `/home/user/testWebApp` and execute there: `mix deps.get \u0026\u0026 mix compile --all --force`. Then you can try to run your testWeberApplication with:\n\n```\n./start.sh\n```\n\nor run it in daemon mode:\n\n```\n./start.sh --no-shell\n```\n\nand go to the [http://localhost:8080/](http://localhost:8080/)\n\nFor more details see in `examples` directory and Weber's [API](http://0xax.github.io/weber/public/docs/index.html).\n\n## Directory structure\n\n| Dir/File              | Description                                               |\n| --------------------- |:---------------------------------------------------------:|\n|    ./start.sh         | Startup script                                            |\n|    ./lib/controllers  | Directory with web controllers                            |\n|    ./lib/helpers      | Helper functions                                          |\n|    ./lib/models       | Directory for models (ecto)                               |\n|    ./lib/views        | Directory with EEx views                                  |\n|    ./lib/app.ex       | Application startup settings                              |\n|    ./lib/config.ex    | Configuration file.                                       |\n|    ./lib/route.ex     | File with routes declaration                              |\n|    ./public           | Directory for static files (css, js ....)                 |\n\n## Routing\n\nRouting declaration is in `route.ex` files:\n\n```elixir\n    route on(\"GET\", \"/\", :Simpletodo.Main, :action)\n       |\u003e on(\"POST\", \"/add/:note\", :Simpletodo.Main, :add)\n       |\u003e redirect(\"GET\", \"/redirect\", \"/weber\")\n       |\u003e on(\"ANY\", %r{/hello/([\\w]+)}, :Simpletodo.Main, :action)\n```\n\nAlso `on` supports following syntax:\n\n```elixir\n    route on(\"GET\", \"/\", \"Simpletodo.Main#action\")\n       |\u003e on(\"POST\", \"/add/:note\", \"Simpletodo.Main#add\")\n```\n\nIt is `route` macro which value is chain of `on` functions with 3 parametes:\n\n  * Http method\n  * Route path, can be binding (starts with ':' symbol);\n  * Module name of controller;\n  * Function name from this controller.\n\nHttp method can be:\n\n  * `\"GET\"`\n  * `\"POST\"`\n  * `\"PUT\"`\n  * `\"DELETE\"`\n  * `\"PATCH\"`\n  * `\"ANY\"`\n\nYou can set up resource in routing:\n\n```elixir\n    route resources(:Controller.Photos)\n```\n\nIt will be the same as\n\n```elxir\nroute on(\"GET\",    \"/controller/photos\",            :Controller.Photos, :index)\n   |\u003e on(\"GET\",    \"/controller/photos/new\",        :Controller.Photos, :new)\n   |\u003e on(\"POST\",   \"/controller/photos\",            :Controller.Photos, :create)\n   |\u003e on(\"GET\",    \"/controller/photos/:id,         :Controller.Photos, :show)\n   |\u003e on(\"GET\",    \"/controller/photos/:id/edit,    :Controller.Photos, :edit)\n   |\u003e on(\"PUT\",    \"/controller/photos/:id,         :Controller.Photos, :update)\n   |\u003e on(\"DELETE\", \"/controller/photos/:id,         :Controller.Photos, :destroy)\n```\n\n### Build url from code\n\nYou can build url from your `elixir` code with:\n\n```elixir\nimport Weber.Route\n\nroute on(\"GET\", \"/\", \"Simpletodo.Main#action\")\n   |\u003e on(\"POST\", \"/add/:note\", \"Simpletodo.Main#add\")\n\n# generates: /add/1\nlink(:Elixir.Simpletodo.Main, :add, [note: 1])\n```\n\n## Controllers\n\nEvery Weber's controller is just an elixir module, like:\n\n```elixir\ndefmodule Simpletodo.Main do\n\n  import Simplemodel\n\n  use Weber.Controller\n\n  layout false\n\n  def action(_, conn) do\n    {:render, [project: \"simpleTodo\"], []}\n  end\n\n  def add([body: body], conn) do\n    new(body)\n    {:json, [response: \"ok\"], [{\"Content-Type\", \"application/json\"}]}\n  end\n\nend\n```\n\nEvery controller's action passes 2 parameters:\n\n  * List of URL bindings\n  * [Plug.Conn](https://github.com/elixir-lang/plug) record\n\nController can return:\n\n  * `{:render, [project: \"simpleTodo\"], [{\"HttpHeaderName\", \"HttpHeaderValheaderVal\"}]}` - Renders views from `views/controller/action.html` and sends it to response;\n  * `{:render, [project: \"simpleTodo\"]}` - the same without headers;\n  * `{:render_inline, \"foo \u003c%= bar %\u003e\", [bar: \"baz\"]}}` - Renders inline template;\n  * `{:file, path, headers}` - Sends file in response;\n  * `{:file, path}` - the same without headers;\n  * `{:json, [response: \"ok\"], [{\"HttpHeaderName\", \"HttpHeaderValheaderVal\"}]}` - Weber converts keyword to json and sends it to response;\n  * `{:json, 200, [response: \"ok\"], [{\"HttpHeaderName\", \"HttpHeaderValheaderVal\"}]}` - Allows a custom status;\n  * `{:json, [response: \"ok\"]}` - the same without headers;\n  * `{:redirect, \"/main\"}` - Redirects to other resource;\n  * `{:text, status, data, headers}` - Sends plain text;\n  * `{:text, data, headers}` - the same without status;\n  * `{:text, data}` - the same without headers;\n  * `{:nothing, [\"Cache-Control\", \"no-cache\"]}` - Sends empty response with status `200` and headers;\n  * `{:nothing, [\"Cache-Control\", \"no-cache\"], http_status :: integer}` - Sends empty response with custom status.\n\nControllers can also raise at any point in the action and immediately render a response:\n```elixir\ndefmodule Simpletodo.Main do\n  import Simplemodel\n\n  # Add :unauthorized to list of known responses\n  render_when_raise :unauthorized, {:text, 401, \"Action prohibited.\", []}\n\n  def action([user_id: user_id], conn) do\n    if unauthorized_user_id?(user_id) do\n      # Immediately render the known response\n      raise_and_render :unauthorized\n    end\n    {:render, [project: \"simpleTodo\"], []}\n  end\nend\n```\n\n* `render_when_raise(value, response)` - macro that adds to the known responses to render if specific value is raised\n* `raise_and_render(value)` - raises a WeberControllerException and renders a response based on the known responses\n\n## Request params\n\nSometimes 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).\n\n```elixir\ndefmodule Simplechat.Main.Login do\n\n  import Weber.Http.Params\n\n  use Weber.Controller\n\n  layout false\n\n  def render_login([], conn) do\n    # get body request\n    body = get_body(conn)\n    #\n    # Do something with param\n    #\n    {:render, [project: \"SimpleChat\"]}\n  end\n\nend\n```\n\nIf 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:\n\n```elixir\ndefmodule Simplechat.Main.Login do\n\n  import Weber.Http.Params\n\n  use Weber.Controller\n\n  def render_login([], conn) do\n    name = param(:name, conn)\n    #\n    # Do something with param\n    #\n    {:render, [project: \"SimpleChat\", name: name]}\n  end\n\nend\n```\n\nYou can find the full API at the [wiki](https://github.com/0xAX/weber/wiki/Weber.Http.Params-API).\n\n## Before/After request hooks\n\nYou can define `__before__` or after `__after__` hooks in your controller. It will pass two parameters:\n\n  * `:action` - action name\n  * `conn` - connection parameter\n\n```elixir\ndefmodule Simplechat.Main.Login do\n\n  def render_login([], conn) do\n    {:render, [project: \"SimpleChat\", name: \"WeberChat\"]}\n  end\n\n  #\n  # Executes before request\n  #\n  def __before__(:render_login, conn) do\n    conn\n  end\n\n  #\n  # Execute after response\n  #\n  def __after__(:render_login, conn) do\n    conn\n  end\n\nend\n```\n\n## Helper\n\n### Html Helper\nHtml helpers helps to generate html templates from elixir:\n\n```elixir\ndefmodule Simpletodo.Helper.MyHelper\n  import Weber.Helper.Html\n\n  # Generates \u003cp\u003etest\u003c/p\u003e\n  def do_something do\n    tag(:p, \"test\")\n  end\n\n  # Generates \u003cp class=\"class_test\"\u003etest\u003c/p\u003e\n  def do_something do\n    tag(:p, \"test\", [class: \"class_test\"])\n  end\n\n  # Generates \u003cimg src=\"path/to/file\"\u003e\n  def do_something do\n    tag(:img, [src: \"path/to/file\"])\n  end\nend\n```\n\nTags with blocks\n\n```elixir\ndefmodule Simpletodo.Helper.MyHelper\n  import Weber.Helper.Html\n\n  # Generates \u003cdiv id=\"test\"\u003e\u003cp\u003etest\u003c/p\u003e\u003c/div\u003e\n  def do_something do\n    tag(:div, [id: \"test\"]) do\n      tag(:p, \"test\")\n    end\n  end\nend\n```\n\n### Partials\n\nInclude html partials to the your template with:\n\n```html\n\u003c%= render \"Partial\", [test: \"Hello\"] %\u003e\n```\n\nYou must have `\"your_project_name/lib/views/partials/Partial.html\"` with:\n\n```html\n\u003c%= @test %\u003e\n```\n\n### Resource Helpers\n\nYou can include your static resources like `javascript`, `css`, `favicon` or `image` files with resource helpers:\n\n```elixir\n#\n# Generates: \u003cscript type=\"text/javascript\" src=\"/static/test.js\"\u003e\u003c/script\u003e\nscript(\"/static/test.js\")\n# If no value is passed for src it defaults to \"/public/js/application.js\"\nscript()\n\n#\n# Generates: \u003clink href=\"/static/test.css\" rel=\"stylesheet\" media=\"screen\"\u003e\n#\nstyle(\"/static/test.css\")\n# If no value is passed for href it defaults to \"/public/css/application.css\"\nstyle()\n\n#\n# Generates: \u003clink href=\"/public/img/favicon.ico\" rel=\"shortcut icon\" type=\"image/png\"\u003e\nfavicon(\"/public/img/favicon.ico\")\n# If no value is passed for href it defaults to \"/public/img/favicon.ico\"\nfavicon()\n\n#\n# Generates: \u003cimg src=\"/public/img/example.jpg\" alt=\"Image\" class=\"some-class\" height=\"100\" width=\"100\"\u003e\"\nimage(\"/public/img/example.jpg\", [alt: \"Image\", class: \"some-class\", height: 100, width: 100])\n\n#\n# Generates: \u003caudio src=\"/public/audio/sound\"\u003e\naudio(\"/public/audio/sound\")\n\n#\n# Generates \u003clink href=\"my.rss\" type=\"application/atom+xml\" title=\"My feed\"\u003e\natom(\"my.atom\", \"My feed\")\n\n#\n# Generates \u003clink href=\"my.rss\" type=\"application/rss+xml\" title=\"My feed\"\u003e\nrss(\"my.rss\", \"My feed\")\n\n#\n# Generates:\n#  \u003caudio autoplay=\"autoplay\"\u003e\n#    \u003csouce src=\"/public/audio/sound1\"\u003e\u003c/souce\u003e\n#    \u003csouce src=\"/public/audio/sound2\"\u003e\u003c/souce\u003e\n#  \u003c/audio\u003e\n#\naudio([\"/public/audio/sound1\", \"/public/audio/sound2\"], [autoplay: true])\n\n#\n# Generates: \u003cvideo src=\"public/videos/trailer\"\u003e\nvideo(\"public/videos/trailer\")\n\n#\n# Generates:\n#  \u003cvideo height=\"48\" width=\"48\"\u003e\n#    \u003csouce src=\"/public/videos/video1\"\u003e\u003c/souce\u003e\n#    \u003csouce src=\"/public/videos/video2\"\u003e\u003c/souce\u003e\n#  \u003c/video\u003e\nvideo([\"/public/videos/video1\", \"/public/videos/video2\"], [height: 48, width: 48])\n```\n\n## Controller Helpers\n\n#### `content_for_layout` and `layout`\n\n**NOTE: Now all `views` and `layout` files must start with capital letter.**\n\nAll controllers got `main.html` by default for views, but you'd might change it.\n\nYou can create custom `layout` for you controller:\n\nCreate `Layout.html` in the `lib/views/layouts` directory and put there:\n\n```HTML\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003ctitle\u003e\n      My Project\n    \u003c/title\u003e\n    \u003cmeta http-equiv=\"content-type\" content=\"text/html;charset=utf-8\" /\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003cdiv id=\"container\"\u003e\n    \u003c%= @content_for_layout %\u003e\n    \u003c/div\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\nThan declare `layout` helper in your controller:\n\n```elixir\ndefmodule TestController.Main do\n\n  use Weber.Controller\n\n  layout \"Layout.html\"\n\n  #\n  # Here are some actions\n  #\n\nend\n\n```\n\nAnd you have `lib/views/Main.html` with:\n\n```\nHello World!\n```\n\nWeber puts `lib/views/Main.html` content inside `\u003c%= content_for_layout %\u003e ` and renders\nit in the response.\n\n## Logging\n\nWeber uses [exlager](https://github.com/khia/exlager) for the logging. For using it just set up:\n\n```elixir\nlog: true\n```\n\nin your config and use it:\n\n```elixir\ndefmodule LogTest.Main do\n\n  require Lager\n\n  def action([], _conn) do\n    Lager.info \"New request\"\n    {:render, []}\n  end\n\nend\n```\n\n## Internationalization\n\n**Important** Experemental now\n\nSee - [Weber Internationalization](https://github.com/0xAX/weber/tree/master/lib/weber/i18n#weber-i18n)\n\n```\n{\n  \"HELLO_STR\" : \"Hello, It is weber framework!\",\n  \"FRAMEWORK_DESCRIPTION\" : \"Weber - is a MVC Web framework for Elixir.\"\n}\n```\n\nand you can use it like:\n\n```html\n\u003cspan\u003e\u003c%= t(@conn, \"HELLO_STR\") %\u003e\u003c/span\u003e\n```\n\nin your html template.\n\n## Websocket\n\nYou can handle websocket connection and incoming/outcoming websocket message in your controllers.\n\nFirst of all you need to designate websocket controller in your `config.ex` file in `webserver:` section, like:\n\n```elixir\nws:\n  [\n   ws_mod: :Handler\n  ]\n```\n\nAfter it you must implement 3 callbacks in your controller like this:\n\n```elixir\ndefmodule Simplechat.Main.Chat do\n\n  def websocket_init(pid, conn) do\n    #\n    # new websocket connection init\n    #\n  end\n\n  def websocket_message(pid, message, conn) do\n    #\n    # handle incoming message here\n    #\n  end\n\n  def websocket_terminate(pid, conn) do\n    #\n    # connection terminated\n    #\n  end\n\nend\n```\n\nAll websocket connections are must start with prefix `/_ws/`.\n\n## Session\n\n[Session API](https://github.com/elixir-web/weber/wiki/Weber.Session-API)\n\n## Testing requests\n\nCurrently, 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)\n\nThis 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.\n\n## Mix tasks\n\n### Create new project\n\n```\nmix weber.new /home/user/projectName\n```\n\n### Version\n\n```\nmix weber --version\n```\n\n### Help\n\n```\nmix weber --help\n```\n\n### Print all current routes\n\n```\nmix weber.routes\n```\n\n## Dependencies\n\n  * [cowboy](https://github.com/ninenines/cowboy)\n  * [ecto](https://github.com/elixir-lang/ecto)\n  * [postgrex](https://github.com/ericmj/postgrex)\n  * [exjson](https://github.com/guedes/exjson)\n  * [plug](https://github.com/elixir-lang/plug)\n  * [exlager](https://github.com/khia/exlager)\n\n## Contributing\n\nSee [Contributing.md](https://github.com/0xAX/weber/blob/master/Contributing.md)\n\n## Additional info\n\n  * Introduction to the Weber - [Weber](http://0xax.blogspot.com/2013/12/weber-high-performance-web-framework.html)\n  * Weber example for Heroku - [heroku_weber_example](https://github.com/tsloughter/heroku_weber_example)\n  * A template for using Vagrant for developing Elixir applications with Weber - [vagrant-weber](https://github.com/slogsdon/vagrant-weber)\n  * [ElixirSips. Episode 035: Weber](http://elixirsips.com/episodes/035_weber.html)\n  * [ElixirSips. Weber, Part 2 - Performance](http://elixirsips.com/episodes/036_weber_part_2.html)\n\n## Author\n\n[@0xAX](https://twitter.com/0xAX).\n","funding_links":[],"categories":["Elixir","Frameworks"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-web%2Fweber","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Felixir-web%2Fweber","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Felixir-web%2Fweber/lists"}