maca-rosc 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +11 -0
- data/ChangeLog +6 -0
- data/GPL.txt +340 -0
- data/History.txt +4 -0
- data/LICENSE +57 -0
- data/Manifest.txt +22 -0
- data/PostInstall.txt +7 -0
- data/README +55 -0
- data/README.rdoc +55 -0
- data/Rakefile +29 -0
- data/TODO +3 -0
- data/examples/readme.rb +19 -0
- data/lib/osc.rb +327 -0
- data/lib/osc/pattern.rb +131 -0
- data/lib/osc/server.rb +94 -0
- data/lib/osc/transport.rb +42 -0
- data/lib/osc/udp.rb +30 -0
- data/lib/rosc.rb +8 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/test/test_osc.rb +160 -0
- metadata +99 -0
data/lib/osc/pattern.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'set'
|
2
|
+
module OSC
|
3
|
+
class Pattern < String
|
4
|
+
# Create an OSC pattern from a string or (experimental) from a Regex.
|
5
|
+
def initialize(s)
|
6
|
+
case s
|
7
|
+
when Regexp # This is experimental
|
8
|
+
s = Regexp.source s
|
9
|
+
s.gsub! /(\\\\)*\[^\/\]\*/, "\1*"
|
10
|
+
s.gsub! /(\\\\)*\[^\/\]/, "\1?"
|
11
|
+
s.gsub! /(\\\\)*\[^/, "\1[!"
|
12
|
+
s.gsub! /(\\\\)*\(/, "\1{"
|
13
|
+
s.gsub! /(\\\\)*\|/, "\1,"
|
14
|
+
s.gsub! /(\\\\)*\)/, "\1}"
|
15
|
+
s.gsub! /\\\\/, "\\"
|
16
|
+
end
|
17
|
+
super s
|
18
|
+
end
|
19
|
+
|
20
|
+
# Return a Regex representing this pattern
|
21
|
+
def regexp
|
22
|
+
s = Regexp.escape self
|
23
|
+
s.gsub! /\\\?/, '[^/]'
|
24
|
+
s.gsub! /\\\*/, '[^/]*'
|
25
|
+
s.gsub! /\\\[!/, '[^'
|
26
|
+
s.gsub! /\\\]/, ']'
|
27
|
+
s.gsub! /\\\{/, '('
|
28
|
+
s.gsub! /,/, '|'
|
29
|
+
s.gsub! /\\\}/, ')'
|
30
|
+
Regexp.new s
|
31
|
+
end
|
32
|
+
|
33
|
+
# Do these two patterns intersect?
|
34
|
+
#--
|
35
|
+
# This might be improved by following the (much simpler, but related)
|
36
|
+
# algorithm here:
|
37
|
+
#
|
38
|
+
# http://groups.google.com/group/comp.theory/browse_frm/thread/f33e033269bd5ab0/c87e19081f45454c?lnk=st&q=regular+expression+intersection&rnum=1&hl=en#c87e19081f45454c
|
39
|
+
#
|
40
|
+
# That is, convert each regexp into an NFA, then generate the set of valid
|
41
|
+
# state pairs, then check if the pair of final states is included.
|
42
|
+
# That's basically what I'm doing here, but I'm not generating all the
|
43
|
+
# state pairs, I'm just doing a search. My way may be faster and/or
|
44
|
+
# smaller, or it may not. My initial feeling is that it is faster since
|
45
|
+
# we're basically doing a depth-first search and OSC patterns are going to
|
46
|
+
# tend to be fairly simple. Still it might be a fun experiment for the
|
47
|
+
# masochistic.
|
48
|
+
def self.intersect?(s1,s2)
|
49
|
+
r = /\*|\?|\[[^\]]*\]|\{[^\}]*\}|./
|
50
|
+
a = s1.to_s.scan r
|
51
|
+
b = s2.to_s.scan r
|
52
|
+
q = [[a,b]]
|
53
|
+
until q.empty?
|
54
|
+
q.uniq!
|
55
|
+
a,b = q.pop
|
56
|
+
a = a.dup
|
57
|
+
b = b.dup
|
58
|
+
|
59
|
+
return true if a.empty? and b.empty?
|
60
|
+
next if a.empty? or b.empty?
|
61
|
+
|
62
|
+
x,y = a.shift, b.shift
|
63
|
+
|
64
|
+
# branch {}
|
65
|
+
if x =~ /^\{/
|
66
|
+
x.scan /[^\{\},]+/ do |x|
|
67
|
+
q.push [x.scan(/./)+a,[y]+b]
|
68
|
+
end
|
69
|
+
next
|
70
|
+
end
|
71
|
+
if y =~ /^\{/
|
72
|
+
y.scan /[^\{\},]+/ do |y|
|
73
|
+
q.push [[x]+a,y.scan(/./)+b]
|
74
|
+
end
|
75
|
+
next
|
76
|
+
end
|
77
|
+
|
78
|
+
# sort
|
79
|
+
if y =~ /^\[/
|
80
|
+
x,y = y,x
|
81
|
+
a,b = b,a
|
82
|
+
end
|
83
|
+
if y =~ /^(\*|\?)/
|
84
|
+
x,y = y,x
|
85
|
+
a,b = b,a
|
86
|
+
end
|
87
|
+
|
88
|
+
# match
|
89
|
+
case x
|
90
|
+
when '*'
|
91
|
+
unless y == '/'
|
92
|
+
q.push [a,b]
|
93
|
+
q.push [[x]+a,b]
|
94
|
+
end
|
95
|
+
if y == '*'
|
96
|
+
q.push [a,[y]+b]
|
97
|
+
q.push [[x]+a,b]
|
98
|
+
end
|
99
|
+
when '?'
|
100
|
+
q.push [a,b] unless y == '/'
|
101
|
+
q.push [a,[y]+b] if y == '*'
|
102
|
+
when /^\[/
|
103
|
+
xinv = (x[1] == ?!)
|
104
|
+
yinv = (y =~ /^\[!/)
|
105
|
+
x = x[(xinv ? 2 : 1)..-2].scan(/./).to_set
|
106
|
+
if y =~ /^\[/
|
107
|
+
y = y[(yinv ? 2 : 1)..-2].scan(/./).to_set
|
108
|
+
else
|
109
|
+
y = [y].to_set
|
110
|
+
end
|
111
|
+
|
112
|
+
# simplifying assumption: nobody in their right mind is going to do
|
113
|
+
# [^everyprintablecharacter]
|
114
|
+
if xinv and yinv
|
115
|
+
q.push [a,b]
|
116
|
+
elsif xinv and not yinv
|
117
|
+
q.push [a,b] unless (y-x).empty?
|
118
|
+
elsif not xinv and yinv
|
119
|
+
q.push [a,b] unless (x-y).empty?
|
120
|
+
else
|
121
|
+
q.push [a,b] unless (x&y).empty?
|
122
|
+
end
|
123
|
+
else
|
124
|
+
q.push [a,b] if x == y
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
false # no intersection
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
data/lib/osc/server.rb
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
module OSC
|
2
|
+
# Mixin for making servers.
|
3
|
+
# Your job is to read a packet and call dispatch(Packet.decode(raw)), ad
|
4
|
+
# infinitum (e.g. in a method named serve).
|
5
|
+
module Server
|
6
|
+
# prock.respond_to?(:call) #=> true
|
7
|
+
# Pass an OSC pattern, a typespec, and either prock or a block.
|
8
|
+
# The block/prock will be called if the pattern and typspec match. Numeric
|
9
|
+
# types will be coerced, so e.g. 'fi' would match 'ii' and the float would
|
10
|
+
# be coerced to an int.
|
11
|
+
def add_method(pat, typespec, prock=nil, &block)
|
12
|
+
pat = Pattern.new(pat) unless Pattern === pat
|
13
|
+
if block_given? and prock
|
14
|
+
raise ArgumentError, 'Specify either a block or a Proc, not both.'
|
15
|
+
end
|
16
|
+
prock = block if block_given?
|
17
|
+
unless prock.respond_to?(:call)
|
18
|
+
raise ArgumentError, "Prock doesn't respond to :call"
|
19
|
+
end
|
20
|
+
unless typespec.nil? or typespec =~ /[ifsb]*/
|
21
|
+
raise ArgumentError, "Bad typespec '#{typespec}'"
|
22
|
+
end
|
23
|
+
@cb ||= []
|
24
|
+
@cb << [pat, typespec, prock]
|
25
|
+
end
|
26
|
+
|
27
|
+
# dispatch the provided message. It can be raw or already decoded with
|
28
|
+
# Packet.decode
|
29
|
+
def dispatch(mesg)
|
30
|
+
case mesg
|
31
|
+
when Bundle, Message
|
32
|
+
else
|
33
|
+
mesg = Packet.decode(mesg)
|
34
|
+
end
|
35
|
+
|
36
|
+
case mesg
|
37
|
+
when Bundle; dispatch_bundle(mesg)
|
38
|
+
when Message
|
39
|
+
unless @cb.nil?
|
40
|
+
@cb.each do |pat, typespec, obj|
|
41
|
+
if pat.nil? or Pattern.intersect?(pat, mesg.address)
|
42
|
+
if typespec
|
43
|
+
if typespec.size == mesg.args.size
|
44
|
+
match = true
|
45
|
+
typespec.size.times do |i|
|
46
|
+
c = typespec[i]
|
47
|
+
case c
|
48
|
+
when ?i, ?f
|
49
|
+
match &&= (Numeric === mesg.args[i])
|
50
|
+
when ?s, ?b
|
51
|
+
match &&= (String === mesg.args[i])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
if match
|
55
|
+
typespec.size.times do |i|
|
56
|
+
case typespec[i]
|
57
|
+
when ?i
|
58
|
+
mesg.args[i] = mesg.args[i].to_i
|
59
|
+
when ?f
|
60
|
+
mesg.args[i] = mesg.args[i].to_f
|
61
|
+
when ?s,?b
|
62
|
+
mesg.args[i] = mesg.args[i].to_s
|
63
|
+
mesg.args[i] = mesg.args[i].to_s
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
obj.call(mesg)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
else # no typespec
|
71
|
+
obj.call(mesg)
|
72
|
+
end
|
73
|
+
end # pattern match
|
74
|
+
end # @cb.each
|
75
|
+
end # unless @cb.nil?
|
76
|
+
else
|
77
|
+
raise "bad mesg"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# May create a new thread to wait to dispatch according to p.timetag.
|
82
|
+
def dispatch_bundle(p)
|
83
|
+
diff = p.timetag.to_f - TimeTag.now.to_f
|
84
|
+
if diff <= 0
|
85
|
+
p.each {|m| m.source = p.source; dispatch m}
|
86
|
+
else
|
87
|
+
Thread.new do
|
88
|
+
sleep diff
|
89
|
+
p.each {|m| m.source = p.source; dispatch m}
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module OSC
|
2
|
+
# Mixin for OSC transports. You implement (or in many cases just alias)
|
3
|
+
# send_raw, recvfrom_raw, and recv_raw, which have the semantics of send,
|
4
|
+
# recvfrom, and recv in e.g. UDPSocket
|
5
|
+
module Transport
|
6
|
+
# Send a Message, Bundle, or even raw data
|
7
|
+
def send(msg, *args)
|
8
|
+
case msg
|
9
|
+
when Message,Bundle
|
10
|
+
send_raw(msg.encode, *args)
|
11
|
+
else
|
12
|
+
send_raw(msg, *args)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Receive a Message, Bundle, or raw data and the sender. The source
|
17
|
+
# attribute of the Message or Bundle is also set to sender. e.g.
|
18
|
+
# packet, sender = udp_osc_client.recvfrom(32768)
|
19
|
+
def recvfrom(*args)
|
20
|
+
data, sender = recvfrom_raw(*args)
|
21
|
+
m = Packet.decode(data)
|
22
|
+
m.source = sender
|
23
|
+
[m, sender]
|
24
|
+
rescue
|
25
|
+
[data, sender]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Receive a Message, Bundle, or raw data.
|
29
|
+
def recv(*args)
|
30
|
+
data = recv_raw(*args)
|
31
|
+
Packet.decode(data)
|
32
|
+
rescue
|
33
|
+
end
|
34
|
+
|
35
|
+
# Send a Message/Bundle with a timestamp (a Time or TimeTag object).
|
36
|
+
def send_timestamped(msg, ts, *args)
|
37
|
+
m = Bundle.new(ts, msg)
|
38
|
+
send(m, *args)
|
39
|
+
end
|
40
|
+
alias :send_ts :send_timestamped
|
41
|
+
end
|
42
|
+
end
|
data/lib/osc/udp.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'osc/transport'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
module OSC
|
5
|
+
# A ::UDPSocket with a send method that accepts a Message or Bundle or
|
6
|
+
# a raw String.
|
7
|
+
class UDPSocket < ::UDPSocket
|
8
|
+
alias :send_raw :send
|
9
|
+
alias :recvfrom_raw :recvfrom
|
10
|
+
alias :recv_raw :recv
|
11
|
+
include Transport
|
12
|
+
end
|
13
|
+
|
14
|
+
class UDPServer < OSC::UDPSocket
|
15
|
+
MAX_MSG_SIZE=32768
|
16
|
+
include Server
|
17
|
+
def serve
|
18
|
+
loop do
|
19
|
+
p, sender = recvfrom(MAX_MSG_SIZE)
|
20
|
+
dispatch p
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# send msg2 as a reply to msg1
|
25
|
+
def reply(msg1, msg2)
|
26
|
+
domain, port, host, ip = msg2.source
|
27
|
+
send(msg2, 0, host, port)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/rosc.rb
ADDED
data/script/console
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# File: script/console
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
4
|
+
|
5
|
+
libs = " -r irb/completion"
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/rosc.rb'}"
|
9
|
+
puts "Loading rosc gem"
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/destroy'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'rubigen'
|
6
|
+
rescue LoadError
|
7
|
+
require 'rubygems'
|
8
|
+
require 'rubigen'
|
9
|
+
end
|
10
|
+
require 'rubigen/scripts/generate'
|
11
|
+
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|
data/test/test_osc.rb
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__) + "/../lib/")
|
2
|
+
require 'osc'
|
3
|
+
require 'time'
|
4
|
+
require 'test/unit'
|
5
|
+
|
6
|
+
class TC_OSC < Test::Unit::TestCase
|
7
|
+
include OSC
|
8
|
+
# def setup
|
9
|
+
# end
|
10
|
+
|
11
|
+
# def teardown
|
12
|
+
# end
|
13
|
+
|
14
|
+
def test_datatype
|
15
|
+
s = 'foo'
|
16
|
+
i = 42
|
17
|
+
f = 3.14
|
18
|
+
|
19
|
+
assert_equal 'i', Packet.tag(i)
|
20
|
+
assert_equal 'f', Packet.tag(f)
|
21
|
+
assert_equal 's', Packet.tag(s)
|
22
|
+
assert_equal s+"\000", Packet.encode(s)
|
23
|
+
b = Blob.new("foobardoobar\0\0x200")
|
24
|
+
assert_equal 'b', Packet.tag(b)
|
25
|
+
assert_equal b.size+4 + (b.size+4)%4, Packet.encode(b).size
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_timetag
|
29
|
+
t1 = TimeTag::JAN_1970
|
30
|
+
t2 = Time.now
|
31
|
+
t3 = t2.to_f+t1
|
32
|
+
|
33
|
+
tt = TimeTag.new t2
|
34
|
+
assert_equal t3, tt.to_f
|
35
|
+
assert_equal t3.floor, tt.to_i
|
36
|
+
assert_equal t3.floor - t3, tt.to_i - tt.to_f
|
37
|
+
assert_equal [0,1].pack('NN'), Packet.encode(TimeTag.new(nil))
|
38
|
+
assert_equal t2.to_i,tt.to_time.to_i # to_f has roundoff error at the lsb
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_message
|
42
|
+
a = 'foo'
|
43
|
+
b = 'quux'
|
44
|
+
m = Message.new '/foobar', 'ssi', a, b, 1
|
45
|
+
assert_equal "/foobar\000"+",ssi\000\000\000\000"+
|
46
|
+
"foo\000"+"quux\000\000\000\000"+"\000\000\000\001", Packet.encode(m)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_bundle
|
50
|
+
m1 = Message.new '/foo','s','foo'
|
51
|
+
m2 = Message.new '/bar','s','bar'
|
52
|
+
t = Time.now
|
53
|
+
b = Bundle.new(TimeTag.new(Time.at(t + 10)), m1, m2)
|
54
|
+
b2 = Bundle.new(nil, b, m1)
|
55
|
+
|
56
|
+
assert_equal 10, b.timetag.to_time.to_i - t.to_i
|
57
|
+
e = Packet.encode(b2)
|
58
|
+
assert_equal '#bundle', e[0,7]
|
59
|
+
assert_equal "\000\000\000\000\000\000\000\001", e[8,8]
|
60
|
+
assert_equal '#bundle', e[16+4,7]
|
61
|
+
assert_equal '/foo', e[16+4+Packet.encode(b).size+4,4]
|
62
|
+
assert_equal 0, e.size % 4
|
63
|
+
|
64
|
+
assert_instance_of Array, b2.to_a
|
65
|
+
assert_instance_of Bundle, b2.to_a[0]
|
66
|
+
assert_instance_of Message, b2.to_a[1]
|
67
|
+
|
68
|
+
bundle = Packet.decode(e)
|
69
|
+
assert_instance_of Bundle, bundle
|
70
|
+
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
|
76
|
+
def test_packet
|
77
|
+
m = Message.new '/foo','s','foo'
|
78
|
+
b = Bundle.new nil,m
|
79
|
+
|
80
|
+
m2 = Packet.decode("/foo\000\000\000\000,s\000\000foo\000")
|
81
|
+
assert_equal m.address,m2.address
|
82
|
+
m2 = Packet.decode(Packet.encode(m))
|
83
|
+
assert_equal m.address,m2.address
|
84
|
+
assert_equal m.typetag,m2.typetag
|
85
|
+
assert_equal m.args.size,m2.args.size
|
86
|
+
b2 = Packet.decode(Packet.encode(b))
|
87
|
+
assert_equal b.args.size,b2.args.size
|
88
|
+
end
|
89
|
+
|
90
|
+
class TestServer
|
91
|
+
include OSC::Transport
|
92
|
+
include OSC::Server
|
93
|
+
def test_request(p)
|
94
|
+
send p
|
95
|
+
end
|
96
|
+
|
97
|
+
def send_raw(msg, *args)
|
98
|
+
dispatch msg
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
def test_server
|
104
|
+
s = TestServer.new
|
105
|
+
s.add_method('/foo/bar',nil) { |msg|
|
106
|
+
assert_equal 'si',msg.typetag
|
107
|
+
assert_equal 'Hello, World!',msg[0]
|
108
|
+
assert_equal 42,msg[1]
|
109
|
+
}
|
110
|
+
s.test_request Message.new("/foo/bar",'si','Hello, World!',42)
|
111
|
+
end
|
112
|
+
|
113
|
+
def test_server_with_bundle
|
114
|
+
s = TestServer.new
|
115
|
+
s.add_method('/foo/bar',nil) { |msg|
|
116
|
+
assert_equal 'si',msg.typetag
|
117
|
+
assert_equal 'Hello, World!',msg[0]
|
118
|
+
assert_equal 42,msg[1]
|
119
|
+
}
|
120
|
+
s.test_request Bundle.new(nil, Message.new("/foo/bar",'si','Hello, World!',42), Message.new("/foo/bar",'si','Hello, World!',42), Message.new("/foo/bar",'si','Hello, World!',42))
|
121
|
+
end
|
122
|
+
|
123
|
+
def test_pattern
|
124
|
+
# test *
|
125
|
+
assert Pattern.intersect?('/*/bar/baz','/foo/*/baz')
|
126
|
+
assert Pattern.intersect?('/f*','/*o')
|
127
|
+
assert ! Pattern.intersect?('/f*','/foo/bar')
|
128
|
+
assert ! Pattern.intersect?('/f*','/bar')
|
129
|
+
# test ?
|
130
|
+
assert Pattern.intersect?('/fo?/bar','/foo/?ar')
|
131
|
+
assert ! Pattern.intersect?('/foo?','/foo')
|
132
|
+
# test []
|
133
|
+
assert Pattern.intersect?('/foo/ba[rz]','/foo/bar')
|
134
|
+
assert Pattern.intersect?('/[!abcde]/a','/[!abcde]/a')
|
135
|
+
assert Pattern.intersect?('/[!abcde]/a','/f/a')
|
136
|
+
assert Pattern.intersect?('/[!abcde]/a','/[abf]/a')
|
137
|
+
assert ! Pattern.intersect?('/[ab]/a','/[!abc]/a')
|
138
|
+
assert ! Pattern.intersect?('/[abcde]','/[!abcde]')
|
139
|
+
assert ! Pattern.intersect?('/[abcde]','/f')
|
140
|
+
assert ! Pattern.intersect?('/[!abcde]','/a')
|
141
|
+
# test {}
|
142
|
+
assert Pattern.intersect?('/{foo,bar,baz}','/foo')
|
143
|
+
assert Pattern.intersect?('/{foo,bar,baz}','/bar')
|
144
|
+
assert Pattern.intersect?('/{foo,bar,baz}','/baz')
|
145
|
+
assert ! Pattern.intersect?('/{foo,bar,baz}','/quux')
|
146
|
+
assert ! Pattern.intersect?('/{foo,bar,baz}','/fo')
|
147
|
+
# * with *,?,[]
|
148
|
+
assert Pattern.intersect?('/*/bar','/*/ba?')
|
149
|
+
assert Pattern.intersect?('/*/bar','/*x/ba?')
|
150
|
+
assert Pattern.intersect?('/*/bar','/?/ba?')
|
151
|
+
assert Pattern.intersect?('/*/bar','/?x/ba?')
|
152
|
+
assert Pattern.intersect?('/*/bar','/[abcde]/ba?')
|
153
|
+
assert Pattern.intersect?('/*/bar','/[abcde]x/ba?')
|
154
|
+
assert Pattern.intersect?('/*/bar','/[!abcde]/ba?')
|
155
|
+
assert Pattern.intersect?('/*/bar','/[!abcde]x/ba?')
|
156
|
+
# ? with []
|
157
|
+
assert Pattern.intersect?('/?','/[abcde]')
|
158
|
+
assert Pattern.intersect?('/?','/[!abcde]')
|
159
|
+
end
|
160
|
+
end
|