otaku 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/HISTORY.txt +4 -0
- data/LICENSE +20 -0
- data/README.rdoc +96 -0
- data/Rakefile +82 -0
- data/VERSION +1 -0
- data/lib/otaku.rb +235 -0
- data/spec/processing_spec.rb +30 -0
- data/spec/spec_helper.rb +8 -0
- metadata +139 -0
data/.document
ADDED
data/.gitignore
ADDED
data/HISTORY.txt
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 NgTzeYang
|
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.rdoc
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
= OTAKU
|
2
|
+
|
3
|
+
Dead simple server/client service built using eventmachine.
|
4
|
+
|
5
|
+
== Introduction
|
6
|
+
|
7
|
+
Otaku's original intent is to support testing of cross-process stubbing in cross-stub (http://github.com/ngty/cross-stub). It's usefulness in other aspects of my hacking life prompts me to extract it out, & package it as a generic solution. Its primary intent is to be dead simple to use & easy to customize, of course, both criteria subjected to very my own tastes.
|
8
|
+
|
9
|
+
== Getting Started
|
10
|
+
|
11
|
+
It's hosted on rubygems.org:
|
12
|
+
|
13
|
+
$ sudo gem install otaku
|
14
|
+
|
15
|
+
== Using It
|
16
|
+
|
17
|
+
=== 1. Starting service & defining handler
|
18
|
+
|
19
|
+
require 'otaku'
|
20
|
+
|
21
|
+
Otaku.start do |data|
|
22
|
+
result = '~ %s ~' % data
|
23
|
+
end
|
24
|
+
|
25
|
+
=== 2. Sending processing request
|
26
|
+
|
27
|
+
require 'otaku'
|
28
|
+
|
29
|
+
Otaku.process('hello')
|
30
|
+
# >> '~ hello ~'
|
31
|
+
|
32
|
+
=== Unfortunately ...
|
33
|
+
|
34
|
+
Most of the times, we won't have anything as simple as above, the following illustrates the problem of contextual reference:
|
35
|
+
|
36
|
+
mark = '*'
|
37
|
+
Otaku.start do |data|
|
38
|
+
'%s %s %s' % [mark, data, mark]
|
39
|
+
end
|
40
|
+
|
41
|
+
Otaku.process('hello') # failure !!
|
42
|
+
|
43
|
+
The reason is that the proc that we passed to Otaku.start is being marshalled while being passed to the server as a handler, in the process, the contextual references are lost. A workaround for this problem is:
|
44
|
+
|
45
|
+
Otaku.start(:mark => '*') do |data|
|
46
|
+
'%s %s %s' % [mark, data, mark]
|
47
|
+
end
|
48
|
+
|
49
|
+
Otaku.process('hello') # >> '* hello *'
|
50
|
+
|
51
|
+
== Configuraing It
|
52
|
+
|
53
|
+
Otaku ships with the following defaults:
|
54
|
+
|
55
|
+
Otaku.address # >> '127.0.0.1'
|
56
|
+
Otaku.port # >> 10999
|
57
|
+
Otaku.init_wait_time # >> 2
|
58
|
+
Otaku.log_file # >> '/tmp/otaku.log'
|
59
|
+
|
60
|
+
Configuring can be done via:
|
61
|
+
|
62
|
+
=== 1. Configuration Proc
|
63
|
+
|
64
|
+
Otaku.configure do |config|
|
65
|
+
config.init_wait_time = 10
|
66
|
+
# (more typing, more customizing)
|
67
|
+
end
|
68
|
+
|
69
|
+
=== 2. Configuration Hash
|
70
|
+
|
71
|
+
Otaku.configure({
|
72
|
+
:init_wait_time => 10
|
73
|
+
# (more typing, more customizing)
|
74
|
+
})
|
75
|
+
|
76
|
+
=== 3. Writer Method
|
77
|
+
|
78
|
+
Otaku.init_wait_time = 10
|
79
|
+
|
80
|
+
== TODO
|
81
|
+
|
82
|
+
1. Currently, only integration testing is done ...
|
83
|
+
|
84
|
+
== Note on Patches/Pull Requests
|
85
|
+
|
86
|
+
* Fork the project.
|
87
|
+
* Make your feature addition or bug fix.
|
88
|
+
* Add tests for it. This is important so I don't break it in a
|
89
|
+
future version unintentionally.
|
90
|
+
* Commit, do not mess with rakefile, version, or history.
|
91
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
92
|
+
* Send me a pull request. Bonus points for topic branches.
|
93
|
+
|
94
|
+
== Copyright
|
95
|
+
|
96
|
+
Copyright (c) since 2010 NgTzeYang. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "otaku"
|
8
|
+
gem.summary = %Q{Dead simple server/client service built using eventmachine}
|
9
|
+
gem.description = %Q{}
|
10
|
+
gem.email = "ngty77@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/ngty/otaku"
|
12
|
+
gem.authors = ["NgTzeYang"]
|
13
|
+
gem.add_development_dependency "bacon", ">= 0"
|
14
|
+
gem.add_dependency "eventmachine", ">= 0.12.10"
|
15
|
+
gem.add_dependency "ruby2ruby", ">= 1.2.4"
|
16
|
+
# TODO: Should eventually remove requirement for ParseTree !!
|
17
|
+
gem.add_dependency "ParseTree", ">= 3.0.5"
|
18
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
19
|
+
end
|
20
|
+
Jeweler::GemcutterTasks.new
|
21
|
+
rescue LoadError
|
22
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'rake/testtask'
|
26
|
+
Rake::TestTask.new(:spec) do |spec|
|
27
|
+
spec.libs << 'lib' << 'spec'
|
28
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
29
|
+
spec.verbose = true
|
30
|
+
end
|
31
|
+
|
32
|
+
begin
|
33
|
+
require 'rcov/rcovtask'
|
34
|
+
Rcov::RcovTask.new do |spec|
|
35
|
+
spec.libs << 'spec'
|
36
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
37
|
+
spec.verbose = true
|
38
|
+
end
|
39
|
+
rescue LoadError
|
40
|
+
task :rcov do
|
41
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
task :spec => :check_dependencies
|
46
|
+
|
47
|
+
begin
|
48
|
+
require 'reek/adapters/rake_task'
|
49
|
+
Reek::RakeTask.new do |t|
|
50
|
+
t.fail_on_error = true
|
51
|
+
t.verbose = false
|
52
|
+
t.source_files = 'lib/**/*.rb'
|
53
|
+
end
|
54
|
+
rescue LoadError
|
55
|
+
task :reek do
|
56
|
+
abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
begin
|
61
|
+
require 'roodi'
|
62
|
+
require 'roodi_task'
|
63
|
+
RoodiTask.new do |t|
|
64
|
+
t.verbose = false
|
65
|
+
end
|
66
|
+
rescue LoadError
|
67
|
+
task :roodi do
|
68
|
+
abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
task :default => :spec
|
73
|
+
|
74
|
+
require 'rake/rdoctask'
|
75
|
+
Rake::RDocTask.new do |rdoc|
|
76
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
77
|
+
|
78
|
+
rdoc.rdoc_dir = 'rdoc'
|
79
|
+
rdoc.title = "otaku #{version}"
|
80
|
+
rdoc.rdoc_files.include('README*')
|
81
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
82
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/otaku.rb
ADDED
@@ -0,0 +1,235 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'logger'
|
4
|
+
require 'base64'
|
5
|
+
require 'ruby2ruby'
|
6
|
+
|
7
|
+
# TODO: Preliminary try, should be removed eventually !! Should use RubyParser instead.
|
8
|
+
require 'parse_tree'
|
9
|
+
require 'parse_tree_extensions'
|
10
|
+
|
11
|
+
module Otaku
|
12
|
+
|
13
|
+
class HandlerNotDefinedError < Exception ; end
|
14
|
+
class DataProcessError < Exception ; end
|
15
|
+
|
16
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
17
|
+
# Otaku
|
18
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
19
|
+
|
20
|
+
DEFAULTS = {
|
21
|
+
:address => '127.0.0.1',
|
22
|
+
:port => 10999,
|
23
|
+
:log_file => '/tmp/otaku.log',
|
24
|
+
:init_wait_time => 2
|
25
|
+
}
|
26
|
+
|
27
|
+
class << self
|
28
|
+
|
29
|
+
attr_accessor *DEFAULTS.keys
|
30
|
+
|
31
|
+
def configure(config = {}, &block)
|
32
|
+
block_given? ? yield(self) :
|
33
|
+
config.each{|name, val| send(:"#{name}=", val) }
|
34
|
+
end
|
35
|
+
|
36
|
+
def config
|
37
|
+
DEFAULTS.keys.inject({}) do |memo, name|
|
38
|
+
memo.merge(name => send(name))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def start(context = {}, &handler)
|
43
|
+
raise HandlerNotDefinedError unless block_given?
|
44
|
+
Server.handler = Handler.new(context, handler)
|
45
|
+
Server.start
|
46
|
+
end
|
47
|
+
|
48
|
+
def stop
|
49
|
+
Server.stop
|
50
|
+
end
|
51
|
+
|
52
|
+
def process(data)
|
53
|
+
Client.get(data)
|
54
|
+
end
|
55
|
+
|
56
|
+
Otaku.configure(DEFAULTS)
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
61
|
+
# Otaku::Encoder
|
62
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
63
|
+
|
64
|
+
module Encoder
|
65
|
+
class << self
|
66
|
+
|
67
|
+
def encode(thing)
|
68
|
+
Base64.encode64(Marshal.dump(thing))
|
69
|
+
end
|
70
|
+
|
71
|
+
def decode(thing)
|
72
|
+
Marshal.load(Base64.decode64(thing))
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
79
|
+
# Otaku::Handler
|
80
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
81
|
+
|
82
|
+
class Handler
|
83
|
+
|
84
|
+
def initialize(context, handler)
|
85
|
+
@context = __context_as_code__(context)
|
86
|
+
@proc = __proc_as_code__(handler)
|
87
|
+
end
|
88
|
+
|
89
|
+
def [](data)
|
90
|
+
eval(@context).instance_exec(data, &eval(@proc))
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def __proc_as_code__(block)
|
96
|
+
Ruby2Ruby.new.process(block.to_sexp)
|
97
|
+
end
|
98
|
+
|
99
|
+
def __context_as_code__(methods_hash)
|
100
|
+
'Class.new{ %s }.new' %
|
101
|
+
methods_hash.map do |method, val|
|
102
|
+
"def #{method}; Encoder.decode(%|#{Encoder.encode(val).gsub('|','\|')}|); end"
|
103
|
+
end.join('; ')
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
109
|
+
# Otaku::Server
|
110
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
111
|
+
|
112
|
+
module Server
|
113
|
+
|
114
|
+
class << self
|
115
|
+
|
116
|
+
attr_accessor :handler
|
117
|
+
|
118
|
+
def start(other_process=false)
|
119
|
+
other_process ? run_evented_server : (
|
120
|
+
# print '[Otaku] initializing at %s:%s ... ' % [Otaku.address, Otaku.port] # DBUG
|
121
|
+
run_server_script
|
122
|
+
# puts 'done [pid#%s]' % @process.pid # DEBUG
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
def run_evented_server
|
127
|
+
log 'started with pid #%s' % Process.pid,
|
128
|
+
'listening at %s:%s' % [Otaku.address, Otaku.port]
|
129
|
+
EventMachine::run { EventMachine::start_server(Otaku.address, Otaku.port, EM) }
|
130
|
+
end
|
131
|
+
|
132
|
+
def run_server_script
|
133
|
+
args = Encoder.encode({
|
134
|
+
:config => Otaku.config,
|
135
|
+
:handler => @handler
|
136
|
+
})
|
137
|
+
@process = IO.popen(%|ruby #{__FILE__} "#{args.gsub('"','\"')}"|,'r')
|
138
|
+
sleep Otaku.init_wait_time
|
139
|
+
end
|
140
|
+
|
141
|
+
def stop
|
142
|
+
Process.kill('SIGHUP', @process.pid) if @process
|
143
|
+
end
|
144
|
+
|
145
|
+
def log(*msgs)
|
146
|
+
@logger ||= Logger.new(Otaku.log_file)
|
147
|
+
msgs.each{|msg| @logger << "[Otaku] %s\n" % msg }
|
148
|
+
end
|
149
|
+
|
150
|
+
def cleanup
|
151
|
+
@logger.close
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
|
156
|
+
private
|
157
|
+
|
158
|
+
module EM #:nodoc:
|
159
|
+
|
160
|
+
def receive_data(data)
|
161
|
+
log 'receives data: %s' % data.inspect
|
162
|
+
result = process_data(data)
|
163
|
+
log 'returning result: %s' % result.inspect
|
164
|
+
send_data(Encoder.encode(result))
|
165
|
+
end
|
166
|
+
|
167
|
+
def process_data(data)
|
168
|
+
begin
|
169
|
+
Server.handler[data]
|
170
|
+
rescue
|
171
|
+
error = DataProcessError.new($!.inspect)
|
172
|
+
log(error.inspect)
|
173
|
+
error
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def log(*msg)
|
178
|
+
Server.log(*msg)
|
179
|
+
end
|
180
|
+
|
181
|
+
end
|
182
|
+
|
183
|
+
end
|
184
|
+
|
185
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
186
|
+
# Otaku::Client
|
187
|
+
# //////////////////////////////////////////////////////////////////////////////////////////
|
188
|
+
|
189
|
+
module Client
|
190
|
+
|
191
|
+
class << self
|
192
|
+
def get(data)
|
193
|
+
EventMachine::run do
|
194
|
+
EventMachine::connect(Otaku.address, Otaku.port, EM).
|
195
|
+
execute(data) do |data|
|
196
|
+
@result = Encoder.decode(data)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
@result
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
module EM #:nodoc:
|
206
|
+
|
207
|
+
def receive_data(data)
|
208
|
+
result = @callback.call(data)
|
209
|
+
result.is_a?(DataProcessError) ? raise(result) : result
|
210
|
+
EventMachine::stop_event_loop # ends loop & resumes program flow
|
211
|
+
end
|
212
|
+
|
213
|
+
def execute(method, &callback)
|
214
|
+
@callback = callback
|
215
|
+
send_data(method)
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
if $0 == __FILE__ && (encoded_data = ARGV[0])
|
224
|
+
begin
|
225
|
+
include Otaku
|
226
|
+
data = Encoder.decode(encoded_data)
|
227
|
+
Otaku.configure(data[:config])
|
228
|
+
Server.handler = data[:handler]
|
229
|
+
Server.start(true)
|
230
|
+
rescue
|
231
|
+
Server.log($!.inspect)
|
232
|
+
ensure
|
233
|
+
Server.cleanup
|
234
|
+
end
|
235
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
2
|
+
|
3
|
+
describe "Otaku doing processing" do
|
4
|
+
|
5
|
+
after do
|
6
|
+
Otaku.stop
|
7
|
+
end
|
8
|
+
|
9
|
+
should 'succeed when using proc that has no contextual reference' do
|
10
|
+
Otaku.start{|data| '~ %s ~' % data }
|
11
|
+
Otaku.process('hello').should.equal '~ hello ~'
|
12
|
+
end
|
13
|
+
|
14
|
+
should 'fail when using proc that has contextual reference yet has no specified context' do
|
15
|
+
mark = '*'
|
16
|
+
Otaku.start{|data| '%s %s %s' % [mark, data, mark] }
|
17
|
+
lambda { Otaku.process('hello') }.should.raise(Otaku::DataProcessError).
|
18
|
+
message.should.match(/#<NameError: undefined local variable or method `mark' for /)
|
19
|
+
end
|
20
|
+
|
21
|
+
should 'succeed when using proc that has contextual reference & has context specified' do
|
22
|
+
Otaku.start(:mark => '*') {|data| '%s %s %s' % [mark, data, mark] }
|
23
|
+
Otaku.process('hello').should.equal('* hello *')
|
24
|
+
end
|
25
|
+
|
26
|
+
should 'raise Otaku::HandlerNotDefinedError when processing wo specified proc' do
|
27
|
+
lambda { Otaku.start }.should.raise(Otaku::HandlerNotDefinedError)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: otaku
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 27
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- NgTzeYang
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-07-18 00:00:00 +08:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: bacon
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: eventmachine
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
hash: 59
|
44
|
+
segments:
|
45
|
+
- 0
|
46
|
+
- 12
|
47
|
+
- 10
|
48
|
+
version: 0.12.10
|
49
|
+
type: :runtime
|
50
|
+
version_requirements: *id002
|
51
|
+
- !ruby/object:Gem::Dependency
|
52
|
+
name: ruby2ruby
|
53
|
+
prerelease: false
|
54
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
hash: 23
|
60
|
+
segments:
|
61
|
+
- 1
|
62
|
+
- 2
|
63
|
+
- 4
|
64
|
+
version: 1.2.4
|
65
|
+
type: :runtime
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: ParseTree
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 13
|
76
|
+
segments:
|
77
|
+
- 3
|
78
|
+
- 0
|
79
|
+
- 5
|
80
|
+
version: 3.0.5
|
81
|
+
type: :runtime
|
82
|
+
version_requirements: *id004
|
83
|
+
description: ""
|
84
|
+
email: ngty77@gmail.com
|
85
|
+
executables: []
|
86
|
+
|
87
|
+
extensions: []
|
88
|
+
|
89
|
+
extra_rdoc_files:
|
90
|
+
- LICENSE
|
91
|
+
- README.rdoc
|
92
|
+
files:
|
93
|
+
- .document
|
94
|
+
- .gitignore
|
95
|
+
- HISTORY.txt
|
96
|
+
- LICENSE
|
97
|
+
- README.rdoc
|
98
|
+
- Rakefile
|
99
|
+
- VERSION
|
100
|
+
- lib/otaku.rb
|
101
|
+
- spec/processing_spec.rb
|
102
|
+
- spec/spec_helper.rb
|
103
|
+
has_rdoc: true
|
104
|
+
homepage: http://github.com/ngty/otaku
|
105
|
+
licenses: []
|
106
|
+
|
107
|
+
post_install_message:
|
108
|
+
rdoc_options:
|
109
|
+
- --charset=UTF-8
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 3
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
121
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
122
|
+
none: false
|
123
|
+
requirements:
|
124
|
+
- - ">="
|
125
|
+
- !ruby/object:Gem::Version
|
126
|
+
hash: 3
|
127
|
+
segments:
|
128
|
+
- 0
|
129
|
+
version: "0"
|
130
|
+
requirements: []
|
131
|
+
|
132
|
+
rubyforge_project:
|
133
|
+
rubygems_version: 1.3.7
|
134
|
+
signing_key:
|
135
|
+
specification_version: 3
|
136
|
+
summary: Dead simple server/client service built using eventmachine
|
137
|
+
test_files:
|
138
|
+
- spec/processing_spec.rb
|
139
|
+
- spec/spec_helper.rb
|