ruby-osc 0.3.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/History.txt +4 -0
- data/Manifest.txt +15 -0
- data/README.rdoc +81 -0
- data/Rakefile +25 -0
- data/examples/localtest.rb +30 -0
- data/lib/ruby-osc/bundle.rb +52 -0
- data/lib/ruby-osc/client.rb +39 -0
- data/lib/ruby-osc/message.rb +66 -0
- data/lib/ruby-osc/server.rb +73 -0
- data/lib/ruby-osc.rb +60 -0
- data/ruby-osc.gemspec +42 -0
- data/spec/bundle_spec.rb +99 -0
- data/spec/message_spec.rb +164 -0
- data/spec/server_spec.rb +5 -0
- data/spec/spec_helper.rb +7 -0
- metadata +111 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
examples/localtest.rb
|
6
|
+
lib/ruby-osc.rb
|
7
|
+
lib/ruby-osc/bundle.rb
|
8
|
+
lib/ruby-osc/client.rb
|
9
|
+
lib/ruby-osc/message.rb
|
10
|
+
lib/ruby-osc/server.rb
|
11
|
+
ruby-osc.gemspec
|
12
|
+
spec/bundle_spec.rb
|
13
|
+
spec/message_spec.rb
|
14
|
+
spec/server_spec.rb
|
15
|
+
spec/spec_helper.rb
|
data/README.rdoc
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
= ruby-osc
|
2
|
+
|
3
|
+
http://github.com/maca/ruby-osc
|
4
|
+
|
5
|
+
Concise OSC implementation for Ruby
|
6
|
+
|
7
|
+
* 1.9.1 and jRuby compatible
|
8
|
+
* Complies with OSC 1.0 specification
|
9
|
+
* Uses EventMachine for networking and lightweight soft-real-time bundle scheduling
|
10
|
+
|
11
|
+
== SYNOPSIS:
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'ruby-osc'
|
15
|
+
|
16
|
+
include OSC
|
17
|
+
|
18
|
+
server = Server.new 9090
|
19
|
+
client = Client.new 9090
|
20
|
+
|
21
|
+
server.add_pattern /.*/ do |*args| # this will match any address
|
22
|
+
p "/.*/: #{ args.join(', ') }"
|
23
|
+
end
|
24
|
+
|
25
|
+
server.add_pattern %r{foo/.*} do |*args| # this will match any /foo node
|
26
|
+
p "%r{foo/.*}: #{ args.join(', ') }"
|
27
|
+
end
|
28
|
+
|
29
|
+
server.add_pattern "/foo/bar" do |*args| # this will just match /foo/bar address
|
30
|
+
p "'/foo/bar': #{ args.join(', ') }"
|
31
|
+
end
|
32
|
+
|
33
|
+
server.add_pattern "/exit" do |*args| # this will just match /exit address
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
|
37
|
+
client.send Message.new('/foo/bar', 1, 1.2, 'a string')
|
38
|
+
client.send Message.new('/foo/bar/zar', 1, 1.2, 'a string')
|
39
|
+
client.send Bundle.new(Time.now + 2, Message.new('/exit'))
|
40
|
+
|
41
|
+
OSC::Thread.join
|
42
|
+
|
43
|
+
== REQUIREMENTS:
|
44
|
+
|
45
|
+
eventmachine
|
46
|
+
|
47
|
+
== INSTALL:
|
48
|
+
|
49
|
+
sudo gem install ruby-osc –-source http://gemcutter.org
|
50
|
+
|
51
|
+
Although I prefer not to use sudo: http://dev.innovationfactory.nl/2009/10/26/never-use-sudo-gem-install-again/
|
52
|
+
|
53
|
+
== TODO:
|
54
|
+
|
55
|
+
- OSC 1.1 compatibility mode
|
56
|
+
- Full documentation
|
57
|
+
|
58
|
+
== LICENSE:
|
59
|
+
|
60
|
+
(The MIT License)
|
61
|
+
|
62
|
+
Copyright (c) 2009 Macario Ortega
|
63
|
+
|
64
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
65
|
+
a copy of this software and associated documentation files (the
|
66
|
+
'Software'), to deal in the Software without restriction, including
|
67
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
68
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
69
|
+
permit persons to whom the Software is furnished to do so, subject to
|
70
|
+
the following conditions:
|
71
|
+
|
72
|
+
The above copyright notice and this permission notice shall be
|
73
|
+
included in all copies or substantial portions of the Software.
|
74
|
+
|
75
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
76
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
77
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
78
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
79
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
80
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
81
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'hoe', '>= 2.1.0'
|
3
|
+
require 'hoe'
|
4
|
+
require 'fileutils'
|
5
|
+
require './lib/ruby-osc'
|
6
|
+
|
7
|
+
Hoe.plugin :newgem
|
8
|
+
# Hoe.plugin :website
|
9
|
+
# Hoe.plugin :cucumberfeatures
|
10
|
+
|
11
|
+
# Generate all the Rake tasks
|
12
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
13
|
+
$hoe = Hoe.spec 'ruby-osc' do
|
14
|
+
self.developer 'Macario Ortega', 'macarui@gmail.com'
|
15
|
+
self.rubyforge_name = self.name # TODO this is default value
|
16
|
+
self.extra_deps = [['eventmachine','>= 0.12.8']]
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'newgem/tasks'
|
21
|
+
Dir['tasks/**/*.rake'].each { |t| load t }
|
22
|
+
|
23
|
+
# TODO - want other tests/tasks run by default? Add them to the list
|
24
|
+
# remove_task :default
|
25
|
+
# task :default => [:spec, :features]
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
# require "#{ File.dirname __FILE__ }/../lib/ruby-osc"
|
3
|
+
require 'ruby-osc'
|
4
|
+
|
5
|
+
include OSC
|
6
|
+
|
7
|
+
server = Server.new 9090
|
8
|
+
client = Client.new 9090
|
9
|
+
|
10
|
+
server.add_pattern /.*/ do |*args| # this will match any address
|
11
|
+
p "/.*/: #{ args.join(', ') }"
|
12
|
+
end
|
13
|
+
|
14
|
+
server.add_pattern %r{foo/.*} do |*args| # this will match any /foo node
|
15
|
+
p "%r{foo/.*}: #{ args.join(', ') }"
|
16
|
+
end
|
17
|
+
|
18
|
+
server.add_pattern "/foo/bar" do |*args| # this will just match /foo/bar address
|
19
|
+
p "'/foo/bar': #{ args.join(', ') }"
|
20
|
+
end
|
21
|
+
|
22
|
+
server.add_pattern "/exit" do |*args| # this will just match /exit address
|
23
|
+
exit
|
24
|
+
end
|
25
|
+
|
26
|
+
client.send Message.new('/foo/bar', 1, 1.2, 'a string')
|
27
|
+
client.send Message.new('/foo/bar/zar', 1, 1.2, 'a string')
|
28
|
+
client.send Bundle.new(Time.now + 2, Message.new('/exit'))
|
29
|
+
|
30
|
+
OSC::Thread.join
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module OSC
|
2
|
+
class Bundle < Array
|
3
|
+
attr_accessor :timetag
|
4
|
+
|
5
|
+
def initialize timetag = nil, *args
|
6
|
+
args.each{ |arg| raise TypeError, "#{ arg.inspect } is required to be a Bundle or Message" unless Bundle === arg or Message === arg }
|
7
|
+
raise TypeError, "#{ timetag.inspect } is required to be Time or nil" unless timetag == nil or Time === timetag
|
8
|
+
super args
|
9
|
+
@timetag = timetag
|
10
|
+
end
|
11
|
+
|
12
|
+
def encode
|
13
|
+
timetag =
|
14
|
+
if @timetag
|
15
|
+
time, tag, dir = OSC.encoding_directive @timetag
|
16
|
+
time.pack dir
|
17
|
+
else "\000\000\000\000\000\000\000\001" end
|
18
|
+
|
19
|
+
"#bundle\000#{ timetag }" + collect do |x|
|
20
|
+
x = x.encode
|
21
|
+
[x.size].pack('N') + x
|
22
|
+
end.join
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.decode string
|
26
|
+
string.gsub! /^#bundle\000/, ''
|
27
|
+
t1, t2, content_str = string.unpack('N2a*')
|
28
|
+
|
29
|
+
timetag = Time.at(t1 + t2 / (2**32.0) - 2_208_988_800) rescue nil
|
30
|
+
scanner = StringScanner.new content_str
|
31
|
+
args = []
|
32
|
+
|
33
|
+
until scanner.eos?
|
34
|
+
size = scanner.scan(/.{4}/).unpack('N').first
|
35
|
+
arg_str = scanner.scan(/.{#{ size }}/nm) rescue raise(DecodeError, "An error occured while trying to decode bad formatted osc bundle")
|
36
|
+
args << OSC.decode(arg_str)
|
37
|
+
end
|
38
|
+
|
39
|
+
new timetag, *args
|
40
|
+
end
|
41
|
+
|
42
|
+
def == other
|
43
|
+
self.class == other.class and self.timetag == other.timetag and self.to_a == other.to_a
|
44
|
+
end
|
45
|
+
|
46
|
+
def to_a; Array.new self; end
|
47
|
+
|
48
|
+
def to_s
|
49
|
+
"OSC::Bundle(#{ self.join(', ') })"
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# From the Funaba osc gem:
|
2
|
+
module OSC
|
3
|
+
class Client
|
4
|
+
|
5
|
+
def initialize port, host = 'localhost'
|
6
|
+
@socket = UDPSocket.new
|
7
|
+
@socket.connect 'localhost', port
|
8
|
+
end
|
9
|
+
|
10
|
+
def send mesg, *args
|
11
|
+
@socket.send mesg.encode, 0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# module OSC
|
17
|
+
# class Client
|
18
|
+
#
|
19
|
+
# def initialize port, address = 'localhost'
|
20
|
+
# @address, @port = address, port
|
21
|
+
# run
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# def run
|
25
|
+
# @connection = EventMachine.open_datagram_socket 'localhost', 0, Connection
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# def stop
|
29
|
+
# @connection.close_connection if @connection
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# def send item
|
33
|
+
# @connection.send_datagram item.encode, @address, @port
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# class Connection < EventMachine::Connection #:nodoc:
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
# end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module OSC
|
2
|
+
class Message
|
3
|
+
attr_accessor :address, :time, :args
|
4
|
+
|
5
|
+
def initialize address = '', *args
|
6
|
+
args.collect! { |arg| OSC.coerce_argument arg }
|
7
|
+
args.flatten! # won't harm we're not accepting arrays anyway, in case an custom coerced arg coerces to Array eg. Hash
|
8
|
+
raise(TypeError, "Expected address to be a string") unless String === address
|
9
|
+
@address, @args = address, args
|
10
|
+
end
|
11
|
+
|
12
|
+
def encode
|
13
|
+
objs, tags, dirs = @args.collect { |arg| OSC.encoding_directive arg }.transpose
|
14
|
+
dirs ||= [] and objs ||= []
|
15
|
+
|
16
|
+
[",#{ tags and tags.join }", @address].each do |str|
|
17
|
+
obj, tag, dir = OSC.encoding_directive str
|
18
|
+
objs.unshift obj
|
19
|
+
dirs.unshift dir
|
20
|
+
end
|
21
|
+
|
22
|
+
objs.flatten.compact.pack dirs.join
|
23
|
+
end
|
24
|
+
|
25
|
+
def == other
|
26
|
+
self.class == other.class and to_a == other.to_a
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_a; @args.dup.unshift(@address) end
|
30
|
+
def to_s; "OSC::Message(#{ args.join(', ') })" end
|
31
|
+
|
32
|
+
def self.decode string
|
33
|
+
scanner = StringScanner.new string
|
34
|
+
address, tags = (1..2).map do
|
35
|
+
string = scanner.scan(/[^\000]+\000/)
|
36
|
+
scanner.pos += OSC.padding_size(string.size)
|
37
|
+
string.chomp("\000")
|
38
|
+
end
|
39
|
+
|
40
|
+
args = []
|
41
|
+
tags.scan(/\w/) do |tag|
|
42
|
+
case tag
|
43
|
+
when 'i'
|
44
|
+
int = scanner.scan(/.{4}/).unpack('N').first
|
45
|
+
args.push( int > (2**31-1) ? int - 2**32 : int )
|
46
|
+
when 'f'
|
47
|
+
args.push scanner.scan(/.{4}/nm).unpack('g').first
|
48
|
+
when 's'
|
49
|
+
str = scanner.scan(/[^\000]+\000/)
|
50
|
+
scanner.pos += OSC.padding_size(str.size)
|
51
|
+
args.push str.chomp("\000")
|
52
|
+
when 'b'
|
53
|
+
size = scanner.scan(/.{4}/).unpack('N').first
|
54
|
+
str = scanner.scan(/.{#{ size }}/nm)
|
55
|
+
scanner.pos += OSC.padding_size(size + 4)
|
56
|
+
args.push Blob.new(str)
|
57
|
+
else
|
58
|
+
raise DecodeError, "#{ t } is not a known tag"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
new address, *args
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
|
2
|
+
module OSC
|
3
|
+
class Server
|
4
|
+
attr_accessor :port, :address
|
5
|
+
|
6
|
+
def initialize port, address = 'localhost'
|
7
|
+
@port, @address = port, address
|
8
|
+
@queue, @patterns = [], []
|
9
|
+
@mutex = Mutex.new
|
10
|
+
run
|
11
|
+
end
|
12
|
+
|
13
|
+
def run
|
14
|
+
@connection = EventMachine.open_datagram_socket @address, @port, Connection, self
|
15
|
+
check_queue
|
16
|
+
end
|
17
|
+
|
18
|
+
def stop
|
19
|
+
return unless @connection
|
20
|
+
@connection.close_connection
|
21
|
+
@timer.cancel
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_pattern pattern, &block
|
25
|
+
raise ArgumentError.new("A block must be given") unless block
|
26
|
+
@patterns << [pattern, block]
|
27
|
+
end
|
28
|
+
|
29
|
+
def delete_pattern pattern
|
30
|
+
@patterns.delete pattern
|
31
|
+
end
|
32
|
+
|
33
|
+
def receive data
|
34
|
+
case decoded = OSC.decode(data)
|
35
|
+
when Bundle
|
36
|
+
decoded.timetag.nil? ? decoded.each{ |m| dispatch m } : @mutex.synchronize{@queue.push(decoded)}
|
37
|
+
when Message
|
38
|
+
dispatch decoded
|
39
|
+
end
|
40
|
+
rescue => e
|
41
|
+
warn "Bad data received: #{ e }"
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
def check_queue
|
46
|
+
@timer = EventMachine::PeriodicTimer.new 0.002 do
|
47
|
+
now = Time.now
|
48
|
+
@mutex.synchronize do
|
49
|
+
@queue.delete_if do |bundle|
|
50
|
+
bundle.each{ |m| dispatch m } if delete = now >= bundle.timetag
|
51
|
+
delete
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def dispatch message
|
58
|
+
@patterns.each do |pat, block|
|
59
|
+
block.call(*message.to_a) if pat === message.address
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
class Connection < EventMachine::Connection #:nodoc:
|
64
|
+
def initialize server
|
65
|
+
@server = server
|
66
|
+
end
|
67
|
+
|
68
|
+
def receive_data data
|
69
|
+
@server.receive(data)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/ruby-osc.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
require 'rubygems'
|
3
|
+
require 'eventmachine'
|
4
|
+
require 'socket' # Strange side effects with eventmachine udp client and SuperCollider
|
5
|
+
require 'strscan'
|
6
|
+
require 'thread'
|
7
|
+
|
8
|
+
$:.unshift( File.dirname( __FILE__ ) )
|
9
|
+
|
10
|
+
require 'ruby-osc/message'
|
11
|
+
require 'ruby-osc/bundle'
|
12
|
+
require 'ruby-osc/server'
|
13
|
+
require 'ruby-osc/client'
|
14
|
+
|
15
|
+
module OSC
|
16
|
+
VERSION = '0.3.1'
|
17
|
+
Thread = EM.reactor_running? ? nil : Thread.new { EM.run }
|
18
|
+
Thread.run if RUBY_VERSION.to_f >= 1.9
|
19
|
+
EM.error_handler { |e| puts e }
|
20
|
+
EM.set_quantum 5
|
21
|
+
|
22
|
+
class DecodeError < StandardError; end
|
23
|
+
|
24
|
+
class Blob < String; end
|
25
|
+
|
26
|
+
module OSCArgument
|
27
|
+
def to_osc_type
|
28
|
+
raise NotImplementedError, "#to_osc_type method should be implemented for #{ self.class }"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.coerce_argument arg
|
33
|
+
case arg
|
34
|
+
when OSCArgument then arg.to_osc_type
|
35
|
+
when Symbol then arg.to_s
|
36
|
+
when String, Float, Fixnum, Blob, String then arg # Pure osc 1.0 specification
|
37
|
+
else raise(TypeError, "#{ arg.inspect } is not a valid Message argument") end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.decode str #:nodoc:
|
41
|
+
str.match(/^#bundle/) ? Bundle.decode(str) : Message.decode(str)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.padding_size size
|
45
|
+
(4 - (size) % 4) % 4
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.encoding_directive obj #:nodoc:
|
49
|
+
case obj
|
50
|
+
when Float then [obj, 'f', 'g']
|
51
|
+
when Fixnum then [obj, 'i', 'N']
|
52
|
+
when Blob then [[obj.size, obj], 'b', "Na*x#{ padding_size obj.size + 4 }"]
|
53
|
+
when String then [obj, 's', "Z*x#{ padding_size obj.size + 1 }"]
|
54
|
+
when Time
|
55
|
+
t1, fr = (obj.to_f + 2208988800).divmod(1)
|
56
|
+
t2 = (fr * (2**32)).to_i
|
57
|
+
[[t1, t2], 't', 'N2']
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/ruby-osc.gemspec
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{ruby-osc}
|
5
|
+
s.version = "0.3.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Macario Ortega"]
|
9
|
+
s.date = %q{2010-01-17}
|
10
|
+
s.description = %q{}
|
11
|
+
s.email = ["macarui@gmail.com"]
|
12
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt"]
|
13
|
+
s.files = ["History.txt", "Manifest.txt", "README.rdoc", "Rakefile", "examples/localtest.rb", "lib/ruby-osc.rb", "lib/ruby-osc/bundle.rb", "lib/ruby-osc/client.rb", "lib/ruby-osc/message.rb", "lib/ruby-osc/server.rb", "ruby-osc.gemspec", "spec/bundle_spec.rb", "spec/message_spec.rb", "spec/server_spec.rb", "spec/spec_helper.rb"]
|
14
|
+
s.homepage = %q{http://github.com/maca/ruby-osc}
|
15
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{ruby-osc}
|
18
|
+
s.rubygems_version = %q{1.3.5}
|
19
|
+
s.summary = %q{}
|
20
|
+
|
21
|
+
if s.respond_to? :specification_version then
|
22
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
23
|
+
s.specification_version = 3
|
24
|
+
|
25
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
26
|
+
s.add_runtime_dependency(%q<eventmachine>, [">= 0.12.8"])
|
27
|
+
s.add_development_dependency(%q<rubyforge>, [">= 2.0.3"])
|
28
|
+
s.add_development_dependency(%q<gemcutter>, [">= 0.3.0"])
|
29
|
+
s.add_development_dependency(%q<hoe>, [">= 2.5.0"])
|
30
|
+
else
|
31
|
+
s.add_dependency(%q<eventmachine>, [">= 0.12.8"])
|
32
|
+
s.add_dependency(%q<rubyforge>, [">= 2.0.3"])
|
33
|
+
s.add_dependency(%q<gemcutter>, [">= 0.3.0"])
|
34
|
+
s.add_dependency(%q<hoe>, [">= 2.5.0"])
|
35
|
+
end
|
36
|
+
else
|
37
|
+
s.add_dependency(%q<eventmachine>, [">= 0.12.8"])
|
38
|
+
s.add_dependency(%q<rubyforge>, [">= 2.0.3"])
|
39
|
+
s.add_dependency(%q<gemcutter>, [">= 0.3.0"])
|
40
|
+
s.add_dependency(%q<hoe>, [">= 2.5.0"])
|
41
|
+
end
|
42
|
+
end
|
data/spec/bundle_spec.rb
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
require "#{ File.dirname __FILE__ }/spec_helper"
|
2
|
+
|
3
|
+
describe Bundle do
|
4
|
+
|
5
|
+
it "should accept bundle" do
|
6
|
+
Bundle.new( Time.now, Bundle.new ).to_a.should == [Bundle.new]
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should accept message" do
|
10
|
+
Bundle.new( Time.now, Message.new ).to_a.should == [Message.new]
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should rise TypeError if passing passing incorrect type" do
|
14
|
+
lambda { Bundle.new Time.now, 1 }.should raise_error(TypeError)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should raise TypeError if timetag is not Time" do
|
18
|
+
lambda { Bundle.new 1 }.should raise_error(TypeError)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should accept nil for timetag" do
|
22
|
+
Bundle.new nil
|
23
|
+
end
|
24
|
+
|
25
|
+
describe 'Encode/decode' do
|
26
|
+
shared_examples_for 'Encodable Bundle' do
|
27
|
+
it "should encode" do
|
28
|
+
@bundle.encode.should == @expected
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should decode to bundle" do
|
32
|
+
Bundle.decode(@expected).should be_a(Bundle)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should decode timetag" do
|
36
|
+
Bundle.decode(@expected).timetag.should == @bundle.timetag
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should actually decode" do
|
40
|
+
Bundle.decode(@expected).should == @bundle
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'Empty bundle nil timetag' do
|
46
|
+
before do
|
47
|
+
@bundle = Bundle.new
|
48
|
+
@expected = "#bundle\000\000\000\000\000\000\000\000\001"
|
49
|
+
end
|
50
|
+
it_should_behave_like 'Encodable Bundle'
|
51
|
+
end
|
52
|
+
|
53
|
+
describe 'Empty bundle with timetag' do
|
54
|
+
before do
|
55
|
+
@bundle = Bundle.new Time.at(1251420949.16959)
|
56
|
+
@expected = [35, 98, 117, 110, 100, 108, 101, 0, 206, 65, 169, 149, 43, 106, 64, 0].pack('C*')
|
57
|
+
end
|
58
|
+
it_should_behave_like 'Encodable Bundle'
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'Bundle with timetag and messages' do
|
62
|
+
before do
|
63
|
+
@bundle = Bundle.new Time.at(946702800), Message.new('/bar/foo', 4, 3, 2, 1), Message.new('/foo/bar', 1, 2, 3, 4)
|
64
|
+
@expected = [35, 98, 117, 110, 100, 108, 101, 0, 188, 24, 8, 80, 0, 0, 0, 0, 0, 0, 0, 36, 47, 98, 97, 114, 47, 102, 111, 111, 0, 0, 0, 0, 44, 105, 105, 105, 105, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 36, 47, 102, 111, 111, 47, 98, 97, 114, 0, 0, 0, 0, 44, 105, 105, 105, 105, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 4].pack('C*')
|
65
|
+
end
|
66
|
+
it_should_behave_like 'Encodable Bundle'
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'Nested bundles' do
|
70
|
+
before do
|
71
|
+
@bundle = Bundle.new( nil, Bundle.new(nil, Message.new('/a')), Message.new('/b') )
|
72
|
+
@expected = "#bundle\000\000\000\000\000\000\000\000\001\000\000\000\034#bundle\000\000\000\000\000\000\000\000\001\000\000\000\b/a\000\000,\000\000\000\000\000\000\b/b\000\000,\000\000\000"
|
73
|
+
end
|
74
|
+
it_should_behave_like 'Encodable Bundle'
|
75
|
+
end
|
76
|
+
|
77
|
+
describe 'Complex blob' do
|
78
|
+
before do
|
79
|
+
data = [ 83, 67, 103, 102, 0, 0, 0, 1, 0, 1, 4, 104, 111, 108, 97, 0, 2, 67, -36, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 83, 105, 110, 79, 115, 99, 2, 0, 2, 0, 1, 0, 0, -1, -1, 0, 0, -1, -1, 0, 1, 2, 0, 0 ].pack('C*')
|
80
|
+
@bundle = Bundle.new nil, Message.new('/d_recv', Blob.new(data), 0)
|
81
|
+
@expected = [35, 98, 117, 110, 100, 108, 101, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 76, 47, 100, 95, 114, 101, 99, 118, 0, 44, 98, 105, 0, 0, 0, 0, 56, 83, 67, 103, 102, 0, 0, 0, 1, 0, 1, 4, 104, 111, 108, 97, 0, 2, 67, 220, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 6, 83, 105, 110, 79, 115, 99, 2, 0, 2, 0, 1, 0, 0, 255, 255, 0, 0, 255, 255, 0, 1, 2, 0, 0, 0, 0, 0, 0].pack('C*')
|
82
|
+
end
|
83
|
+
it_should_behave_like 'Encodable Bundle'
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'Complex blob 2' do
|
87
|
+
before do
|
88
|
+
data = [ 83, 67, 103, 102, 0, 0, 0, 1, 0, 1, 2, 97, 109, 0, 7, 0, 0, 0, 0, 63, 0, 0, 0, 63, -128, 0, 0, 64, 0, 0, 0, -62, -58, 0, 0, 64, -96, 0, 0, -64, -128, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 103, 97, 116, 101, 0, 0, 9, 112, 111, 114, 116, 97, 100, 111, 114, 97, 0, 1, 10, 109, 111, 100, 117, 108, 97, 100, 111, 114, 97, 0, 2, 3, 97, 109, 112, 0, 3, 0, 8, 7, 67, 111, 110, 116, 114, 111, 108, 1, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 6, 83, 105, 110, 79, 115, 99, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 2, -1, -1, 0, 0, 1, 6, 77, 117, 108, 65, 100, 100, 1, 0, 3, 0, 1, 0, 0, 0, 1, 0, 0, -1, -1, 0, 1, -1, -1, 0, 1, 1, 6, 83, 105, 110, 79, 115, 99, 2, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, -1, -1, 0, 0, 2, 12, 66, 105, 110, 97, 114, 121, 79, 112, 85, 71, 101, 110, 2, 0, 2, 0, 1, 0, 2, 0, 3, 0, 0, 0, 2, 0, 0, 2, 6, 69, 110, 118, 71, 101, 110, 1, 0, 17, 0, 1, 0, 0, 0, 0, 0, 0, -1, -1, 0, 2, -1, -1, 0, 0, -1, -1, 0, 2, -1, -1, 0, 3, -1, -1, 0, 0, -1, -1, 0, 3, -1, -1, 0, 2, -1, -1, 0, 4, -1, -1, 0, 2, -1, -1, 0, 3, -1, -1, 0, 5, -1, -1, 0, 6, -1, -1, 0, 0, -1, -1, 0, 3, -1, -1, 0, 5, -1, -1, 0, 6, 1, 12, 66, 105, 110, 97, 114, 121, 79, 112, 85, 71, 101, 110, 2, 0, 2, 0, 1, 0, 2, 0, 4, 0, 0, 0, 5, 0, 0, 2, 3, 79, 117, 116, 2, 0, 2, 0, 0, 0, 0, -1, -1, 0, 0, 0, 6, 0, 0, 0, 0 ].pack('C*')
|
89
|
+
@bundle = Bundle.new nil, Message.new('/d_recv', Blob.new(data), 0)
|
90
|
+
@expected = [35, 98, 117, 110, 100, 108, 101, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 124, 47, 100, 95, 114, 101, 99, 118, 0, 44, 98, 105, 0, 0, 0, 1, 101, 83, 67, 103, 102, 0, 0, 0, 1, 0, 1, 2, 97, 109, 0, 7, 0, 0, 0, 0, 63, 0, 0, 0, 63, 128, 0, 0, 64, 0, 0, 0, 194, 198, 0, 0, 64, 160, 0, 0, 192, 128, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 103, 97, 116, 101, 0, 0, 9, 112, 111, 114, 116, 97, 100, 111, 114, 97, 0, 1, 10, 109, 111, 100, 117, 108, 97, 100, 111, 114, 97, 0, 2, 3, 97, 109, 112, 0, 3, 0, 8, 7, 67, 111, 110, 116, 114, 111, 108, 1, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 6, 83, 105, 110, 79, 115, 99, 1, 0, 2, 0, 1, 0, 0, 0, 0, 0, 2, 255, 255, 0, 0, 1, 6, 77, 117, 108, 65, 100, 100, 1, 0, 3, 0, 1, 0, 0, 0, 1, 0, 0, 255, 255, 0, 1, 255, 255, 0, 1, 1, 6, 83, 105, 110, 79, 115, 99, 2, 0, 2, 0, 1, 0, 0, 0, 0, 0, 1, 255, 255, 0, 0, 2, 12, 66, 105, 110, 97, 114, 121, 79, 112, 85, 71, 101, 110, 2, 0, 2, 0, 1, 0, 2, 0, 3, 0, 0, 0, 2, 0, 0, 2, 6, 69, 110, 118, 71, 101, 110, 1, 0, 17, 0, 1, 0, 0, 0, 0, 0, 0, 255, 255, 0, 2, 255, 255, 0, 0, 255, 255, 0, 2, 255, 255, 0, 3, 255, 255, 0, 0, 255, 255, 0, 3, 255, 255, 0, 2, 255, 255, 0, 4, 255, 255, 0, 2, 255, 255, 0, 3, 255, 255, 0, 5, 255, 255, 0, 6, 255, 255, 0, 0, 255, 255, 0, 3, 255, 255, 0, 5, 255, 255, 0, 6, 1, 12, 66, 105, 110, 97, 114, 121, 79, 112, 85, 71, 101, 110, 2, 0, 2, 0, 1, 0, 2, 0, 4, 0, 0, 0, 5, 0, 0, 2, 3, 79, 117, 116, 2, 0, 2, 0, 0, 0, 0, 255, 255, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0].pack('C*')
|
91
|
+
end
|
92
|
+
it_should_behave_like 'Encodable Bundle'
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'Should raise OSC::DecodeError with bad encoded bundle' do
|
96
|
+
bad_data = "#bundle\000\000\000\000\000\000\000\000\001\000\000\000\034#bundle\000\000\000\000\000\001\000\000\000\b/a\000\000,\000\000\000\000\000\000\b/b\000\000,\000\000\000"
|
97
|
+
lambda { Bundle.decode bad_data }.should raise_error(DecodeError)
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,164 @@
|
|
1
|
+
require "#{ File.dirname __FILE__ }/spec_helper"
|
2
|
+
|
3
|
+
describe Message do
|
4
|
+
|
5
|
+
it "should raise TypeError if passing wrong type" do
|
6
|
+
lambda { Message.new('address', Class) }.should raise_error(TypeError)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should raise TypeError if not passing a string for address" do
|
10
|
+
lambda { Message.new(OSC) }.should raise_error(TypeError)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have address" do
|
14
|
+
Message.new('/foo/bar').address.should == '/foo/bar'
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should collect args" do
|
18
|
+
Message.new('/foo/bar', 1, 2, 3, 4).args.size.should == 4
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should accept integer" do
|
22
|
+
Message.new('/foo/bar', 1).args.should == [1]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should should accept float" do
|
26
|
+
Message.new('/foo/bar', 1.0).args.should == [1.0]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should accept string" do
|
30
|
+
Message.new('/foo/bar', 'string').args.should == ['string']
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should accept Blob" do
|
34
|
+
Message.new('/foo/bar', Blob.new('blob')).args.should == [Blob.new('blob')]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should convert to array" do
|
38
|
+
Message.new('/foo/bar', 1, 2, 3, 4).to_a.should == ['/foo/bar', 1, 2, 3, 4]
|
39
|
+
end
|
40
|
+
|
41
|
+
describe 'Custom argument coercion' do
|
42
|
+
|
43
|
+
before do
|
44
|
+
TrueClass.send(:include, OSCArgument)
|
45
|
+
TrueClass.send( :define_method, :to_osc_type){ 1 }
|
46
|
+
FalseClass.send(:include, OSCArgument)
|
47
|
+
FalseClass.send( :define_method, :to_osc_type){ 0 }
|
48
|
+
Hash.send(:include, OSCArgument)
|
49
|
+
Hash.send( :define_method, :to_osc_type) do
|
50
|
+
self.to_a.collect{ |pair| pair.collect{ |a| OSC.coerce_argument a } }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should accept true" do
|
55
|
+
Message.new('/foo/bar', true).args.should == [1]
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should accept false" do
|
59
|
+
Message.new('/foo/bar', false).args.should == [0]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should accept hash" do
|
63
|
+
Message.new('/foo/bar', {:a => :b}).args.should == ["a", "b"]
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe 'Encode/decode' do
|
68
|
+
shared_examples_for 'Encodable Message' do
|
69
|
+
it "should encode" do
|
70
|
+
@message.encode.should == @expected
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should decode to message" do
|
74
|
+
Message.decode(@expected).should be_a(Message)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should decode address" do
|
78
|
+
Message.decode(@expected).address.should == @message.address
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should actually decode" do
|
82
|
+
Message.decode(@expected).to_a.inspect.should == @message.to_a.inspect.to_s # Problem with float comparing
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe 'Address' do
|
87
|
+
before do
|
88
|
+
@message = Message.new('/foo/bar/long/very/long/long/long/address')
|
89
|
+
@expected = "/foo/bar/long/very/long/long/long/address\000\000\000,\000\000\000"
|
90
|
+
end
|
91
|
+
it_should_behave_like 'Encodable Message'
|
92
|
+
end
|
93
|
+
|
94
|
+
describe 'Integer' do
|
95
|
+
before do
|
96
|
+
@message = Message.new('/foo/barz', 2)
|
97
|
+
@expected = "/foo/barz\000\000\000,i\000\000\000\000\000\002"
|
98
|
+
end
|
99
|
+
it_should_behave_like 'Encodable Message'
|
100
|
+
end
|
101
|
+
|
102
|
+
describe 'Negative Integer' do
|
103
|
+
before do
|
104
|
+
@message = Message.new('/foo/barz', -2)
|
105
|
+
@expected = "/foo/barz\000\000\000,i\000\000\377\377\377\376"
|
106
|
+
end
|
107
|
+
it_should_behave_like 'Encodable Message'
|
108
|
+
end
|
109
|
+
|
110
|
+
describe 'Float' do
|
111
|
+
before do
|
112
|
+
@message = Message.new('/foo/bar', 1.10000002384186)
|
113
|
+
@expected = [47, 102, 111, 111, 47, 98, 97, 114, 0, 0, 0, 0, 44, 102, 0, 0, 63, 140, 204, 205].pack('C*')
|
114
|
+
end
|
115
|
+
it_should_behave_like 'Encodable Message'
|
116
|
+
end
|
117
|
+
|
118
|
+
describe 'Negative Float' do
|
119
|
+
before do
|
120
|
+
@message = Message.new('/foo/bar', -1.10000002384186)
|
121
|
+
@expected = [47, 102, 111, 111, 47, 98, 97, 114, 0, 0, 0, 0, 44, 102, 0, 0, 191, 140, 204, 205].pack('C*')
|
122
|
+
end
|
123
|
+
it_should_behave_like 'Encodable Message'
|
124
|
+
end
|
125
|
+
|
126
|
+
describe 'String' do
|
127
|
+
before do
|
128
|
+
@message = Message.new('/foo/bar', 'a string to encode')
|
129
|
+
@expected = "/foo/bar\000\000\000\000,s\000\000a string to encode\000\000"
|
130
|
+
end
|
131
|
+
it_should_behave_like 'Encodable Message'
|
132
|
+
end
|
133
|
+
|
134
|
+
describe 'Multiple types' do
|
135
|
+
before do
|
136
|
+
@message = Message.new('/foo/barzzz', 2, 1.44000005722046, 'basho')
|
137
|
+
@expected = [47, 102, 111, 111, 47, 98, 97, 114, 122, 122, 122, 0, 44, 105, 102, 115, 0, 0, 0, 0, 0, 0, 0, 2, 63, 184, 81, 236, 98, 97, 115, 104, 111, 0, 0, 0].pack('C*')
|
138
|
+
end
|
139
|
+
it_should_behave_like 'Encodable Message'
|
140
|
+
end
|
141
|
+
|
142
|
+
describe 'Blob' do
|
143
|
+
before do
|
144
|
+
@message = Message.new('/foo/bar', Blob.new('test blob'))
|
145
|
+
@expected = "/foo/bar\000\000\000\000,b\000\000\000\000\000\ttest blob\000\000\000"
|
146
|
+
end
|
147
|
+
it_should_behave_like 'Encodable Message'
|
148
|
+
|
149
|
+
it "should raise if size doesn't correspond and return empty message" do
|
150
|
+
lambda do
|
151
|
+
Message.decode("/foo/bar\000\000\000\000,b\000\000\000\000\000\020test blob\000\000\000")
|
152
|
+
end.should raise_error
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe 'Lots of ints' do
|
157
|
+
before do
|
158
|
+
@message = Message.new('/bar/foo', 4, 3, 2, 1)
|
159
|
+
@expected = "/bar/foo\000\000\000\000,iiii\000\000\000\000\000\000\004\000\000\000\003\000\000\000\002\000\000\000\001"
|
160
|
+
end
|
161
|
+
it_should_behave_like 'Encodable Message'
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
data/spec/server_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-osc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.3.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Macario Ortega
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-17 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: eventmachine
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.12.8
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rubyforge
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 2.0.3
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: gemcutter
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 0.3.0
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: hoe
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 2.5.0
|
54
|
+
version:
|
55
|
+
description: ""
|
56
|
+
email:
|
57
|
+
- macarui@gmail.com
|
58
|
+
executables: []
|
59
|
+
|
60
|
+
extensions: []
|
61
|
+
|
62
|
+
extra_rdoc_files:
|
63
|
+
- History.txt
|
64
|
+
- Manifest.txt
|
65
|
+
files:
|
66
|
+
- History.txt
|
67
|
+
- Manifest.txt
|
68
|
+
- README.rdoc
|
69
|
+
- Rakefile
|
70
|
+
- examples/localtest.rb
|
71
|
+
- lib/ruby-osc.rb
|
72
|
+
- lib/ruby-osc/bundle.rb
|
73
|
+
- lib/ruby-osc/client.rb
|
74
|
+
- lib/ruby-osc/message.rb
|
75
|
+
- lib/ruby-osc/server.rb
|
76
|
+
- ruby-osc.gemspec
|
77
|
+
- spec/bundle_spec.rb
|
78
|
+
- spec/message_spec.rb
|
79
|
+
- spec/server_spec.rb
|
80
|
+
- spec/spec_helper.rb
|
81
|
+
has_rdoc: true
|
82
|
+
homepage: http://github.com/maca/ruby-osc
|
83
|
+
licenses: []
|
84
|
+
|
85
|
+
post_install_message:
|
86
|
+
rdoc_options:
|
87
|
+
- --main
|
88
|
+
- README.rdoc
|
89
|
+
require_paths:
|
90
|
+
- lib
|
91
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: "0"
|
96
|
+
version:
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: "0"
|
102
|
+
version:
|
103
|
+
requirements: []
|
104
|
+
|
105
|
+
rubyforge_project: ruby-osc
|
106
|
+
rubygems_version: 1.3.5
|
107
|
+
signing_key:
|
108
|
+
specification_version: 3
|
109
|
+
summary: ""
|
110
|
+
test_files: []
|
111
|
+
|