ernie 1.3.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -0
- data/README.md +167 -36
- data/VERSION.yml +3 -3
- data/bin/ernie +6 -12
- data/contrib/ebench.erl +76 -0
- data/elib/asset_pool.erl +18 -19
- data/elib/asset_pool_sup.erl +5 -7
- data/elib/bert.erl +69 -0
- data/elib/ernie.hrl +10 -0
- data/elib/ernie_admin.erl +60 -0
- data/elib/ernie_config.erl +30 -0
- data/elib/ernie_native.erl +22 -0
- data/elib/ernie_server.erl +139 -74
- data/elib/ernie_server_app.erl +1 -2
- data/elib/ernie_server_sup.erl +4 -1
- data/ernie.gemspec +17 -9
- data/examples/example.cfg +12 -0
- data/examples/ext.erl +8 -0
- data/examples/{expose.rb → ext.rb} +14 -2
- data/examples/nat.erl +12 -0
- data/lib/ernie.rb +1 -9
- data/test/ernie_server_test.rb +11 -11
- data/test/ernie_test.rb +0 -44
- data/test/sample/ext.rb +42 -0
- data/test/sample/sample.cfg +4 -0
- metadata +16 -8
- data/examples/dsl.rb +0 -28
- data/test/handler.rb +0 -41
@@ -0,0 +1,30 @@
|
|
1
|
+
-module(ernie_config).
|
2
|
+
-export([load/1]).
|
3
|
+
|
4
|
+
load(ConfigFile) ->
|
5
|
+
{ok, Configs} = file:consult(ConfigFile),
|
6
|
+
Configs2 = lists:map((fun load_single/1), Configs),
|
7
|
+
{ok, Configs2}.
|
8
|
+
|
9
|
+
load_single(Config) ->
|
10
|
+
case proplists:get_value(type, Config) of
|
11
|
+
native ->
|
12
|
+
verify(native, Config),
|
13
|
+
CodePaths = proplists:get_value(codepaths, Config),
|
14
|
+
lists:map((fun code:add_patha/1), CodePaths),
|
15
|
+
Mod = proplists:get_value(module, Config),
|
16
|
+
code:load_file(Mod),
|
17
|
+
[{id, native} | Config];
|
18
|
+
external ->
|
19
|
+
verify(external, Config),
|
20
|
+
Handler = proplists:get_value(command, Config),
|
21
|
+
Number = proplists:get_value(count, Config),
|
22
|
+
{ok, SupPid} = asset_pool_sup:start_link(Handler, Number),
|
23
|
+
[{_Id, ChildPid, _Type, _Modules}] = supervisor:which_children(SupPid),
|
24
|
+
[{id, ChildPid} | Config]
|
25
|
+
end.
|
26
|
+
|
27
|
+
verify(native, _Config) ->
|
28
|
+
ok;
|
29
|
+
verify(external, _Config) ->
|
30
|
+
ok.
|
@@ -0,0 +1,22 @@
|
|
1
|
+
-module(ernie_native).
|
2
|
+
-export([process/2]).
|
3
|
+
-include_lib("ernie.hrl").
|
4
|
+
|
5
|
+
process(ActionTerm, Request) ->
|
6
|
+
{_Type, Mod, Fun, Args} = ActionTerm,
|
7
|
+
Sock = Request#request.sock,
|
8
|
+
logger:debug("Calling ~p:~p(~p)~n", [Mod, Fun, Args]),
|
9
|
+
try apply(Mod, Fun, Args) of
|
10
|
+
Result ->
|
11
|
+
logger:debug("Result was ~p~n", [Result]),
|
12
|
+
Data = bert:encode({reply, Result}),
|
13
|
+
gen_tcp:send(Sock, Data)
|
14
|
+
catch
|
15
|
+
error:Error ->
|
16
|
+
BError = list_to_binary(io_lib:format("~p", [Error])),
|
17
|
+
Trace = erlang:get_stacktrace(),
|
18
|
+
BTrace = lists:map(fun(X) -> list_to_binary(io_lib:format("~p", [X])) end, Trace),
|
19
|
+
Data = term_to_binary({error, [user, 0, <<"RuntimeError">>, BError, BTrace]}),
|
20
|
+
gen_tcp:send(Sock, Data)
|
21
|
+
end,
|
22
|
+
ok = gen_tcp:close(Sock).
|
data/elib/ernie_server.erl
CHANGED
@@ -1,21 +1,14 @@
|
|
1
1
|
-module(ernie_server).
|
2
2
|
-behaviour(gen_server).
|
3
|
+
-include_lib("ernie.hrl").
|
3
4
|
|
4
5
|
%% api
|
5
|
-
-export([start_link/1, start/1, process/1,
|
6
|
+
-export([start_link/1, start/1, process/1, kick/0]).
|
6
7
|
|
7
8
|
%% gen_server callbacks
|
8
9
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
9
10
|
terminate/2, code_change/3]).
|
10
11
|
|
11
|
-
-record(state, {lsock = undefined,
|
12
|
-
pending = queue:new(),
|
13
|
-
count = 0}).
|
14
|
-
|
15
|
-
-record(request, {sock = undefined,
|
16
|
-
info = undefined,
|
17
|
-
action = undefined}).
|
18
|
-
|
19
12
|
%%====================================================================
|
20
13
|
%% API
|
21
14
|
%%====================================================================
|
@@ -29,8 +22,8 @@ start(Args) ->
|
|
29
22
|
process(Sock) ->
|
30
23
|
gen_server:cast({global, ?MODULE}, {process, Sock}).
|
31
24
|
|
32
|
-
|
33
|
-
gen_server:cast({global, ?MODULE},
|
25
|
+
kick() ->
|
26
|
+
gen_server:cast({global, ?MODULE}, kick).
|
34
27
|
|
35
28
|
%%====================================================================
|
36
29
|
%% gen_server callbacks
|
@@ -43,12 +36,14 @@ asset_freed() ->
|
|
43
36
|
%% {stop, Reason}
|
44
37
|
%% Description: Initiates the server
|
45
38
|
%%--------------------------------------------------------------------
|
46
|
-
init([Port]) ->
|
39
|
+
init([Port, Configs]) ->
|
47
40
|
process_flag(trap_exit, true),
|
48
41
|
error_logger:info_msg("~p starting~n", [?MODULE]),
|
49
42
|
{ok, LSock} = try_listen(Port, 500),
|
50
43
|
spawn(fun() -> loop(LSock) end),
|
51
|
-
|
44
|
+
Map = init_map(Configs),
|
45
|
+
io:format("pidmap = ~p~n", [Map]),
|
46
|
+
{ok, #state{lsock = LSock, map = Map}}.
|
52
47
|
|
53
48
|
%%--------------------------------------------------------------------
|
54
49
|
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
@@ -72,21 +67,19 @@ handle_cast({process, Sock}, State) ->
|
|
72
67
|
Request = #request{sock = Sock},
|
73
68
|
State2 = receive_term(Request, State),
|
74
69
|
{noreply, State2};
|
75
|
-
handle_cast(
|
76
|
-
case queue:
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
70
|
+
handle_cast(kick, State) ->
|
71
|
+
case queue:out(State#state.hq) of
|
72
|
+
{{value, Request}, Hq2} ->
|
73
|
+
State2 = process_request(Request, hq, Hq2, State),
|
74
|
+
{noreply, State2};
|
75
|
+
{empty, _Hq} ->
|
76
|
+
case queue:out(State#state.lq) of
|
77
|
+
{{value, Request}, Lq2} ->
|
78
|
+
State2 = process_request(Request, lq, Lq2, State),
|
79
|
+
{noreply, State2};
|
80
|
+
{empty, _Lq} ->
|
86
81
|
{noreply, State}
|
87
|
-
end
|
88
|
-
true ->
|
89
|
-
{noreply, State}
|
82
|
+
end
|
90
83
|
end;
|
91
84
|
handle_cast(_Msg, State) -> {noreply, State}.
|
92
85
|
|
@@ -101,6 +94,20 @@ code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
|
101
94
|
%% Internal
|
102
95
|
%%====================================================================
|
103
96
|
|
97
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
98
|
+
% Module mapping
|
99
|
+
|
100
|
+
init_map(Configs) ->
|
101
|
+
lists:map((fun extract_mapping/1), Configs).
|
102
|
+
|
103
|
+
extract_mapping(Config) ->
|
104
|
+
Id = proplists:get_value(id, Config),
|
105
|
+
Mod = proplists:get_value(module, Config),
|
106
|
+
{Mod, Id}.
|
107
|
+
|
108
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
109
|
+
% Listen and loop
|
110
|
+
|
104
111
|
try_listen(Port, 0) ->
|
105
112
|
error_logger:error_msg("Could not listen on port ~p~n", [Port]),
|
106
113
|
{error, "Could not listen on port"};
|
@@ -122,25 +129,8 @@ loop(LSock) ->
|
|
122
129
|
ernie_server:process(Sock),
|
123
130
|
loop(LSock).
|
124
131
|
|
125
|
-
|
126
|
-
|
127
|
-
gen_tcp:send(Sock, term_to_binary({reply, <<"Handlers reloaded.">>})),
|
128
|
-
ok = gen_tcp:close(Sock),
|
129
|
-
State;
|
130
|
-
process_admin(Sock, stats, _Args, State) ->
|
131
|
-
Count = State#state.count,
|
132
|
-
CountString = list_to_binary([<<"connections.total=">>, integer_to_list(Count), <<"\n">>]),
|
133
|
-
IdleWorkers = asset_pool:idle_worker_count(),
|
134
|
-
IdleWorkersString = list_to_binary([<<"workers.idle=">>, integer_to_list(IdleWorkers), <<"\n">>]),
|
135
|
-
QueueLength = queue:len(State#state.pending),
|
136
|
-
QueueLengthString = list_to_binary([<<"connections.pending=">>, integer_to_list(QueueLength), <<"\n">>]),
|
137
|
-
gen_tcp:send(Sock, term_to_binary({reply, list_to_binary([CountString, IdleWorkersString, QueueLengthString])})),
|
138
|
-
ok = gen_tcp:close(Sock),
|
139
|
-
State;
|
140
|
-
process_admin(Sock, _Fun, _Args, State) ->
|
141
|
-
gen_tcp:send(Sock, term_to_binary({reply, <<"Admin function not supported.">>})),
|
142
|
-
ok = gen_tcp:close(Sock),
|
143
|
-
State.
|
132
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
133
|
+
% Receive and process
|
144
134
|
|
145
135
|
receive_term(Request, State) ->
|
146
136
|
Sock = Request#request.sock,
|
@@ -151,61 +141,136 @@ receive_term(Request, State) ->
|
|
151
141
|
logger:info("Got term: ~p~n", [Term]),
|
152
142
|
case Term of
|
153
143
|
{call, '__admin__', Fun, Args} ->
|
154
|
-
|
155
|
-
{info,
|
156
|
-
|
157
|
-
|
144
|
+
ernie_admin:process(Sock, Fun, Args, State);
|
145
|
+
{info, Command, Args} ->
|
146
|
+
Infos = Request#request.infos,
|
147
|
+
Infos2 = [BinaryTerm | Infos],
|
148
|
+
Request2 = Request#request{infos = Infos2},
|
149
|
+
Request3 = process_info(Request2, Command, Args),
|
150
|
+
receive_term(Request3, State);
|
158
151
|
_Any ->
|
159
152
|
Request2 = Request#request{action = BinaryTerm},
|
160
|
-
|
153
|
+
close_if_cast(Term, Request2),
|
154
|
+
case Request2#request.priority of
|
155
|
+
high ->
|
156
|
+
Hq2 = queue:in(Request2, State#state.hq),
|
157
|
+
Lq2 = State#state.lq;
|
158
|
+
low ->
|
159
|
+
Hq2 = State#state.hq,
|
160
|
+
Lq2 = queue:in(Request2, State#state.lq)
|
161
|
+
end,
|
162
|
+
ernie_server:kick(),
|
163
|
+
State#state{hq = Hq2, lq = Lq2}
|
161
164
|
end;
|
162
165
|
{error, closed} ->
|
163
166
|
ok = gen_tcp:close(Sock),
|
164
167
|
State
|
165
168
|
end.
|
166
169
|
|
167
|
-
|
168
|
-
|
170
|
+
process_info(Request, priority, [Priority]) ->
|
171
|
+
Request#request{priority = Priority};
|
172
|
+
process_info(Request, _Command, _Args) ->
|
173
|
+
Request.
|
174
|
+
|
175
|
+
process_request(Request, Priority, Q2, State) ->
|
176
|
+
ActionTerm = bert:decode(Request#request.action),
|
177
|
+
{_Type, Mod, _Fun, _Args} = ActionTerm,
|
178
|
+
Specs = lists:filter(fun({X, _Id}) -> Mod =:= X end, State#state.map),
|
179
|
+
process_module(ActionTerm, Specs, Request, Priority, Q2, State).
|
180
|
+
|
181
|
+
process_module(ActionTerm, [], Request, Priority, Q2, State) ->
|
182
|
+
{_Type, Mod, _Fun, _Args} = ActionTerm,
|
183
|
+
logger:debug("No such module ~p~n", [Mod]),
|
184
|
+
Sock = Request#request.sock,
|
185
|
+
Class = <<"ServerError">>,
|
186
|
+
Message = list_to_binary(io_lib:format("No such module '~p'", [Mod])),
|
187
|
+
gen_tcp:send(Sock, term_to_binary({error, [server, 0, Class, Message, []]})),
|
188
|
+
ok = gen_tcp:close(Sock),
|
189
|
+
finish(Priority, Q2, State);
|
190
|
+
process_module(ActionTerm, Specs, Request, Priority, Q2, State) ->
|
191
|
+
[{_Mod, Id} | OtherSpecs] = Specs,
|
192
|
+
case Id of
|
193
|
+
native ->
|
194
|
+
logger:debug("Dispatching to native module~n", []),
|
195
|
+
{_Type, Mod, Fun, Args} = ActionTerm,
|
196
|
+
case erlang:function_exported(Mod, Fun, length(Args)) of
|
197
|
+
false ->
|
198
|
+
logger:debug("Not found in native module ~p~n", [Mod]),
|
199
|
+
process_module(ActionTerm, OtherSpecs, Request, Priority, Q2, State);
|
200
|
+
true ->
|
201
|
+
PredFun = list_to_atom(atom_to_list(Fun) ++ "_pred"),
|
202
|
+
logger:debug("Checking ~p:~p(~p) for selection.~n", [Mod, PredFun, Args]),
|
203
|
+
case erlang:function_exported(Mod, PredFun, length(Args)) of
|
204
|
+
false ->
|
205
|
+
logger:debug("No such predicate function ~p:~p(~p).~n", [Mod, PredFun, Args]),
|
206
|
+
process_native_request(ActionTerm, Request, Priority, Q2, State);
|
207
|
+
true ->
|
208
|
+
case apply(Mod, PredFun, Args) of
|
209
|
+
false ->
|
210
|
+
logger:debug("Predicate ~p:~p(~p) returned false.~n", [Mod, PredFun, Args]),
|
211
|
+
process_module(ActionTerm, OtherSpecs, Request, Priority, Q2, State);
|
212
|
+
true ->
|
213
|
+
logger:debug("Predicate ~p:~p(~p) returned true.~n", [Mod, PredFun, Args]),
|
214
|
+
process_native_request(ActionTerm, Request, Priority, Q2, State)
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end;
|
218
|
+
ValidPid when is_pid(ValidPid) ->
|
219
|
+
logger:debug("Found external pid ~p~n", [ValidPid]),
|
220
|
+
process_external_request(ValidPid, Request, Priority, Q2, State)
|
221
|
+
end.
|
222
|
+
|
223
|
+
close_if_cast(ActionTerm, Request) ->
|
169
224
|
case ActionTerm of
|
170
225
|
{cast, _Mod, _Fun, _Args} ->
|
171
226
|
Sock = Request#request.sock,
|
172
227
|
gen_tcp:send(Sock, term_to_binary({noreply})),
|
173
228
|
ok = gen_tcp:close(Sock),
|
174
|
-
logger:debug("
|
229
|
+
logger:debug("Closed cast.~n", []);
|
175
230
|
_Any ->
|
176
231
|
ok
|
177
|
-
end,
|
178
|
-
case queue:is_empty(State#state.pending) of
|
179
|
-
false ->
|
180
|
-
Pending2 = queue:in(Request, State#state.pending),
|
181
|
-
% io:format("Q", []),
|
182
|
-
State#state{pending = Pending2};
|
183
|
-
true ->
|
184
|
-
try_process_now(Request, State)
|
185
232
|
end.
|
186
233
|
|
187
|
-
|
234
|
+
finish(Priority, Q2, State) ->
|
235
|
+
case Priority of
|
236
|
+
hq -> State#state{hq = Q2};
|
237
|
+
lq -> State#state{lq = Q2}
|
238
|
+
end.
|
239
|
+
|
240
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
241
|
+
% Native
|
242
|
+
|
243
|
+
process_native_request(ActionTerm, Request, Priority, Q2, State) ->
|
244
|
+
Count = State#state.count,
|
245
|
+
State2 = State#state{count = Count + 1},
|
246
|
+
logger:debug("Count = ~p~n", [Count + 1]),
|
247
|
+
spawn(fun() -> ernie_native:process(ActionTerm, Request) end),
|
248
|
+
finish(Priority, Q2, State2).
|
249
|
+
|
250
|
+
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
251
|
+
% External
|
252
|
+
|
253
|
+
process_external_request(Pid, Request, Priority, Q2, State) ->
|
188
254
|
Count = State#state.count,
|
189
255
|
State2 = State#state{count = Count + 1},
|
190
|
-
|
256
|
+
logger:debug("Count = ~p~n", [Count + 1]),
|
257
|
+
case asset_pool:lease(Pid) of
|
191
258
|
{ok, Asset} ->
|
192
|
-
|
193
|
-
spawn(fun() -> process_now(Request, Asset) end),
|
194
|
-
State2;
|
259
|
+
logger:debug("Leased asset for pool ~p~n", [Pid]),
|
260
|
+
spawn(fun() -> process_now(Pid, Request, Asset) end),
|
261
|
+
finish(Priority, Q2, State2);
|
195
262
|
empty ->
|
196
|
-
|
197
|
-
Pending2 = queue:in(Request, State#state.pending),
|
198
|
-
State2#state{pending = Pending2}
|
263
|
+
State
|
199
264
|
end.
|
200
265
|
|
201
|
-
process_now(Request, Asset) ->
|
266
|
+
process_now(Pid, Request, Asset) ->
|
202
267
|
try unsafe_process_now(Request, Asset) of
|
203
268
|
_AnyResponse -> ok
|
204
269
|
catch
|
205
|
-
_AnyError -> ok
|
270
|
+
_AnyClass:_AnyError -> ok
|
206
271
|
after
|
207
|
-
asset_pool:return(Asset),
|
208
|
-
ernie_server:
|
272
|
+
asset_pool:return(Pid, Asset),
|
273
|
+
ernie_server:kick(),
|
209
274
|
gen_tcp:close(Request#request.sock)
|
210
275
|
end.
|
211
276
|
|
data/elib/ernie_server_app.erl
CHANGED
data/elib/ernie_server_sup.erl
CHANGED
@@ -14,6 +14,9 @@ init([]) ->
|
|
14
14
|
ok = file:write_file(Location, list_to_binary(Pid));
|
15
15
|
undefined -> ok
|
16
16
|
end,
|
17
|
+
{ok, Config} = application:get_env(ernie_server_app, config),
|
18
|
+
{ok, Configs} = ernie_config:load(Config),
|
19
|
+
io:format("~p~n", [Configs]),
|
17
20
|
{ok, {{one_for_one, 1, 60},
|
18
|
-
[{ernie_server, {ernie_server, start_link, [[Port]]},
|
21
|
+
[{ernie_server, {ernie_server, start_link, [[Port, Configs]]},
|
19
22
|
permanent, brutal_kill, worker, [ernie_server]}]}}.
|
data/ernie.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{ernie}
|
8
|
-
s.version = "
|
8
|
+
s.version = "2.0.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Tom Preston-Werner"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2010-02-16}
|
13
13
|
s.default_executable = %q{ernie}
|
14
14
|
s.email = %q{tom@mojombo.com}
|
15
15
|
s.executables = ["ernie"]
|
@@ -27,9 +27,15 @@ Gem::Specification.new do |s|
|
|
27
27
|
"Rakefile",
|
28
28
|
"VERSION.yml",
|
29
29
|
"bin/ernie",
|
30
|
+
"contrib/ebench.erl",
|
30
31
|
"ebin/ernie_server_app.app",
|
31
32
|
"elib/asset_pool.erl",
|
32
33
|
"elib/asset_pool_sup.erl",
|
34
|
+
"elib/bert.erl",
|
35
|
+
"elib/ernie.hrl",
|
36
|
+
"elib/ernie_admin.erl",
|
37
|
+
"elib/ernie_config.erl",
|
38
|
+
"elib/ernie_native.erl",
|
33
39
|
"elib/ernie_server.erl",
|
34
40
|
"elib/ernie_server_app.erl",
|
35
41
|
"elib/ernie_server_sup.erl",
|
@@ -37,16 +43,19 @@ Gem::Specification.new do |s|
|
|
37
43
|
"elib/logger_sup.erl",
|
38
44
|
"elib/port_wrapper.erl",
|
39
45
|
"ernie.gemspec",
|
40
|
-
"examples/
|
41
|
-
"examples/
|
46
|
+
"examples/example.cfg",
|
47
|
+
"examples/ext.erl",
|
48
|
+
"examples/ext.rb",
|
49
|
+
"examples/nat.erl",
|
42
50
|
"ext/Makefile",
|
43
51
|
"ext/extconf.rb",
|
44
52
|
"lib/ernie.rb",
|
45
53
|
"test/ernie_server_test.rb",
|
46
54
|
"test/ernie_test.rb",
|
47
|
-
"test/handler.rb",
|
48
55
|
"test/helper.rb",
|
49
|
-
"test/load.rb"
|
56
|
+
"test/load.rb",
|
57
|
+
"test/sample/ext.rb",
|
58
|
+
"test/sample/sample.cfg"
|
50
59
|
]
|
51
60
|
s.homepage = %q{http://github.com/mojombo/ernie}
|
52
61
|
s.rdoc_options = ["--charset=UTF-8"]
|
@@ -56,11 +65,10 @@ Gem::Specification.new do |s|
|
|
56
65
|
s.test_files = [
|
57
66
|
"test/ernie_server_test.rb",
|
58
67
|
"test/ernie_test.rb",
|
59
|
-
"test/handler.rb",
|
60
68
|
"test/helper.rb",
|
61
69
|
"test/load.rb",
|
62
|
-
"
|
63
|
-
"examples/
|
70
|
+
"test/sample/ext.rb",
|
71
|
+
"examples/ext.rb"
|
64
72
|
]
|
65
73
|
|
66
74
|
if s.respond_to? :specification_version then
|
data/examples/ext.erl
ADDED
@@ -1,12 +1,24 @@
|
|
1
1
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
2
2
|
require 'ernie'
|
3
3
|
|
4
|
-
module
|
4
|
+
module Ext
|
5
5
|
# Add two numbers together
|
6
6
|
def add(a, b)
|
7
7
|
a + b
|
8
8
|
end
|
9
9
|
|
10
|
+
def fib(n)
|
11
|
+
if n == 0 || n == 1
|
12
|
+
1
|
13
|
+
else
|
14
|
+
fib(n - 1) + fib(n - 2)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def shadow(x)
|
19
|
+
"ruby"
|
20
|
+
end
|
21
|
+
|
10
22
|
# Return the given number of bytes
|
11
23
|
def bytes(n)
|
12
24
|
'x' * n
|
@@ -24,4 +36,4 @@ module Test
|
|
24
36
|
end
|
25
37
|
end
|
26
38
|
|
27
|
-
Ernie.expose(:
|
39
|
+
Ernie.expose(:ext, Ext)
|
data/examples/nat.erl
ADDED
data/lib/ernie.rb
CHANGED
@@ -11,7 +11,7 @@ class Ernie
|
|
11
11
|
self.mods = {}
|
12
12
|
self.current_mod = nil
|
13
13
|
self.log = Logger.new(STDOUT)
|
14
|
-
self.log.level = Logger::
|
14
|
+
self.log.level = Logger::FATAL
|
15
15
|
self.auto_start = true
|
16
16
|
|
17
17
|
# Record a module.
|
@@ -189,14 +189,6 @@ end
|
|
189
189
|
|
190
190
|
# Root level calls
|
191
191
|
|
192
|
-
def mod(name, &block)
|
193
|
-
Ernie.mod(name, block)
|
194
|
-
end
|
195
|
-
|
196
|
-
def fun(name, &block)
|
197
|
-
Ernie.fun(name, block)
|
198
|
-
end
|
199
|
-
|
200
192
|
def logfile(name)
|
201
193
|
Ernie.logfile(name)
|
202
194
|
end
|
data/test/ernie_server_test.rb
CHANGED
@@ -5,14 +5,14 @@ PORT = 27118
|
|
5
5
|
class ErnieServerTest < Test::Unit::TestCase
|
6
6
|
context "An Ernie Server" do
|
7
7
|
setup do
|
8
|
-
`#{ERNIE_ROOT}/bin/ernie -
|
8
|
+
`#{ERNIE_ROOT}/bin/ernie -c #{ERNIE_ROOT}/test/sample/sample.cfg \
|
9
9
|
-P /tmp/ernie.pid \
|
10
10
|
-p #{PORT} \
|
11
11
|
-d`
|
12
12
|
@svc = BERTRPC::Service.new('localhost', PORT)
|
13
13
|
loop do
|
14
14
|
begin
|
15
|
-
@svc.call.
|
15
|
+
@svc.call.ext.zeronary
|
16
16
|
break
|
17
17
|
rescue Object => e
|
18
18
|
sleep 0.1
|
@@ -22,23 +22,23 @@ class ErnieServerTest < Test::Unit::TestCase
|
|
22
22
|
|
23
23
|
context "call" do
|
24
24
|
should "handle zeronary" do
|
25
|
-
assert_equal :foo, @svc.call.
|
25
|
+
assert_equal :foo, @svc.call.ext.zeronary
|
26
26
|
end
|
27
27
|
|
28
28
|
should "handle unary" do
|
29
|
-
assert_equal 5, @svc.call.
|
29
|
+
assert_equal 5, @svc.call.ext.unary(5)
|
30
30
|
end
|
31
31
|
|
32
32
|
should "handle binary" do
|
33
|
-
assert_equal 7, @svc.call.
|
33
|
+
assert_equal 7, @svc.call.ext.binary(5, 2)
|
34
34
|
end
|
35
35
|
|
36
36
|
should "handle ternary" do
|
37
|
-
assert_equal 10, @svc.call.
|
37
|
+
assert_equal 10, @svc.call.ext.ternary(5, 2, 3)
|
38
38
|
end
|
39
39
|
|
40
40
|
should "handle massive binaries" do
|
41
|
-
assert_equal 8 * 1024 * 1024, @svc.call.
|
41
|
+
assert_equal 8 * 1024 * 1024, @svc.call.ext.big(8 * 1024 * 1024).size
|
42
42
|
end
|
43
43
|
|
44
44
|
should "get an error on missing module" do
|
@@ -52,10 +52,10 @@ class ErnieServerTest < Test::Unit::TestCase
|
|
52
52
|
|
53
53
|
should "get an error on missing function" do
|
54
54
|
begin
|
55
|
-
@svc.call.
|
55
|
+
@svc.call.ext.mcfail(:fail)
|
56
56
|
fail "Expected a BERTRPC::ServerError"
|
57
57
|
rescue BERTRPC::ServerError => e
|
58
|
-
assert_equal "No such function '
|
58
|
+
assert_equal "No such function 'ext:mcfail'", e.message
|
59
59
|
end
|
60
60
|
end
|
61
61
|
end
|
@@ -63,9 +63,9 @@ class ErnieServerTest < Test::Unit::TestCase
|
|
63
63
|
context "cast" do
|
64
64
|
should "be received and return immediately" do
|
65
65
|
t0 = Time.now
|
66
|
-
assert_equal nil, @svc.cast.
|
66
|
+
assert_equal nil, @svc.cast.ext.set_state(7)
|
67
67
|
assert Time.now - t0 < 1
|
68
|
-
assert_equal 7, @svc.call.
|
68
|
+
assert_equal 7, @svc.call.ext.get_state
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
data/test/ernie_test.rb
CHANGED
@@ -1,50 +1,6 @@
|
|
1
1
|
require File.dirname(__FILE__) + '/helper'
|
2
2
|
|
3
3
|
class ErnieTest < Test::Unit::TestCase
|
4
|
-
context "mod" do
|
5
|
-
should "yield to the block" do
|
6
|
-
called = false
|
7
|
-
mod(:foo) { called = true }
|
8
|
-
assert called, "mod did not yield to the block"
|
9
|
-
end
|
10
|
-
|
11
|
-
should "add a mod to the mods hash" do
|
12
|
-
mod(:foo) { }
|
13
|
-
assert Ernie.mods[:foo]
|
14
|
-
assert Ernie.mods[:foo].instance_of?(Ernie::Mod)
|
15
|
-
end
|
16
|
-
|
17
|
-
should "overwrite previous mod with the same name" do
|
18
|
-
first_mod_object, second_mod_object = nil, nil
|
19
|
-
mod(:foo) { first_mod_object = Ernie.current_mod }
|
20
|
-
mod(:foo) { second_mod_object = Ernie.current_mod }
|
21
|
-
assert_not_same second_mod_object, first_mod_object
|
22
|
-
end
|
23
|
-
|
24
|
-
should "set the mod's name" do
|
25
|
-
mod(:foo) { }
|
26
|
-
assert_equal :foo, Ernie.mods[:foo].name
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
context "fun" do
|
31
|
-
should "add a fun to the funs hash of the mod" do
|
32
|
-
mod(:foo) { fun(:bar) { } }
|
33
|
-
assert Ernie.mods[:foo].funs[:bar]
|
34
|
-
end
|
35
|
-
|
36
|
-
should "dispatch to a fun" do
|
37
|
-
mod(:foo) { fun(:echo) { |arg| arg } }
|
38
|
-
assert 'hello', Ernie.dispatch(:foo, :echo, 'hello')
|
39
|
-
end
|
40
|
-
|
41
|
-
should "fail when no block is provided" do
|
42
|
-
assert_raises(TypeError) do
|
43
|
-
mod(:foo) { fun(:bar) }
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
4
|
module TestExposingModule
|
49
5
|
def foo
|
50
6
|
end
|