Permalink
Browse files

Support for erlang packages is added

  • Loading branch information...
1 parent 55a4506 commit 838a2bdadb4d8ac67e09768cd86f084a66872a3e @khia committed Sep 1, 2011
View
@@ -4,6 +4,9 @@ DEST:=$(PREFIX)$(PROJECT)
REBAR=./rebar
+GIT_USER=$(shell git config --global user.name)
+GIT_EMAIL=$(shell git config --global user.email)
+
all:
@$(REBAR) get-deps compile
@@ -27,3 +30,5 @@ dialyzer:
app:
@$(REBAR) create template=mochiwebapp dest=$(DEST) appid=$(PROJECT)
+package:
+ @$(REBAR) create template=mochiwebpackage dest=$(DEST) appid=$(PROJECT) author="${GIT_USER} <${GIT_EMAIL}>"
View
@@ -13,3 +13,6 @@ To create a new mochiweb using project:
To create a new mochiweb using project in a specific directory:
make app PROJECT=project_name PREFIX=$HOME/projects/
+
+If you are using erlang packages (modules with names like in Java) use:
+ make package PROJECT=project_name
@@ -0,0 +1,22 @@
+%% -*- erlang -*-
+{variables, [{appid, "mochiwebpackage"},
+ {author, "Mochi Media <dev@mochimedia.com>"},
+ {year, "2011"},
+ {version, "0.1"},
+ {port, 8080},
+ {dest, "{{appid}}"}]}.
+{dir, "{{dest}}"}.
+{template, "mochiwebpackage_skel/src/app.src", "{{dest}}/src/{{appid}}.app.src"}.
+{template, "mochiwebpackage_skel/src/main.erl", "{{dest}}/src/main.erl"}.
+{template, "mochiwebpackage_skel/src/app.erl", "{{dest}}/src/app.erl"}.
+{template, "mochiwebpackage_skel/src/deps.erl", "{{dest}}/src/deps.erl"}.
+{template, "mochiwebpackage_skel/src/sup.erl", "{{dest}}/src/sup.erl"}.
+{template, "mochiwebpackage_skel/src/web.erl", "{{dest}}/src/web.erl"}.
+{template, "mochiwebpackage_skel/start-dev.sh", "{{dest}}/start-dev.sh"}.
+{template, "mochiwebpackage_skel/priv/www/index.html", "{{dest}}/priv/www/index.html"}.
+%%{file, "../../.gitignore", "{{dest}}/.gitignore"}.
+{file, "../../Makefile", "{{dest}}/Makefile"}.
+{file, "mochiwebpackage_skel/rebar.config", "{{dest}}/rebar.config"}.
+{file, "../../rebar", "{{dest}}/rebar"}.
+{chmod, 8#755, "{{dest}}/rebar"}.
+{chmod, 8#755, "{{dest}}/start-dev.sh"}.
@@ -0,0 +1,8 @@
+<html>
+<head>
+<title>It Worked</title>
+</head>
+<body>
+{{appid}} running.
+</body>
+</html>
@@ -0,0 +1,10 @@
+%% -*- erlang -*-
+{erl_opts, [debug_info]}.
+{sub_dirs, [
+ "deps/mochiweb"
+ ]}.
+{deps, [
+ {mochiweb, ".*",
+ {git, "git://github.com/khia/mochiweb.git", ""}}]}.
+{cover_enabled, true}.
+{eunit_opts, [verbose, {report,{eunit_surefire,[{dir,"."}]}}]}.
@@ -0,0 +1,22 @@
+%% @author {{author}}
+%% @copyright {{appid}} {{author}}
+
+%% @doc Callbacks for the {{appid}} application.
+
+-module({{appid}}.app).
+-author("{{author}}").
+
+-behaviour(application).
+-export([start/2,stop/1]).
+
+
+%% @spec start(_Type, _StartArgs) -> ServerRet
+%% @doc application start callback for {{appid}}.
+start(_Type, _StartArgs) ->
+ {{appid}}.deps:ensure(),
+ {{appid}}.sup:start_link().
+
+%% @spec stop(_State) -> ServerRet
+%% @doc application stop callback for {{appid}}.
+stop(_State) ->
+ ok.
@@ -0,0 +1,15 @@
+%% -*- erlang -*-
+{application, {{appid}},
+ [{description, "{{appid}}"},
+ {vsn, "{{version}}"},
+ {modules, [
+ {{appid}}.main,
+ {{appid}}.app,
+ {{appid}}.deps,
+ {{appid}}.sup,
+ {{appid}}.web
+ ]},
+ {registered, []},
+ {mod, {'{{appid}}.app', []}},
+ {env, []},
+ {applications, [kernel, stdlib, crypto]}]}.
@@ -0,0 +1,101 @@
+%% @author {{author}}
+%% @copyright {{year}} {{author}}
+
+%% @doc Ensure that the relatively-installed dependencies are on the code
+%% loading path, and locate resources relative
+%% to this application's path.
+
+-module({{appid}}.deps).
+-author("{{author}}").
+
+-import(erlang).
+-import(string).
+-import(filename).
+-import(ordsets).
+-import(filelib).
+-import(lists).
+-import(code).
+
+-export([ensure/0, ensure/1]).
+-export([get_base_dir/0, get_base_dir/1]).
+-export([local_path/1, local_path/2]).
+-export([deps_on_path/0, new_siblings/1]).
+
+%% @spec deps_on_path() -> [ProjNameAndVers]
+%% @doc List of project dependencies on the path.
+deps_on_path() ->
+ F = fun (X, Acc) ->
+ ProjDir = filename:dirname(X),
+ case {filename:basename(X),
+ filename:basename(filename:dirname(ProjDir))} of
+ {"ebin", "deps"} ->
+ [filename:basename(ProjDir) | Acc];
+ _ ->
+ Acc
+ end
+ end,
+ ordsets:from_list(lists:foldl(F, [], code:get_path())).
+
+%% @spec new_siblings(Module) -> [Dir]
+%% @doc Find new siblings paths relative to Module that aren't already on the
+%% code path.
+new_siblings(Module) ->
+ Existing = deps_on_path(),
+ SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)),
+ Siblings = [filename:dirname(X) || X <- SiblingEbin,
+ ordsets:is_element(
+ filename:basename(filename:dirname(X)),
+ Existing) =:= false],
+ lists:filter(fun filelib:is_dir/1,
+ lists:append([[filename:join([X, "ebin"]),
+ filename:join([X, "include"])] ||
+ X <- Siblings])).
+
+
+%% @spec ensure(Module) -> ok
+%% @doc Ensure that all ebin and include paths for dependencies
+%% of the application for Module are on the code path.
+ensure(Module) ->
+ code:add_paths(new_siblings(Module)),
+ code:clash(),
+ ok.
+
+%% @spec ensure() -> ok
+%% @doc Ensure that the ebin and include paths for dependencies of
+%% this application are on the code path. Equivalent to
+%% ensure(?Module).
+ensure() ->
+ ensure(?MODULE).
+
+%% @spec get_base_dir(Module) -> string()
+%% @doc Return the application directory for Module. It assumes Module is in
+%% a standard OTP layout application in the ebin or src directory.
+get_base_dir(Module) ->
+ ModuleString = atom_to_list(Module),
+ NComponents = erlang:length(string:tokens(ModuleString, [$.])),
+ case code:which(Module) of
+ non_existing ->
+ {error, {cannot_find_module, Module}};
+ Module_Path ->
+ Splitted = filename:split(filename:absname(Module_Path)),
+ Elements = lists:sublist( %% -1 is in order to remove ebin or src
+ Splitted, erlang:length(Splitted) - NComponents - 1),
+ filename:join(Elements)
+ end.
+
+%% @spec get_base_dir() -> string()
+%% @doc Return the application directory for this application. Equivalent to
+%% get_base_dir(?MODULE).
+get_base_dir() ->
+ get_base_dir(?MODULE).
+
+%% @spec local_path([string()], Module) -> string()
+%% @doc Return an application-relative directory from Module's application.
+local_path(Components, Module) ->
+ filename:join([get_base_dir(Module) | Components]).
+
+%% @spec local_path(Components) -> string()
+%% @doc Return an application-relative directory for this application.
+%% Equivalent to local_path(Components, ?MODULE).
+local_path(Components) ->
+ local_path(Components, ?MODULE).
@@ -0,0 +1,33 @@
+%% @author {{author}}
+%% @copyright {{year}} {{author}}
+
+%% @doc {{appid}}.
+
+-module({{appid}}.main).
+-author("{{author}}").
+
+-import(application).
+
+-export([start/0, stop/0]).
+
+ensure_started(App) ->
+ case application:start(App) of
+ ok ->
+ ok;
+ {error, {already_started, App}} ->
+ ok
+ end.
+
+
+%% @spec start() -> ok
+%% @doc Start the {{appid}} server.
+start() ->
+ {{appid}}.deps:ensure(),
+ ensure_started(crypto),
+ application:start({{appid}}).
+
+
+%% @spec stop() -> ok
+%% @doc Stop the {{appid}} server.
+stop() ->
+ application:stop({{appid}}).
@@ -0,0 +1,60 @@
+%% @author {{author}}
+%% @copyright {{year}} {{author}}
+
+%% @doc Supervisor for the {{appid}} application.
+
+-module({{appid}}.sup).
+-author("{{author}}").
+
+-import(supervisor).
+-import(sets).
+-import(lists).
+
+-behaviour(supervisor).
+
+%% External exports
+-export([start_link/0, upgrade/0]).
+
+%% supervisor callbacks
+-export([init/1]).
+
+%% @spec start_link() -> ServerRet
+%% @doc API for starting the supervisor.
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%% @spec upgrade() -> ok
+%% @doc Add processes if necessary.
+upgrade() ->
+ {ok, {_, Specs}} = init([]),
+
+ Old = sets:from_list(
+ [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]),
+ New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]),
+ Kill = sets:subtract(Old, New),
+
+ sets:fold(fun (Id, ok) ->
+ supervisor:terminate_child(?MODULE, Id),
+ supervisor:delete_child(?MODULE, Id),
+ ok
+ end, ok, Kill),
+
+ [supervisor:start_child(?MODULE, Spec) || Spec <- Specs],
+ ok.
+
+%% @spec init([]) -> SupervisorTree
+%% @doc supervisor callback.
+init([]) ->
+ Web = web_specs({{appid}}.web, {{port}}),
+ Processes = [Web],
+ Strategy = {one_for_one, 10, 10},
+ {ok,
+ {Strategy, lists:flatten(Processes)}}.
+
+web_specs(Mod, Port) ->
+ WebConfig = [{ip, {0,0,0,0}},
+ {port, Port},
+ {docroot, {{appid}}.deps:local_path(["priv", "www"])}],
+ {Mod,
+ {Mod, start, [WebConfig]},
+ permanent, 5000, worker, dynamic}.
@@ -0,0 +1,76 @@
+%% @author {{author}}
+%% @copyright {{year}} {{author}}
+
+%% @doc Web server for {{appid}}.
+
+-module({{appid}}.web).
+-author("{{author}}").
+
+-import(mochiweb_http).
+-import(error_logger).
+-import(proplists).
+-import(error_logger).
+-import(io).
+-import(mochijson2).
+
+-export([start/1, stop/0, loop/2]).
+
+%% External API
+
+start(Options) ->
+ {DocRoot, Options1} = get_option(docroot, Options),
+ Loop = fun (Req) ->
+ ?MODULE:loop(Req, DocRoot)
+ end,
+ mochiweb_http:start([{name, ?MODULE}, {loop, Loop} | Options1]).
+
+stop() ->
+ mochiweb_http:stop(?MODULE).
+
+loop(Req, DocRoot) ->
+ "/" ++ Path = Req:get(path),
+ try
+ case Req:get(method) of
+ Method when Method =:= 'GET'; Method =:= 'HEAD' ->
+ case Path of
+ _ ->
+ Req:serve_file(Path, DocRoot)
+ end;
+ 'POST' ->
+ case Path of
+ _ ->
+ Req:not_found()
+ end;
+ _ ->
+ Req:respond({501, [], []})
+ end
+ catch
+ Type:What ->
+ Report = ["web request failed",
+ {path, Path},
+ {type, Type}, {what, What},
+ {trace, erlang:get_stacktrace()}],
+ error_logger:error_report(Report),
+ %% NOTE: mustache templates need \\ because they are not awesome.
+ Req:respond({500, [{"Content-Type", "text/plain"}],
+ "request failed, sorry\\n"})
+ end.
+
+%% Internal API
+
+get_option(Option, Options) ->
+ {proplists:get_value(Option, Options), proplists:delete(Option, Options)}.
+
+%%
+%% Tests
+%%
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+you_should_write_a_test() ->
+ ?assertEqual(
+ "No, but I will!",
+ "Have you written any tests?"),
+ ok.
+
+-endif.
@@ -0,0 +1,7 @@
+#!/bin/bash
+# NOTE: mustache templates need \\ because they are not awesome.
+APP={{appid}}
+exec erl -pa ebin edit deps/*/ebin -boot start_sasl \\
+ -sname ${APP//./_}_dev \\
+ -s {{appid}}.main start \\
+ -s reloader

0 comments on commit 838a2bd

Please sign in to comment.