mojombo-ernie 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -25,12 +25,26 @@ Installation
25
25
  Running
26
26
  -------
27
27
 
28
- Usage: ernie [options] <handler>
28
+ Usage: ernie [command] [options]
29
+ -h, --handler HANDLER Handler file
29
30
  -p, --port PORT Port
30
31
  -n, --number NUMBER Number of handler instances
31
32
  -d, --detached Run as a daemon
32
33
  -P, --pidfile PIDFILE Location to write pid file.
33
-
34
+
35
+ Commands:
36
+ <none> Start an Ernie server.
37
+ reload-handlers Gracefully reload all of the the ruby handlers
38
+ and use the new code for all subsequent requests.
39
+
40
+ Examples:
41
+ ernie -d -p 9999 -n 10 -h calc.rb
42
+ Start the ernie server in the background on port 9999 with ten
43
+ handlers, using the calc.rb handler file.
44
+
45
+ ernie reload-handlers -p 9999
46
+ Reload the handlers for the ernie server currently running on
47
+ port 9999.
34
48
 
35
49
  Example Handler
36
50
  ---------------
@@ -60,7 +74,7 @@ You can make BERT-RPC calls from Ruby with the [BERTRPC gem](http://github.com/m
60
74
  require 'bertrpc'
61
75
 
62
76
  svc = BERTRPC::Service.new('localhost', 8000)
63
- svc.calc.add.call(1, 2)
77
+ svc.call.calc.add(1, 2)
64
78
  # => 3
65
79
 
66
80
 
data/Rakefile CHANGED
@@ -12,6 +12,7 @@ begin
12
12
  gem.files.include(["ext"])
13
13
  gem.extensions << 'ext/extconf.rb'
14
14
  gem.add_dependency('erlectricity', '>= 1.0.1')
15
+ gem.add_dependency('bertrpc', '>= 0.2.0')
15
16
 
16
17
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
18
  end
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
- :minor: 2
3
- :patch: 0
4
2
  :major: 0
3
+ :minor: 3
4
+ :patch: 0
data/bin/ernie CHANGED
@@ -4,6 +4,7 @@ $:.unshift(File.join(File.dirname(__FILE__), *%w[.. lib]))
4
4
  ERNIE_ROOT = File.join(File.dirname(__FILE__), *%w[..])
5
5
 
6
6
  DEFAULT_ERLANG_CODEPATHS = %w[ebin]
7
+ DEFAULT_PORT = 8000
7
8
 
8
9
  def rel(path)
9
10
  File.join(ERNIE_ROOT, path)
@@ -29,6 +30,10 @@ options = {}
29
30
  OptionParser.new do |opts|
30
31
  opts.banner = help
31
32
 
33
+ opts.on("-h HANDLER", "--handler HANDLER", "Handler ruby file") do |x|
34
+ options[:handler] = x
35
+ end
36
+
32
37
  opts.on("-p PORT", "--port PORT", "Port") do |x|
33
38
  options[:port] = x
34
39
  end
@@ -37,6 +42,10 @@ OptionParser.new do |opts|
37
42
  options[:number] = x
38
43
  end
39
44
 
45
+ opts.on("-l LOGLEVEL", "--log-level LOGLEVEL", "Log level (0-4)") do |x|
46
+ options[:log_level] = x
47
+ end
48
+
40
49
  opts.on("-d", "--detached", "Run as a daemon") do
41
50
  options[:detached] = true
42
51
  end
@@ -46,28 +55,43 @@ OptionParser.new do |opts|
46
55
  end
47
56
  end.parse!
48
57
 
49
- handler = ARGV[0]
58
+ if command = ARGV[0]
59
+ if !%w{reload-handlers}.include?(command)
60
+ puts "Invlalid command. Valid commands are:"
61
+ puts " reload-handlers"
62
+ exit(1)
63
+ end
50
64
 
51
- unless handler
52
- puts "A handler must be specified: ernie /path/to/handler.rb"
53
- exit(1)
54
- end
65
+ require 'rubygems'
66
+ require 'bertrpc'
67
+ port = options[:port] || DEFAULT_PORT
68
+ svc = BERTRPC::Service.new('localhost', port)
69
+ puts svc.call.__admin__.send(command.gsub(/-/, '_'))
70
+ else
71
+ if !options[:handler]
72
+ puts "A handler must be specified: ernie -h /path/to/handler.rb"
73
+ exit(1)
74
+ end
55
75
 
56
- port = options[:port] || 8000
57
- number = options[:number] || 1
58
- pidfile = options[:pidfile] ? "-ernie_server_app pidfile \"'#{options[:pidfile]}'\"" : ''
59
- detached = options[:detached] ? '-detached' : ''
60
-
61
- cmd = %Q{erl -boot start_sasl \
62
- #{detached} \
63
- +Bc \
64
- +K true \
65
- -smp enable \
66
- #{code_paths}
67
- #{pidfile} \
68
- -ernie_server_app port #{port} \
69
- -ernie_server_app handler '"#{handler}"' \
70
- -ernie_server_app number #{number} \
71
- -run ernie_server_app boot}.squeeze(' ')
72
- puts cmd
73
- exec(cmd)
76
+ handler = options[:handler]
77
+ port = options[:port] || DEFAULT_PORT
78
+ number = options[:number] || 1
79
+ log_level = options[:log_level] || 2
80
+ pidfile = options[:pidfile] ? "-ernie_server_app pidfile \"'#{options[:pidfile]}'\"" : ''
81
+ detached = options[:detached] ? '-detached' : ''
82
+
83
+ cmd = %Q{erl -boot start_sasl \
84
+ #{detached} \
85
+ +Bc \
86
+ +K true \
87
+ -smp enable \
88
+ #{code_paths}
89
+ #{pidfile} \
90
+ -ernie_server_app port #{port} \
91
+ -ernie_server_app handler '"#{handler}"' \
92
+ -ernie_server_app number #{number} \
93
+ -ernie_server_app log_level #{log_level} \
94
+ -run ernie_server_app boot}.squeeze(' ')
95
+ puts cmd
96
+ exec(cmd)
97
+ end
data/elib/asset_pool.erl CHANGED
@@ -2,14 +2,15 @@
2
2
  -behaviour(gen_server).
3
3
 
4
4
  %% api
5
- -export([start_link/1, start/1, lease/0, return/1]).
5
+ -export([start_link/1, start/1, lease/0, return/1, reload_assets/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, {assets = undefined,
12
- handler = undefined}).
12
+ handler = undefined,
13
+ token = undefined}).
13
14
 
14
15
  %%====================================================================
15
16
  %% API
@@ -27,6 +28,9 @@ lease() ->
27
28
  return(Asset) ->
28
29
  gen_server:call({global, ?MODULE}, {return, Asset}).
29
30
 
31
+ reload_assets() ->
32
+ gen_server:call({global, ?MODULE}, {reload_assets}).
33
+
30
34
  %%====================================================================
31
35
  %% gen_server callbacks
32
36
  %%====================================================================
@@ -41,8 +45,9 @@ return(Asset) ->
41
45
  init([Count, Handler]) ->
42
46
  process_flag(trap_exit, true),
43
47
  error_logger:info_msg("~p starting~n", [?MODULE]),
44
- Assets = start_handlers(Count, Handler),
45
- {ok, #state{assets = Assets, handler = Handler}}.
48
+ Token = make_ref(),
49
+ Assets = start_handlers(Count, Handler, Token),
50
+ {ok, #state{assets = Assets, handler = Handler, token = Token}}.
46
51
 
47
52
  %%--------------------------------------------------------------------
48
53
  %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
@@ -54,13 +59,38 @@ init([Count, Handler]) ->
54
59
  %% Description: Handling call messages
55
60
  %%--------------------------------------------------------------------
56
61
  handle_call({lease}, _From, State) ->
62
+ Token = State#state.token,
57
63
  case queue:out(State#state.assets) of
58
- {{value, Asset}, Assets2} -> {reply, {ok, Asset}, State#state{assets = Assets2}};
59
- {empty, _Assets2} -> {reply, empty, State}
64
+ {{value, Asset}, Assets2} ->
65
+ {asset, Port, AssetToken} = Asset,
66
+ case AssetToken =:= Token of
67
+ false ->
68
+ port_wrapper:close(Port),
69
+ Handler = State#state.handler,
70
+ NewAsset = create_asset(Handler, Token);
71
+ true ->
72
+ NewAsset = Asset
73
+ end,
74
+ {reply, {ok, NewAsset}, State#state{assets = Assets2}};
75
+ {empty, _Assets2} ->
76
+ {reply, empty, State}
60
77
  end;
61
78
  handle_call({return, Asset}, _From, State) ->
62
- Assets2 = queue:in(Asset, State#state.assets),
79
+ Token = State#state.token,
80
+ {asset, Port, AssetToken} = Asset,
81
+ case AssetToken =:= Token of
82
+ false ->
83
+ port_wrapper:close(Port),
84
+ Handler = State#state.handler,
85
+ NewAsset = create_asset(Handler, Token);
86
+ true ->
87
+ NewAsset = Asset
88
+ end,
89
+ Assets2 = queue:in(NewAsset, State#state.assets),
63
90
  {reply, ok, State#state{assets = Assets2}};
91
+ handle_call({reload_assets}, _From, State) ->
92
+ Token = make_ref(),
93
+ {reply, ok, State#state{token = Token}};
64
94
  handle_call(_Request, _From, State) ->
65
95
  {reply, ok, State}.
66
96
 
@@ -72,11 +102,15 @@ handle_call(_Request, _From, State) ->
72
102
  %%--------------------------------------------------------------------
73
103
  handle_cast(_Msg, State) -> {noreply, State}.
74
104
 
75
- handle_info({'EXIT', _Pid, _Error}, State) ->
76
- error_logger:error_msg("Port closed, restarting port...~n", []),
105
+ handle_info({'EXIT', _Pid, normal}, State) ->
106
+ {noreply, State};
107
+ handle_info({'EXIT', Pid, Error}, State) ->
108
+ error_logger:error_msg("Port ~p closed with ~p, restarting port...~n", [Pid, Error]),
109
+ ValidAssets = queue:filter(fun(Item) -> {asset, A, _T} = Item, A =/= Pid end, State#state.assets),
77
110
  Handler = State#state.handler,
78
- Asset = port_wrapper:wrap_link("ruby " ++ Handler),
79
- Assets = queue:in(Asset, State#state.assets),
111
+ Token = State#state.token,
112
+ NewAsset = create_asset(Handler, Token),
113
+ Assets = queue:in(NewAsset, ValidAssets),
80
114
  {noreply, State#state{assets = Assets}};
81
115
  handle_info(Msg, State) ->
82
116
  error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
@@ -89,12 +123,15 @@ code_change(_OldVersion, State, _Extra) -> {ok, State}.
89
123
  %% Internal
90
124
  %%====================================================================
91
125
 
92
- start_handlers(Count, Handler) ->
93
- start_handlers(queue:new(), Count, Handler).
126
+ start_handlers(Count, Handler, Token) ->
127
+ start_handlers(queue:new(), Count, Handler, Token).
94
128
 
95
- start_handlers(Assets, 0, _Handler) ->
129
+ start_handlers(Assets, 0, _Handler, _Token) ->
96
130
  Assets;
97
- start_handlers(Assets, Count, Handler) ->
98
- Asset = port_wrapper:wrap_link("ruby " ++ Handler),
131
+ start_handlers(Assets, Count, Handler, Token) ->
132
+ Asset = create_asset(Handler, Token),
99
133
  Assets2 = queue:in(Asset, Assets),
100
- start_handlers(Assets2, Count - 1, Handler).
134
+ start_handlers(Assets2, Count - 1, Handler, Token).
135
+
136
+ create_asset(Handler, Token) ->
137
+ {asset, port_wrapper:wrap_link("ruby " ++ Handler), Token}.
@@ -64,31 +64,38 @@ handle_call(_Request, _From, State) ->
64
64
  %% Description: Handling cast messages
65
65
  %%--------------------------------------------------------------------
66
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;
67
+ case gen_tcp:recv(Sock, 0) of
68
+ {ok, BinaryTerm} ->
69
+ logger:debug("Got binary term: ~p~n", [BinaryTerm]),
70
+ Term = binary_to_term(BinaryTerm),
71
+ logger:info("Got term: ~p~n", [Term]),
72
+ case Term of
73
+ {call, '__admin__', Fun, Args} ->
74
+ State2 = process_admin(Sock, Fun, Args, State);
75
+ Any ->
76
+ State2 = process_normal(BinaryTerm, Sock, State)
77
+ end,
78
+ {noreply, State2};
79
+ {error, closed} ->
80
+ ok = gen_tcp:close(Sock),
81
+ {noreply, State}
82
+ end;
76
83
  handle_cast({asset_freed}, State) ->
77
84
  case queue:is_empty(State#state.pending) of
78
85
  false ->
79
86
  case asset_pool:lease() of
80
87
  {ok, Asset} ->
81
- {{value, Sock}, Pending2} = queue:out(State#state.pending),
82
- io:format("d", []),
83
- spawn(fun() -> process_now(Sock, Asset) end),
88
+ {{value, {pending, BinaryTerm, Sock}}, Pending2} = queue:out(State#state.pending),
89
+ % io:format("d", []),
90
+ spawn(fun() -> process_now(BinaryTerm, Sock, Asset) end),
84
91
  {noreply, State#state{pending = Pending2}};
85
92
  empty ->
86
- io:format(".", []),
93
+ % io:format(".", []),
87
94
  {noreply, State}
88
95
  end;
89
96
  true ->
90
97
  {noreply, State}
91
- end;
98
+ end;
92
99
  handle_cast(_Msg, State) -> {noreply, State}.
93
100
 
94
101
  handle_info(Msg, State) ->
@@ -106,7 +113,7 @@ try_listen(Port, 0) ->
106
113
  error_logger:error_msg("Could not listen on port ~p~n", [Port]),
107
114
  {error, "Could not listen on port"};
108
115
  try_listen(Port, Times) ->
109
- Res = gen_tcp:listen(Port, [binary, {packet, 4}, {active, false}]),
116
+ Res = gen_tcp:listen(Port, [binary, {packet, 4}, {active, false}, {reuseaddr, true}]),
110
117
  case Res of
111
118
  {ok, LSock} ->
112
119
  error_logger:info_msg("Listening on port ~p~n", [Port]),
@@ -119,35 +126,50 @@ try_listen(Port, Times) ->
119
126
 
120
127
  loop(LSock) ->
121
128
  {ok, Sock} = gen_tcp:accept(LSock),
129
+ logger:debug("Accepted socket: ~p~n", [Sock]),
122
130
  ernie_server:process(Sock),
123
131
  loop(LSock).
124
132
 
125
- try_process_now(Sock, State) ->
133
+ process_admin(Sock, reload_handlers, _Args, State) ->
134
+ asset_pool:reload_assets(),
135
+ gen_tcp:send(Sock, term_to_binary({reply, <<"Handlers reloaded.">>})),
136
+ ok = gen_tcp:close(Sock),
137
+ State;
138
+ process_admin(Sock, Fun, _Args, State) ->
139
+ gen_tcp:send(Sock, term_to_binary({reply, <<"Admin function not supported.">>})),
140
+ ok = gen_tcp:close(Sock),
141
+ State.
142
+
143
+ process_normal(BinaryTerm, Sock, State) ->
144
+ case queue:is_empty(State#state.pending) of
145
+ false ->
146
+ Pending2 = queue:in({pending, BinaryTerm, Sock}, State#state.pending),
147
+ % io:format("Q", []),
148
+ State#state{pending = Pending2};
149
+ true ->
150
+ try_process_now(BinaryTerm, Sock, State)
151
+ end.
152
+
153
+ try_process_now(BinaryTerm, Sock, State) ->
126
154
  case asset_pool:lease() of
127
155
  {ok, Asset} ->
128
- io:format("i", []),
129
- spawn(fun() -> process_now(Sock, Asset) end),
156
+ % io:format("i", []),
157
+ spawn(fun() -> process_now(BinaryTerm, Sock, Asset) end),
130
158
  State;
131
159
  empty ->
132
- io:format("q", []),
133
- Pending2 = queue:in(Sock, State#state.pending),
160
+ % io:format("q", []),
161
+ Pending2 = queue:in({pending, BinaryTerm, Sock}, State#state.pending),
134
162
  State#state{pending = Pending2}
135
163
  end.
136
164
 
137
- process_now(Sock, Asset) ->
138
- case gen_tcp:recv(Sock, 0) of
139
- {ok, BinaryTerm} ->
140
- % io:format(".", []),
141
- % error_logger:info_msg("From Internet: ~p~n", [BinaryTerm]),
142
- {ok, Data} = port_wrapper:rpc(Asset, BinaryTerm),
143
- % error_logger:info_msg("From Port: ~p~n", [Data]),
144
- asset_pool:return(Asset),
145
- ernie_server:asset_freed(),
146
- gen_tcp:send(Sock, Data),
147
- ok = gen_tcp:close(Sock);
148
- {error, closed} ->
149
- asset_pool:return(Asset),
150
- ernie_server:asset_freed(),
151
- io:format("c", []),
152
- ok = gen_tcp:close(Sock)
153
- end.
165
+ process_now(BinaryTerm, Sock, Asset) ->
166
+ % io:format(".", []),
167
+ % error_logger:info_msg("From Internet: ~p~n", [BinaryTerm]),
168
+ {asset, Port, Token} = Asset,
169
+ logger:debug("Asset: ~p ~p~n", [Port, Token]),
170
+ {ok, Data} = port_wrapper:rpc(Port, BinaryTerm),
171
+ % error_logger:info_msg("From Port: ~p~n", [Data]),
172
+ asset_pool:return(Asset),
173
+ ernie_server:asset_freed(),
174
+ gen_tcp:send(Sock, Data),
175
+ ok = gen_tcp:close(Sock).
@@ -7,6 +7,7 @@ boot() ->
7
7
  application:start(ernie_server_app).
8
8
 
9
9
  start(_Type, _Args) ->
10
+ logger_sup:start_link(),
10
11
  ernie_server_sup:start_link(),
11
12
  asset_pool_sup:start_link().
12
13
 
data/elib/logger.erl ADDED
@@ -0,0 +1,108 @@
1
+ -module(logger).
2
+ -behaviour(gen_server).
3
+
4
+ %% api
5
+ -export([start_link/1, start/1, set_log_level/1, debug/2, info/2, warn/2, error/2, fatal/2]).
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, {log_level = undefined}).
12
+
13
+ %%====================================================================
14
+ %% API
15
+ %%====================================================================
16
+
17
+ start_link(Args) ->
18
+ gen_server:start_link({global, ?MODULE}, ?MODULE, Args, []).
19
+
20
+ start(Args) ->
21
+ gen_server:start({global, ?MODULE}, ?MODULE, Args, []).
22
+
23
+ set_log_level(Level) ->
24
+ gen_server:call({global, ?MODULE}, {set_log_level, Level}).
25
+
26
+ debug(Msg, Args) ->
27
+ gen_server:cast({global, ?MODULE}, {debug, Msg, Args}).
28
+
29
+ info(Msg, Args) ->
30
+ gen_server:cast({global, ?MODULE}, {info, Msg, Args}).
31
+
32
+ warn(Msg, Args) ->
33
+ gen_server:cast({global, ?MODULE}, {warn, Msg, Args}).
34
+
35
+ error(Msg, Args) ->
36
+ gen_server:cast({global, ?MODULE}, {error, Msg, Args}).
37
+
38
+ fatal(Msg, Args) ->
39
+ gen_server:cast({global, ?MODULE}, {fatal, Msg, Args}).
40
+
41
+ %%====================================================================
42
+ %% gen_server callbacks
43
+ %%====================================================================
44
+
45
+ %%--------------------------------------------------------------------
46
+ %% Function: init(Args) -> {ok, State} |
47
+ %% {ok, State, Timeout} |
48
+ %% ignore |
49
+ %% {stop, Reason}
50
+ %% Description: Initiates the server
51
+ %%--------------------------------------------------------------------
52
+ init([LogLevel]) ->
53
+ error_logger:info_msg("~p starting~n", [?MODULE]),
54
+ {ok, #state{log_level = LogLevel}}.
55
+
56
+ %%--------------------------------------------------------------------
57
+ %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
58
+ %% {reply, Reply, State, Timeout} |
59
+ %% {noreply, State} |
60
+ %% {noreply, State, Timeout} |
61
+ %% {stop, Reason, Reply, State} |
62
+ %% {stop, Reason, State}
63
+ %% Description: Handling call messages
64
+ %%--------------------------------------------------------------------
65
+ handle_call({set_log_level, Level}, _From, State) ->
66
+ {reply, ok, State#state{log_level = Level}};
67
+ handle_call(_Request, _From, State) ->
68
+ {reply, ok, State}.
69
+
70
+ %%--------------------------------------------------------------------
71
+ %% Function: handle_cast(Msg, State) -> {noreply, State} |
72
+ %% {noreply, State, Timeout} |
73
+ %% {stop, Reason, State}
74
+ %% Description: Handling cast messages
75
+ %%--------------------------------------------------------------------
76
+ handle_cast({debug, Msg, Args}, State) ->
77
+ log(State#state.log_level, 4, Msg, Args),
78
+ {noreply, State};
79
+ handle_cast({info, Msg, Args}, State) ->
80
+ log(State#state.log_level, 3, Msg, Args),
81
+ {noreply, State};
82
+ handle_cast({warn, Msg, Args}, State) ->
83
+ log(State#state.log_level, 2, Msg, Args),
84
+ {noreply, State};
85
+ handle_cast({error, Msg, Args}, State) ->
86
+ log(State#state.log_level, 1, Msg, Args),
87
+ {noreply, State};
88
+ handle_cast({fatal, Msg, Args}, State) ->
89
+ log(State#state.log_level, 0, Msg, Args),
90
+ {noreply, State};
91
+ handle_cast(_Msg, State) -> {noreply, State}.
92
+
93
+ handle_info(Msg, State) ->
94
+ error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
95
+ {noreply, State}.
96
+
97
+ terminate(_Reason, _State) -> ok.
98
+ code_change(_OldVersion, State, _Extra) -> {ok, State}.
99
+
100
+ %%====================================================================
101
+ %% Internal
102
+ %%====================================================================
103
+
104
+ log(SystemLogLevel, MessageLogLevel, Message, Args) ->
105
+ case SystemLogLevel >= MessageLogLevel of
106
+ false -> ok;
107
+ true -> io:format(Message, Args)
108
+ end.
@@ -0,0 +1,13 @@
1
+ -module(logger_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, LogLevel} = application:get_env(ernie_server_app, log_level),
10
+ io:format("Using log level ~p~n", [LogLevel]),
11
+ {ok, {{one_for_one, 1, 60},
12
+ [{logger, {logger, start_link, [[LogLevel]]},
13
+ permanent, brutal_kill, worker, [logger]}]}}.
@@ -1,5 +1,5 @@
1
1
  -module(port_wrapper).
2
- -export([wrap/1, wrap/2, wrap_link/1, wrap_link/2, send/2, shutdown/1, rpc/2]).
2
+ -export([wrap/1, wrap/2, wrap_link/1, wrap_link/2, send/2, shutdown/1, close/1, rpc/2]).
3
3
 
4
4
  wrap(Command) ->
5
5
  spawn(fun() -> process_flag(trap_exit, true), Port = create_port(Command), loop(Port, infinity, Command) end).
@@ -27,6 +27,10 @@ shutdown(WrappedPort) ->
27
27
  WrappedPort ! shutdown,
28
28
  true.
29
29
 
30
+ close(WrappedPort) ->
31
+ WrappedPort ! noose,
32
+ true.
33
+
30
34
  create_port(Command) ->
31
35
  open_port({spawn, Command}, [{packet, 4}, nouse_stdio, exit_status, binary]).
32
36
 
data/ernie.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{ernie}
5
- s.version = "0.2.0"
5
+ s.version = "0.3.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Tom Preston-Werner"]
9
- s.date = %q{2009-07-09}
9
+ s.date = %q{2009-08-13}
10
10
  s.default_executable = %q{ernie}
11
11
  s.email = %q{tom@mojombo.com}
12
12
  s.executables = ["ernie"]
@@ -29,6 +29,8 @@ Gem::Specification.new do |s|
29
29
  "elib/ernie_server.erl",
30
30
  "elib/ernie_server_app.erl",
31
31
  "elib/ernie_server_sup.erl",
32
+ "elib/logger.erl",
33
+ "elib/logger_sup.erl",
32
34
  "elib/port_wrapper.erl",
33
35
  "ernie.gemspec",
34
36
  "examples/calc.rb",
@@ -36,30 +38,34 @@ Gem::Specification.new do |s|
36
38
  "ext/extconf.rb",
37
39
  "lib/ernie.rb",
38
40
  "test/ernie_test.rb",
41
+ "test/load.rb",
39
42
  "test/test_helper.rb"
40
43
  ]
41
- s.has_rdoc = true
42
44
  s.homepage = %q{http://github.com/mojombo/ernie}
43
45
  s.rdoc_options = ["--charset=UTF-8"]
44
46
  s.require_paths = ["lib"]
45
- s.rubygems_version = %q{1.3.0}
47
+ s.rubygems_version = %q{1.3.5}
46
48
  s.summary = %q{TODO}
47
49
  s.test_files = [
48
50
  "test/ernie_test.rb",
51
+ "test/load.rb",
49
52
  "test/test_helper.rb",
50
53
  "examples/calc.rb"
51
54
  ]
52
55
 
53
56
  if s.respond_to? :specification_version then
54
57
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
55
- s.specification_version = 2
58
+ s.specification_version = 3
56
59
 
57
60
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
58
61
  s.add_runtime_dependency(%q<erlectricity>, [">= 1.0.1"])
62
+ s.add_runtime_dependency(%q<bertrpc>, [">= 0.2.0"])
59
63
  else
60
64
  s.add_dependency(%q<erlectricity>, [">= 1.0.1"])
65
+ s.add_dependency(%q<bertrpc>, [">= 0.2.0"])
61
66
  end
62
67
  else
63
68
  s.add_dependency(%q<erlectricity>, [">= 1.0.1"])
69
+ s.add_dependency(%q<bertrpc>, [">= 0.2.0"])
64
70
  end
65
71
  end
data/test/load.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'bertrpc'
2
+
3
+ threads = []
4
+ svc = BERTRPC::Service.new('localhost', 8000)
5
+
6
+ 5.times do
7
+ threads << Thread.new do
8
+ i = 0
9
+ 100.times { i += svc.call.calc.add(1, 2) }
10
+ print "#{i}\n"
11
+ end
12
+ end
13
+
14
+ threads.each { |t| t.join }
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.2.0
4
+ version: 0.3.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-07-09 00:00:00 -07:00
12
+ date: 2009-08-13 00:00:00 -07:00
13
13
  default_executable: ernie
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -22,6 +22,16 @@ dependencies:
22
22
  - !ruby/object:Gem::Version
23
23
  version: 1.0.1
24
24
  version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: bertrpc
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.2.0
34
+ version:
25
35
  description:
26
36
  email: tom@mojombo.com
27
37
  executables:
@@ -45,6 +55,8 @@ files:
45
55
  - elib/ernie_server.erl
46
56
  - elib/ernie_server_app.erl
47
57
  - elib/ernie_server_sup.erl
58
+ - elib/logger.erl
59
+ - elib/logger_sup.erl
48
60
  - elib/port_wrapper.erl
49
61
  - ernie.gemspec
50
62
  - examples/calc.rb
@@ -52,9 +64,11 @@ files:
52
64
  - ext/extconf.rb
53
65
  - lib/ernie.rb
54
66
  - test/ernie_test.rb
67
+ - test/load.rb
55
68
  - test/test_helper.rb
56
- has_rdoc: true
69
+ has_rdoc: false
57
70
  homepage: http://github.com/mojombo/ernie
71
+ licenses:
58
72
  post_install_message:
59
73
  rdoc_options:
60
74
  - --charset=UTF-8
@@ -75,11 +89,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
89
  requirements: []
76
90
 
77
91
  rubyforge_project:
78
- rubygems_version: 1.2.0
92
+ rubygems_version: 1.3.5
79
93
  signing_key:
80
- specification_version: 2
94
+ specification_version: 3
81
95
  summary: TODO
82
96
  test_files:
83
97
  - test/ernie_test.rb
98
+ - test/load.rb
84
99
  - test/test_helper.rb
85
100
  - examples/calc.rb