Skip to content

Commit

Permalink
Add initial support for filename module overloading
Browse files Browse the repository at this point in the history
  • Loading branch information
yrashk committed Jul 11, 2011
1 parent 2ac12c7 commit 373556a
Show file tree
Hide file tree
Showing 7 changed files with 243 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/evfs.erl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ register(Handler, Args) ->
register(?FILE_SERVER, Handler, Args).

register(Server, Handler, Args) ->
{module, Handler} = code:ensure_loaded(Handler),
gen_server:call(Server, {register_handler, Handler, Args}).


Expand Down
13 changes: 9 additions & 4 deletions src/evfs_app.erl
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,23 @@
-include("internal.hrl").

%% Application callbacks
-export([start/2, stop/1]).
-export([start/2, prep_stop/1, stop/1]).

%% ===================================================================
%% Application callbacks
%% ===================================================================

start(_StartType, _StartArgs) ->
{ok, Pid} = evfs_sup:start_link(),
{ok, OriginalFilename} = evfs_filename:load(),
{evfs_file_server, FileServer, _Type, _Modules} =
lists:keyfind(evfs_file_server, 1, supervisor:which_children(Pid)),
{ok, Pid, {FileServer, evfs:original_file_server(FileServer)}}.
{ok, Pid, {FileServer, evfs:original_file_server(FileServer), OriginalFilename}}.

stop({_FileServer, OriginalFileServer}) ->
register(?FILE_SERVER, OriginalFileServer),
prep_stop({_FileServer, _OriginalFileServer, Filename} = State) ->
ok = evfs_filename:unload(Filename),
State.

stop({_FileServer, OriginalFileServer, _Filename}) ->
ok = register(?FILE_SERVER, OriginalFileServer),
ok.
65 changes: 65 additions & 0 deletions src/evfs_default_handler.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,22 @@
make_symlink/3,
copy/6
]).
-export([filename_absname/2,
filename_absname/3,
filename_absname_join/3,
filename_basename/2,
filename_basename/3,
filename_dirname/2,
filename_extension/2,
filename_join/2,
filename_join/3,
filename_pathtype/2,
filename_rootname/2,
filename_rootname/3,
filename_split/2,
filename_nativename/2,
filename_flatten/2,
filename_append/3]).

init(FileServer) ->
{ok, FileServer}.
Expand Down Expand Up @@ -93,6 +109,55 @@ copy(SourceName, SourceOpts, DestName, DestOpts, Length, FileServer) ->
terminate(_Reason, _State) ->
ok.

%% filename
filename_absname(File, State) ->
{ok, filename_1:absname(filename(File)), State}.

filename_absname(File, Dir, State) ->
{ok, filename_1:absname(filename(File), Dir), State}.

filename_absname_join(Dir, File, State) ->
{ok, filename_1:absname_join(filename(Dir), File), State}.

filename_basename(File, State) ->
{ok, filename_1:basename(filename(File)), State}.

filename_basename(File, Ext, State) ->
{ok, filename_1:basename(filename(File), Ext), State}.

filename_dirname(File, State) ->
{ok, filename_1:dirname(filename(File)), State}.

filename_extension(File, State) ->
{ok, filename_1:extension(filename(File)), State}.

filename_join(Components, State) ->
{ok, filename_1:join([filename(hd(Components))|tl(Components)]), State}.

filename_join(File1, File2, State) ->
{ok, filename_1:join(filename(File1), File2), State}.

filename_pathtype(File, State) ->
{ok, filename_1:pathtype(filename(File)), State}.

filename_rootname(File, State) ->
{ok, filename_1:rootname(filename(File)), State}.

filename_rootname(File, Ext, State) ->
{ok, filename_1:rootname(filename(File), Ext), State}.

filename_split(File, State) ->
{ok, filename_1:split(filename(File)), State}.

filename_nativename(File, State) ->
{ok, filename_1:nativename(filename(File)), State}.

filename_flatten(File, State) ->
{ok, filename_1:flatten(filename(File)), State}.

filename_append(Dir, File, State) ->
{ok, filename_1:append(filename(Dir), File), State}.

%% Internal
filename("file://" ++ Filename) ->
Filename;
Expand Down
73 changes: 73 additions & 0 deletions src/evfs_file_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,17 @@ init(FileServer) ->
Error
end.

-type filename_command() :: absname | absname_join | basename |
dirname | extension | join | pathtype |
rootname | split | nativename |
find_src | flatten.

-type file_server_command() ::
original_file_server |
{unregister_handler, module()} |
{register_handler, module(), term()} |
%% Filename API
{filename, filename_command(), [term()]} |
%% Filesystem API
{open, file:filename(), [file:mode()]} |
{read_file, file:filename()} |
Expand Down Expand Up @@ -90,6 +97,59 @@ handle_call(original_file_server, _From,
#state{ file_server = FileServer } = State) ->
{reply, FileServer, State };

handle_call({filename, absname, [Filename]}, _From, State) ->
safe_call_handler(Filename, filename_absname, [Filename], State);

handle_call({filename, absname, [Filename, Dir]}, _From, State) ->
safe_call_handler(Filename, filename_absname, [Filename, Dir], State);

handle_call({filename, absname_join, [Dir, Filename]}, _From, State) ->
safe_call_handler(Dir, filename_absname_join, [Dir, Filename], State);

handle_call({filename, basename, [Filename]}, _From, State) ->
safe_call_handler(Filename, filename_basename, [Filename], State);

handle_call({filename, basename, [Filename, Ext]}, _From, State) ->
safe_call_handler(Filename, filename_basename, [Filename, Ext], State);

handle_call({filename, dirname, [Filename]}, _From, State) ->
safe_call_handler(Filename, filename_dirname, [Filename], State);

handle_call({filename, extension, [Filename]}, _From, State) ->
safe_call_handler(Filename, filename_extension, [Filename], State);

handle_call({filename, join, [Components]}, _From, State) ->
safe_call_handler(hd(Components), filename_join, [Components], State);

handle_call({filename, join, [Name1, Name2]}, _From, State) ->
safe_call_handler(Name1, filename_join, [Name1, Name2], State);

handle_call({filename, append, [Dir, Name]}, _From, State) ->
safe_call_handler(Dir, filename_append, [Dir, Name], State);

handle_call({filename, pathtype, [Path]}, _From, State) ->
safe_call_handler(Path, filename_pathtype, [Path], State);

handle_call({filename, rootname, [Filename]}, _From, State) ->
safe_call_handler(Filename, filename_rootname, [Filename], State);

handle_call({filename, rootname, [Filename, Ext]}, _From, State) ->
safe_call_handler(Filename, filename_rootname, [Filename, Ext], State);

handle_call({filename, split, [Filename]}, _From, State) ->
safe_call_handler(Filename, filename_split, [Filename], State);

handle_call({filename, nativename, [Filename]}, _From, State) ->
safe_call_handler(Filename, filename_nativename, [Filename], State);

handle_call({filename, find_src, Args}, _From, State) ->
{reply, apply(filename_1,find_src, Args), State};

handle_call({filename, flatten, [Filename]}, _From, State) ->
safe_call_handler(Filename, filename_flatten, [Filename], State);

%%

handle_call({open, Filename, ModeList}, _From, State) ->
call_handler(Filename, open, [Filename, ModeList], State);

Expand Down Expand Up @@ -230,3 +290,16 @@ call_handler(Filename, Function, Arguments, #state{ handlers = Handlers } = Stat
{false, Handlers1} ->
{reply, {error, notsup}, State#state{ handlers = Handlers1 }}
end.

safe_call_handler(Filename, Function, Arguments,
#state{ handlers = Handlers } = State) ->
case (catch call_handler(Filename, Function, Arguments, State)) of
{'EXIT',{undef, _}} ->
call_handler(Filename, Function, Arguments,
State#state{
handlers = tl(Handlers)
});
Result ->
Result
end.

79 changes: 79 additions & 0 deletions src/evfs_filename.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
-module(evfs_filename).
-export([load/0,unload/1]).

-include("internal.hrl").

load() ->
{ok, filename, Bin} = module(),
Original = code:which(filename),
Dir = filename:dirname(Original),

%% Process original filename module
{ok,{filename,[{abstract_code,{raw_abstract_v1, Abs0}}]}} =
beam_lib:chunks(Original, [abstract_code]),
Abs = lists:keyreplace(module, 3, Abs0, {attribute, 1, module, filename_1}),
{ok, filename, Bin0} = compile:forms(Abs0),
{ok, filename_1, Bin1} = compile:forms(Abs),
%%

ok = code:unstick_dir(Dir),
{module, filename} = code:load_binary(filename, code:which(?MODULE), Bin),
{module, filename_1} = code:load_binary(filename_1, Original, Bin1),
ok = code:stick_dir(Dir),
{ok, {Bin0, Original, Dir}}.

unload({Original, Filename, Dir}) ->
ok = code:unstick_dir(Dir),

{module, filename} = code:load_binary(filename, Filename, Original),

code:delete(filename_1),
code:purge(filename_1),

ok = code:stick_dir(Dir),
ok.


module() ->
compile:forms(forms()).

forms() ->
Exports0 = filename:module_info(exports),
Exports = Exports0 -- [{module_info, 0}, {module_info, 1}],
Attributes =
[{attribute, 1, file, {code:which(?MODULE), 1}},
{attribute, 1, module, filename},
{attribute, 1, export, Exports}],
Functions = [ export(Export) || Export <- Exports ],
Attributes ++ Functions.

export({Name, Arity}) when is_atom(Name) ->
Args = [ list_to_atom("Arg__" ++ integer_to_list(ArgNo)) || ArgNo <- lists:seq(1, Arity) ],
ArgVars = [ {var, 1, Arg} || Arg <- Args ],
{function,1,Name,Arity,[
{clause, 1, ArgVars,
[], %% guard
[ %% body
{call, 1, {remote, 1,
{atom, 1, gen_server},
{atom, 1, call}},
[{atom, 1, ?FILE_SERVER},
{tuple, 1, [
{atom, 1, filename},
{atom, 1, Name},
cons(ArgVars)
]}

]}
]}
]}.

cons([]) ->
{nil, 1};
cons([H|T]) ->
{cons, 1, H, cons(T)}.





5 changes: 5 additions & 0 deletions test/evfs_test_fs.erl
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
copy/6
]).
-export([io_list/1]).
-export([filename_dirname/2]).

init(Args) ->
{ok, Args}.
Expand Down Expand Up @@ -98,3 +99,7 @@ terminate(_Reason, _State) ->
%% IO server
io_list(Handle) ->
Handle.

%% Filename
filename_dirname("test://" ++ _, State) ->
{ok, "test://", State}.
15 changes: 11 additions & 4 deletions test/evfs_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,27 @@ t_pread() ->
evfs:register(evfs_test_fs, []),
{ok, File} = file:open("test://anyfile",[read]),
?assertEqual({ok, ["test://any"]}, file:pread(File, [{0,10}])).


t_filename() ->
?assertEqual("/", filename:dirname("/")),
?assertEqual("/", filename:dirname("file:///")),
evfs:register(evfs_test_fs, []),
?assertEqual("test://", filename:dirname("test://any")).

evfs_test_() ->
{foreach,
fun () ->
application:start(evfs)
ok = application:start(evfs)
end,
fun (_) ->
application:stop(evfs)
ok = application:stop(evfs)
end,
[
{"unregister", ?_test(t_unregister())},
{"file:// scheme should route to the default file server", ?_test(t_file_scheme())},
{"test_fs read_file", ?_test(t_read_file())},
{"test_fs pread", ?_test(t_pread())}
{"test_fs pread", ?_test(t_pread())},
{"test_fs & default fs filename", ?_test(t_filename())}
]
}.

Expand Down

0 comments on commit 373556a

Please sign in to comment.