I've spent several hours trying to troubleshoot this issue using the yaws documentation and web searches. The existing threads here haven't helped me.
I am new to erlang and I am trying to run yaws in embedded mode using the sample code provided on http://yaws.hyber.org/embed.yaws. I am missing something because I cannot get it to work. I have four files:
ybed.app
{application, ybed_app,
[
{description, "Yaws Embedded Application Test"},
{vsn, "0.1.0"},
{registered, []},
{applications, [kernel, stdlib, yaws]},
{mod, {ybed_app, []}},
{env, []}
]}.
ybed_app.erl
-module(ybed_app).
-behaviour(application).
%% Application callbacks
-export([start/2,
stop/1]).
start(_StartType, _StartArgs) ->
case ybed_sup:start_link() of
{ok, Pid} ->
{ok, Pid};
Other ->
{error, Other}
end.
stop(_State) ->
ok.
ybed_sup.erl
-module(ybed_sup).
-behaviour(supervisor).
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
start_link() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
YBed = {ybed, {ybed,start,[]},
permanent,2000,worker,[ybed]},
{ok,{{one_for_all,0,1}, [YBed]}}.
ybed.erl
-module(ybed).
-compile(export_all).
start() ->
{ok, spawn(?MODULE, run, [])}.
run() ->
Id = "embedded",
GconfList = [{id, Id}],
Docroot = "/tmp",
SconfList = [{port, 8000},
{servername, "foobar"},
{listen, {127,0,0,1}},
{docroot, Docroot}],
{ok, SCList, GC, ChildSpecs} =
yaws_api:embedded_start_conf(Docroot, SconfList, GconfList, Id),
[supervisor:start_child(ybed_sup, Ch) || Ch <- ChildSpecs],
yaws_api:setconf(GC, SCList),
{ok, self()}.
When I compile it (successfully) and try to start the application, I get a return value:
{error,{not_loaded,yaws}}
When I try to run the compiled ybed.erl, ybed:run(), I get:
** exception error: undefined function yaws_api:embedded_start_conf/4
in function ybed:run/0 (src/ybed.erl, line 16)
If I start yaws before I start the application, it still doesn't work.
I am not trying to build releases yet, just compile and test yaws in embedded mode. Can anyone tell me what I'm missing?
Thanks in advance
When you get error
** exception error: undefined function yaws_api:embedded_start_conf/4
in function ybed:run/0 (src/ybed.erl, line 16)
You clearly don't have yaws_api.beam
in your code
server search path. Start your erl
with proper -pa
argument or call code:add_patha/1
in your application initialization if you are not planning use embedded mode.
BTW there is a neat way how to start yaws
under your own supervisor described in yaws
documentation but without complete code so here we go in one module and with neat debugging resource and preparation for REST servicing.
-module(ybed_yaws).
-behaviour(supervisor).
-include_lib("yaws/include/yaws_api.hrl").
%% API
-export([start_link/0]).
%% Supervisor callbacks
-export([init/1]).
%% Internal functions export
-export([init_yaws/1, out/1]).
%%%===================================================================
%%% Defaults
%%%===================================================================
default_global() ->
#{id => "yaws", logdir => "log"}.
default_server() ->
#{port => 9900,
listen => {0,0,0,0},
docroot => "www",
appmods => [{"/", ?MODULE}]}.
%%%===================================================================
%%% API functions
%%%===================================================================
start_link() ->
supervisor:start_link(?MODULE, []).
%%%===================================================================
%%% Supervisor callbacks
%%%===================================================================
init([]) ->
{ok,
{{one_for_all, 0, 1},
[#{id => init_yaws,
start => {?MODULE, init_yaws, [self()]},
restart => transient}]
}}.
%%%===================================================================
%%% Internal functions
%%%===================================================================
init_yaws(Sup) ->
{ok, proc_lib:spawn_link(fun() -> config(Sup) end)}.
ensure_dir(Dir) ->
{ok, App} = application:get_application(),
D = filename:join([code:priv_dir(App), Dir])
filelib:ensure_dir(filename:join([D, "foo"])),
D.
config(Supervisor) ->
#{id := Id} = GCMap = default_global(),
#{docroot := DR} = SCMap = default_server(),
Docroot = ensure_dir(DR),
{ok, SC, GC, ChildSpecs} =
yaws_api:embedded_start_conf(
Docroot,
maps:to_list(SCMap#{docroot => Docroot}),
maps:to_list(GCMap),
Id),
[supervisor:start_child(Supervisor, Ch) || Ch <- ChildSpecs],
yaws_api:setconf(GC, SC),
ok.
-compile({inline, [h/1, f/2]}).
h(A) when is_atom(A) -> h(atom_to_binary(A, latin1));
h(S) -> yaws_api:htmlize(S).
f(Fmt, Args) -> yaws_api:f(Fmt, Args).
box(Str) ->
{'div',[{class,"box"}],
{pre, [], h(Str)}}.
out(A) ->
PathString = case A#arg.pathinfo of
undefined -> "";
P -> P
end,
Path = string:tokens(PathString, "/"),
Method = A#arg.req#http_request.method,
out(A, Method, Path).
out(A, Method, Path) ->
{ehtml,
{html, [],
[{head},
{body, [],
[
{h5, [], "Paths:"},
{hr},
box(f("Method = ~p~n"
"Path = ~p~n"
"A#arg.querydata = ~p~n",
[Method,
Path,
A#arg.querydata])),
{h5, [], "Headers:"},
{hr},
{ol, [], yaws_api:reformat_header(
A#arg.headers,
fun(H, V)->
{li, [], [h(H), ": ", {code, [], h(V)}]}
end
)}
]}
]}
}.
Note the way how is yaws
initialised in OTP compliant transient process but without gen_server
.
Add {yaws, [{embedded, true}]}
to your .config
file to keep yaws
application common services from starting. It will work even without it but it will not be fully embedded.