mojombo-ernie 0.2.0 → 0.3.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/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