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 ADDED
@@ -0,0 +1,4 @@
1
+ === 0.0.1 2009-08-27
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
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
@@ -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
@@ -0,0 +1,5 @@
1
+ require "#{ File.dirname __FILE__ }/spec_helper"
2
+
3
+ describe Server do
4
+ it "should be specked but I am lazy"
5
+ end
@@ -0,0 +1,7 @@
1
+ require 'rubygems'
2
+ require 'spec'
3
+
4
+ $:.unshift( File.join( File.dirname( __FILE__), '..', 'lib' ) )
5
+
6
+ require 'ruby-osc'
7
+ include OSC
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
+