Ewebmachine is a very simple Elixir DSL around Webmachine from basho : https://github.com/basho/webmachine
Resources module are grouped into a module which has to use Ewebmachine.
defmodule MyApp1 do
use Ewebmachine
resource ['hello',:name] do
to_html do:
"""
<html>
<body>
<h1> Hello #{:wrq.path_info(:name,_req)} </h1>
</body>
</html>
"""
end
Each "resource" declares a webmachine resource module. The resource takes the route as a parameter, so that MyApp1.routes returns the list of webmachine dispatch rules corresponding to the resources declared in MyApp1.
Ewebmachine.Sup is a default supervisor for web application, it
launches mochiweb configured to use webmachine with the following
configuration options (start_link
dictlist parameter):
- listen ip : default to "0.0.0.0"
- listen port : default to 7272
- log_dir : default to "priv/log"
- modules : lists the ewebmachine module routes to be include, mandatory
An initial state (which is used in webmachine "init" function)
can be declared with ini
, the default one is a list (because of the resource
function response shortut described below)
resource [] do
ini [:init]
end
The trace mode is activated for every resources when executed in
Mix dev environment. Traces are stored in directory defined by
{:webmachine,:trace_dir}
if defined, else in /tmp
.
Default Supervisor add the /debug
route to access the
webmachine traces in dev environment.
Resource functions can be declared directly by name,
body-producing function must start with to_*
or
from_*
. Every resource functions are declared without
ReqData and Context parameters declaration, which are implicitly
declared as variable \_req
and _ctx
.
The resource functions response is wrap so that you replace the standard {res,req,ctx} webmachine response by :
res
is a shortcut to{res,_req,_ctx}
pass(res, opt1: value1, opt2: value2)
is a shortcut to{res,_req,updated_ctx}
whereupdated_ctx
is the listdict merge between old listdict state and keywords arguments (opt1,opt2 here), works only if_ctx
is a dictlist
So for instance, the following resource functions are equivalent :
resource_exists do: true
resource_exists do: {true,_req,_ctx}
And you can transmit some variable in the context like this :
resource ['user',:name] do
resource_exists do
user = User.get(:wrq.path_info(:name,_req))
pass user != nil, user: user
end
to_html do: (_ctx[:user] |> template("user_template"))
end
Declare two ewebmachine module :
defmodule WebMain do
use Ewebmachine
resource [] do
to_html do: "<html><body>Hello world</body>"
end
resource['sitemap'] do
content_types_provided do: [{'application/xml',:to_xml}]
to_xml do
"""
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://mon-domaine.fr/</loc>
<lastmod>2012-12-15</lastmod>
<changefreq>daily</changefreq>
<priority>1</priority>
</url>
</urlset>
"""
end
end
end
defmodule WebContact do
use Ewebmachine
resource ['contact'] do
to_html do: "<html><body>contact page</body>"
end
end
Then to launch the webserver, add the supervisor in your App tree with the Ewebmachine.Sup.start_link functions. You can configure here the port/ip and the ewebmachine modules to be included.
defmodule MySup do
use Supervisor.Behaviour
def start_link, do: :supervisor.start_link({:local,__MODULE__},__MODULE__,[])
def init([]) do
supervise([
supervisor(Ewebmachine.Sup,[[modules: [WebMain,WebContact],port: 8080]]),
supervisor(MyOtherSup,[])
], strategy: :one_for_one)
end
end
As configuration of Ewebmachine.Sup
is passed "as is" to
webmachine_mochiweb
then to mochiweb_socket
, you can configure your entire
configuration as you wish when launching the supervisors.
Below an example configuration : listen HTTP on 0.0.0.0:80 and ::80 with a redirect handler => https://samehost/samepath then listen HTTPS on 0.0.0.0:443 and ::443 with applications handlers.
defmodule SSLRedirect do
use Ewebmachine
resource [:*] do
resource_exists do: false
previously_existed do: true
moved_permanently do:
{true,'https://#{hostname(_req)}#{:wrq.raw_path(_req)}'}
defp hostname(req), do:
"#{:wrq.get_req_header('host',req)}"|>String.split(":")|>Enum.at(0)
end
end
defmodule MySup do
use Supervisor.Behaviour
def start_link, do: :supervisor.start_link({:local,__MODULE__},__MODULE__,[])
def init([]) do
http = [{"0.0.0.0",80},{"::",80}]
https = [{"0.0.0.0","/etc/certs/my.crt","/etc/certs/my.pem"},{"::","/etc/certs/my.crt","/etc/certs/my.pem"}]
supervise(
(http|>Enum.map(fn {ip,port}->
supervisor(Ewebmachine.Sup,[[modules: [SSLRedirect],ip: '#{ip}', port: port ]],id: :"web#{inspect ip}#{port}")
end))++
(https|>Enum.map(fn {ip,cert,key}->
supervisor(Ewebmachine.Sup,[[modules: [WebMain,WebContact],ip: '#{ip}', port: 443, ssl: true, ssl_opts: [certfile: '#{cert}', keyfile: '#{key}']]],id: :"web#{inspect ip}443")
end))
], strategy: :one_for_all)
end
end