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 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
@@ -1,4 +1,4 @@
1
1
  ---
2
- :minor: 1
2
+ :minor: 2
3
3
  :patch: 0
4
4
  :major: 0
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)
@@ -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]}]}}.
@@ -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
- ducky = undefined}).
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, Handler]) ->
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, Ducky) end),
41
- {ok, #state{lsock = LSock, ducky = Ducky}}.
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, Ducky) ->
120
+ loop(LSock) ->
90
121
  {ok, Sock} = gen_tcp:accept(LSock),
91
- spawn(fun() -> handle_method(Sock, Ducky) end),
92
- loop(LSock, Ducky).
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
- handle_method(Sock, Ducky) ->
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(Ducky, BinaryTerm),
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.
@@ -7,7 +7,8 @@ boot() ->
7
7
  application:start(ernie_server_app).
8
8
 
9
9
  start(_Type, _Args) ->
10
- ernie_server_sup:start_link().
10
+ ernie_server_sup:start_link(),
11
+ asset_pool_sup:start_link().
11
12
 
12
13
  stop(_State) ->
13
14
  ok.
@@ -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, Handler]]},
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
@@ -0,0 +1,8 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ require 'ernie'
3
+
4
+ mod(:calc) do
5
+ fun(:add) do |a, b|
6
+ a + b
7
+ end
8
+ end
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.1.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-05-18 00:00:00 -07:00
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