rb-kqueue 0.0.1
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/MIT-LICENSE +20 -0
- data/README.md +5 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/lib/rb-kqueue.rb +28 -0
- data/lib/rb-kqueue/event.rb +32 -0
- data/lib/rb-kqueue/native.rb +26 -0
- data/lib/rb-kqueue/native/flags.rb +85 -0
- data/lib/rb-kqueue/queue.rb +61 -0
- data/lib/rb-kqueue/watcher.rb +56 -0
- data/lib/rb-kqueue/watcher/process.rb +12 -0
- data/lib/rb-kqueue/watcher/read_write.rb +14 -0
- data/lib/rb-kqueue/watcher/socket_read_write.rb +22 -0
- data/lib/rb-kqueue/watcher/vnode.rb +13 -0
- metadata +87 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Nathan Weizenbaum
|
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,5 @@
|
|
1
|
+
# rb-kqueue
|
2
|
+
|
3
|
+
This is a simple wrapper over the [kqueue](http://en.wikipedia.org/wiki/Kqueue)
|
4
|
+
BSD event notification interface (supported on FreeBSD, NetBSD, OpenBSD, and Darwin).
|
5
|
+
It uses the [FFI](http://wiki.github.com/ffi/ffi) gem to avoid having to compile a C extension.
|
data/Rakefile
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "rb-kqueue"
|
8
|
+
gem.summary = "A Ruby wrapper for BSD's kqueue, using FFI"
|
9
|
+
gem.description = gem.summary
|
10
|
+
gem.email = "nex342@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/nex3/rb-kqueue"
|
12
|
+
gem.authors = ["Nathan Weizenbaum"]
|
13
|
+
gem.add_dependency "ffi", ">= 0.5.0"
|
14
|
+
gem.add_development_dependency "yard", ">= 0.4.0"
|
15
|
+
end
|
16
|
+
Jeweler::GemcutterTasks.new
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
module Jeweler::VersionHelper::PlaintextExtension
|
22
|
+
def write_with_inotify
|
23
|
+
write_without_inotify
|
24
|
+
filename = File.join(File.dirname(__FILE__), "lib/rb-kqueue.rb")
|
25
|
+
text = File.read(filename)
|
26
|
+
File.open(filename, 'w') do |f|
|
27
|
+
f.write text.gsub(/^( VERSION = ).*/, '\1' + [major, minor, patch].inspect)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
alias_method :write_without_inotify, :write
|
31
|
+
alias_method :write, :write_with_inotify
|
32
|
+
end
|
33
|
+
|
34
|
+
class Jeweler::Commands::Version::Base
|
35
|
+
def commit_version_with_inotify
|
36
|
+
return unless self.repo
|
37
|
+
self.repo.add(File.join(File.dirname(__FILE__), "lib/rb-kqueue.rb"))
|
38
|
+
commit_version_without_inotify
|
39
|
+
end
|
40
|
+
alias_method :commit_version_without_inotify, :commit_version
|
41
|
+
alias_method :commit_version, :commit_version_with_inotify
|
42
|
+
end
|
43
|
+
|
44
|
+
begin
|
45
|
+
require 'yard'
|
46
|
+
YARD::Rake::YardocTask.new
|
47
|
+
rescue LoadError
|
48
|
+
task :yardoc do
|
49
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
50
|
+
end
|
51
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/lib/rb-kqueue.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'rb-kqueue/native'
|
2
|
+
require 'rb-kqueue/native/flags'
|
3
|
+
require 'rb-kqueue/watcher'
|
4
|
+
require 'rb-kqueue/watcher/vnode'
|
5
|
+
require 'rb-kqueue/watcher/read_write'
|
6
|
+
require 'rb-kqueue/watcher/process'
|
7
|
+
require 'rb-kqueue/event'
|
8
|
+
require 'rb-kqueue/queue'
|
9
|
+
|
10
|
+
module KQueue
|
11
|
+
VERSION = [0, 0, 1]
|
12
|
+
|
13
|
+
def self.handle_error(errno = FFI.errno)
|
14
|
+
raise SystemCallError.new(
|
15
|
+
"KQueue failed" +
|
16
|
+
case errno
|
17
|
+
when Errno::EFAULT::Errno; ": There was an error reading or writing the kevent structure."
|
18
|
+
when Errno::EBADF::Errno; ": The specified descriptor is invalid."
|
19
|
+
when Errno::EINTR::Errno; ": A signal was delivered before the timeout expired and before any events were placed on the kqueue for return."
|
20
|
+
when Errno::EINVAL::Errno; ": The specified time limit or filter is invalid."
|
21
|
+
when Errno::ENOENT::Errno; ": The event could not be found to be modified or deleted."
|
22
|
+
when Errno::ENOMEM::Errno; ": No memory was available to register the event."
|
23
|
+
when Errno::ESRCH::Errno; ": The specified process to attach to does not exist."
|
24
|
+
else; ""
|
25
|
+
end,
|
26
|
+
errno)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module KQueue
|
2
|
+
class Event
|
3
|
+
attr_reader :data
|
4
|
+
attr_reader :filter
|
5
|
+
|
6
|
+
def watcher
|
7
|
+
@watcher ||= @queue.watchers[[filter, @native[:ident]]]
|
8
|
+
end
|
9
|
+
|
10
|
+
def flags
|
11
|
+
@fflags ||= Native::Flags.from_mask("NOTE", @native[:fflags])
|
12
|
+
end
|
13
|
+
|
14
|
+
def eof?
|
15
|
+
@flags.include?(:eof)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize(native, queue)
|
19
|
+
@native = native
|
20
|
+
@queue = queue
|
21
|
+
@data = @native[:data]
|
22
|
+
@filter = KQueue::Native::Flags.from_flag("EVFILT", @native[:filter])
|
23
|
+
@flags = Native::Flags.from_mask("EV", @native[:flags])
|
24
|
+
|
25
|
+
KQueue.handle_error @native[:data] if @flags.inclue?(:error)
|
26
|
+
end
|
27
|
+
|
28
|
+
def callback!
|
29
|
+
watcher.callback! self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'ffi'
|
2
|
+
|
3
|
+
module KQueue
|
4
|
+
module Native
|
5
|
+
extend FFI::Library
|
6
|
+
|
7
|
+
class KEvent < FFI::Struct
|
8
|
+
layout(
|
9
|
+
:ident, :uintptr_t,
|
10
|
+
:filter, :int16,
|
11
|
+
:flags, :uint16,
|
12
|
+
:fflags, :uint32,
|
13
|
+
:data, :intptr_t,
|
14
|
+
:udata, :pointer)
|
15
|
+
end
|
16
|
+
|
17
|
+
class TimeSpec < FFI::Struct
|
18
|
+
layout(
|
19
|
+
:tv_sec, :time_t,
|
20
|
+
:tv_nsec, :long)
|
21
|
+
end
|
22
|
+
|
23
|
+
attach_function :kqueue, [], :int
|
24
|
+
attach_function :kevent, [:int, :pointer, :int, :pointer, :int, :pointer], :int
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module KQueue
|
2
|
+
module Native
|
3
|
+
module Flags
|
4
|
+
# Filters
|
5
|
+
EVFILT_READ = -1
|
6
|
+
EVFILT_WRITE = -2
|
7
|
+
EVFILT_AIO = -3 # Attached to aio requests
|
8
|
+
EVFILT_VNODE = -4 # Attached to vnodes
|
9
|
+
EVFILT_PROC = -5 # Attached to struct proc
|
10
|
+
EVFILT_SIGNAL = -6 # Attached to struct proc
|
11
|
+
EVFILT_TIMER = -7 # Timers
|
12
|
+
EVFILT_MACHPORT = -8 # Mach portsets
|
13
|
+
EVFILT_FS = -9 # Filesystem events
|
14
|
+
EVFILT_USER = -10 # User events
|
15
|
+
EVFILT_SESSION = -11 # Audit session events
|
16
|
+
|
17
|
+
|
18
|
+
# Actions
|
19
|
+
EV_ADD = 0x0001 # Add event to kq (implies enable)
|
20
|
+
EV_DELETE = 0x0002 # Delete event from kq
|
21
|
+
EV_ENABLE = 0x0004 # Enable event
|
22
|
+
EV_DISABLE = 0x0008 # Disable event (not reported)
|
23
|
+
EV_RECEIPT = 0x0040 # Force EV_ERROR on success, data == 0
|
24
|
+
|
25
|
+
# Flags
|
26
|
+
EV_ONESHOT = 0x0010 # Only report one occurrence
|
27
|
+
EV_CLEAR = 0x0020 # Clear event state after reporting
|
28
|
+
EV_DISPATCH = 0x0080 # Disable event after reporting
|
29
|
+
|
30
|
+
# Returned values
|
31
|
+
EV_EOF = 0x8000 # EOF detected
|
32
|
+
EV_ERROR = 0x4000 # Error, data contains errno
|
33
|
+
|
34
|
+
|
35
|
+
# For EVFILT_{READ,WRITE}
|
36
|
+
NOTE_LOWAT = 0x00000001 # Low water mark
|
37
|
+
|
38
|
+
# For EVFILT_VNODE
|
39
|
+
NOTE_DELETE = 0x00000001 # Vnode was removed
|
40
|
+
NOTE_WRITE = 0x00000002 # Data contents changed
|
41
|
+
NOTE_EXTEND = 0x00000004 # Size increased
|
42
|
+
NOTE_ATTRIB = 0x00000008 # Attributes changed
|
43
|
+
NOTE_LINK = 0x00000010 # Link count changed
|
44
|
+
NOTE_RENAME = 0x00000020 # Vnode was renamed
|
45
|
+
NOTE_REVOKE = 0x00000040 # Vnode access was revoked
|
46
|
+
NOTE_NONE = 0x00000080 # No specific vnode event: to test for EVFILT_READ activation
|
47
|
+
|
48
|
+
|
49
|
+
# Converts a list of flags to the bitmask that the C API expects.
|
50
|
+
#
|
51
|
+
# @param prefix [String] The prefix for the C names of the flags
|
52
|
+
# @param flags [Array<Symbol>]
|
53
|
+
# @return [Fixnum]
|
54
|
+
def self.to_mask(prefix, flags)
|
55
|
+
flags.map {|flag| const_get("#{prefix}_#{flag.to_s.upcase}")}.
|
56
|
+
inject(0) {|mask, flag| mask | flag}
|
57
|
+
end
|
58
|
+
|
59
|
+
# Converts a bitmask from the C API into a list of flags.
|
60
|
+
#
|
61
|
+
# @param prefix [String] The prefix for the C names of the flags
|
62
|
+
# @param mask [Fixnum]
|
63
|
+
# @return [Array<Symbol>]
|
64
|
+
def self.from_mask(prefix, mask)
|
65
|
+
re = /^#{Regexp.quote prefix}_/
|
66
|
+
constants.select do |c|
|
67
|
+
next false unless c =~ re
|
68
|
+
const_get(c) & mask != 0
|
69
|
+
end.map {|c| c.sub("#{prefix}_", "").downcase.to_sym}
|
70
|
+
end
|
71
|
+
|
72
|
+
def self.to_flag(prefix, flag)
|
73
|
+
const_get("#{prefix}_#{flag.to_s.upcase}")
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.from_flag(prefix, flag)
|
77
|
+
re = /^#{Regexp.quote prefix}_/
|
78
|
+
constants.each do |c|
|
79
|
+
next unless c =~ re
|
80
|
+
return c.sub("#{prefix}_", "").downcase.to_sym if const_get(c) == flag
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module KQueue
|
2
|
+
class Queue
|
3
|
+
attr_reader :fd
|
4
|
+
attr_reader :watchers
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
@fd = Native.kqueue
|
8
|
+
@watchers = {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def watch_for_read(fd, &callback)
|
12
|
+
fd = fd.fileno if fd.respond_to?(:fileno)
|
13
|
+
Watcher::ReadWrite.new(self, fd, :read, callback)
|
14
|
+
end
|
15
|
+
|
16
|
+
def watch_for_socket_read(fd, low_water = nil, &callback)
|
17
|
+
fd = fd.fileno if fd.respond_to?(:fileno)
|
18
|
+
Watcher::SocketReadWrite.new(self, fd, :read, low_water, callback)
|
19
|
+
end
|
20
|
+
|
21
|
+
def watch_for_write(fd, &callback)
|
22
|
+
fd = fd.fileno if fd.respond_to?(:fileno)
|
23
|
+
Watcher::ReadWrite.new(self, fd, :write, callback)
|
24
|
+
end
|
25
|
+
|
26
|
+
def watch_for_socket_write(fd, low_water = nil, &callback)
|
27
|
+
fd = fd.fileno if fd.respond_to?(:fileno)
|
28
|
+
Watcher::SocketReadWrite.new(self, fd, :write, low_water, callback)
|
29
|
+
end
|
30
|
+
|
31
|
+
def watch_for_file_change(path, *flags, &callback)
|
32
|
+
Watcher::VNode.new(self, path, flags, callback)
|
33
|
+
end
|
34
|
+
|
35
|
+
def watch_for_process_change(pid, *flags, &callback)
|
36
|
+
Watcher::Process.new(self, path, flags, callback)
|
37
|
+
end
|
38
|
+
|
39
|
+
def run
|
40
|
+
@stop = false
|
41
|
+
process until @stop
|
42
|
+
end
|
43
|
+
|
44
|
+
def stop
|
45
|
+
@stop = true
|
46
|
+
end
|
47
|
+
|
48
|
+
def process
|
49
|
+
read_events.each {|event| event.callback!}
|
50
|
+
end
|
51
|
+
|
52
|
+
def read_events
|
53
|
+
size = 1024
|
54
|
+
eventlist = FFI::MemoryPointer.new(Native::KEvent, size)
|
55
|
+
res = Native.kevent(@fd, nil, 0, eventlist, size, nil)
|
56
|
+
|
57
|
+
KQueue.handle_error if res < 0
|
58
|
+
(0...res).map {|i| KQueue::Event.new(Native::KEvent.new(eventlist[i]), self)}
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module KQueue
|
2
|
+
class Watcher
|
3
|
+
attr_reader :queue
|
4
|
+
|
5
|
+
def initialize(queue, ident, filter, fflags, data, callback)
|
6
|
+
@queue = queue
|
7
|
+
@ident = ident
|
8
|
+
@filter = filter
|
9
|
+
@flags = []
|
10
|
+
@fflags = fflags
|
11
|
+
@data = data
|
12
|
+
@callback = callback
|
13
|
+
add!
|
14
|
+
end
|
15
|
+
|
16
|
+
def add!
|
17
|
+
kqueue! :add, :clear # TODO: Don't always enable :clear
|
18
|
+
@queue.watchers[[@filter, @ident]] = self
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete!
|
22
|
+
kqueue! :delete
|
23
|
+
@queue.watchers.delete([@filter, @ident])
|
24
|
+
end
|
25
|
+
|
26
|
+
def enable!
|
27
|
+
kqueue! :enable
|
28
|
+
end
|
29
|
+
|
30
|
+
def disable!
|
31
|
+
kqueue! :disable
|
32
|
+
end
|
33
|
+
|
34
|
+
def callback!(event)
|
35
|
+
@callback.call event
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def native(flags)
|
41
|
+
native = Native::KEvent.new
|
42
|
+
native[:ident] = @ident
|
43
|
+
native[:filter] = Native::Flags.to_flag("EVFILT", @filter)
|
44
|
+
native[:flags] = Native::Flags.to_mask("EV", @flags | flags)
|
45
|
+
native[:fflags] = Native::Flags.to_mask("NOTE", @fflags)
|
46
|
+
native[:data] = @data if @data
|
47
|
+
native
|
48
|
+
end
|
49
|
+
|
50
|
+
def kqueue!(*flags)
|
51
|
+
if Native.kevent(@queue.fd, native(flags).pointer, 1, nil, 0, nil) < 0
|
52
|
+
KQueue.handle_error
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module KQueue
|
2
|
+
class Watcher
|
3
|
+
class SocketReadWrite < ReadWrite
|
4
|
+
attr_reader :low_water
|
5
|
+
|
6
|
+
def initialize(queue, fd, type, low_water, callback)
|
7
|
+
@fd = fd
|
8
|
+
@type = type
|
9
|
+
|
10
|
+
if low_water
|
11
|
+
fflags = [:lowat]
|
12
|
+
data = low_water
|
13
|
+
else
|
14
|
+
fflags = []
|
15
|
+
data = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
super(queue, @fd, type, fflags, data, callback)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module KQueue
|
2
|
+
class Watcher
|
3
|
+
class VNode < Watcher
|
4
|
+
attr_reader :path
|
5
|
+
|
6
|
+
def initialize(queue, path, flags, callback)
|
7
|
+
@path = path
|
8
|
+
@file = File.open(path) # TODO: not JRuby-compatible
|
9
|
+
super(queue, @file.fileno, :vnode, flags, nil, callback)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
metadata
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rb-kqueue
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nathan Weizenbaum
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-07 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ffi
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.5.0
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: yard
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.4.0
|
34
|
+
version:
|
35
|
+
description: A Ruby wrapper for BSD's kqueue, using FFI
|
36
|
+
email: nex342@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.md
|
43
|
+
files:
|
44
|
+
- MIT-LICENSE
|
45
|
+
- README.md
|
46
|
+
- Rakefile
|
47
|
+
- VERSION
|
48
|
+
- lib/rb-kqueue.rb
|
49
|
+
- lib/rb-kqueue/event.rb
|
50
|
+
- lib/rb-kqueue/native.rb
|
51
|
+
- lib/rb-kqueue/native/flags.rb
|
52
|
+
- lib/rb-kqueue/queue.rb
|
53
|
+
- lib/rb-kqueue/watcher.rb
|
54
|
+
- lib/rb-kqueue/watcher/process.rb
|
55
|
+
- lib/rb-kqueue/watcher/read_write.rb
|
56
|
+
- lib/rb-kqueue/watcher/socket_read_write.rb
|
57
|
+
- lib/rb-kqueue/watcher/vnode.rb
|
58
|
+
has_rdoc: true
|
59
|
+
homepage: http://github.com/nex3/rb-kqueue
|
60
|
+
licenses: []
|
61
|
+
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options:
|
64
|
+
- --charset=UTF-8
|
65
|
+
require_paths:
|
66
|
+
- lib
|
67
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
requirements: []
|
80
|
+
|
81
|
+
rubyforge_project:
|
82
|
+
rubygems_version: 1.3.5
|
83
|
+
signing_key:
|
84
|
+
specification_version: 3
|
85
|
+
summary: A Ruby wrapper for BSD's kqueue, using FFI
|
86
|
+
test_files: []
|
87
|
+
|