ernie 1.3.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|