mojombo-ernie 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +13 -1
- data/VERSION.yml +1 -1
- data/bin/ernie +6 -0
- data/elib/asset_pool.erl +100 -0
- data/elib/asset_pool_sup.erl +15 -0
- data/elib/ernie_server.erl +60 -11
- data/elib/ernie_server_app.erl +2 -1
- data/elib/ernie_server_sup.erl +1 -3
- data/ernie.gemspec +65 -0
- data/examples/calc.rb +8 -0
- metadata +7 -2
data/README.md
CHANGED
@@ -26,8 +26,8 @@ Running
|
|
26
26
|
-------
|
27
27
|
|
28
28
|
Usage: ernie [options] <handler>
|
29
|
-
-n, --name NAME Node name
|
30
29
|
-p, --port PORT Port
|
30
|
+
-n, --number NUMBER Number of handler instances
|
31
31
|
-d, --detached Run as a daemon
|
32
32
|
-P, --pidfile PIDFILE Location to write pid file.
|
33
33
|
|
@@ -52,6 +52,18 @@ Example BERT-RPC call for above example
|
|
52
52
|
<- {reply, 3}
|
53
53
|
|
54
54
|
|
55
|
+
Using the BERTRPC gem to make calls to Ernie
|
56
|
+
--------------------------------------------
|
57
|
+
|
58
|
+
You can make BERT-RPC calls from Ruby with the [BERTRPC gem](http://github.com/mojombo/bertrpc):
|
59
|
+
|
60
|
+
require 'bertrpc'
|
61
|
+
|
62
|
+
svc = BERTRPC::Service.new('localhost', 8000)
|
63
|
+
svc.calc.add.call(1, 2)
|
64
|
+
# => 3
|
65
|
+
|
66
|
+
|
55
67
|
Contribute
|
56
68
|
----------
|
57
69
|
|
data/VERSION.yml
CHANGED
data/bin/ernie
CHANGED
@@ -33,6 +33,10 @@ OptionParser.new do |opts|
|
|
33
33
|
options[:port] = x
|
34
34
|
end
|
35
35
|
|
36
|
+
opts.on("-n NUMBER", "--number NUMBER", "Number of handler instances") do |x|
|
37
|
+
options[:number] = x
|
38
|
+
end
|
39
|
+
|
36
40
|
opts.on("-d", "--detached", "Run as a daemon") do
|
37
41
|
options[:detached] = true
|
38
42
|
end
|
@@ -50,6 +54,7 @@ unless handler
|
|
50
54
|
end
|
51
55
|
|
52
56
|
port = options[:port] || 8000
|
57
|
+
number = options[:number] || 1
|
53
58
|
pidfile = options[:pidfile] ? "-ernie_server_app pidfile \"'#{options[:pidfile]}'\"" : ''
|
54
59
|
detached = options[:detached] ? '-detached' : ''
|
55
60
|
|
@@ -62,6 +67,7 @@ cmd = %Q{erl -boot start_sasl \
|
|
62
67
|
#{pidfile} \
|
63
68
|
-ernie_server_app port #{port} \
|
64
69
|
-ernie_server_app handler '"#{handler}"' \
|
70
|
+
-ernie_server_app number #{number} \
|
65
71
|
-run ernie_server_app boot}.squeeze(' ')
|
66
72
|
puts cmd
|
67
73
|
exec(cmd)
|
data/elib/asset_pool.erl
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
-module(asset_pool).
|
2
|
+
-behaviour(gen_server).
|
3
|
+
|
4
|
+
%% api
|
5
|
+
-export([start_link/1, start/1, lease/0, return/1]).
|
6
|
+
|
7
|
+
%% gen_server callbacks
|
8
|
+
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
9
|
+
terminate/2, code_change/3]).
|
10
|
+
|
11
|
+
-record(state, {assets = undefined,
|
12
|
+
handler = undefined}).
|
13
|
+
|
14
|
+
%%====================================================================
|
15
|
+
%% API
|
16
|
+
%%====================================================================
|
17
|
+
|
18
|
+
start_link(Args) ->
|
19
|
+
gen_server:start_link({global, ?MODULE}, ?MODULE, Args, []).
|
20
|
+
|
21
|
+
start(Args) ->
|
22
|
+
gen_server:start({global, ?MODULE}, ?MODULE, Args, []).
|
23
|
+
|
24
|
+
lease() ->
|
25
|
+
gen_server:call({global, ?MODULE}, {lease}).
|
26
|
+
|
27
|
+
return(Asset) ->
|
28
|
+
gen_server:call({global, ?MODULE}, {return, Asset}).
|
29
|
+
|
30
|
+
%%====================================================================
|
31
|
+
%% gen_server callbacks
|
32
|
+
%%====================================================================
|
33
|
+
|
34
|
+
%%--------------------------------------------------------------------
|
35
|
+
%% Function: init(Args) -> {ok, State} |
|
36
|
+
%% {ok, State, Timeout} |
|
37
|
+
%% ignore |
|
38
|
+
%% {stop, Reason}
|
39
|
+
%% Description: Initiates the server
|
40
|
+
%%--------------------------------------------------------------------
|
41
|
+
init([Count, Handler]) ->
|
42
|
+
process_flag(trap_exit, true),
|
43
|
+
error_logger:info_msg("~p starting~n", [?MODULE]),
|
44
|
+
Assets = start_handlers(Count, Handler),
|
45
|
+
{ok, #state{assets = Assets, handler = Handler}}.
|
46
|
+
|
47
|
+
%%--------------------------------------------------------------------
|
48
|
+
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
49
|
+
%% {reply, Reply, State, Timeout} |
|
50
|
+
%% {noreply, State} |
|
51
|
+
%% {noreply, State, Timeout} |
|
52
|
+
%% {stop, Reason, Reply, State} |
|
53
|
+
%% {stop, Reason, State}
|
54
|
+
%% Description: Handling call messages
|
55
|
+
%%--------------------------------------------------------------------
|
56
|
+
handle_call({lease}, _From, State) ->
|
57
|
+
case queue:out(State#state.assets) of
|
58
|
+
{{value, Asset}, Assets2} -> {reply, {ok, Asset}, State#state{assets = Assets2}};
|
59
|
+
{empty, _Assets2} -> {reply, empty, State}
|
60
|
+
end;
|
61
|
+
handle_call({return, Asset}, _From, State) ->
|
62
|
+
Assets2 = queue:in(Asset, State#state.assets),
|
63
|
+
{reply, ok, State#state{assets = Assets2}};
|
64
|
+
handle_call(_Request, _From, State) ->
|
65
|
+
{reply, ok, State}.
|
66
|
+
|
67
|
+
%%--------------------------------------------------------------------
|
68
|
+
%% Function: handle_cast(Msg, State) -> {noreply, State} |
|
69
|
+
%% {noreply, State, Timeout} |
|
70
|
+
%% {stop, Reason, State}
|
71
|
+
%% Description: Handling cast messages
|
72
|
+
%%--------------------------------------------------------------------
|
73
|
+
handle_cast(_Msg, State) -> {noreply, State}.
|
74
|
+
|
75
|
+
handle_info({'EXIT', _Pid, _Error}, State) ->
|
76
|
+
error_logger:error_msg("Port closed, restarting port...~n", []),
|
77
|
+
Handler = State#state.handler,
|
78
|
+
Asset = port_wrapper:wrap_link("ruby " ++ Handler),
|
79
|
+
Assets = queue:in(Asset, State#state.assets),
|
80
|
+
{noreply, State#state{assets = Assets}};
|
81
|
+
handle_info(Msg, State) ->
|
82
|
+
error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
|
83
|
+
{noreply, State}.
|
84
|
+
|
85
|
+
terminate(_Reason, _State) -> ok.
|
86
|
+
code_change(_OldVersion, State, _Extra) -> {ok, State}.
|
87
|
+
|
88
|
+
%%====================================================================
|
89
|
+
%% Internal
|
90
|
+
%%====================================================================
|
91
|
+
|
92
|
+
start_handlers(Count, Handler) ->
|
93
|
+
start_handlers(queue:new(), Count, Handler).
|
94
|
+
|
95
|
+
start_handlers(Assets, 0, _Handler) ->
|
96
|
+
Assets;
|
97
|
+
start_handlers(Assets, Count, Handler) ->
|
98
|
+
Asset = port_wrapper:wrap_link("ruby " ++ Handler),
|
99
|
+
Assets2 = queue:in(Asset, Assets),
|
100
|
+
start_handlers(Assets2, Count - 1, Handler).
|
@@ -0,0 +1,15 @@
|
|
1
|
+
-module(asset_pool_sup).
|
2
|
+
-behaviour(supervisor).
|
3
|
+
-export([start_link/0, init/1]).
|
4
|
+
|
5
|
+
start_link() ->
|
6
|
+
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
|
7
|
+
|
8
|
+
init([]) ->
|
9
|
+
{ok, Handler} = application:get_env(ernie_server_app, handler),
|
10
|
+
io:format("Using handler ~p~n", [Handler]),
|
11
|
+
{ok, Number} = application:get_env(ernie_server_app, number),
|
12
|
+
io:format("Using ~p handler instances~n", [Number]),
|
13
|
+
{ok, {{one_for_one, 1, 60},
|
14
|
+
[{asset_pool, {asset_pool, start_link, [[Number, Handler]]},
|
15
|
+
permanent, brutal_kill, worker, [asset_pool]}]}}.
|
data/elib/ernie_server.erl
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
-behaviour(gen_server).
|
3
3
|
|
4
4
|
%% api
|
5
|
-
-export([start_link/1, start/1]).
|
5
|
+
-export([start_link/1, start/1, process/1, asset_freed/0]).
|
6
6
|
|
7
7
|
%% gen_server callbacks
|
8
8
|
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
|
9
9
|
terminate/2, code_change/3]).
|
10
10
|
|
11
11
|
-record(state, {lsock = undefined,
|
12
|
-
|
12
|
+
pending = queue:new()}).
|
13
13
|
|
14
14
|
%%====================================================================
|
15
15
|
%% API
|
@@ -21,6 +21,12 @@ start_link(Args) ->
|
|
21
21
|
start(Args) ->
|
22
22
|
gen_server:start({global, ?MODULE}, ?MODULE, Args, []).
|
23
23
|
|
24
|
+
process(Sock) ->
|
25
|
+
gen_server:cast({global, ?MODULE}, {process, Sock}).
|
26
|
+
|
27
|
+
asset_freed() ->
|
28
|
+
gen_server:cast({global, ?MODULE}, {asset_freed}).
|
29
|
+
|
24
30
|
%%====================================================================
|
25
31
|
%% gen_server callbacks
|
26
32
|
%%====================================================================
|
@@ -32,13 +38,12 @@ start(Args) ->
|
|
32
38
|
%% {stop, Reason}
|
33
39
|
%% Description: Initiates the server
|
34
40
|
%%--------------------------------------------------------------------
|
35
|
-
init([Port
|
41
|
+
init([Port]) ->
|
36
42
|
process_flag(trap_exit, true),
|
37
43
|
error_logger:info_msg("~p starting~n", [?MODULE]),
|
38
|
-
Ducky = port_wrapper:wrap("ruby " ++ Handler),
|
39
44
|
{ok, LSock} = try_listen(Port, 500),
|
40
|
-
spawn(fun() -> loop(LSock
|
41
|
-
{ok, #state{lsock = LSock
|
45
|
+
spawn(fun() -> loop(LSock) end),
|
46
|
+
{ok, #state{lsock = LSock}}.
|
42
47
|
|
43
48
|
%%--------------------------------------------------------------------
|
44
49
|
%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
|
@@ -58,6 +63,32 @@ handle_call(_Request, _From, State) ->
|
|
58
63
|
%% {stop, Reason, State}
|
59
64
|
%% Description: Handling cast messages
|
60
65
|
%%--------------------------------------------------------------------
|
66
|
+
handle_cast({process, Sock}, State) ->
|
67
|
+
case queue:is_empty(State#state.pending) of
|
68
|
+
false ->
|
69
|
+
Pending2 = queue:in(Sock, State#state.pending),
|
70
|
+
io:format("Q", []),
|
71
|
+
{noreply, State#state{pending = Pending2}};
|
72
|
+
true ->
|
73
|
+
State2 = try_process_now(Sock, State),
|
74
|
+
{noreply, State2}
|
75
|
+
end;
|
76
|
+
handle_cast({asset_freed}, State) ->
|
77
|
+
case queue:is_empty(State#state.pending) of
|
78
|
+
false ->
|
79
|
+
case asset_pool:lease() of
|
80
|
+
{ok, Asset} ->
|
81
|
+
{{value, Sock}, Pending2} = queue:out(State#state.pending),
|
82
|
+
io:format("d", []),
|
83
|
+
spawn(fun() -> process_now(Sock, Asset) end),
|
84
|
+
{noreply, State#state{pending = Pending2}};
|
85
|
+
empty ->
|
86
|
+
io:format(".", []),
|
87
|
+
{noreply, State}
|
88
|
+
end;
|
89
|
+
true ->
|
90
|
+
{noreply, State}
|
91
|
+
end;
|
61
92
|
handle_cast(_Msg, State) -> {noreply, State}.
|
62
93
|
|
63
94
|
handle_info(Msg, State) ->
|
@@ -86,19 +117,37 @@ try_listen(Port, Times) ->
|
|
86
117
|
try_listen(Port, Times - 1)
|
87
118
|
end.
|
88
119
|
|
89
|
-
loop(LSock
|
120
|
+
loop(LSock) ->
|
90
121
|
{ok, Sock} = gen_tcp:accept(LSock),
|
91
|
-
|
92
|
-
loop(LSock
|
122
|
+
ernie_server:process(Sock),
|
123
|
+
loop(LSock).
|
124
|
+
|
125
|
+
try_process_now(Sock, State) ->
|
126
|
+
case asset_pool:lease() of
|
127
|
+
{ok, Asset} ->
|
128
|
+
io:format("i", []),
|
129
|
+
spawn(fun() -> process_now(Sock, Asset) end),
|
130
|
+
State;
|
131
|
+
empty ->
|
132
|
+
io:format("q", []),
|
133
|
+
Pending2 = queue:in(Sock, State#state.pending),
|
134
|
+
State#state{pending = Pending2}
|
135
|
+
end.
|
93
136
|
|
94
|
-
|
137
|
+
process_now(Sock, Asset) ->
|
95
138
|
case gen_tcp:recv(Sock, 0) of
|
96
139
|
{ok, BinaryTerm} ->
|
140
|
+
% io:format(".", []),
|
97
141
|
% error_logger:info_msg("From Internet: ~p~n", [BinaryTerm]),
|
98
|
-
{ok, Data} = port_wrapper:rpc(
|
142
|
+
{ok, Data} = port_wrapper:rpc(Asset, BinaryTerm),
|
99
143
|
% error_logger:info_msg("From Port: ~p~n", [Data]),
|
144
|
+
asset_pool:return(Asset),
|
145
|
+
ernie_server:asset_freed(),
|
100
146
|
gen_tcp:send(Sock, Data),
|
101
147
|
ok = gen_tcp:close(Sock);
|
102
148
|
{error, closed} ->
|
149
|
+
asset_pool:return(Asset),
|
150
|
+
ernie_server:asset_freed(),
|
151
|
+
io:format("c", []),
|
103
152
|
ok = gen_tcp:close(Sock)
|
104
153
|
end.
|
data/elib/ernie_server_app.erl
CHANGED
data/elib/ernie_server_sup.erl
CHANGED
@@ -8,8 +8,6 @@ start_link() ->
|
|
8
8
|
init([]) ->
|
9
9
|
{ok, Port} = application:get_env(ernie_server_app, port),
|
10
10
|
io:format("Using port ~p~n", [Port]),
|
11
|
-
{ok, Handler} = application:get_env(ernie_server_app, handler),
|
12
|
-
io:format("Using handler ~p~n", [Handler]),
|
13
11
|
case application:get_env(ernie_server_app, pidfile) of
|
14
12
|
{ok, Location} ->
|
15
13
|
Pid = os:getpid(),
|
@@ -17,5 +15,5 @@ init([]) ->
|
|
17
15
|
undefined -> ok
|
18
16
|
end,
|
19
17
|
{ok, {{one_for_one, 1, 60},
|
20
|
-
[{ernie_server, {ernie_server, start_link, [[Port
|
18
|
+
[{ernie_server, {ernie_server, start_link, [[Port]]},
|
21
19
|
permanent, brutal_kill, worker, [ernie_server]}]}}.
|
data/ernie.gemspec
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{ernie}
|
5
|
+
s.version = "0.2.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Tom Preston-Werner"]
|
9
|
+
s.date = %q{2009-07-09}
|
10
|
+
s.default_executable = %q{ernie}
|
11
|
+
s.email = %q{tom@mojombo.com}
|
12
|
+
s.executables = ["ernie"]
|
13
|
+
s.extensions = ["ext/extconf.rb"]
|
14
|
+
s.extra_rdoc_files = [
|
15
|
+
"LICENSE",
|
16
|
+
"README.md"
|
17
|
+
]
|
18
|
+
s.files = [
|
19
|
+
".document",
|
20
|
+
".gitignore",
|
21
|
+
"LICENSE",
|
22
|
+
"README.md",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION.yml",
|
25
|
+
"bin/ernie",
|
26
|
+
"ebin/ernie_server_app.app",
|
27
|
+
"elib/asset_pool.erl",
|
28
|
+
"elib/asset_pool_sup.erl",
|
29
|
+
"elib/ernie_server.erl",
|
30
|
+
"elib/ernie_server_app.erl",
|
31
|
+
"elib/ernie_server_sup.erl",
|
32
|
+
"elib/port_wrapper.erl",
|
33
|
+
"ernie.gemspec",
|
34
|
+
"examples/calc.rb",
|
35
|
+
"ext/Makefile",
|
36
|
+
"ext/extconf.rb",
|
37
|
+
"lib/ernie.rb",
|
38
|
+
"test/ernie_test.rb",
|
39
|
+
"test/test_helper.rb"
|
40
|
+
]
|
41
|
+
s.has_rdoc = true
|
42
|
+
s.homepage = %q{http://github.com/mojombo/ernie}
|
43
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
44
|
+
s.require_paths = ["lib"]
|
45
|
+
s.rubygems_version = %q{1.3.0}
|
46
|
+
s.summary = %q{TODO}
|
47
|
+
s.test_files = [
|
48
|
+
"test/ernie_test.rb",
|
49
|
+
"test/test_helper.rb",
|
50
|
+
"examples/calc.rb"
|
51
|
+
]
|
52
|
+
|
53
|
+
if s.respond_to? :specification_version then
|
54
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
55
|
+
s.specification_version = 2
|
56
|
+
|
57
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
58
|
+
s.add_runtime_dependency(%q<erlectricity>, [">= 1.0.1"])
|
59
|
+
else
|
60
|
+
s.add_dependency(%q<erlectricity>, [">= 1.0.1"])
|
61
|
+
end
|
62
|
+
else
|
63
|
+
s.add_dependency(%q<erlectricity>, [">= 1.0.1"])
|
64
|
+
end
|
65
|
+
end
|
data/examples/calc.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mojombo-ernie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Preston-Werner
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-07-09 00:00:00 -07:00
|
13
13
|
default_executable: ernie
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -40,10 +40,14 @@ files:
|
|
40
40
|
- VERSION.yml
|
41
41
|
- bin/ernie
|
42
42
|
- ebin/ernie_server_app.app
|
43
|
+
- elib/asset_pool.erl
|
44
|
+
- elib/asset_pool_sup.erl
|
43
45
|
- elib/ernie_server.erl
|
44
46
|
- elib/ernie_server_app.erl
|
45
47
|
- elib/ernie_server_sup.erl
|
46
48
|
- elib/port_wrapper.erl
|
49
|
+
- ernie.gemspec
|
50
|
+
- examples/calc.rb
|
47
51
|
- ext/Makefile
|
48
52
|
- ext/extconf.rb
|
49
53
|
- lib/ernie.rb
|
@@ -78,3 +82,4 @@ summary: TODO
|
|
78
82
|
test_files:
|
79
83
|
- test/ernie_test.rb
|
80
84
|
- test/test_helper.rb
|
85
|
+
- examples/calc.rb
|