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 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
+