mojombo-ernie 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.md
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.beam
2
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Tom Preston-Werner
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,79 @@
1
+ Ernie
2
+ =====
3
+
4
+ By Tom Preston-Werner (tom@mojombo.com)
5
+
6
+ WARNING: This software is alpha and should not be used in production without
7
+ extensive testing. You should not consider this project production ready until
8
+ it is released as 1.0.
9
+
10
+
11
+ Description
12
+ -----------
13
+
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
+
16
+
17
+ Installation
18
+ ------------
19
+
20
+ You must have Erlang installed before installing Ernie.
21
+
22
+ gem install mojombo-ernie -s http://gems.github.com
23
+
24
+
25
+ Running
26
+ -------
27
+
28
+ Usage: ernie [options] <handler>
29
+ -n, --name NAME Node name
30
+ -p, --port PORT Port
31
+ -d, --detached Run as a daemon
32
+ -P, --pidfile PIDFILE Location to write pid file.
33
+
34
+
35
+ Example Handler
36
+ ---------------
37
+
38
+ require 'ernie'
39
+
40
+ mod(:calc) do
41
+ fun(:add) do |a, b|
42
+ a + b
43
+ end
44
+ end
45
+
46
+
47
+ Example BERT-RPC call for above example
48
+ ---------------------------------------
49
+
50
+ -> {call, calc, add, [1, 2]}
51
+
52
+ <- {reply, 3}
53
+
54
+
55
+ Contribute
56
+ ----------
57
+
58
+ If you'd like to hack on Ernie, start by forking my repo on GitHub:
59
+
60
+ http://github.com/mojombo/ernie
61
+
62
+ To get all of the dependencies, install the gem first. The best way to get
63
+ your changes merged back into core is as follows:
64
+
65
+ 1. Clone down your fork
66
+ 1. Create a topic branch to contain your change
67
+ 1. Hack away
68
+ 1. Add tests and make sure everything still passes by running `rake`
69
+ 1. If you are adding new functionality, document it in the README.md
70
+ 1. Do not change the version number, I will do that on my end
71
+ 1. If necessary, rebase your commits into logical chunks, without errors
72
+ 1. Push the branch up to GitHub
73
+ 1. Send me (mojombo) a pull request for your branch
74
+
75
+
76
+ Copyright
77
+ ---------
78
+
79
+ Copyright (c) 2009 Tom Preston-Werner. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,64 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "ernie"
8
+ gem.summary = %Q{TODO}
9
+ gem.email = "tom@mojombo.com"
10
+ gem.homepage = "http://github.com/mojombo/ernie"
11
+ gem.authors = ["Tom Preston-Werner"]
12
+ gem.files.include(["ext"])
13
+ gem.extensions << 'ext/extconf.rb'
14
+ gem.add_dependency('erlectricity', '>= 1.0.1')
15
+
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ rescue LoadError
19
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
20
+ end
21
+
22
+ require 'rake/testtask'
23
+ Rake::TestTask.new(:test) do |test|
24
+ test.libs << 'lib' << 'test'
25
+ test.pattern = 'test/**/*_test.rb'
26
+ test.verbose = true
27
+ end
28
+
29
+ begin
30
+ require 'rcov/rcovtask'
31
+ Rcov::RcovTask.new do |test|
32
+ test.libs << 'test'
33
+ test.pattern = 'test/**/*_test.rb'
34
+ test.verbose = true
35
+ end
36
+ rescue LoadError
37
+ task :rcov do
38
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
39
+ end
40
+ end
41
+
42
+ task :default => :test
43
+
44
+ # require 'rake/rdoctask'
45
+ # Rake::RDocTask.new do |rdoc|
46
+ # if File.exist?('VERSION.yml')
47
+ # config = YAML.load(File.read('VERSION.yml'))
48
+ # version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
49
+ # else
50
+ # version = ""
51
+ # end
52
+ #
53
+ # rdoc.rdoc_dir = 'rdoc'
54
+ # rdoc.title = "ernie #{version}"
55
+ # rdoc.rdoc_files.include('README*')
56
+ # rdoc.rdoc_files.include('lib/**/*.rb')
57
+ # end
58
+
59
+ task :ebuild do
60
+ ERLC_TEST_FLAGS = ""
61
+ ERLC_FLAGS = "-o ../ebin"
62
+ cd "elib"
63
+ sh "erlc #{ERLC_FLAGS} #{ERLC_TEST_FLAGS} #{Dir["**/*.erl"].join(" ")}"
64
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 1
3
+ :patch: 0
4
+ :major: 0
data/bin/ernie ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift(File.join(File.dirname(__FILE__), *%w[.. lib]))
4
+ ERNIE_ROOT = File.join(File.dirname(__FILE__), *%w[..])
5
+
6
+ DEFAULT_ERLANG_CODEPATHS = %w[ebin]
7
+
8
+ def rel(path)
9
+ File.join(ERNIE_ROOT, path)
10
+ end
11
+
12
+ def code_paths
13
+ DEFAULT_ERLANG_CODEPATHS.map {|n| "-pz #{rel(n)}" }.join(" ") + " \\"
14
+ end
15
+
16
+ require 'optparse'
17
+ require 'pp'
18
+
19
+ help = <<HELP
20
+ Ernie is an Erlang/Ruby BERT-RPC Server.
21
+
22
+ Basic Command Line Usage:
23
+ ernie [options] <path to handler file>
24
+
25
+ Options:
26
+ HELP
27
+
28
+ options = {}
29
+ OptionParser.new do |opts|
30
+ opts.banner = help
31
+
32
+ opts.on("-p PORT", "--port PORT", "Port") do |x|
33
+ options[:port] = x
34
+ end
35
+
36
+ opts.on("-d", "--detached", "Run as a daemon") do
37
+ options[:detached] = true
38
+ end
39
+
40
+ opts.on("-P", "--pidfile PIDFILE", "Location to write pid file.") do |x|
41
+ options[:pidfile] = x
42
+ end
43
+ end.parse!
44
+
45
+ handler = ARGV[0]
46
+
47
+ unless handler
48
+ puts "A handler must be specified: ernie /path/to/handler.rb"
49
+ exit(1)
50
+ end
51
+
52
+ port = options[:port] || 8000
53
+ pidfile = options[:pidfile] ? "-ernie_server_app pidfile \"'#{options[:pidfile]}'\"" : ''
54
+ detached = options[:detached] ? '-detached' : ''
55
+
56
+ cmd = %Q{erl -boot start_sasl \
57
+ #{detached} \
58
+ +Bc \
59
+ +K true \
60
+ -smp enable \
61
+ #{code_paths}
62
+ #{pidfile} \
63
+ -ernie_server_app port #{port} \
64
+ -ernie_server_app handler '"#{handler}"' \
65
+ -run ernie_server_app boot}.squeeze(' ')
66
+ puts cmd
67
+ exec(cmd)
@@ -0,0 +1,2 @@
1
+ {application, ernie_server_app,
2
+ [{mod, {ernie_server_app, []}}]}.
@@ -0,0 +1,104 @@
1
+ -module(ernie_server).
2
+ -behaviour(gen_server).
3
+
4
+ %% api
5
+ -export([start_link/1, start/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, {lsock = undefined,
12
+ ducky = 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
+ %%====================================================================
25
+ %% gen_server callbacks
26
+ %%====================================================================
27
+
28
+ %%--------------------------------------------------------------------
29
+ %% Function: init(Args) -> {ok, State} |
30
+ %% {ok, State, Timeout} |
31
+ %% ignore |
32
+ %% {stop, Reason}
33
+ %% Description: Initiates the server
34
+ %%--------------------------------------------------------------------
35
+ init([Port, Handler]) ->
36
+ process_flag(trap_exit, true),
37
+ error_logger:info_msg("~p starting~n", [?MODULE]),
38
+ Ducky = port_wrapper:wrap("ruby " ++ Handler),
39
+ {ok, LSock} = try_listen(Port, 500),
40
+ spawn(fun() -> loop(LSock, Ducky) end),
41
+ {ok, #state{lsock = LSock, ducky = Ducky}}.
42
+
43
+ %%--------------------------------------------------------------------
44
+ %% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |
45
+ %% {reply, Reply, State, Timeout} |
46
+ %% {noreply, State} |
47
+ %% {noreply, State, Timeout} |
48
+ %% {stop, Reason, Reply, State} |
49
+ %% {stop, Reason, State}
50
+ %% Description: Handling call messages
51
+ %%--------------------------------------------------------------------
52
+ handle_call(_Request, _From, State) ->
53
+ {reply, ok, State}.
54
+
55
+ %%--------------------------------------------------------------------
56
+ %% Function: handle_cast(Msg, State) -> {noreply, State} |
57
+ %% {noreply, State, Timeout} |
58
+ %% {stop, Reason, State}
59
+ %% Description: Handling cast messages
60
+ %%--------------------------------------------------------------------
61
+ handle_cast(_Msg, State) -> {noreply, State}.
62
+
63
+ handle_info(Msg, State) ->
64
+ error_logger:error_msg("Unexpected message: ~p~n", [Msg]),
65
+ {noreply, State}.
66
+
67
+ terminate(_Reason, _State) -> ok.
68
+ code_change(_OldVersion, State, _Extra) -> {ok, State}.
69
+
70
+ %%====================================================================
71
+ %% Internal
72
+ %%====================================================================
73
+
74
+ try_listen(Port, 0) ->
75
+ error_logger:error_msg("Could not listen on port ~p~n", [Port]),
76
+ {error, "Could not listen on port"};
77
+ try_listen(Port, Times) ->
78
+ Res = gen_tcp:listen(Port, [binary, {packet, 4}, {active, false}]),
79
+ case Res of
80
+ {ok, LSock} ->
81
+ error_logger:info_msg("Listening on port ~p~n", [Port]),
82
+ {ok, LSock};
83
+ {error, Reason} ->
84
+ error_logger:info_msg("Could not listen on port ~p: ~p~n", [Port, Reason]),
85
+ timer:sleep(5000),
86
+ try_listen(Port, Times - 1)
87
+ end.
88
+
89
+ loop(LSock, Ducky) ->
90
+ {ok, Sock} = gen_tcp:accept(LSock),
91
+ spawn(fun() -> handle_method(Sock, Ducky) end),
92
+ loop(LSock, Ducky).
93
+
94
+ handle_method(Sock, Ducky) ->
95
+ case gen_tcp:recv(Sock, 0) of
96
+ {ok, BinaryTerm} ->
97
+ % error_logger:info_msg("From Internet: ~p~n", [BinaryTerm]),
98
+ {ok, Data} = port_wrapper:rpc(Ducky, BinaryTerm),
99
+ % error_logger:info_msg("From Port: ~p~n", [Data]),
100
+ gen_tcp:send(Sock, Data),
101
+ ok = gen_tcp:close(Sock);
102
+ {error, closed} ->
103
+ ok = gen_tcp:close(Sock)
104
+ end.
@@ -0,0 +1,13 @@
1
+ -module(ernie_server_app).
2
+ -behaviour(application).
3
+
4
+ -export([boot/0, start/2, stop/1]).
5
+
6
+ boot() ->
7
+ application:start(ernie_server_app).
8
+
9
+ start(_Type, _Args) ->
10
+ ernie_server_sup:start_link().
11
+
12
+ stop(_State) ->
13
+ ok.
@@ -0,0 +1,21 @@
1
+ -module(ernie_server_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, Port} = application:get_env(ernie_server_app, port),
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
+ case application:get_env(ernie_server_app, pidfile) of
14
+ {ok, Location} ->
15
+ Pid = os:getpid(),
16
+ ok = file:write_file(Location, list_to_binary(Pid));
17
+ undefined -> ok
18
+ end,
19
+ {ok, {{one_for_one, 1, 60},
20
+ [{ernie_server, {ernie_server, start_link, [[Port, Handler]]},
21
+ permanent, brutal_kill, worker, [ernie_server]}]}}.
@@ -0,0 +1,63 @@
1
+ -module(port_wrapper).
2
+ -export([wrap/1, wrap/2, wrap_link/1, wrap_link/2, send/2, shutdown/1, rpc/2]).
3
+
4
+ wrap(Command) ->
5
+ spawn(fun() -> process_flag(trap_exit, true), Port = create_port(Command), loop(Port, infinity, Command) end).
6
+ wrap(Command, Timeout) ->
7
+ spawn(fun() -> process_flag(trap_exit, true), Port = create_port(Command), loop(Port, Timeout, Command) end).
8
+
9
+ wrap_link(Command) ->
10
+ spawn_link(fun() -> process_flag(trap_exit, true), Port = create_port(Command), link(Port), loop(Port, infinity, Command) end).
11
+ wrap_link(Command, Timeout) ->
12
+ spawn_link(fun() -> process_flag(trap_exit, true), Port = create_port(Command), link(Port), loop(Port, Timeout, Command) end).
13
+
14
+ rpc(WrappedPort, Message) ->
15
+ send(WrappedPort, Message),
16
+ receive
17
+ {WrappedPort, Result} -> {ok, Result}
18
+ after 15000 ->
19
+ {error, timed_out, WrappedPort}
20
+ end.
21
+
22
+ send(WrappedPort, Message) ->
23
+ WrappedPort ! {self(), {command, Message}},
24
+ WrappedPort.
25
+
26
+ shutdown(WrappedPort) ->
27
+ WrappedPort ! shutdown,
28
+ true.
29
+
30
+ create_port(Command) ->
31
+ open_port({spawn, Command}, [{packet, 4}, nouse_stdio, exit_status, binary]).
32
+
33
+ loop(Port, Timeout, Command) ->
34
+ receive
35
+ noose ->
36
+ port_close(Port),
37
+ noose;
38
+ shutdown ->
39
+ port_close(Port),
40
+ exit(shutdown);
41
+ {Source, {command, Message}} ->
42
+ Port ! {self(), {command, Message}},
43
+ receive
44
+ {Port, {data, Result}} ->
45
+ Source ! {self(), Result}
46
+ after Timeout ->
47
+ error_logger:error_msg("Port Wrapper ~p timed out in mid operation (~p)!~n", [self(),Message]),
48
+ % We timed out, which means we need to close and then restart the port
49
+ port_close(Port), % Should SIGPIPE the child.
50
+ exit(timed_out)
51
+ end,
52
+ loop(Port,Timeout,Command);
53
+ {Port, {exit_status, _Code}} ->
54
+ % Hard and Unanticipated Crash
55
+ error_logger:error_msg( "Port closed! ~p~n", [Port] ),
56
+ exit({error, _Code});
57
+ {'EXIT',_Pid,shutdown} ->
58
+ port_close(Port),
59
+ exit(shutdown);
60
+ Any ->
61
+ error_logger:warning_msg("PortWrapper ~p got unexpected message: ~p~n", [self(), Any]),
62
+ loop(Port, Timeout, Command)
63
+ end.
data/ext/Makefile ADDED
@@ -0,0 +1,2 @@
1
+ install:
2
+ erlc -o ../ebin ../elib/*.erl
data/ext/extconf.rb ADDED
@@ -0,0 +1 @@
1
+ # does nothing, Makefile is handwritten
data/lib/ernie.rb ADDED
@@ -0,0 +1,126 @@
1
+ require 'rubygems'
2
+ require 'erlectricity'
3
+ require 'logger'
4
+
5
+ class Ernie
6
+ class << self
7
+ attr_accessor :mods, :current_mod, :logger
8
+ end
9
+
10
+ self.mods = {}
11
+ self.current_mod = nil
12
+ self.logger = nil
13
+
14
+ def self.mod(name, block)
15
+ m = Mod.new(name)
16
+ self.current_mod = m
17
+ self.mods[name] = m
18
+ block.call
19
+ end
20
+
21
+ def self.fun(name, block)
22
+ self.current_mod.fun(name, block)
23
+ end
24
+
25
+ def self.logfile(file)
26
+ self.logger = Logger.new(file)
27
+ end
28
+
29
+ def self.log(text)
30
+ self.logger.info(text) if self.logger
31
+ end
32
+
33
+ def self.convert(item)
34
+ if item.instance_of?(Hash)
35
+ a = [:dict]
36
+ item.each_pair { |k, v| a << [convert(k), convert(v)] }
37
+ a
38
+ elsif item.instance_of?(Array)
39
+ item.map { |x| convert(x) }
40
+ else
41
+ item
42
+ end
43
+ end
44
+
45
+ def self.deconvert(item)
46
+ if item.instance_of?(Array)
47
+ if item.first == :dict
48
+ item[1..-1].inject({}) do |acc, x|
49
+ acc[deconvert(x[0])] = deconvert(x[1]); acc
50
+ end
51
+ else
52
+ item.map { |x| deconvert(x) }
53
+ end
54
+ else
55
+ item
56
+ end
57
+ end
58
+
59
+ def self.dispatch(mod, fun, args)
60
+ xargs = deconvert(args)
61
+ self.log("-- " + [mod, fun, xargs].inspect)
62
+ res = self.mods[mod].funs[fun].call(*xargs)
63
+ convert(res)
64
+ end
65
+
66
+ def self.start
67
+ self.log("Starting")
68
+ self.log(self.mods.inspect)
69
+ receive do |f|
70
+ f.when([:call, Symbol, Symbol, Array]) do |mod, fun, args|
71
+ self.log("-> " + [:call, mod, fun, args].inspect)
72
+ begin
73
+ res = self.dispatch(mod, fun, args)
74
+ xres = [:reply, res]
75
+ self.log("<- " + xres.inspect)
76
+ f.send!(xres)
77
+ rescue Object => e
78
+ xres = [:error, [:user, 0, e.message]]
79
+ self.log("<- " + xres.inspect)
80
+ self.log(e.backtrace.join("\n"))
81
+ f.send!(xres)
82
+ end
83
+ f.receive_loop
84
+ end
85
+
86
+ f.when(Any) do |any|
87
+ self.log("-> " + any.inspect)
88
+ xres = [:error, [:server, 0, "Invalid request: #{any.inspect}"]]
89
+ self.log("<- " + xres.inspect)
90
+ f.send!(xres)
91
+ f.receive_loop
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ class Ernie::Mod
98
+ attr_accessor :name, :funs
99
+
100
+ def initialize(name)
101
+ self.name = name
102
+ self.funs = {}
103
+ end
104
+
105
+ def fun(name, block)
106
+ self.funs[name] = block
107
+ end
108
+ end
109
+
110
+ # Root level calls
111
+
112
+ def mod(name, &block)
113
+ Ernie.mod(name, block)
114
+ end
115
+
116
+ def fun(name, &block)
117
+ Ernie.fun(name, block)
118
+ end
119
+
120
+ def logfile(name)
121
+ Ernie.logfile(name)
122
+ end
123
+
124
+ at_exit do
125
+ Ernie.start unless $test
126
+ end
@@ -0,0 +1,18 @@
1
+ require 'test_helper'
2
+
3
+ class ErnieTest < Test::Unit::TestCase
4
+ context "mod" do
5
+ should "add a mod to the mods hash" do
6
+ mod(:foo) { }
7
+ assert Ernie.mods[:foo]
8
+ assert Ernie.mods[:foo].instance_of?(Ernie::Mod)
9
+ end
10
+ end
11
+
12
+ context "fun" do
13
+ should "add a fun to the funs hash of the mod" do
14
+ mod(:foo) { fun(:bar) { } }
15
+ assert Ernie.mods[:foo].funs[:bar]
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'ernie'
8
+
9
+ class Test::Unit::TestCase
10
+ end
11
+
12
+ $test = true
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mojombo-ernie
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tom Preston-Werner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-05-18 00:00:00 -07:00
13
+ default_executable: ernie
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: erlectricity
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.0.1
24
+ version:
25
+ description:
26
+ email: tom@mojombo.com
27
+ executables:
28
+ - ernie
29
+ extensions:
30
+ - ext/extconf.rb
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.md
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.md
39
+ - Rakefile
40
+ - VERSION.yml
41
+ - bin/ernie
42
+ - ebin/ernie_server_app.app
43
+ - elib/ernie_server.erl
44
+ - elib/ernie_server_app.erl
45
+ - elib/ernie_server_sup.erl
46
+ - elib/port_wrapper.erl
47
+ - ext/Makefile
48
+ - ext/extconf.rb
49
+ - lib/ernie.rb
50
+ - test/ernie_test.rb
51
+ - test/test_helper.rb
52
+ has_rdoc: true
53
+ homepage: http://github.com/mojombo/ernie
54
+ post_install_message:
55
+ rdoc_options:
56
+ - --charset=UTF-8
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ requirements: []
72
+
73
+ rubyforge_project:
74
+ rubygems_version: 1.2.0
75
+ signing_key:
76
+ specification_version: 2
77
+ summary: TODO
78
+ test_files:
79
+ - test/ernie_test.rb
80
+ - test/test_helper.rb