mojombo-ernie 0.1.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/.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