Permalink
Browse files

Support for #file.producer = fs

This enables usage of an implicit knowledge base dynamically built off the filesystem
  • Loading branch information...
1 parent 6206956 commit a6f1aaf5debb45db16059b36456ec0f9a6615c99 Yurii Rashkovskii committed Feb 4, 2012
@@ -22,7 +22,9 @@
path :: undefined | string(),
ensure = present :: present | absent,
mode,
- content = ""
+ content = "",
+ %% special flag denoting file request
+ producer :: undefined | fs
}).
-record(package,
@@ -0,0 +1 @@
+-compile({parse_transform, htoad_transform}).
@@ -13,7 +13,8 @@
sasl,
dynamic_compile,
erlydtl,
- parse_trans
+ parse_trans,
+ syntax_tools
]},
{mod, { htoad_app, []}},
{env, [
View
@@ -1,5 +1,5 @@
-module(htoad).
--export([start/0, assert/1, assert/2, retract/1, retract/2]).
+-export([start/0, add_rules/1, assert/1, assert/2, retract/1, retract/2]).
-include_lib("htoad/include/htoad.hrl").
start() ->
@@ -14,6 +14,17 @@ start(App) ->
Other
end.
+add_rules(Rules) ->
+ seresye:add_rules(?ENGINE, Rules),
+ case Rules of
+ Module when is_atom(Module) ->
+ ok;
+ {Module, _} ->
+ ok
+ end,
+ FReqs = proplists:get_value(htoad_file_requests, Module:module_info(attributes), []),
+ assert([{file_request, F} || F <- FReqs]).
+
assert(Fact) ->
seresye:assert(?ENGINE, Fact).
@@ -1,5 +1,6 @@
-module(htoad_deps).
-include_lib("htoad/include/htoad.hrl").
+-include_lib("htoad/include/toadie.hrl").
-include_lib("htoad/include/stdlib.hrl").
-export([init/2,on/3,on_match/3]).
@@ -1,5 +1,6 @@
-module(htoad_host).
-include_lib("htoad/include/htoad.hrl").
+-include_lib("htoad/include/toadie.hrl").
-include_lib("htoad/include/stdlib.hrl").
-neg_rule({init, [{htoad_argument, {host, '__IGNORE_UNDERSCORE__'}}]}).
@@ -1,5 +1,6 @@
-module(htoad_io).
-include_lib("htoad/include/htoad.hrl").
+-include_lib("htoad/include/toadie.hrl").
-include_lib("htoad/include/stdlib.hrl").
-export([init/2, print/2]).
@@ -1,11 +1,13 @@
-module(htoad_lfs).
-include_lib("htoad/include/htoad.hrl").
+-include_lib("htoad/include/toadie.hrl").
-include_lib("htoad/include/stdlib.hrl").
-export([init/2, ensure_file_present/2,
ensure_dir_present/2]).
--rules([init, ensure_file_present,
- ensure_dir_present]).
+-export([fs_file_present/2, fs_file_absent/2, fs_dir_present/2, fs_dir_absent/2]).
+-rules([init, ensure_file_present, ensure_dir_present,
+ fs_file_present, fs_file_absent, fs_dir_present, fs_dir_absent]).
init(Engine, #init{}) ->
lager:debug("Initialized htoad_lfs"),
@@ -28,6 +30,56 @@ ensure_dir_present(Engine, #file{ ensure = present,
chmod(Dir#file.path, Dir#file.mode),
Engine.
+%% file system
+fs_file_present(Engine, {file_request, #file{ ensure = present,
+ type = file,
+ producer = fs
+ } = File}) ->
+ case filelib:is_regular(File#file.path) of
+ true ->
+ lager:debug("File ~s exists",[File#file.path]),
+ htoad:assert(Engine, load_content(File));
+ false ->
+ Engine
+ end.
+
+fs_file_absent(Engine, {file_request, #file{ ensure = absent,
+ type = file,
+ producer = fs
+ } = File}) ->
+ case filelib:is_regular(File#file.path) of
+ false ->
+ lager:debug("File ~s does not exist",[File#file.path]),
+ htoad:assert(Engine, File);
+ true ->
+ Engine
+ end.
+
+fs_dir_present(Engine, {file_request, #file{ ensure = present,
+ type = dir,
+ producer = fs
+ } = File}) ->
+ case filelib:is_dir(File#file.path) of
+ true ->
+ lager:debug("Directory ~s exists",[File#file.path]),
+ htoad:assert(Engine, File);
+ false ->
+ Engine
+ end.
+
+fs_dir_absent(Engine, {file_request, #file{ ensure = absent,
+ type = dir,
+ producer = fs
+ } = File}) ->
+ case filelib:is_dir(File#file.path) of
+ false ->
+ lager:debug("Directory ~s does not exist",[File#file.path]),
+ htoad:assert(Engine, File);
+ true ->
+ Engine
+ end.
+
+
%% private
chmod(Path, Mode) ->
case Mode of
@@ -37,3 +89,20 @@ chmod(Path, Mode) ->
file:change_mode(Path, Mode),
lager:debug("Ensured ~s mode ~w",[Path, Mode])
end.
+
+load_content(#file{ content = dontread } = File) ->
+ File;
+load_content(#file{ content = "" } = File) ->
+ {ok, B} = file:read_file(File#file.path),
+ File#file{ content = B };
+load_content(#file{ content = Content } = File) when is_list(Content);
+ is_binary(Content) ->
+ {ok, B} = file:read_file(File#file.path),
+ case iolist_to_binary(Content) of
+ B ->
+ File;
+ _ ->
+ []
+ end.
+
+
@@ -1,5 +1,6 @@
-module(htoad_pkg).
-include_lib("htoad/include/htoad.hrl").
+-include_lib("htoad/include/toadie.hrl").
-include_lib("htoad/include/stdlib.hrl").
-export([ensure_package/3,
@@ -1,5 +1,6 @@
-module(htoad_pkg_apt).
-include_lib("htoad/include/htoad.hrl").
+-include_lib("htoad/include/toadie.hrl").
-include_lib("htoad/include/stdlib.hrl").
-export([init/4, ensure_package/3, package_not_present/3]).
@@ -1,5 +1,6 @@
-module(htoad_pkg_brew).
-include_lib("htoad/include/htoad.hrl").
+-include_lib("htoad/include/toadie.hrl").
-include_lib("htoad/include/stdlib.hrl").
-export([init/3, ensure_package/3, package_not_present/3]).
@@ -1,5 +1,6 @@
-module(htoad_shell).
-include_lib("htoad/include/htoad.hrl").
+-include_lib("htoad/include/toadie.hrl").
-include_lib("htoad/include/stdlib.hrl").
-export([init/3, command/2]).
@@ -57,7 +57,7 @@ init([_Args, Files]) ->
init_engine(Args) ->
{ok, Modules} = application:get_env(htoad, modules),
- [ ok = seresye:add_rules(?ENGINE, Module) || Module <- Modules ],
+ [ ok = htoad:add_rules(Module) || Module <- Modules ],
htoad:assert([{htoad_argument, Arg} || Arg <- Args ]),
Signals = [ {htoad_toadie_server_ready, Pid} || {_, Pid, _, _} <- supervisor:which_children(htoad_toadies) ],
htoad:assert(htoad_utils:on(Signals, #init{})).
@@ -167,12 +167,12 @@ load_file(File) ->
", "),
Source =
"-module('" ++ atom_to_list(Module) ++ "').\n"
+ "-include(\"toadie.hrl\").\n"
"-include(\"stdlib.hrl\").\n"
"-htoad_absname(\"" ++ File ++ "\").\n"
"-import(htoad_utils, [" ++ Utils ++ "]).\n" ++ S ++ "\n \n",
{Module, Binary} = dynamic_compile:from_string(Source, [export_all,
return_errors, debug_info,
- {parse_transform, htoad_transform},
{i, filename:dirname(File)},
{i, code:lib_dir(htoad,include)},{i, htoad_utils:file(".")}]),
code:load_binary(Module, htoad_utils:file(File ++ ".beam"), Binary),
@@ -186,7 +186,7 @@ load_rules(File, Toadie, Bin) ->
lager:debug("[+ Adding following rules for ~s: ~p]", [File, Rules]),
Beam = htoad_utils:file(File ++ ".beam"),
file:write_file(Beam, Bin),
- Result = seresye:add_rules(?ENGINE, Toadie),
+ Result = htoad:add_rules(Toadie),
file:delete(Beam),
Result = ok
end.
@@ -1,6 +1,7 @@
-module(htoad_toadies).
-include_lib("htoad/include/htoad.hrl").
+-include_lib("htoad/include/toadie.hrl").
-include_lib("htoad/include/stdlib.hrl").
-include_lib("esupervisor/include/esupervisor.hrl").
@@ -1,21 +1,54 @@
-module(htoad_transform).
-export([parse_transform/2]).
+-include_lib("htoad/include/stdlib.hrl").
-record(state,{
+ rules = [],
+ current_function = [], %% [] :: atom()
+ file_requests = [],
options,
absname
}).
parse_transform(Forms, Options) ->
- {Forms1, _State} = parse_trans:transform(fun do_transform/4,
+ {Forms1, State} = parse_trans:transform(fun do_transform/4,
#state{ options = Options },
Forms, Options),
Result = parse_trans:revert(Forms1),
- Result.
+ parse_trans:do_insert_forms(above, [{attribute, 0, htoad_file_requests, State#state.file_requests}], Result, parse_trans:initial_context(Result, Options)).
+
+transform(Fun, State, Form, Context) when is_tuple(Form) ->
+ {L,Rec,State1} = transform(Fun, State, [Form], Context),
+ {hd(L),Rec,State1};
+
+transform(Fun, State, Forms, Context) when is_list(Forms) ->
+ {Form1, State1} = parse_trans:do_transform(Fun,
+ State,
+ Forms,
+ Context),
+ {parse_trans:revert(Form1),false,State1}.
+
+
+do_transform(attribute,{attribute, _, rules, Rules} = Attr, _Context, #state{} = State) ->
+ {Attr, false, State#state{ rules = Rules }};
do_transform(attribute,{attribute, _, htoad_absname, AbsName} = Attr, _Context, #state{} = State) ->
{Attr, false, State#state{ absname = AbsName }};
+do_transform(function, {function, _, Fun, _Arity, _Cs} = Form, _Context, #state{ rules = Rules } = State) ->
+ case lists:member(Fun, Rules) of
+ false ->
+ {Form, true, State#state{ current_function = [] }};
+ true ->
+ {Form, true, State#state{ current_function = Fun }}
+ end;
+
+do_transform(clause, {clause, Line, Head, G, B}, Context,
+ #state{ current_function = CurFun} = State) when CurFun /= [] ->
+ {Head1, _Rec, State1} = transform(fun clause_scanner/4, State, Head, Context),
+ {B1, Rec, State2} = transform(fun do_transform/4, State1, B, Context),
+ {{clause, Line, Head1, G, B1}, Rec, State2};
+
do_transform(application,{call, Line, {atom, Line1, F}, [File]}, _Context,
#state{ absname = AbsName } = State) when F == load; F == file ->
{{call, Line, {atom, Line1, F}, [
@@ -36,6 +69,34 @@ do_transform(_Type, Form, _Context, State) ->
{Form, true, State}.
+clause_scanner(record_expr, {record, _L, file, Fields} = Form, _Context,
+ #state{ file_requests = FReqs } = State) ->
+ case scan_file_record(Fields) of
+ #file{ producer = fs, path = Path } = File when is_list(Path) ->
+ {Form, true, State#state{ file_requests = [File|FReqs] }};
+ _ ->
+ {Form, true, State}
+ end;
+clause_scanner(_Type, Form, _Context, State) ->
+ {Form, true, State}.
+
+
+scan_file_record(Fields) ->
+ DefaultFile = #file{},
+ RFields = record_info(fields, file),
+ {RFields, Vals} = lists:unzip(scan_file_record_1(Fields, lists:zip(RFields, tl(tuple_to_list(DefaultFile))))),
+ list_to_tuple([file|Vals]).
+
+scan_file_record_1([], Acc) ->
+ Acc;
+scan_file_record_1([{record_field, _, {atom, _, _Name}, {var, _, _}}|T], Acc) ->
+ scan_file_record_1(T, Acc);
+scan_file_record_1([{record_field, _, {atom, _, Name}, Value0}|T], Acc) ->
+ Value = erl_syntax:concrete(Value0),
+ scan_file_record_1(T, lists:keyreplace(Name, 1, Acc, {Name, Value})).
+
+
+
list_to_cons([],Line) ->
{nil,Line};
list_to_cons([H|T],Line) ->
View
@@ -34,7 +34,8 @@
{app, erlware_commons, [{incl_cond, include}]},
{app, dynamic_compile, [{incl_cond, include}]},
{app, erlydtl, [{incl_cond, include}]},
- {app, parse_trans, [{incl_cond, include}]}
+ {app, parse_trans, [{incl_cond, include}]},
+ {app, syntax_tools, [{incl_cond, include}]}
]}.
{target_dir, "htoad"}.

0 comments on commit a6f1aaf

Please sign in to comment.