Salut 0.3.1 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +1 -1
- data/README.markdown +20 -14
- data/lib/Salut/Browser.rb +30 -49
- data/lib/Salut/Service.rb +24 -49
- data/spec/Browser_spec.rb +134 -0
- data/spec/Service_spec.rb +352 -211
- data/spec/spec_helper.rb +8 -0
- metadata +74 -93
data/LICENSE.txt
CHANGED
data/README.markdown
CHANGED
@@ -3,6 +3,12 @@ Salut
|
|
3
3
|
|
4
4
|
Salut is just a little bit of example code of using Bonjour with MacRuby.
|
5
5
|
|
6
|
+
If all you want to do is log that callbacks were called back, or just add
|
7
|
+
very little to a callback, then this code will work well for you. But Apple
|
8
|
+
has already done a very good job of distilling Bonjour to the point that
|
9
|
+
even without this gem you would not have to add much code in order to use
|
10
|
+
Bonjour.
|
11
|
+
|
6
12
|
Of course, this is not a substitute for reading the [Bonjour Overview](http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/NetServices/Introduction.html%23//apple_ref/doc/uid/TP40002445-SW1) and related documentation (not even close).
|
7
13
|
|
8
14
|
Reference Documentation
|
@@ -22,27 +28,28 @@ Example Usage
|
|
22
28
|
|
23
29
|
Advertising a hypothetical service:
|
24
30
|
|
25
|
-
service = Salut::Service.new(
|
26
|
-
|
27
|
-
instance_name
|
28
|
-
|
29
|
-
|
31
|
+
service = Salut::Service.new(
|
32
|
+
service_type:'_http._tcp.',
|
33
|
+
instance_name:'SalutDemo',
|
34
|
+
port:3000
|
35
|
+
)
|
30
36
|
service.start_advertising
|
31
37
|
|
32
38
|
Finding the service using the browser:
|
33
39
|
|
34
40
|
browser = Salut::Browser.new
|
35
|
-
#
|
36
|
-
|
41
|
+
# the NSNetServiceBrowserDelegate lists all the possible delegates
|
42
|
+
# or look at the 'Delegate methods' group in lib/Salut/Browser.rb
|
43
|
+
browser.delegate :"netServiceBrowser:didFindService:moreComing:" do
|
37
44
|
|sender, service, more|
|
38
|
-
service.resolve #
|
45
|
+
service.resolve # if you want to resolve all services found
|
39
46
|
if more
|
40
47
|
NSLog('Not up to date yet')
|
41
48
|
else
|
42
49
|
NSLog('All caught up')
|
43
50
|
end
|
44
|
-
|
45
|
-
browser.find_services '_http._tcp.'
|
51
|
+
end
|
52
|
+
browser.find_services '_http._tcp.'
|
46
53
|
|
47
54
|
If you want to stop advertising:
|
48
55
|
|
@@ -58,12 +65,11 @@ a GUI app, but a command line app will have to force the loop to run.
|
|
58
65
|
You can only use a Service or Browser for one thing at a time, and the API is designed
|
59
66
|
with that assumption in mind.
|
60
67
|
|
61
|
-
TODO
|
62
|
-
|
68
|
+
TODO for 1.0
|
69
|
+
==========
|
63
70
|
|
64
71
|
- Monitoring and TXT record stuff
|
65
72
|
- publishWithOptions default argument
|
66
|
-
- Do not pass NSNetService or NSNetServiceBrowser to callbacks
|
67
73
|
|
68
74
|
Contributing to Salut
|
69
75
|
=====================
|
@@ -79,6 +85,6 @@ Contributing to Salut
|
|
79
85
|
Copyright
|
80
86
|
=========
|
81
87
|
|
82
|
-
Copyright (c) 2010 Mark Rada. See LICENSE.txt for
|
88
|
+
Copyright (c) 2010-2011 Mark Rada. See LICENSE.txt for
|
83
89
|
further details.
|
84
90
|
|
data/lib/Salut/Browser.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require 'Salut/Service'
|
2
2
|
|
3
3
|
module Salut
|
4
4
|
|
@@ -21,9 +21,12 @@ module Salut
|
|
21
21
|
# @return [Array<String>]
|
22
22
|
attr_reader :domains
|
23
23
|
|
24
|
-
# @return [Array<
|
24
|
+
# @return [Array<Salut::Service>]
|
25
25
|
attr_reader :services
|
26
26
|
|
27
|
+
# @return [NSNetServiceBrowser]
|
28
|
+
attr_reader :browser
|
29
|
+
|
27
30
|
# Ensure that some instance variables are initialized
|
28
31
|
def initialize
|
29
32
|
@browser = NSNetServiceBrowser.alloc.init
|
@@ -42,26 +45,17 @@ module Salut
|
|
42
45
|
|
43
46
|
# @group Adding callback extensions
|
44
47
|
|
45
|
-
#
|
46
|
-
|
47
|
-
|
48
|
-
# A shortcut for reading from the delegate methods
|
49
|
-
# @param [Symbol] key
|
50
|
-
# @return [Proc]
|
51
|
-
def [] key
|
52
|
-
@delegates[key]
|
53
|
-
end
|
54
|
-
|
55
|
-
# A shortcut for writing to the delegate methods hash
|
56
|
-
# @param [Symbol] key
|
57
|
-
# @param [Proc] value
|
48
|
+
# A shortcut for reading/writing to the delegate methods hash
|
49
|
+
# @param [Symbol] method the name of the callback
|
58
50
|
# @return [Proc]
|
59
|
-
def
|
60
|
-
|
51
|
+
def delegate method
|
52
|
+
if block_given?
|
53
|
+
@delegates[method] = Proc.new
|
54
|
+
else
|
55
|
+
@delegates[method]
|
56
|
+
end
|
61
57
|
end
|
62
58
|
|
63
|
-
# @endgroup
|
64
|
-
|
65
59
|
|
66
60
|
# @group Finding domains
|
67
61
|
|
@@ -72,99 +66,86 @@ module Salut
|
|
72
66
|
|
73
67
|
alias_method :stop_finding_domains, :stop_finding
|
74
68
|
|
75
|
-
# @endgroup
|
76
|
-
|
77
69
|
|
78
70
|
# @group Finding services
|
79
71
|
|
80
72
|
# @todo find a way to use default arguments
|
81
73
|
# @param [String] service_name
|
82
74
|
# @param [String] domain_name
|
83
|
-
def find_services service_type,
|
75
|
+
def find_services service_type, domain_name = ''
|
84
76
|
@browser.searchForServicesOfType service_type, inDomain:domain_name
|
85
77
|
end
|
86
78
|
|
87
79
|
alias_method :stop_finding_services, :stop_finding
|
88
80
|
|
89
|
-
# @endgroup
|
90
|
-
|
91
81
|
|
92
82
|
# @group Delegate methods
|
93
83
|
|
94
|
-
# @yieldparam [
|
84
|
+
# @yieldparam [Salut::Browser] sender
|
95
85
|
# @yieldparam [String] domain_name
|
96
86
|
# @yieldparam [Boolean] more
|
97
|
-
# @return [nil]
|
98
87
|
def netServiceBrowser sender, didFindDomain:domain_name, moreComing:more
|
99
88
|
@domains << domain_name
|
100
|
-
@delegates[__method__].call
|
89
|
+
@delegates[__method__].call self, domain_name, more if @delegates[__method__]
|
101
90
|
Salut.log.info "Found domain: #{domain_name}"
|
102
91
|
end
|
103
92
|
|
104
|
-
# @yieldparam [
|
93
|
+
# @yieldparam [Salut::Browser] sender
|
105
94
|
# @yieldparam [String] domain_name
|
106
95
|
# @yieldparam [Boolean] more
|
107
|
-
# @return [nil]
|
108
96
|
def netServiceBrowser sender, didRemoveDomain:domain_name, moreComing:more
|
109
97
|
@domains.delete domain_name
|
110
|
-
@delegates[__method__].call
|
98
|
+
@delegates[__method__].call self, domain_name, more if @delegates[__method__]
|
111
99
|
Salut.log.info "Removing domain: #{domain_name}"
|
112
100
|
end
|
113
101
|
|
114
|
-
# @yieldparam [
|
102
|
+
# @yieldparam [Salut::Browser] sender
|
115
103
|
# @yieldparam [Salut::Service] service
|
116
104
|
# @yieldparam [Boolean] more
|
117
|
-
# @return [nil]
|
118
105
|
def netServiceBrowser sender, didFindService:service, moreComing:more
|
119
|
-
salut_service = Service.new
|
106
|
+
salut_service = Service.new service:service
|
120
107
|
@services << salut_service
|
121
|
-
@delegates[__method__].call
|
108
|
+
@delegates[__method__].call self, salut_service, more if @delegates[__method__]
|
122
109
|
Salut.log.info "Found service (#{service.description})"
|
123
110
|
end
|
124
111
|
|
125
|
-
# @yieldparam [
|
112
|
+
# @yieldparam [Salut::Browser] sender
|
126
113
|
# @yieldparam [Salut::Service] removed_service
|
127
114
|
# @yieldparam [Boolean] more
|
128
|
-
# @return [nil]
|
129
115
|
def netServiceBrowser sender, didRemoveService:service, moreComing:more
|
130
|
-
removed_service
|
116
|
+
removed_service = nil
|
131
117
|
@services.delete_if { |salut_service|
|
132
118
|
if salut_service.service == service
|
133
119
|
removed_service = salut_service
|
134
120
|
true
|
135
121
|
end
|
136
122
|
}
|
137
|
-
@delegates[__method__].call
|
123
|
+
@delegates[__method__].call self, removed_service, more if @delegates[__method__]
|
138
124
|
Salut.log.info "Removing service (#{service.description})"
|
139
125
|
end
|
140
126
|
|
141
|
-
# @yieldparam [
|
142
|
-
# @return [nil]
|
127
|
+
# @yieldparam [Salut::Browser] sender
|
143
128
|
def netServiceBrowserWillSearch sender
|
144
129
|
@searching = true
|
145
|
-
@delegates[__method__].call
|
130
|
+
@delegates[__method__].call self if @delegates[__method__]
|
146
131
|
Salut.log.info "Starting search (#{sender.description})"
|
147
132
|
end
|
148
133
|
|
149
|
-
# @yieldparam [
|
134
|
+
# @yieldparam [Salut::Browser] sender
|
150
135
|
# @yieldparam [Hash] error_dict
|
151
|
-
# @return [nil]
|
152
136
|
def netServiceBrowser sender, didNotSearch:error_dict
|
153
137
|
@searching = false
|
154
|
-
@delegates[__method__].call
|
138
|
+
@delegates[__method__].call self, error_dict if @delegates[__method__]
|
155
139
|
Salut.log.info "Failed to search (#{sender.description})\n\t problem was\n#{error_dict.description}"
|
156
140
|
end
|
157
141
|
|
158
|
-
# @yieldparam [
|
159
|
-
# @return [nil]
|
142
|
+
# @yieldparam [Salut::Browser] sender
|
160
143
|
def netServiceBrowserDidStopSearch sender
|
161
144
|
@searching = false
|
162
|
-
@delegates[__method__].call
|
145
|
+
@delegates[__method__].call self if @delegates[__method__]
|
163
146
|
Salut.log.info "Done searching (#{sender.description})"
|
164
147
|
end
|
165
148
|
|
166
|
-
# @endgroup
|
167
|
-
|
168
149
|
end
|
169
150
|
|
170
151
|
end
|
data/lib/Salut/Service.rb
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
|
3
1
|
module Salut
|
4
2
|
|
5
3
|
# Advertises its service on the local network using Bonjour or is
|
@@ -53,26 +51,17 @@ module Salut
|
|
53
51
|
|
54
52
|
# @group Adding callback extensions
|
55
53
|
|
56
|
-
#
|
57
|
-
|
58
|
-
|
59
|
-
# A shortcut for reading from the delegate methods
|
60
|
-
# @param [Symbol] key
|
61
|
-
# @return [Proc]
|
62
|
-
def [] key
|
63
|
-
@delegates[key]
|
64
|
-
end
|
65
|
-
|
66
|
-
# A shortcut for writing to the delegate methods hash
|
67
|
-
# @param [Symbol] key
|
68
|
-
# @param [Proc] value
|
54
|
+
# A shortcut for reading/writing from the delegate methods
|
55
|
+
# @param [Symbol] method
|
69
56
|
# @return [Proc]
|
70
|
-
def
|
71
|
-
|
57
|
+
def delegate method
|
58
|
+
if block_given?
|
59
|
+
@delegates[method] = Proc.new
|
60
|
+
else
|
61
|
+
@delegates[method]
|
62
|
+
end
|
72
63
|
end
|
73
64
|
|
74
|
-
# @endgroup
|
75
|
-
|
76
65
|
|
77
66
|
# @group Advertising a service
|
78
67
|
|
@@ -102,8 +91,6 @@ module Salut
|
|
102
91
|
@service = nil
|
103
92
|
end
|
104
93
|
|
105
|
-
# @endgroup
|
106
|
-
|
107
94
|
|
108
95
|
# @group Working with discovered services
|
109
96
|
|
@@ -114,76 +101,64 @@ module Salut
|
|
114
101
|
@service.resolveWithTimeout timeout
|
115
102
|
end
|
116
103
|
|
117
|
-
# @endgroup
|
118
|
-
|
119
104
|
|
120
105
|
# @group Delegate methods
|
121
106
|
|
122
|
-
# @yieldparam [
|
123
|
-
# @return [nil]
|
107
|
+
# @yieldparam [Salut::Service] sender a reference to self
|
124
108
|
def netServiceWillPublish sender
|
125
|
-
@delegates[__method__].call
|
109
|
+
@delegates[__method__].call self if @delegates[__method__]
|
126
110
|
Salut.log.info "Starting to advertise service (#{sender.description})"
|
127
111
|
end
|
128
112
|
|
129
|
-
# @yieldparam [
|
113
|
+
# @yieldparam [Salut::Service] sender a reference to self
|
130
114
|
# @yieldparam [Hash] error_dict
|
131
|
-
# @return [nil]
|
132
115
|
def netService sender, didNotPublish:error_dict
|
133
116
|
@advertising = false
|
134
|
-
@delegates[__method__].call
|
117
|
+
@delegates[__method__].call self, error_dict if @delegates[__method__]
|
135
118
|
Salut.log.info "ERROR: could not advertise service (#{sender.description})\n\t the problem was\n#{error_dict.description}"
|
136
119
|
end
|
137
120
|
|
138
|
-
# @yieldparam [
|
139
|
-
# @return [nil]
|
121
|
+
# @yieldparam [Salut::Service] sender a reference to self
|
140
122
|
def netServiceDidPublish sender
|
141
123
|
@advertising = true
|
142
|
-
@delegates[__method__].call
|
124
|
+
@delegates[__method__].call self if @delegates[__method__]
|
143
125
|
Salut.log.info "Successfully advertising service (#{sender.description})"
|
144
126
|
end
|
145
127
|
|
146
|
-
# @yieldparam [
|
147
|
-
# @return [nil]
|
128
|
+
# @yieldparam [Salut::Service] sender a reference to self
|
148
129
|
def netServiceWillResolve sender
|
149
|
-
@delegates[__method__].call
|
130
|
+
@delegates[__method__].call self if @delegates[__method__]
|
150
131
|
Salut.log.info "Resolving service (#{sender.description})"
|
151
132
|
end
|
152
133
|
|
153
|
-
# @yieldparam [
|
134
|
+
# @yieldparam [Salut::Service] sender a reference to self
|
154
135
|
# @yieldparam [Hash] error_dict
|
155
|
-
# @return [nil]
|
156
136
|
def netService sender, didNotResolve:error_dict
|
157
|
-
@delegates[__method__].call
|
137
|
+
@delegates[__method__].call self, error_dict if @delegates[__method__]
|
158
138
|
Salut.log.info "ERROR: could not resolve service (#{sender.description})\n\t the problem was\n#{error_dict.description}"
|
159
139
|
end
|
160
140
|
|
161
|
-
# @yieldparam [
|
162
|
-
# @return [nil]
|
141
|
+
# @yieldparam [Salut::Service] sender a reference to self
|
163
142
|
def netServiceDidResolveAddress sender
|
164
|
-
@delegates[__method__].call
|
143
|
+
@delegates[__method__].call self if @delegates[__method__]
|
165
144
|
Salut.log.info "Resolved address for service (#{sender.description})"
|
166
145
|
end
|
167
146
|
|
168
147
|
# @todo should I process the TXT record before giving it to the proc?
|
169
|
-
# @yieldparam [
|
148
|
+
# @yieldparam [Salut::Service] sender a reference to self
|
170
149
|
# @yieldparam [NSData] data the new TXT record
|
171
|
-
# @return [nil]
|
172
150
|
def netService sender, didUpdateTXTRecordData:data
|
173
|
-
@delegates[__method__].call
|
151
|
+
@delegates[__method__].call self, data if @delegates[__method__]
|
174
152
|
Salut.log.info "Updated TXT record for service (#{sender.description})"
|
175
153
|
end
|
176
154
|
|
177
|
-
# @yieldparam [
|
178
|
-
# @return [nil]
|
155
|
+
# @yieldparam [Salut::Service] sender a reference to self
|
179
156
|
def netServiceDidStop sender
|
180
157
|
@advertising = false
|
181
|
-
@delegates[__method__].call
|
158
|
+
@delegates[__method__].call self if @delegates[__method__]
|
182
159
|
Salut.log.info "Stopped advertising/resolving service (#{sender.description})"
|
183
160
|
end
|
184
161
|
|
185
|
-
# @endgroup
|
186
|
-
|
187
162
|
end
|
188
163
|
|
189
164
|
end
|
data/spec/Browser_spec.rb
CHANGED
@@ -1,2 +1,136 @@
|
|
1
1
|
require './spec_helper'
|
2
2
|
|
3
|
+
describe Salut::Browser do
|
4
|
+
before do
|
5
|
+
Salut.log.level = Logger::WARN
|
6
|
+
@browser = Salut::Browser.new
|
7
|
+
end
|
8
|
+
|
9
|
+
describe '#searching?' do
|
10
|
+
it 'should be initialized to false' do
|
11
|
+
@browser.searching?.should.be.equal false
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'should be false after I stop searching' do
|
15
|
+
@browser.find_services '_ssh._tcp.'
|
16
|
+
run_run_loop 1
|
17
|
+
@browser.searching?.should.be.equal true
|
18
|
+
|
19
|
+
@browser.stop_finding_services
|
20
|
+
run_run_loop 1
|
21
|
+
@browser.searching?.should.be.equal false
|
22
|
+
|
23
|
+
@browser.find_browsable_domains
|
24
|
+
run_run_loop 1
|
25
|
+
@browser.searching?.should.be.equal true
|
26
|
+
|
27
|
+
@browser.stop_finding_domains
|
28
|
+
run_run_loop 1
|
29
|
+
@browser.searching?.should.be.equal false
|
30
|
+
end
|
31
|
+
|
32
|
+
# I don't know how to cause a failure when searching
|
33
|
+
# for browsable domains
|
34
|
+
it 'should be false if searching fails' do
|
35
|
+
@browser.find_services 'badname'
|
36
|
+
run_run_loop
|
37
|
+
@browser.searching?.should.be.equal false
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'should be true when searching starts' do
|
41
|
+
@browser.find_services '_ssh._tcp.'
|
42
|
+
@browser.searching?.should.be.equal true
|
43
|
+
|
44
|
+
@browser.stop_finding_services
|
45
|
+
run_run_loop 1
|
46
|
+
@browser.searching?.should.be.equal false
|
47
|
+
|
48
|
+
@browser.find_browsable_domains
|
49
|
+
@browser.searching?.should.be.equal true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
describe '#domains' do
|
55
|
+
it 'should be initialized to an empty array' do
|
56
|
+
@browser.domains.class.should.be.equal Array
|
57
|
+
@browser.domains.should.be.equal []
|
58
|
+
end
|
59
|
+
|
60
|
+
# here I assume that local. will always be found
|
61
|
+
it 'should be populated with domains when I browse for domains' do
|
62
|
+
@browser.domains.size.should.be.equal 0
|
63
|
+
@browser.find_browsable_domains
|
64
|
+
run_run_loop
|
65
|
+
@browser.domains.size.should.not.be.equal 0
|
66
|
+
end
|
67
|
+
|
68
|
+
# @todo not sure how to fake this one
|
69
|
+
it 'should shrink when domains disappear'
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
describe '#services' do
|
74
|
+
before do
|
75
|
+
@service = Salut::Service.new(
|
76
|
+
service_type:'_test._tcp.',
|
77
|
+
instance_name:'TEST',
|
78
|
+
port:9000
|
79
|
+
)
|
80
|
+
@service.start_advertising
|
81
|
+
run_run_loop
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should be initialized to an empty array' do
|
85
|
+
@browser.services.class.should.be.equal Array
|
86
|
+
@browser.services.should.be.equal []
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should populate when I call #find_services' do
|
90
|
+
@browser.services.size.should.be.equal 0
|
91
|
+
@browser.find_services '_test._tcp.'
|
92
|
+
run_run_loop
|
93
|
+
@browser.services.size.should.not.be.equal 0
|
94
|
+
end
|
95
|
+
|
96
|
+
it 'should dispopulate if services go away after being discovered by calling #find_services' do
|
97
|
+
@browser.services.size.should.be.equal 0
|
98
|
+
@browser.find_services '_test._tcp.'
|
99
|
+
run_run_loop
|
100
|
+
@browser.services.size.should.be.equal 1
|
101
|
+
@service.stop_advertising
|
102
|
+
run_run_loop
|
103
|
+
@browser.services.size.should.be.equal 0
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
describe '#browser' do
|
109
|
+
it 'should be the underlying NSNetServiceBrowser' do
|
110
|
+
@browser.browser.class.should.be.equal NSNetServiceBrowser
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should be set at initialization' do
|
114
|
+
@browser.browser.should.not.be.equal nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
describe '#initialize' do
|
120
|
+
it 'should initialize browser with an NSNetServiceBrowser' do
|
121
|
+
@browser.browser.should.not.be.equal nil
|
122
|
+
end
|
123
|
+
|
124
|
+
it 'should initialize #domains to an empty array' do
|
125
|
+
@browser.domains.should.be.equal []
|
126
|
+
end
|
127
|
+
|
128
|
+
it 'should initialize #services to an empty array' do
|
129
|
+
@browser.services.should.be.equal []
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'should initialize #searching? to false' do
|
133
|
+
@browser.searching?.should.be.equal false
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|