diff --git a/CHANGELOG.md b/CHANGELOG.md
index a019934..6eb426d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/lib/ewebmachine/builder.handlers.ex b/lib/ewebmachine/builder.handlers.ex
index 3a63974..833ad0b 100644
--- a/lib/ewebmachine/builder.handlers.ex
+++ b/lib/ewebmachine/builder.handlers.ex
@@ -5,19 +5,19 @@ 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}`
@@ -25,7 +25,7 @@ defmodule Ewebmachine.Builder.Handlers do
Below a full example :
```
- defmodule MyJSONApi do
+ defmodule MyJSONApi do
use Ewebmachine.Builder.Handlers
plug :cors
plug :add_handlers, init: %{}
@@ -33,11 +33,11 @@ defmodule Ewebmachine.Builder.Handlers do
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
@@ -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
@@ -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,[])
@@ -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
@@ -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
@@ -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
diff --git a/lib/ewebmachine/core.ex b/lib/ewebmachine/core.ex
index ee41c2a..5682fbd 100644
--- a/lib/ewebmachine/core.ex
+++ b/lib/ewebmachine/core.ex
@@ -25,7 +25,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 503)
end
end
-
+
## "see `v3b13/2`"
decision v3b13b(conn, state) do
{reply, conn, state} = resource_call(conn, state, :service_available)
@@ -35,7 +35,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 503)
end
end
-
+
## "Known method?"
decision v3b12(conn, state) do
{methods, conn, state} = resource_call(conn, state, :known_methods)
@@ -45,7 +45,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 501)
end
end
-
+
## "URI too long?"
decision v3b11(conn, state) do
{reply, conn, state} = resource_call(conn, state, :uri_too_long)
@@ -55,7 +55,7 @@ defmodule Ewebmachine.Core do
v3b10(conn, state)
end
end
-
+
## "Method allowed?"
decision v3b10(conn, state) do
{methods, conn, state} = resource_call(conn, state, :allowed_methods)
@@ -66,7 +66,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 405)
end
end
-
+
## "Content-MD5 present?"
decision v3b9(conn, state) do
if get_header_val(conn, "content-md5") do
@@ -75,7 +75,7 @@ defmodule Ewebmachine.Core do
v3b9b(conn, state)
end
end
-
+
## "Content-MD5 valid?"
decision v3b9a(conn, state) do
case resource_call(conn, state, :validate_content_checksum) do
@@ -97,7 +97,7 @@ defmodule Ewebmachine.Core do
v3b9b(conn, state)
end
end
-
+
## "Malformed?"
decision v3b9b(conn, state) do
case resource_call(conn, state, :malformed_request) do
@@ -107,7 +107,7 @@ defmodule Ewebmachine.Core do
v3b8(conn, state)
end
end
-
+
## "Authorized?"
decision v3b8(conn, state) do
case resource_call(conn, state, :is_authorized) do
@@ -118,7 +118,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 401)
end
end
-
+
## "Forbidden?"
decision v3b7(conn, state) do
case resource_call(conn, state, :forbidden) do
@@ -128,7 +128,7 @@ defmodule Ewebmachine.Core do
v3b6(conn, state)
end
end
-
+
## "Okay Content-* Headers?"
decision v3b6(conn, state) do
{reply, conn, state} = resource_call(conn, state, :valid_content_headers)
@@ -138,7 +138,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 501)
end
end
-
+
## "Known Content-Type?"
decision v3b5(conn, state) do
{reply, conn, state} = resource_call(conn, state, :known_content_type)
@@ -148,7 +148,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 415)
end
end
-
+
## "Req Entity Too Large?"
decision v3b4(conn, state) do
{reply, conn, state} = resource_call(conn, state, :valid_entity_length)
@@ -158,7 +158,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 413)
end
end
-
+
## "OPTIONS?"
decision v3b3(conn, state) do
case method(conn) do
@@ -196,7 +196,7 @@ defmodule Ewebmachine.Core do
v3d4(conn, state)
end
end
-
+
## "Accept-Language exists?"
decision v3d4(conn, state) do
if get_header_val(conn, "accept-language") do
@@ -205,7 +205,7 @@ defmodule Ewebmachine.Core do
v3e5(conn, state)
end
end
-
+
## "Acceptable Language available? %% WMACH-46 (do this as proper conneg)"
decision v3d5(conn, state) do
{reply, conn, state} = resource_call(conn, state, :language_available)
@@ -215,7 +215,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 406)
end
end
-
+
## "Accept-Charset exists?"
decision v3e5(conn, state) do
case get_header_val(conn, "accept-charset") do
@@ -228,7 +228,7 @@ defmodule Ewebmachine.Core do
_ -> v3e6(conn, state)
end
end
-
+
## "Acceptable Charset available?"
decision v3e6(conn, state) do
accept = get_header_val(conn, "accept-charset")
@@ -238,7 +238,7 @@ defmodule Ewebmachine.Core do
_ -> v3f6(conn, state)
end
end
-
+
## Accept-Encoding exists?
## also, set content-type header here, now that charset is chosen)
decision v3f6(conn, state) do
@@ -256,7 +256,7 @@ defmodule Ewebmachine.Core do
_ -> v3f7(conn, state)
end
end
-
+
## "Acceptable encoding available?"
decision v3f7(conn, state) do
accept = get_header_val(conn, "accept-encoding")
@@ -266,7 +266,7 @@ defmodule Ewebmachine.Core do
_ -> v3g7(conn, state)
end
end
-
+
## "Resource exists?"
decision v3g7(conn, state) do
## his is the first place after all conneg, so set Vary here
@@ -284,7 +284,7 @@ defmodule Ewebmachine.Core do
v3h7(conn, state)
end
end
-
+
## "If-Match exists?"
decision v3g8(conn, state) do
if get_header_val(conn, "if-match") do
@@ -293,7 +293,7 @@ defmodule Ewebmachine.Core do
v3h10(conn, state)
end
end
-
+
## "If-Match: * exists"
decision v3g9(conn, state) do
if get_header_val(conn, "if-match") == "*" do
@@ -302,7 +302,7 @@ defmodule Ewebmachine.Core do
v3g11(conn, state)
end
end
-
+
## "ETag in If-Match"
decision v3g11(conn, state) do
etags = split_quoted_strings(get_header_val(conn, "if-match"))
@@ -313,7 +313,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 412)
end
end
-
+
## "If-Match exists"
decision v3h7(conn, state) do
if get_header_val(conn, "if-match") do
@@ -322,7 +322,7 @@ defmodule Ewebmachine.Core do
v3i7(conn, state)
end
end
-
+
## "If-unmodified-since exists?"
decision v3h10(conn, state) do
if get_header_val(conn, "if-unmodified-since") do
@@ -331,7 +331,7 @@ defmodule Ewebmachine.Core do
v3i12(conn, state)
end
end
-
+
## "I-UM-S is valid date?"
decision v3h11(conn, state) do
iums_date = get_header_val(conn, "if-unmodified-since")
@@ -341,7 +341,7 @@ defmodule Ewebmachine.Core do
v3h12(conn, state)
end
end
-
+
## "Last-Modified > I-UM-S?"
decision v3h12(conn, state) do
req_date = get_header_val(conn, "if-unmodified-since")
@@ -353,7 +353,7 @@ defmodule Ewebmachine.Core do
v3i12(conn, state)
end
end
-
+
## "Moved permanently? (apply PUT to different URI)"
decision v3i4(conn, state) do
{reply, conn, state} = resource_call(conn, state, :moved_permanently)
@@ -365,7 +365,7 @@ defmodule Ewebmachine.Core do
v3p3(conn, state)
end
end
-
+
## "PUT?"
decision v3i7(conn, state) do
if method(conn) == "PUT" do
@@ -374,7 +374,7 @@ defmodule Ewebmachine.Core do
v3k7(conn, state)
end
end
-
+
## "If-none-match exists?"
decision v3i12(conn, state) do
if get_header_val(conn, "if-none-match") do
@@ -383,7 +383,7 @@ defmodule Ewebmachine.Core do
v3l13(conn, state)
end
end
-
+
## "If-None-Match: * exists?"
decision v3i13(conn, state) do
if get_header_val(conn, "if-none-match") == "*" do
@@ -392,7 +392,7 @@ defmodule Ewebmachine.Core do
v3k13(conn, state)
end
end
-
+
## "GET or HEAD?"
decision v3j18(conn, state) do
if method(conn) in ["GET","HEAD"] do
@@ -401,7 +401,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 412)
end
end
-
+
## "Moved permanently?"
decision v3k5(conn, state) do
case resource_call(conn, state, :moved_permanently) do
@@ -412,7 +412,7 @@ defmodule Ewebmachine.Core do
v3l5(conn, state)
end
end
-
+
## "Previously existed?"
decision v3k7(conn, state) do
{reply, conn, state} = resource_call(conn, state, :previously_existed)
@@ -422,7 +422,7 @@ defmodule Ewebmachine.Core do
v3l7(conn, state)
end
end
-
+
## "Etag in if-none-match?"
decision v3k13(conn, state) do
etags = split_quoted_strings(get_header_val(conn, "if-none-match"))
@@ -436,7 +436,7 @@ defmodule Ewebmachine.Core do
v3l13(conn, state)
end
end
-
+
## "Moved temporarily?"
decision v3l5(conn, state) do
case resource_call(conn, state, :moved_temporarily) do
@@ -447,7 +447,7 @@ defmodule Ewebmachine.Core do
v3m5(conn, state)
end
end
-
+
## "POST?"
decision v3l7(conn, state) do
if method(conn) == "POST" do
@@ -456,7 +456,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 404)
end
end
-
+
## "IMS exists?"
decision v3l13(conn, state) do
if get_header_val(conn, "if-modified-since") do
@@ -465,7 +465,7 @@ defmodule Ewebmachine.Core do
v3m16(conn, state)
end
end
-
+
## "IMS is valid date?"
decision v3l14(conn, state) do
ims_date = get_header_val(conn, "if-modified-since")
@@ -475,7 +475,7 @@ defmodule Ewebmachine.Core do
v3l15(conn, state)
end
end
-
+
## "IMS > Now?"
decision v3l15(conn, state) do
now_date_time = :calendar.universal_time
@@ -487,7 +487,7 @@ defmodule Ewebmachine.Core do
v3l17(conn, state)
end
end
-
+
## "Last-Modified > IMS?"
decision v3l17(conn, state) do
req_date = get_header_val(conn, "if-modified-since")
@@ -499,7 +499,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 304)
end
end
-
+
## "POST?"
decision v3m5(conn, state) do
if method(conn) == "POST" do
@@ -508,7 +508,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 410)
end
end
-
+
## "Server allows POST to missing resource?"
decision v3m7(conn, state) do
{amp, conn, state} = resource_call(conn, state, :allow_missing_post)
@@ -518,7 +518,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 404)
end
end
-
+
## "DELETE?"
decision v3m16(conn, state) do
if method(conn) == "DELETE" do
@@ -527,7 +527,7 @@ defmodule Ewebmachine.Core do
v3n16(conn, state)
end
end
-
+
## "DELETE enacted immediately? Also where DELETE is forced"
decision v3m20(conn, state) do
{result, conn, state} = resource_call(conn, state, :delete_resource)
@@ -540,7 +540,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 500)
end
end
-
+
decision v3m20b(conn, state) do
{reply, conn, state} = resource_call(conn, state, :delete_completed)
if reply do
@@ -549,7 +549,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 202)
end
end
-
+
## "Server allows POST to missing resource?"
decision v3n5(conn, state) do
{reply, conn, state} = resource_call(conn, state, :allow_missing_post)
@@ -559,7 +559,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 410)
end
end
-
+
## "Redirect?"
decision v3n11(conn, state) do
{reply, conn, state} = resource_call(conn, state, :post_is_create)
@@ -569,23 +569,23 @@ defmodule Ewebmachine.Core do
if is_nil(new_path), do: raise "post_is_create w/o create_path"
if !is_binary(new_path), do: raise "create_path not a string (#{inspect new_path})"
-
+
{base_uri, conn, state} = resource_call(conn, state, :base_uri)
base_uri = if String.last(base_uri) == "/" do
- String.slice(base_uri,0..-2)
+ String.slice(base_uri, 0..-2//1)
else
- base_uri
+ base_uri
end
new_path = if !match?("/"<>_, new_path) do
- "#{path(conn)}/#{new_path}"
+ "#{path(conn)}/#{new_path}"
else
- new_path
+ new_path
end
-
+
conn = if !get_resp_header(conn, "location") do
set_resp_header(conn, "location", base_uri <> new_path)
else
- conn
+ conn
end
redirect_helper(conn, state)
else
@@ -594,7 +594,7 @@ defmodule Ewebmachine.Core do
redirect_helper(conn, state)
end
end
-
+
## "POST?"
decision v3n16(conn, state) do
if method(conn) == "POST" do
@@ -603,18 +603,18 @@ defmodule Ewebmachine.Core do
v3o16(conn, state)
end
end
-
+
## "Conflict?"
decision v3o14(conn, state) do
case resource_call(conn, state, :is_conflict) do
{true, conn, state} ->
respond(conn, state, 409)
- {_, conn, state} ->
+ {_, conn, state} ->
{_, conn, state} = accept_helper(conn, state)
v3p11(conn, state)
end
end
-
+
## "PUT?"
decision v3o16(conn, state) do
if method(conn) == "PUT" do
@@ -623,7 +623,7 @@ defmodule Ewebmachine.Core do
v3o18(conn, state)
end
end
-
+
## Multiple representations?
## also where body generation for GET and HEAD is done)
decision v3o18(conn, state) do
@@ -647,7 +647,7 @@ defmodule Ewebmachine.Core do
v3o18b(conn, state)
end
end
-
+
decision v3o18b(conn, state) do
{mc, conn, state} = resource_call(conn, state, :multiple_choices)
if mc do
@@ -656,7 +656,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 200)
end
end
-
+
## "Response includes an entity?"
decision v3o20(conn, state) do
if has_resp_body(conn) do
@@ -665,7 +665,7 @@ defmodule Ewebmachine.Core do
respond(conn, state, 204)
end
end
-
+
## "Conflict?"
decision v3p3(conn, state) do
{reply, conn, state} = resource_call(conn, state, :is_conflict)
@@ -676,7 +676,7 @@ defmodule Ewebmachine.Core do
v3p11(conn, state)
end
end
-
+
## "New resource? (at this point boils down to \"has location header\")"
decision v3p11(conn, state) do
if get_resp_header(conn, "location") do
@@ -685,7 +685,7 @@ defmodule Ewebmachine.Core do
v3o20(conn, state)
end
end
-
+
###
### Helpers
###
@@ -707,13 +707,13 @@ defmodule Ewebmachine.Core do
{variances, conn, state} = resource_call(conn, state, :variances)
{accept ++ accept_enc ++ accept_char ++ variances, conn, state}
end
-
+
def accept_helper(conn, state) do
ct = get_header_val(conn, "content-type") || "application/octet-stream"
{_, _, h_params} = ct = normalize_mtype(ct)
conn = set_metadata(conn, :mediaparams, h_params)
{ct_accepted, conn, state} = resource_call(conn, state, :content_types_accepted)
-
+
mtfun = Enum.find_value(ct_accepted, fn {accept,f} ->
fuzzy_mt_match(ct,normalize_mtype(accept)) && f
end)
@@ -726,7 +726,7 @@ defmodule Ewebmachine.Core do
throw {:halt, conn}
end
end
-
+
def encode_body_if_set(conn, state) do
if has_resp_body(conn) do
body = resp_body(conn)
@@ -737,7 +737,7 @@ defmodule Ewebmachine.Core do
{:ok, conn, state}
end
end
-
+
def encode_body(conn, state, body) do
chosen_cset = get_metadata(conn, :'chosen-charset')
{charsetter, conn, state} = case resource_call(conn, state, :charsets_provided) do
@@ -761,7 +761,7 @@ defmodule Ewebmachine.Core do
end
{body, conn, state}
end
-
+
def choose_encoding(conn, state, acc_enc_hdr) do
{enc_provided, conn, state} = resource_call(conn, state, :encodings_provided)
encs = for {enc, _} <- enc_provided, do: to_string(enc)
@@ -774,7 +774,7 @@ defmodule Ewebmachine.Core do
conn = set_metadata(conn, :'content-encoding', chosen_enc)
{chosen_enc, conn, state}
end
-
+
def choose_charset(conn, state, acc_char_hdr) do
case resource_call(conn, state, :charsets_provided) do
{:no_charset, conn, state} ->
@@ -802,13 +802,13 @@ defmodule Ewebmachine.Core do
v3p11(conn, state)
end
end
-
+
def respond(conn, state, code) do
{conn, state} = if (code == 304) do
conn = remove_resp_header(conn, "content-type")
{etag, conn, state} = resource_call(conn, state, :generate_etag)
conn = if etag, do: set_resp_header(conn, "etag", quoted_string(etag)), else: conn
-
+
{exp, conn, state} = resource_call(conn, state, :expires)
conn = if exp, do: set_resp_header(conn, "expires", rfc1123_date(exp)), else: conn
{conn, state}
@@ -816,6 +816,6 @@ defmodule Ewebmachine.Core do
{conn, state}
end
conn = set_response_code(conn, code)
- resource_call(conn, state, :finish_request)
+ resource_call(conn, state, :finish_request)
end
end
diff --git a/lib/ewebmachine/core.utils.ex b/lib/ewebmachine/core.utils.ex
index f94f7d4..b8fa260 100644
--- a/lib/ewebmachine/core.utils.ex
+++ b/lib/ewebmachine/core.utils.ex
@@ -29,8 +29,8 @@ defmodule Ewebmachine.Core.Utils do
"""
@spec fuzzy_mt_match(norm_content_type, norm_content_type) :: boolean
def fuzzy_mt_match({h_type, h_subtype, h_params}, {a_type, a_subtype, a_params}) do
- (a_type == h_type or a_type == "*" ) and
- (a_subtype == h_subtype or a_subtype=="*") and
+ (a_type == h_type or a_type == "*" ) and
+ (a_subtype == h_subtype or a_subtype=="*") and
Enum.all?(a_params, fn {k, v} -> h_params[k] == v end)
end
@@ -44,7 +44,7 @@ defmodule Ewebmachine.Core.Utils do
end
@doc """
- HTTP Content negociation, get the content type to send from :
+ HTTP Content negociation, get the content type to send from :
- `accept_header`, the HTTP header `Accept`
- `ct_provided`, the list of provided content types
@@ -58,7 +58,7 @@ defmodule Ewebmachine.Core.Utils do
e -> e
end) |>
Enum.map(&Plug.Conn.Utils.media_type/1)
- accepts = for {:ok, type, subtype, params} <- accepts do
+ accepts = for {:ok, type, subtype, params} <- accepts do
q = case Float.parse(params["q"] || "1") do
{q, _} -> q
_ -> 1
@@ -96,7 +96,7 @@ defmodule Ewebmachine.Core.Utils do
def rfc1123_date({{yyyy, mm, dd}, {hour, min, sec}}) do
day_number = :calendar.day_of_the_week({yyyy, mm, dd})
args = [:httpd_util.day(day_number), dd, :httpd_util.month(mm), yyyy, hour, min, sec]
- :io_lib.format('~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT', args) |> IO.iodata_to_binary()
+ :io_lib.format(~c"~s, ~2.2.0w ~3.s ~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT", args) |> IO.iodata_to_binary()
end
@doc """
@@ -105,13 +105,13 @@ defmodule Ewebmachine.Core.Utils do
@spec convert_request_date(String.t) :: {{year :: integer, month :: integer, day :: integer}, {h :: integer, min :: integer, sec :: integer}} | :bad_date
def convert_request_date(date) do
try do
- :httpd_util.convert_request_date('#{date}')
+ :httpd_util.convert_request_date(~c"#{date}")
catch _, _ -> :bad_date
end
end
@doc """
- HTTP Encoding negociation, get the encoding to use from :
+ HTTP Encoding negociation, get the encoding to use from :
- `acc_enc_hdr`, the HTTP header `Accept-Encoding`
- `encs`, the list of supported encoding
@@ -122,7 +122,7 @@ defmodule Ewebmachine.Core.Utils do
end
@doc """
- HTTP Charset negociation, get the charset to use from :
+ HTTP Charset negociation, get the charset to use from :
- `acc_char_hdr`, the HTTP header `Accept-Charset`
- `charsets`, the list of supported charsets
@@ -183,7 +183,7 @@ defmodule Ewebmachine.Core.Utils do
### Priv
###
alias Ewebmachine.Compat
-
+
defp choose(choices, header, default) do
## sorted set of {prio,value}
prios = prioritized_values(header)
@@ -192,14 +192,14 @@ defmodule Ewebmachine.Core.Utils do
default_prio = Enum.find_value(prios, fn {prio, v} -> v == default && prio end)
start_prio = Enum.find_value(prios, fn {prio, v} -> v == "*" && prio end)
default_ok = case default_prio do
- nil -> start_prio !== 0.0
- 0.0 -> false
+ nil -> start_prio !== +0.0
+ +0.0 -> false
_ -> true
end
- any_ok = start_prio not in [nil, 0.0]
+ any_ok = start_prio not in [nil, +0.0]
# remove choices where prio == 0.0
- {zero_prios, prios} = Compat.Enum.split_with(prios, fn {prio, _} -> prio == 0.0 end)
+ {zero_prios, prios} = Compat.Enum.split_with(prios, fn {prio, _} -> prio == +0.0 end)
choices_to_remove = Enum.map(zero_prios, &elem(&1, 1))
choices = Enum.filter(choices, &!(String.downcase(&1) in choices_to_remove))
@@ -218,7 +218,7 @@ defmodule Ewebmachine.Core.Utils do
end
defp prioritized_values(header) do
- header
+ header
|> Plug.Conn.Utils.list
|> Enum.map(fn e->
{q,v} = case String.split(e,~r"\s;\s", parts: 2) do
diff --git a/lib/ewebmachine/plug.error_as_forward.ex b/lib/ewebmachine/plug.error_as_forward.ex
index 445bce8..6026ea1 100644
--- a/lib/ewebmachine/plug.error_as_forward.ex
+++ b/lib/ewebmachine/plug.error_as_forward.ex
@@ -9,9 +9,15 @@ defmodule Ewebmachine.Plug.ErrorAsForward do
def init(opts), do: (opts[:forward_pattern] || "/error/:status")
@doc false
- def call(%{status: code, state: :set}=conn, pattern) when code > 399 do
- path = pattern |> String.slice(1..-1) |> String.replace(":status", to_string(code)) |> String.split("/")
- %{ conn | path_info: path, method: "GET", state: :unset }
+ def call(%Plug.Conn{status: code, state: :set} = conn, pattern) when code > 399 do
+ # `path_info` info is the request path split as segments.
+ path_info =
+ # Generate a path according to the status code.
+ String.replace(pattern, ":status", to_string(code))
+ # Transform it into segments.
+ |> String.split("/", trim: true)
+
+ %{conn | path_info: path_info, method: "GET", state: :unset}
end
def call(conn, _), do: conn
end
diff --git a/mix.exs b/mix.exs
index 2a7b1dd..7505a0e 100644
--- a/mix.exs
+++ b/mix.exs
@@ -1,7 +1,7 @@
defmodule Ewebmachine.Mixfile do
use Mix.Project
- def version, do: "2.3.2"
+ def version, do: "2.3.3"
@description """
Ewebmachine contains macros and plugs to allow you to compose
diff --git a/test/run_core_test.exs b/test/run_core_test.exs
index 68f23a9..4c9c52a 100644
--- a/test/run_core_test.exs
+++ b/test/run_core_test.exs
@@ -1,27 +1,31 @@
Code.require_file "test_helper.exs", __DIR__
defmodule CommonMacros do
- defmacro resources([do: body]) do
+ defmacro resources([do: body]) do
name = :"#{inspect make_ref()}"
- quote do
- defmodule unquote(name) do
+
+ {:module, module, _, _} = Module.create(
+ name,
+ quote do
use Ewebmachine.Builder.Resources
plug :resource_match
plug Ewebmachine.Plug.Run
plug Ewebmachine.Plug.Send
plug :error_404
- defp error_404(conn,_), do:
- (conn |> send_resp(404,"") |> halt)
+ defp error_404(conn, _), do: conn |> send_resp(404, "") |> halt()
unquote(body)
- end
- unquote(name)
- end
+ end,
+ Macro.Env.location(__ENV__)
+ )
+
+ module
end
end
defmodule EwebmachineTest do
use ExUnit.Case
- use Plug.Test
+ import Plug.Test
+ import Plug.Conn
import CommonMacros
test "Simple Handlers builder with only to_html default GET" do
@@ -30,7 +34,7 @@ defmodule EwebmachineTest do
plug :add_handlers, init: %{}
plug Ewebmachine.Plug.Run
plug Ewebmachine.Plug.Send
-
+
defh to_html, do: "Hello World"
end
conn = SimpleHtml.call(conn(:get, "/"), [])
@@ -39,7 +43,7 @@ defmodule EwebmachineTest do
assert conn.resp_body == "Hello World"
assert conn.state == :sent
end
-
+
test "default plugs" do
defmodule SimpleResources do
use Ewebmachine.Builder.Resources, default_plugs: true
@@ -51,11 +55,11 @@ defmodule EwebmachineTest do
assert conn.resp_body == "toto"
assert conn.state == :sent
end
-
+
test "Simple resource builder with XML and path match param" do
app = resources do
- resource "/hello/:name" do %{name: name} after
- content_types_provided do: ['application/xml': :to_xml]
+ resource "/hello/:name" do %{name: name} after
+ content_types_provided do: ["application/xml": :to_xml]
defh to_xml, do: "#{state.name}"
end
end
@@ -65,10 +69,10 @@ defmodule EwebmachineTest do
assert Enum.into(conn.resp_headers,%{})["content-type"] == "application/xml"
assert conn.resp_body == "arnaud"
end
-
+
test "Implement not exists" do
app = resources do
- resource "/hello/:name" do %{name: name} after
+ resource "/hello/:name" do %{name: name} after
resource_exists do: state.name !== "idonotexist"
defh to_html, do: state.name
end
@@ -76,20 +80,20 @@ defmodule EwebmachineTest do
assert app.call(conn(:get, "/hello/arnaud"), []).status == 200
assert app.call(conn(:get, "/hello/idonotexist"), []).status == 404
end
-
+
test "Service not available" do
app = resources do
- resource "/notok" do %{} after
+ resource "/notok" do %{} after
service_available do: false
end
- resource "/ok" do %{} after
+ resource "/ok" do %{} after
service_available do: true
end
end
assert app.call(conn(:get, "/notok"), []).status == 503
assert app.call(conn(:get, "/ok"), []).status == 200
end
-
+
test "Unknown method" do
app = resources do
resource "/notok" do %{} after known_methods(do: ["TOTO"]) end
@@ -98,7 +102,7 @@ defmodule EwebmachineTest do
assert app.call(conn(:get, "/notok"), []).status == 501
assert app.call(conn(:get, "/ok"), []).status == 200
end
-
+
test "Url too long" do
app = resources do
resource "/notok" do %{} after uri_too_long(do: true) end
@@ -107,7 +111,7 @@ defmodule EwebmachineTest do
assert app.call(conn(:get, "/notok"), []).status == 414
assert app.call(conn(:get, "/ok"), []).status == 200
end
-
+
test "Method allowed ?" do
app = resources do
resource "/notok" do %{} after allowed_methods(do: ["POST"]) end
@@ -116,10 +120,10 @@ defmodule EwebmachineTest do
assert app.call(conn(:get, "/notok"), []).status == 405
assert app.call(conn(:get, "/ok"), []).status == 200
end
-
+
test "Content MD5 check" do
app = resources do
- resource "/" do %{} after
+ resource "/" do %{} after
allowed_methods do: ["PUT"]
content_types_accepted do: ["application/json": :from_json]
defh from_json, do: true
@@ -130,7 +134,7 @@ defmodule EwebmachineTest do
headers = [{"content-type","application/json"},{"content-md5","sZRqySSS0jR8YjW00mERhA=="}]
assert app.call(%{conn(:put,"/","hello\n")|req_headers: headers},[]).status == 204
end
-
+
test "Malformed ?" do
app = resources do
resource "/notok" do %{} after malformed_request(do: true) end
@@ -139,7 +143,7 @@ defmodule EwebmachineTest do
assert app.call(conn(:get, "/notok"), []).status == 400
assert app.call(conn(:get, "/ok"), []).status == 200
end
-
+
test "Is authorized ?" do
app = resources do
resource "/notok" do %{} after is_authorized(do: "myrealm") end
@@ -150,10 +154,10 @@ defmodule EwebmachineTest do
assert conn.status == 401
assert get_resp_header(conn,"www-authenticate") == ["myrealm"]
end
-
+
test "Encoding base64" do
app = resources do
- resource "/" do %{} after
+ resource "/" do %{} after
encodings_provided do: [base64: &Base.encode64/1, identity: &(&1)]
defh to_html, do: "hello"
end
@@ -166,10 +170,10 @@ defmodule EwebmachineTest do
assert get_resp_header(conn,"content-encoding") == []
assert conn.resp_body == "hello"
end
-
+
test "POST create path" do
app = resources do
- resource "/orders" do %{} after
+ resource "/orders" do %{} after
allowed_methods do: ["POST"]
post_is_create do: true
create_path do: "/titus"
@@ -177,11 +181,11 @@ defmodule EwebmachineTest do
defh from_text, do:
{true,put_private(conn,:body_post,read_body(conn)),state}
end
- resource "/orders2" do %{} after
+ resource "/orders2" do %{} after
allowed_methods do: ["POST"]
post_is_create do: true
# Check modified state is propagated
- defh create_path(conn, state), do: {state.path, conn, state}
+ defh create_path(conn, %{path: path} = state), do: {path, conn, state}
content_types_accepted do: ["text/plain": :from_text]
defh from_text(conn, state), do: {true, conn, state |> Map.put(:path, "titus")}
end
@@ -193,10 +197,10 @@ defmodule EwebmachineTest do
conn = app.call(conn(:post,"/orders2","titus") |> put_req_header("content-type", "text/plain"), [])
assert get_resp_header(conn,"location") == ["http://www.example.com/orders2/titus"]
end
-
+
test "POST process post" do
app = resources do
- resource "/orders" do %{} after
+ resource "/orders" do %{} after
allowed_methods do: ["POST"]
process_post do:
{true,put_private(conn,:body_post,"yes"),state}
@@ -206,13 +210,13 @@ defmodule EwebmachineTest do
assert conn.status == 204
assert "yes" = conn.private[:body_post]
end
-
+
test "Cache if modified" do
app = resources do
- resource "/notcached" do %{} after
+ resource "/notcached" do %{} after
last_modified do: {{2013,1,1},{0,0,0}}
end
- resource "/cached" do %{} after
+ resource "/cached" do %{} after
last_modified do: {{2012,12,31},{0,0,0}}
end
end
@@ -221,13 +225,13 @@ defmodule EwebmachineTest do
conn = app.call(%{conn(:get,"/notcached")|req_headers: [{"if-modified-since","Sat, 31 Dec 2012 19:43:31 GMT"}]}, [])
assert conn.status == 200
end
-
+
test "Cache etag" do
app = resources do
- resource "/notcached" do %{} after
+ resource "/notcached" do %{} after
generate_etag do: "titi"
end
- resource "/cached" do %{} after
+ resource "/cached" do %{} after
generate_etag do: "toto"
end
end
@@ -236,10 +240,10 @@ defmodule EwebmachineTest do
conn = app.call(%{conn(:get,"/notcached")|req_headers: [{"if-none-match","toto"}]}, [])
assert conn.status == 200
end
-
+
test "halt test" do
app = resources do
- resource "/error" do %{} after
+ resource "/error" do %{} after
content_types_provided do: {:halt,407}
defh to_html, do: "toto"
end
@@ -251,7 +255,7 @@ defmodule EwebmachineTest do
test "fuzzy acceptance" do
app = resources do
- resource "/" do %{} after
+ resource "/" do %{} after
allowed_methods do: ["PUT"]
content_types_accepted do: %{"application/*"=> :from_app, {"text/*",%{"pretty"=>"true"}}=> :from_pretty}
defh from_app, do: {:halt,601}