Permalink
Browse files

Squashed commit of the following:

commit a7b757a
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 17:40:24 2011 +0200

    restore original behaviour of returning undefined

commit 385dd1c
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 17:38:30 2011 +0200

    delete dead code

commit 134e9d8
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 17:36:12 2011 +0200

    cleanup extract_extensions

commit 2a430ec
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 17:34:30 2011 +0200

    don't fetch list of types and extensions from server

commit 915050d
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 17:31:44 2011 +0200

    add exts and mimes functions to generated module

commit d692e37
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 17:15:59 2011 +0200

    use generated module for lookup functions

commit 33a7666
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 17:04:43 2011 +0200

    recompile generated module on server startup

commit ec5ad78
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 17:03:12 2011 +0200

    add load_mapping function to hide code generation

commit c006ad8
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 16:58:13 2011 +0200

    add mime_to_exts function to generated module

commit bafdf35
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 16:55:48 2011 +0200

    add ext_to_mimes function to generated module

commit 0c961ef
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 16:42:25 2011 +0200

    ensure that generated module compiles

commit 8918d8d
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 16:33:30 2011 +0200

    add codegen module scaffold

commit faff574
Author: Magnus Klaar <magnus.klaar@gmail.com>
Date:   Fri Sep 9 16:19:58 2011 +0200

    add code loading functions
  • Loading branch information...
1 parent 97d9077 commit 9f43fd0fccc624495937a4adbaafb1cfe3f5eea7 @yrashk yrashk committed Sep 9, 2011
Showing with 136 additions and 49 deletions.
  1. +136 −49 src/mimetypes.erl
View
@@ -11,18 +11,20 @@
terminate/2, code_change/3]).
-define(SERVER, ?MODULE).
+-define(MAPMOD, mimetypes_map).
+
+-record(state, {}).
+
+-type erlang_form() :: term().
+-type compile_options() :: [term()].
--record(state, {
- mime_types,
- extensions
- }).
%%%===================================================================
%%% API
%%%===================================================================
extension(Ext) ->
- gen_server:call(?SERVER, {extension, iolist_to_binary(Ext)}).
+ ?MAPMOD:ext_to_mimes(iolist_to_binary(Ext)).
filename(Filename) ->
"." ++ Ext = filename:extension(Filename),
@@ -31,14 +33,14 @@ filename(Filename) ->
extensions(Types) when is_list(Types) ->
lists:usort(lists:flatten([ extensions(Type) || Type <- Types ]));
extensions(Type) when is_binary(Type) ->
- gen_server:call(?SERVER, {extensions, iolist_to_binary(Type)}).
+ ?MAPMOD:mime_to_exts(iolist_to_binary(Type)).
types() ->
- gen_server:call(?SERVER, types).
+ ?MAPMOD:mimes().
extensions() ->
- gen_server:call(?SERVER, extensions).
+ ?MAPMOD:exts().
%%--------------------------------------------------------------------
@@ -81,11 +83,9 @@ init([]) ->
S = binary_to_list(B),
{ok, Tokens, _} = mimetypes_scan:string(S),
{ok, MimeTypes} = mimetypes_parse:parse(Tokens),
- Extensions = aggregate_extensions(extract_extensions(MimeTypes)),
- {ok, #state{
- mime_types = dict:from_list(MimeTypes),
- extensions = dict:from_list(Extensions)
- }}.
+ Mapping = extract_extensions(MimeTypes),
+ load_mapping(Mapping),
+ {ok, #state{}}.
%%--------------------------------------------------------------------
%% @private
@@ -101,29 +101,8 @@ init([]) ->
%% {stop, Reason, State}
%% @end
%%--------------------------------------------------------------------
-handle_call({extension, Ext}, _From, #state{ extensions = Exts } = State) ->
- case dict:is_key(Ext, Exts) of
- false ->
- Reply = undefined;
- true ->
- Reply = dict:fetch(Ext, Exts)
- end,
- {reply, Reply, State};
-
-handle_call({extensions, Type}, _From, #state{ mime_types = Types } = State) ->
- case dict:is_key(Type, Types) of
- false ->
- Reply = undefined;
- true ->
- Reply = dict:fetch(Type, Types)
- end,
- {reply, Reply, State};
-
-handle_call(types, _From, #state{ mime_types = Types } = State) ->
- {reply, dict:fetch_keys(Types), State};
-
-handle_call(extensions, _From, #state{ extensions = Exts } = State) ->
- {reply, dict:fetch_keys(Exts), State}.
+handle_call(_Msg, _From, State) ->
+ {reply, ok, State}.
%%--------------------------------------------------------------------
%% @private
@@ -182,19 +161,127 @@ code_change(_OldVsn, State, _Extra) ->
extract_extensions([]) ->
[];
extract_extensions([{Type, Exts}|Rest]) ->
- lists:concat([
- [ {Ext, Type} || Ext <- Exts ],
- extract_extensions(Rest)]).
+ [{Ext, Type} || Ext <- Exts] ++ extract_extensions(Rest).
-aggregate_extensions(Exts) ->
- aggregate_extensions_1(lists:keysort(1, Exts)).
-aggregate_extensions_1([]) ->
- [];
-aggregate_extensions_1([{Ext, Type1},{Ext, Type2}|Rest]) when is_binary(Type1) ->
- [{Ext, [Type1,Type2]}|aggregate_extensions_1(Rest)];
-aggregate_extensions_1([{Ext, Type1},{Ext, Type2}|Rest]) when is_list(Type1) ->
- [{Ext, [Type1|[Type2]]}|aggregate_extensions_1(Rest)];
-aggregate_extensions_1([H|T]) ->
- [H|aggregate_extensions_1(T)].
+%% @private Load a list of mimetype-extension pairs.
+-spec load_mapping([{binary(), binary()}]) -> ok.
+load_mapping(Pairs) ->
+ Module = ?MAPMOD,
+ AbsCode = map_to_abstract(Module, Pairs),
+ compile_and_load_forms(AbsCode, []).
+
+
+%% @private Generate an abstract mimtype mapping module.
+-spec map_to_abstract(atom(), [{binary(), binary()}]) -> [erl_syntax:syntaxTree()].
+map_to_abstract(Module, Pairs) ->
+ [erl_syntax:revert(E) || E <- map_to_abstract_(Module, Pairs)].
+
+%% @private Generate an abstract mimtype mapping module.
+-spec map_to_abstract_(atom(), [{binary(), binary()}]) -> [erl_syntax:syntaxTree()].
+map_to_abstract_(Module, Pairs) ->
+ [%% -module(Module).
+ erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
+ %% -export([ext_to_mimes/1, mime_to_exts/1]).
+ erl_syntax:attribute(
+ erl_syntax:atom(export),
+ [erl_syntax:list(
+ %% ext_to_mimes/1
+ [erl_syntax:arity_qualifier(
+ erl_syntax:atom(ext_to_mimes),
+ erl_syntax:integer(1)),
+ %% mime_to_exts/1
+ erl_syntax:arity_qualifier(
+ erl_syntax:atom(mime_to_exts),
+ erl_syntax:integer(1)),
+ %% exts/0
+ erl_syntax:arity_qualifier(
+ erl_syntax:atom(exts),
+ erl_syntax:integer(0)),
+ %% mimes/0
+ erl_syntax:arity_qualifier(
+ erl_syntax:atom(mimes),
+ erl_syntax:integer(0))])]),
+ %% ext_to_mimes(Extension) -> [MimeType].
+ erl_syntax:function(
+ erl_syntax:atom(ext_to_mimes),
+ ext_to_mimes_clauses(Pairs) ++
+ [erl_syntax:clause(
+ [erl_syntax:underscore()], none, [erl_syntax:abstract(undefined)])]),
+ %% mime_to_exts(MimeType) -> [Extension].
+ erl_syntax:function(
+ erl_syntax:atom(mime_to_exts),
+ mime_to_exts_clauses(Pairs) ++
+ [erl_syntax:clause(
+ [erl_syntax:underscore()], none, [erl_syntax:abstract(undefined)])]),
+ %% exts() -> [Extension].
+ erl_syntax:function(erl_syntax:atom(exts), [exts_clause(Pairs)]),
+ %% mimes() -> [MimeType].
+ erl_syntax:function(erl_syntax:atom(mimes), [mimes_clause(Pairs)])].
+
+%% @private Generate a set of ext_to_mimes clauses.
+-spec ext_to_mimes_clauses([{binary(), binary()}]) -> [erl_syntax:syntaxTree()].
+ext_to_mimes_clauses(Pairs) ->
+ Exts = lists:usort([E || {E,_} <- Pairs]),
+ Groups = [{E, lists:usort([T || {F,T} <- Pairs, F =:= E])} || E <- Exts],
+ [erl_syntax:clause([erl_syntax:abstract(E)], none, [erl_syntax:abstract(Ts)])
+ || {E, Ts} <- Groups].
+
+
+%% @private Generate a set of mime_to_exts clauses.
+-spec mime_to_exts_clauses([{binary(), binary()}]) -> [erl_syntax:syntaxTree()].
+mime_to_exts_clauses(Pairs) ->
+ Types = lists:usort([T || {_,T} <- Pairs]),
+ Groups = [{T, lists:usort([E || {E,U} <- Pairs, U =:= T])} || T <- Types],
+ [erl_syntax:clause([erl_syntax:abstract(T)], none, [erl_syntax:abstract(Es)])
+ || {T, Es} <- Groups].
+
+
+%% @private Generate a clause returning the set of extensions.
+-spec exts_clause([{binary(), binary()}]) -> [erl_syntax:syntaxTree()].
+exts_clause(Pairs) ->
+ Exts = lists:usort([E || {E,_} <- Pairs]),
+ erl_syntax:clause([], none, [erl_syntax:abstract(Exts)]).
+
+
+%% @private Generate a clause returning the set of mimetypes.
+-spec mimes_clause([{binary(), binary()}]) -> [erl_syntax:syntaxTree()].
+mimes_clause(Pairs) ->
+ Types = lists:usort([T || {_,T} <- Pairs]),
+ erl_syntax:clause([], none, [erl_syntax:abstract(Types)]).
+
+
+%% @private Compile and load a module.
+%% Copied from the meck_mod module of the meck project.
+-spec compile_and_load_forms(erlang_form(), compile_options()) -> ok.
+compile_and_load_forms(AbsCode, Opts) ->
+ case compile:forms(AbsCode, Opts) of
+ {ok, ModName, Binary} ->
+ load_binary(ModName, Binary);
+ {ok, ModName, Binary, _Warnings} ->
+ load_binary(ModName, Binary);
+ Error ->
+ exit({compile_forms, Error})
+ end.
+
+%% @private Load a module.
+%% Copied from the meck_mod module of the meck project.
+load_binary(Name, Binary) ->
+ case code:load_binary(Name, "", Binary) of
+ {module, Name} -> ok;
+ {error, Reason} -> exit({error_loading_module, Name, Reason})
+ end.
+
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+codegen_test() ->
+ ok = load_mapping([{<<"b">>, <<"a">>}]),
+ ?MAPMOD:module_info(),
+ ?assertEqual([<<"a">>], ?MAPMOD:ext_to_mimes(<<"b">>)),
+ ?assertEqual([<<"b">>], ?MAPMOD:mime_to_exts(<<"a">>)),
+ ?assertEqual([<<"b">>], ?MAPMOD:exts()),
+ ?assertEqual([<<"a">>], ?MAPMOD:mimes()).
+-endif.

0 comments on commit 9f43fd0

Please sign in to comment.