Salut 0.3.1 → 0.4.0
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/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
|