Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [2.3.2] (2025-09-17)

### Changed

* Code janitoring to be compliant with elixir 1.18

## [2.3.2] (2023-08-04)

### Changed
Expand Down
57 changes: 40 additions & 17 deletions lib/ewebmachine/builder.handlers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,39 +5,39 @@ defmodule Ewebmachine.Builder.Handlers do
you an `:add_handler` local function plug which adds to the conn
the locally defined ewebmachine handlers (see `Ewebmachine.Handlers`).

So :
So :

- Construct your automate decision handler through multiple `:add_handler` plugs
- Pipe the plug `Ewebmachine.Plug.Run` to run the HTTP automate which
will call these handlers to take decisions.
will call these handlers to take decisions.
- Pipe the plug `Ewebmachine.Plug.Send` to send and halt any conn previsously passed
through an automate run.

To define handlers, use the following helpers :

- the handler specific macros (like `Ewebmachine.Builder.Handlers.resource_exists/1`)
- the macro `defh/2` to define any helpers, usefull for body
producing handlers or to have multiple function clauses
producing handlers or to have multiple function clauses
- in handler implementation `conn` and `state` binding are available
- the response of the handler implementation is wrapped, so that
returning `:my_response` is the same as returning `{:my_response,conn,state}`

Below a full example :

```
defmodule MyJSONApi do
defmodule MyJSONApi do
use Ewebmachine.Builder.Handlers
plug :cors
plug :add_handlers, init: %{}

content_types_provided do: ["application/json": :to_json]
defh to_json, do: Poison.encode!(state[:json_obj])

defp cors(conn,_), do:
defp cors(conn,_), do:
put_resp_header(conn,"Access-Control-Allow-Origin","*")
end

defmodule GetUser do
defmodule GetUser do
use Ewebmachine.Builder.Handlers
plug MyJSONApi
plug :add_handlers
Expand All @@ -46,7 +46,7 @@ defmodule Ewebmachine.Builder.Handlers do
resource_exists do:
pass( !is_nil(user=DB.User.get(conn.params["q"])), json_obj: user)
end
defmodule GetOrder do
defmodule GetOrder do
use Ewebmachine.Builder.Handlers
plug MyJSONApi
plug :add_handlers
Expand All @@ -58,7 +58,7 @@ defmodule Ewebmachine.Builder.Handlers do

defmodule API do
use Plug.Router
plug :match
plug :match
plug :dispatch

get "/get/user", do: GetUser.call(conn,[])
Expand Down Expand Up @@ -100,27 +100,50 @@ defmodule Ewebmachine.Builder.Handlers do
defp sig_to_sigwhen({name,_,_}), do: {name,[quote(do: _),quote(do: _)],true}

defp handler_quote(name,body,guard,conn_match,state_match) do
conn_match = case var_in_patterns?(conn_match, :conn) do
true -> conn_match
false -> quote(do: unquote(conn_match) = var!(conn))
end

state_match = case var_in_patterns?(state_match, :state) do
true -> state_match
false -> quote(do: unquote(state_match) = var!(state))
end

quote do
@resource_handlers Map.put(@resource_handlers,unquote(name),__MODULE__)
def unquote(name)(unquote(conn_match)=var!(conn),unquote(state_match)=var!(state)) when unquote(guard) do
def unquote(name)(unquote(conn_match), unquote(state_match)) when unquote(guard) do
res = unquote(body)
wrap_response(res,var!(conn),var!(state))
end
end
end
end
defp handler_quote(name,body) do
handler_quote(name,body,true,quote(do: _),quote(do: _))
end

defp var_in_patterns?(ast, name) do
case ast do
{:=, meta, [pat1, pat2]} when is_list(meta) ->
var_in_patterns?(pat1, name) or var_in_patterns?(pat2, name)

{^name, meta, context} when is_list(meta) and is_atom(context) ->
true

_ ->
false
end
end

@doc """
define a resource handler function as described at
`Ewebmachine.Handlers`.

Since there is a specific macro in this module for each handler,
this macro is useful :
this macro is useful :

- to define body producing and body processing handlers (the one
referenced in the response of `Ewebmachine.Handlers.content_types_provided/2` or
referenced in the response of `Ewebmachine.Handlers.content_types_provided/2` or
`Ewebmachine.Handlers.content_types_accepted/2`)
- to explicitly take the `conn` and the `state` parameter, which
allows you to add guards and pattern matching for instance to
Expand All @@ -142,13 +165,13 @@ defmodule Ewebmachine.Builder.Handlers do
end

for resource_fun_name<-@resource_fun_names do
Module.eval_quoted(Ewebmachine.Builder.Handlers, quote do
Code.eval_quoted(quote do
@doc "see `Ewebmachine.Handlers.#{unquote(resource_fun_name)}/2`"
defmacro unquote(resource_fun_name)(do_block) do
name = unquote(resource_fun_name)
handler_quote(name,do_block[:do])
end
end)
end, [], __ENV__)
end

@doc false
Expand All @@ -171,7 +194,7 @@ defmodule Ewebmachine.Builder.Handlers do
{true,conn,%{id: "arnaud", current_user: %User{id: "arnaud"}}}
"""
defmacro pass(response,update_state) do
quote do
quote do
{unquote(response),var!(conn),Enum.into(unquote(update_state),var!(state))}
end
end
Expand Down
Loading
Loading