ruby-osc 0.3.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|