ernie 0.3.1 → 0.3.2
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 +9 -2
- data/VERSION.yml +1 -1
- data/bin/ernie +13 -1
- data/elib/ernie_server.erl +77 -36
- data/ernie.gemspec +2 -2
- data/lib/ernie.rb +11 -0
- metadata +2 -2
data/README.md
CHANGED
@@ -13,13 +13,20 @@ Description
|
|
13
13
|
|
14
14
|
Ernie is a BERT-RPC server implementation that uses an Erlang server to accept incoming connections, and then delegates the request to a Ruby handler via Erlectricity.
|
15
15
|
|
16
|
+
Ernie currently supports the following BERT-RPC features:
|
17
|
+
* `call` requests
|
18
|
+
* `cast` requests
|
19
|
+
|
16
20
|
|
17
21
|
Installation
|
18
22
|
------------
|
19
23
|
|
20
|
-
|
24
|
+
You must have Erlang installed before installing Ernie.
|
25
|
+
|
26
|
+
From GitHub:
|
21
27
|
|
22
|
-
gem install mojombo-ernie -s http://gems.github.com
|
28
|
+
gem install mojombo-ernie -s http://gems.github.com \
|
29
|
+
-s http://gemcutter.org
|
23
30
|
|
24
31
|
|
25
32
|
Running
|
data/VERSION.yml
CHANGED
data/bin/ernie
CHANGED
@@ -14,14 +14,25 @@ def code_paths
|
|
14
14
|
DEFAULT_ERLANG_CODEPATHS.map {|n| "-pz #{rel(n)}" }.join(" ") + " \\"
|
15
15
|
end
|
16
16
|
|
17
|
+
def version
|
18
|
+
yml = YAML.load(File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION.yml])))
|
19
|
+
"#{yml[:major]}.#{yml[:minor]}.#{yml[:patch]}"
|
20
|
+
end
|
21
|
+
|
17
22
|
require 'optparse'
|
18
23
|
require 'pp'
|
24
|
+
require 'yaml'
|
19
25
|
|
20
26
|
help = <<HELP
|
21
27
|
Ernie is an Erlang/Ruby BERT-RPC Server.
|
22
28
|
|
23
29
|
Basic Command Line Usage:
|
24
|
-
ernie [options]
|
30
|
+
ernie [command] [options]
|
31
|
+
|
32
|
+
Commands:
|
33
|
+
<none> Start an Ernie server.
|
34
|
+
reload-handlers Gracefully reload all of the the ruby handlers
|
35
|
+
and use the new code for all subsequent requests.
|
25
36
|
|
26
37
|
Options:
|
27
38
|
HELP
|
@@ -29,6 +40,7 @@ HELP
|
|
29
40
|
options = {}
|
30
41
|
OptionParser.new do |opts|
|
31
42
|
opts.banner = help
|
43
|
+
opts.version = version
|
32
44
|
|
33
45
|
opts.on("-h HANDLER", "--handler HANDLER", "Handler ruby file") do |x|
|
34
46
|
options[:handler] = x
|
data/elib/ernie_server.erl
CHANGED
@@ -11,6 +11,10 @@
|
|
11
11
|
-record(state, {lsock = undefined,
|
12
12
|
pending = queue:new()}).
|
13
13
|
|
14
|
+
-record(request, {sock = undefined,
|
15
|
+
info = undefined,
|
16
|
+
action = undefined}).
|
17
|
+
|
14
18
|
%%====================================================================
|
15
19
|
%% API
|
16
20
|
%%====================================================================
|
@@ -64,30 +68,17 @@ handle_call(_Request, _From, State) ->
|
|
64
68
|
%% Description: Handling cast messages
|
65
69
|
%%--------------------------------------------------------------------
|
66
70
|
handle_cast({process, Sock}, State) ->
|
67
|
-
|
68
|
-
|
69
|
-
|
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;
|
71
|
+
Request = #request{sock = Sock},
|
72
|
+
State2 = receive_term(Request, State),
|
73
|
+
{noreply, State2};
|
83
74
|
handle_cast({asset_freed}, State) ->
|
84
75
|
case queue:is_empty(State#state.pending) of
|
85
76
|
false ->
|
86
77
|
case asset_pool:lease() of
|
87
78
|
{ok, Asset} ->
|
88
|
-
{{value,
|
79
|
+
{{value, Request}, Pending2} = queue:out(State#state.pending),
|
89
80
|
% io:format("d", []),
|
90
|
-
spawn(fun() -> process_now(
|
81
|
+
spawn(fun() -> process_now(Request, Asset) end),
|
91
82
|
{noreply, State#state{pending = Pending2}};
|
92
83
|
empty ->
|
93
84
|
% io:format(".", []),
|
@@ -135,41 +126,91 @@ process_admin(Sock, reload_handlers, _Args, State) ->
|
|
135
126
|
gen_tcp:send(Sock, term_to_binary({reply, <<"Handlers reloaded.">>})),
|
136
127
|
ok = gen_tcp:close(Sock),
|
137
128
|
State;
|
138
|
-
process_admin(Sock,
|
129
|
+
process_admin(Sock, _Fun, _Args, State) ->
|
139
130
|
gen_tcp:send(Sock, term_to_binary({reply, <<"Admin function not supported.">>})),
|
140
131
|
ok = gen_tcp:close(Sock),
|
141
132
|
State.
|
142
133
|
|
143
|
-
|
134
|
+
receive_term(Request, State) ->
|
135
|
+
Sock = Request#request.sock,
|
136
|
+
case gen_tcp:recv(Sock, 0) of
|
137
|
+
{ok, BinaryTerm} ->
|
138
|
+
logger:debug("Got binary term: ~p~n", [BinaryTerm]),
|
139
|
+
Term = binary_to_term(BinaryTerm),
|
140
|
+
logger:info("Got term: ~p~n", [Term]),
|
141
|
+
case Term of
|
142
|
+
{call, '__admin__', Fun, Args} ->
|
143
|
+
process_admin(Sock, Fun, Args, State);
|
144
|
+
{info, _Command, _Args} ->
|
145
|
+
Request2 = Request#request{info = BinaryTerm},
|
146
|
+
receive_term(Request2, State);
|
147
|
+
_Any ->
|
148
|
+
Request2 = Request#request{action = BinaryTerm},
|
149
|
+
process_request(Request2, State)
|
150
|
+
end;
|
151
|
+
{error, closed} ->
|
152
|
+
ok = gen_tcp:close(Sock),
|
153
|
+
State
|
154
|
+
end.
|
155
|
+
|
156
|
+
process_request(Request, State) ->
|
157
|
+
ActionTerm = binary_to_term(Request#request.action),
|
158
|
+
case ActionTerm of
|
159
|
+
{cast, _Mod, _Fun, _Args} ->
|
160
|
+
Sock = Request#request.sock,
|
161
|
+
gen_tcp:send(Sock, term_to_binary({noreply})),
|
162
|
+
ok = gen_tcp:close(Sock),
|
163
|
+
logger:debug("Closing cast.~n", []);
|
164
|
+
_Any ->
|
165
|
+
ok
|
166
|
+
end,
|
144
167
|
case queue:is_empty(State#state.pending) of
|
145
168
|
false ->
|
146
|
-
Pending2 = queue:in(
|
169
|
+
Pending2 = queue:in(Request, State#state.pending),
|
147
170
|
% io:format("Q", []),
|
148
171
|
State#state{pending = Pending2};
|
149
172
|
true ->
|
150
|
-
try_process_now(
|
173
|
+
try_process_now(Request, State)
|
151
174
|
end.
|
152
175
|
|
153
|
-
try_process_now(
|
176
|
+
try_process_now(Request, State) ->
|
154
177
|
case asset_pool:lease() of
|
155
178
|
{ok, Asset} ->
|
156
179
|
% io:format("i", []),
|
157
|
-
spawn(fun() -> process_now(
|
180
|
+
spawn(fun() -> process_now(Request, Asset) end),
|
158
181
|
State;
|
159
182
|
empty ->
|
160
183
|
% io:format("q", []),
|
161
|
-
Pending2 = queue:in(
|
184
|
+
Pending2 = queue:in(Request, State#state.pending),
|
162
185
|
State#state{pending = Pending2}
|
163
186
|
end.
|
164
187
|
|
165
|
-
process_now(
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
188
|
+
process_now(Request, Asset) ->
|
189
|
+
try unsafe_process_now(Request, Asset) of
|
190
|
+
_AnyResponse -> ok
|
191
|
+
catch
|
192
|
+
_AnyError -> ok
|
193
|
+
after
|
194
|
+
asset_pool:return(Asset),
|
195
|
+
ernie_server:asset_freed(),
|
196
|
+
gen_tcp:close(Request#request.sock)
|
197
|
+
end.
|
198
|
+
|
199
|
+
unsafe_process_now(Request, Asset) ->
|
200
|
+
BinaryTerm = Request#request.action,
|
201
|
+
Term = binary_to_term(BinaryTerm),
|
202
|
+
case Term of
|
203
|
+
{call, Mod, Fun, Args} ->
|
204
|
+
logger:debug("Calling ~p:~p(~p)~n", [Mod, Fun, Args]),
|
205
|
+
Sock = Request#request.sock,
|
206
|
+
{asset, Port, Token} = Asset,
|
207
|
+
logger:debug("Asset: ~p ~p~n", [Port, Token]),
|
208
|
+
{ok, Data} = port_wrapper:rpc(Port, BinaryTerm),
|
209
|
+
gen_tcp:send(Sock, Data),
|
210
|
+
ok = gen_tcp:close(Sock);
|
211
|
+
{cast, Mod, Fun, Args} ->
|
212
|
+
logger:debug("Casting ~p:~p(~p)~n", [Mod, Fun, Args]),
|
213
|
+
{asset, Port, Token} = Asset,
|
214
|
+
logger:debug("Asset: ~p ~p~n", [Port, Token]),
|
215
|
+
{ok, _Data} = port_wrapper:rpc(Port, BinaryTerm)
|
216
|
+
end.
|
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.3.
|
5
|
+
s.version = "0.3.2"
|
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-08-
|
9
|
+
s.date = %q{2009-08-20}
|
10
10
|
s.default_executable = %q{ernie}
|
11
11
|
s.email = %q{tom@mojombo.com}
|
12
12
|
s.executables = ["ernie"]
|
data/lib/ernie.rb
CHANGED
@@ -83,6 +83,17 @@ class Ernie
|
|
83
83
|
f.receive_loop
|
84
84
|
end
|
85
85
|
|
86
|
+
f.when([:cast, Symbol, Symbol, Array]) do |mod, fun, args|
|
87
|
+
self.log("-> " + [:cast, mod, fun, args].inspect)
|
88
|
+
begin
|
89
|
+
self.dispatch(mod, fun, args)
|
90
|
+
rescue Object => e
|
91
|
+
# ignore
|
92
|
+
end
|
93
|
+
f.send!([:noreply])
|
94
|
+
f.receive_loop
|
95
|
+
end
|
96
|
+
|
86
97
|
f.when(Any) do |any|
|
87
98
|
self.log("-> " + any.inspect)
|
88
99
|
xres = [:error, [:server, 0, "Invalid request: #{any.inspect}"]]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ernie
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.2
|
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-08-
|
12
|
+
date: 2009-08-20 00:00:00 -07:00
|
13
13
|
default_executable: ernie
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|