mojombo-ernie 0.1.0 → 0.2.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
@@ -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