presence 0.0.1 → 0.0.2
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/.simplecov +2 -1
- data/Rakefile +1 -1
- data/bin/presence_scan +14 -0
- data/lib/presence/commands.rb +2 -1
- data/lib/presence/listeners/http_notifier.rb +83 -0
- data/lib/presence/listeners/logger.rb +45 -18
- data/lib/presence/listeners/tracker.rb +9 -17
- data/lib/presence/scanner.rb +5 -6
- data/lib/presence/version.rb +1 -1
- data/lib/presence.rb +1 -0
- data/spec/presence/http_notifier_spec.rb +69 -0
- data/spec/presence/logger_spec.rb +103 -0
- data/spec/presence/scanner_spec.rb +58 -1
- data/spec/presence/tracker_spec.rb +4 -0
- data/spec/support/io_spec_helpers.rb +33 -0
- metadata +11 -2
data/.simplecov
CHANGED
data/Rakefile
CHANGED
@@ -13,7 +13,7 @@ begin
|
|
13
13
|
cane.abc_max = 15
|
14
14
|
cane.style_measure = 100
|
15
15
|
cane.style_glob = '{lib}/**/*.rb'
|
16
|
-
cane.gte = {'coverage/covered_percent' =>
|
16
|
+
cane.gte = {'coverage/covered_percent' => 80}
|
17
17
|
end
|
18
18
|
rescue LoadError
|
19
19
|
warn "cane not available, quality task not provided."
|
data/bin/presence_scan
CHANGED
@@ -8,14 +8,28 @@ if File.exists?(File.join(File.expand_path('../..', __FILE__), '.git'))
|
|
8
8
|
end
|
9
9
|
|
10
10
|
require 'presence'
|
11
|
+
|
11
12
|
# Presence::CLI::Root.start
|
12
13
|
|
14
|
+
# Usage: presence [OPTIONS]
|
15
|
+
#
|
16
|
+
# presence --start 10.0.1.1 --end 10.0.1.30
|
17
|
+
# presence --prefix 192.168.0
|
18
|
+
# presence --verbose
|
19
|
+
# presence --quiet
|
20
|
+
# presence --once
|
21
|
+
# presence --loop
|
22
|
+
# presence --listener Carnegie::Listener
|
23
|
+
|
24
|
+
# --once
|
13
25
|
# s = Presence::Scanner.new
|
14
26
|
# s.register_listener(Presence::Logger.new)
|
15
27
|
# s.register_listener(Presence::Tracker.new)
|
16
28
|
# s.scan
|
17
29
|
|
30
|
+
# --loop
|
18
31
|
Presence::Scanner.scan_loop do |s|
|
32
|
+
# s.options[:octet_range] = (30..32)
|
19
33
|
s.register_listener(Presence::Logger.new)
|
20
34
|
s.register_listener(Presence::Tracker.new)
|
21
35
|
end
|
data/lib/presence/commands.rb
CHANGED
@@ -0,0 +1,83 @@
|
|
1
|
+
require "net/https"
|
2
|
+
require "uri"
|
3
|
+
|
4
|
+
module Presence
|
5
|
+
|
6
|
+
# Scanner listener that sends an HTTP notification whenever a new client
|
7
|
+
# connects or when a connected client disconnects. This listener is intended
|
8
|
+
# to be used with Presence::Tracker when scanning in a loop.
|
9
|
+
class HTTPNotifier
|
10
|
+
|
11
|
+
def initialize(options = nil)
|
12
|
+
options ||= {}
|
13
|
+
@options = {
|
14
|
+
use_ssl: false,
|
15
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER
|
16
|
+
}.merge(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def listener_registered(l, scanner)
|
20
|
+
@scanner ||= scanner
|
21
|
+
end
|
22
|
+
|
23
|
+
def mac_connected(mac, ip)
|
24
|
+
notify(:mac_connected, { mac: mac, ip: ip })
|
25
|
+
end
|
26
|
+
|
27
|
+
def mac_disconnected(mac, old_ip)
|
28
|
+
notify(:mac_disconnected, { mac: mac, ip: old_ip })
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def notify_uri
|
34
|
+
@notify_uri ||= URI.parse(@options[:url])
|
35
|
+
end
|
36
|
+
|
37
|
+
def notify(event, params)
|
38
|
+
request = prepare_request(event, params)
|
39
|
+
do_request(request)
|
40
|
+
end
|
41
|
+
|
42
|
+
def do_request(request)
|
43
|
+
response = nil
|
44
|
+
|
45
|
+
begin
|
46
|
+
http = prepare_http
|
47
|
+
response = http.request(request)
|
48
|
+
check_response(response)
|
49
|
+
rescue SocketError,
|
50
|
+
Timeout::Error,
|
51
|
+
Errno::ECONNREFUSED,
|
52
|
+
Errno::EHOSTDOWN,
|
53
|
+
Errno::EHOSTUNREACH,
|
54
|
+
Errno::ETIMEDOUT => e
|
55
|
+
@scanner.dispatch(:http_notification_failed, e.class.name, e.message)
|
56
|
+
end
|
57
|
+
|
58
|
+
response
|
59
|
+
end
|
60
|
+
|
61
|
+
def prepare_request(event, params)
|
62
|
+
request = Net::HTTP::Post.new(notify_uri.request_uri, @options[:headers])
|
63
|
+
request.set_form_data(params.merge(event: event))
|
64
|
+
if @options[:username]
|
65
|
+
request.basic_auth(@options[:username], @options[:password])
|
66
|
+
end
|
67
|
+
request
|
68
|
+
end
|
69
|
+
|
70
|
+
def prepare_http
|
71
|
+
http = Net::HTTP.new(notify_uri.host, notify_uri.port)
|
72
|
+
http.use_ssl = @options[:use_ssl]
|
73
|
+
http.verify_mode = @options[:verify_mode]
|
74
|
+
http
|
75
|
+
end
|
76
|
+
|
77
|
+
def check_response(response)
|
78
|
+
unless (200..206).map(&:to_s).include?(response.code)
|
79
|
+
@scanner.dispatch(:http_notification_failed, response.code, response.body)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -5,61 +5,88 @@ module Presence
|
|
5
5
|
# default, it uses a Logger instance that writes to stdout. To enable more
|
6
6
|
# verbose output, set:
|
7
7
|
#
|
8
|
-
# logger.level = ::Logger::INFO
|
8
|
+
# logger.log.level = ::Logger::INFO
|
9
9
|
#
|
10
10
|
# or:
|
11
11
|
#
|
12
|
-
# logger.level = ::Logger::DEBUG
|
12
|
+
# logger.log.level = ::Logger::DEBUG
|
13
13
|
#
|
14
|
-
# To write to a file or some other outlet, pass
|
14
|
+
# To write to a file or some other outlet, pass a Ruby Logger instance to the
|
15
15
|
# constructor.
|
16
16
|
class Logger
|
17
|
-
attr_accessor :
|
17
|
+
attr_accessor :log
|
18
18
|
|
19
19
|
def initialize(log = nil)
|
20
20
|
if log.nil?
|
21
|
-
log = ::Logger.new(
|
21
|
+
log = ::Logger.new($stdout)
|
22
22
|
log.level = ::Logger::WARN
|
23
23
|
log.formatter = proc do |severity, datetime, progname, msg|
|
24
24
|
"#{msg}\n"
|
25
25
|
end
|
26
26
|
end
|
27
|
-
self.
|
27
|
+
self.log = log
|
28
28
|
end
|
29
29
|
|
30
30
|
def listener_registered(listener, scanner)
|
31
|
-
|
31
|
+
log.info "Registered listener: <#{listener.class}> for: #{scanner}"
|
32
32
|
end
|
33
33
|
|
34
34
|
def scan_started(ip_prefix, range)
|
35
|
-
|
35
|
+
log.info "Scanning range: #{ip_prefix}.#{range.first} to #{ip_prefix}.#{range.last}"
|
36
36
|
end
|
37
37
|
|
38
38
|
def scan_finished(ip_prefix, range)
|
39
|
-
|
39
|
+
log.info "Scan finished."
|
40
40
|
end
|
41
41
|
|
42
42
|
def ip_scanned(ip, cmd, result)
|
43
|
-
|
43
|
+
log.debug " - Checked #{ip} with #{cmd}"
|
44
44
|
result.split("\n").each do |l|
|
45
|
-
|
45
|
+
log.debug " #{l}"
|
46
46
|
end
|
47
47
|
end
|
48
48
|
|
49
49
|
def localhost_found(ip, mac)
|
50
|
-
|
50
|
+
log.debug " * Found localhost!"
|
51
51
|
end
|
52
52
|
|
53
53
|
def mac_found(ip, mac)
|
54
|
-
|
55
|
-
logger.debug " * Found #{mac} at #{ip}"
|
56
|
-
logger.debug
|
54
|
+
log.debug " * Found #{mac} at #{ip}"
|
57
55
|
end
|
58
56
|
|
59
57
|
def mac_not_found(ip)
|
60
|
-
|
61
|
-
|
62
|
-
|
58
|
+
log.debug " * No MAC found at #{ip}"
|
59
|
+
end
|
60
|
+
|
61
|
+
protected
|
62
|
+
|
63
|
+
def log_unknown_event(method_sym, *arguments)
|
64
|
+
# puts "method_sym = #{method_sym}"
|
65
|
+
# puts "arguments = #{arguments.inspect}"
|
66
|
+
# puts "log = #{log}"
|
67
|
+
# raise 'hell'
|
68
|
+
log.warn("#{method_sym}: #{arguments.inspect}")
|
69
|
+
end
|
70
|
+
|
71
|
+
# Always log events, even if there is no explicit handler. Generate a
|
72
|
+
# method at runtime so we don't have to do method_missing every time.
|
73
|
+
def method_missing(method_sym, *arguments, &block)
|
74
|
+
arg_names = arguments.each_with_index.map { |arg, i| "arg#{i}" }
|
75
|
+
arg_list = arg_names.join(',')
|
76
|
+
instance_eval <<-RUBY
|
77
|
+
def #{method_sym}(#{arg_list})
|
78
|
+
log_unknown_event(:#{method_sym}, #{arg_list})
|
79
|
+
end
|
80
|
+
RUBY
|
81
|
+
# def method_sym(arg1, arg2, ...)
|
82
|
+
# log_unknown_event(:method_sym, arg1, arg2, ...)
|
83
|
+
# end
|
84
|
+
send(method_sym, *arguments)
|
85
|
+
end
|
86
|
+
|
87
|
+
# Respond to anything.
|
88
|
+
def respond_to_missing?(method_sym, include_private = false)
|
89
|
+
true
|
63
90
|
end
|
64
91
|
end
|
65
92
|
end
|
@@ -7,20 +7,23 @@ module Presence
|
|
7
7
|
# list of MAC addresses on the network and detect when a new client connects
|
8
8
|
# or when a connected client disconnects.
|
9
9
|
class Tracker
|
10
|
+
|
10
11
|
def initialize
|
11
12
|
@mac_history = {}
|
12
13
|
@current_list = Presence::MACList.new
|
13
14
|
end
|
14
15
|
|
15
16
|
def listener_registered(l, scanner)
|
16
|
-
scanner
|
17
|
+
@scanner ||= scanner
|
18
|
+
@scanner.register_listener(@current_list) if l == self
|
17
19
|
end
|
18
20
|
|
19
21
|
def mac_found(ip, mac)
|
20
22
|
if @mac_history[mac].nil?
|
21
|
-
mac_connected
|
23
|
+
dispatch(:mac_connected, mac, ip)
|
22
24
|
elsif @mac_history[mac] != ip
|
23
|
-
|
25
|
+
old_ip = @mac_history[mac]
|
26
|
+
dispatch(:mac_changed, mac, old_ip, ip)
|
24
27
|
end
|
25
28
|
@mac_history[mac] = ip
|
26
29
|
end
|
@@ -29,25 +32,14 @@ module Presence
|
|
29
32
|
macs_left = @mac_history.keys - @current_list.macs_found.keys
|
30
33
|
macs_left.each do |mac|
|
31
34
|
old_ip = @mac_history[mac]
|
32
|
-
mac_disconnected
|
35
|
+
dispatch(:mac_disconnected, mac, old_ip)
|
33
36
|
@mac_history.delete(mac)
|
34
37
|
end
|
35
38
|
@current_list.macs_found.clear
|
36
39
|
end
|
37
40
|
|
38
|
-
def
|
39
|
-
|
40
|
-
# Do something interesting.
|
41
|
-
end
|
42
|
-
|
43
|
-
def mac_changed(mac, old_ip, new_ip)
|
44
|
-
puts " ** #{mac} changed ip from: #{old_ip} to #{new_ip}"
|
45
|
-
# Do something interesting.
|
46
|
-
end
|
47
|
-
|
48
|
-
def mac_disconnected(mac, old_ip)
|
49
|
-
puts " ** #{mac} disconnected from #{old_ip}"
|
50
|
-
# Do something interesting.
|
41
|
+
def dispatch(event, *args)
|
42
|
+
@scanner.dispatch(event, *args)
|
51
43
|
end
|
52
44
|
end
|
53
45
|
end
|
data/lib/presence/scanner.rb
CHANGED
@@ -82,12 +82,11 @@ module Presence
|
|
82
82
|
class << self
|
83
83
|
def check_env
|
84
84
|
commands = Commands.new
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
raise Presence::InvalidEnvironment.new("Install arping first: brew install arping")
|
85
|
+
required_commands = %w{ifconfig arping}
|
86
|
+
required_commands.each do |cmd|
|
87
|
+
if commands.run("which #{cmd}").size == 0
|
88
|
+
raise Presence::InvalidEnvironment.new("Unsupported platform: #{cmd} not found.")
|
89
|
+
end
|
91
90
|
end
|
92
91
|
end
|
93
92
|
|
data/lib/presence/version.rb
CHANGED
data/lib/presence.rb
CHANGED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Presence::HTTPNotifier do
|
4
|
+
let(:username) { nil }
|
5
|
+
let(:password) { nil }
|
6
|
+
let(:notifier) {
|
7
|
+
Presence::HTTPNotifier.new(
|
8
|
+
url: 'http://example.com/presence_events.json',
|
9
|
+
username: username,
|
10
|
+
password: password
|
11
|
+
)
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:scanner) { stub(Presence::Scanner) }
|
15
|
+
before { notifier.listener_registered(notifier, scanner) }
|
16
|
+
|
17
|
+
let(:http) { stub(Net::HTTP, :use_ssl= => nil, :verify_mode= => nil) }
|
18
|
+
let(:response) { stub(code: '200') }
|
19
|
+
before {
|
20
|
+
http.stub(:request).and_return(response)
|
21
|
+
Net::HTTP.stub(:new).with('example.com', 80).and_return(http)
|
22
|
+
}
|
23
|
+
|
24
|
+
context 'with a username and password' do
|
25
|
+
let(:username) { 'margot' }
|
26
|
+
let(:password) { 'poppie' }
|
27
|
+
|
28
|
+
it 'sends the username and password in the request' do
|
29
|
+
http.should_receive(:request) do |r|
|
30
|
+
r['authorization'].should == 'Basic bWFyZ290OnBvcHBpZQ=='
|
31
|
+
response
|
32
|
+
end
|
33
|
+
notifier.mac_connected('AA:BB:CC:DD:EE:FF', '10.0.1.49')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'dispatches an error notification if the request returns an error code' do
|
38
|
+
response.should_receive(:code).at_least(1).times.and_return('400')
|
39
|
+
response.should_receive(:body).and_return('Invalid request')
|
40
|
+
scanner.should_receive(:dispatch).with(:http_notification_failed, '400', 'Invalid request')
|
41
|
+
|
42
|
+
notifier.mac_connected('AA:BB:CC:DD:EE:FF', '10.0.1.49')
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'dispatches an error notification if the request raises a network error' do
|
46
|
+
http.should_receive(:request).and_raise(SocketError.new('bad things occurred'))
|
47
|
+
scanner.should_receive(:dispatch).with(:http_notification_failed, 'SocketError', 'bad things occurred')
|
48
|
+
|
49
|
+
notifier.mac_connected('AA:BB:CC:DD:EE:FF', '10.0.1.49')
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'sends a POST request on mac_connected' do
|
53
|
+
http.should_receive(:request) do |r|
|
54
|
+
r.method.should == 'POST'
|
55
|
+
r.body.should == 'mac=AA%3ABB%3ACC%3ADD%3AEE%3AFF&ip=10.0.1.49&event=mac_connected'
|
56
|
+
response
|
57
|
+
end
|
58
|
+
notifier.mac_connected('AA:BB:CC:DD:EE:FF', '10.0.1.49')
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'sends a POST request on mac_disconnected' do
|
62
|
+
http.should_receive(:request) do |r|
|
63
|
+
r.method.should == 'POST'
|
64
|
+
r.body.should == 'mac=AA%3ABB%3ACC%3ADD%3AEE%3AFF&ip=10.0.1.49&event=mac_disconnected'
|
65
|
+
response
|
66
|
+
end
|
67
|
+
notifier.mac_disconnected('AA:BB:CC:DD:EE:FF', '10.0.1.49')
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Presence::Logger do
|
4
|
+
let(:level) { ::Logger::INFO }
|
5
|
+
let(:io) { StringIO.new }
|
6
|
+
let(:log) {
|
7
|
+
log = ::Logger.new(io)
|
8
|
+
log.level = level
|
9
|
+
log.formatter = proc do |severity, datetime, progname, msg|
|
10
|
+
"#{msg}\n"
|
11
|
+
end
|
12
|
+
log
|
13
|
+
}
|
14
|
+
let(:logger) { Presence::Logger.new(log) }
|
15
|
+
|
16
|
+
context 'with no ::Logger specified' do
|
17
|
+
let(:log) { nil }
|
18
|
+
|
19
|
+
it 'logs warnings to stdout by default' do
|
20
|
+
result = capture_stdout do |variable|
|
21
|
+
logger.log.warn('Run, baby, run!')
|
22
|
+
end
|
23
|
+
result.should == "Run, baby, run!\n"
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'does not log info by default' do
|
27
|
+
result = capture_stdout do |variable|
|
28
|
+
logger.log.info('Run, baby, run!')
|
29
|
+
end
|
30
|
+
result.should == ''
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'with DEBUG logging' do
|
35
|
+
let(:level) { ::Logger::DEBUG }
|
36
|
+
|
37
|
+
it 'logs ip_scanned events' do
|
38
|
+
logger.ip_scanned('10.0.1.1', 'sudo arping', 'nada')
|
39
|
+
io.string.should == " - Checked 10.0.1.1 with sudo arping\n nada\n"
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'logs localhost_found events' do
|
43
|
+
logger.localhost_found('10.0.1.1', 'AA:BB:CC:DD:EE:FF')
|
44
|
+
io.string.should == " * Found localhost!\n"
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'logs mac_found events' do
|
48
|
+
logger.mac_found('10.0.1.1', 'aa:bb:cc:dd:ee:ff')
|
49
|
+
io.string.should == " * Found aa:bb:cc:dd:ee:ff at 10.0.1.1\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'logs mac_not_found events' do
|
53
|
+
logger.mac_not_found('10.0.1.1')
|
54
|
+
io.string.should == " * No MAC found at 10.0.1.1\n"
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'logs unknown events' do
|
58
|
+
logger.respond_to?(:miracle_occurred).should be_true
|
59
|
+
logger.miracle_occurred('256.256.256.256', 'GG:GG:GG:GG:GG:GG', 49)
|
60
|
+
io.string.should == "miracle_occurred: [\"256.256.256.256\", \"GG:GG:GG:GG:GG:GG\", 49]\n"
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'with INFO logging' do
|
66
|
+
let(:level) { ::Logger::INFO }
|
67
|
+
|
68
|
+
it 'logs listener_registered events' do
|
69
|
+
logger.listener_registered(logger, 'Scanner')
|
70
|
+
io.string.should == "Registered listener: <Presence::Logger> for: Scanner\n"
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'logs scan_started events' do
|
74
|
+
logger.scan_started('10.0.1', (1..255))
|
75
|
+
io.string.should == "Scanning range: 10.0.1.1 to 10.0.1.255\n"
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'logs scan_finished events' do
|
79
|
+
logger.scan_finished('10.0.1', (1..255))
|
80
|
+
io.string.should == "Scan finished.\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'does not log ip_scanned events' do
|
84
|
+
logger.ip_scanned('10.0.1.1', 'sudo arping', 'nada')
|
85
|
+
io.string.should == ''
|
86
|
+
end
|
87
|
+
|
88
|
+
it 'does not log localhost_found events' do
|
89
|
+
logger.localhost_found('10.0.1.1', 'aa:bb:cc:dd:ee:ff')
|
90
|
+
io.string.should == ''
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'does not log mac_found events' do
|
94
|
+
logger.mac_found('10.0.1.1', 'aa:bb:cc:dd:ee:ff')
|
95
|
+
io.string.should == ''
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'does not log mac_not_found events' do
|
99
|
+
logger.mac_not_found('10.0.1.1')
|
100
|
+
io.string.should == ''
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -10,7 +10,7 @@ describe Presence::Scanner do
|
|
10
10
|
|
11
11
|
let(:commands) {
|
12
12
|
commands = stub(Presence::Commands)
|
13
|
-
Presence::Commands.
|
13
|
+
Presence::Commands.stub(:new).and_return(commands)
|
14
14
|
commands
|
15
15
|
}
|
16
16
|
let(:listener) {
|
@@ -28,6 +28,63 @@ describe Presence::Scanner do
|
|
28
28
|
commands.stub(:arping).with(ip).and_return([arping_cmd, mac])
|
29
29
|
}
|
30
30
|
|
31
|
+
describe '.check_env' do
|
32
|
+
before {
|
33
|
+
commands.stub(:run).with('which ifconfig').and_return('/sbin/ifconfig')
|
34
|
+
commands.stub(:run).with('which arping').and_return('/usr/local/sbin/arping')
|
35
|
+
}
|
36
|
+
|
37
|
+
it 'checks for ifconfig' do
|
38
|
+
commands.should_receive(:run).with('which ifconfig').and_return('/sbin/ifconfig')
|
39
|
+
Presence::Scanner.check_env
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'raises an exception if ifconfig is not found' do
|
43
|
+
commands.should_receive(:run).with('which ifconfig').and_return('')
|
44
|
+
expect {
|
45
|
+
Presence::Scanner.check_env
|
46
|
+
}.to raise_error(Presence::InvalidEnvironment, "Unsupported platform: ifconfig not found.")
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'checks for arping' do
|
50
|
+
commands.should_receive(:run).with('which arping').and_return('/usr/local/sbin/arping')
|
51
|
+
Presence::Scanner.check_env
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'raises an exception if arping is not found' do
|
55
|
+
commands.should_receive(:run).with('which arping').and_return('')
|
56
|
+
expect {
|
57
|
+
Presence::Scanner.check_env
|
58
|
+
}.to raise_error(Presence::InvalidEnvironment, "Unsupported platform: arping not found.")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe '.scan_loop' do
|
63
|
+
let(:scanner) { mock(Presence::Scanner) }
|
64
|
+
before { Presence::Scanner.should_receive(:new).and_return(scanner) }
|
65
|
+
|
66
|
+
it 'scans until interrupted' do
|
67
|
+
scanner.should_receive(:scan)
|
68
|
+
scanner.should_receive(:scan)
|
69
|
+
scanner.should_receive(:scan).and_raise(Interrupt.new)
|
70
|
+
Presence::Scanner.scan_loop
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'yields the scanner instance to a block' do
|
74
|
+
scanner.stub(:scan).and_raise(Interrupt.new)
|
75
|
+
scanner.should_receive(:register_listener)
|
76
|
+
Presence::Scanner.scan_loop do |s|
|
77
|
+
s.register_listener(Object.new)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '#to_s' do
|
83
|
+
it 'overrides to_s' do
|
84
|
+
scanner.to_s.should == "<Presence::Scanner ip_prefix: '10.0.1' octet_range: (30..32)>"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
31
88
|
describe '#scan' do
|
32
89
|
it 'dispatches a scan_started event' do
|
33
90
|
listener.should_receive(:scan_started).with('10.0.1', octet_range)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Presence
|
2
|
+
module IOSpecHelpers
|
3
|
+
def suppress_output
|
4
|
+
@original_stdout, $stdout = $stdout, StringIO.new
|
5
|
+
@original_stderr, $stderr = $stderr, StringIO.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def reset_output
|
9
|
+
$stdout = @original_stdout
|
10
|
+
$stderr = @original_stderr
|
11
|
+
end
|
12
|
+
|
13
|
+
def capture_stdout
|
14
|
+
original_stdout, $stdout = $stdout, StringIO.new
|
15
|
+
yield
|
16
|
+
$stdout.string
|
17
|
+
ensure
|
18
|
+
$stdout = original_stdout
|
19
|
+
end
|
20
|
+
|
21
|
+
def capture_stderr
|
22
|
+
original_stderr, $stderr = $stderr, StringIO.new
|
23
|
+
yield
|
24
|
+
$stderr.string
|
25
|
+
ensure
|
26
|
+
$stderr = original_stderr
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
RSpec.configure do |config|
|
32
|
+
config.include Presence::IOSpecHelpers
|
33
|
+
end
|
metadata
CHANGED
@@ -2,14 +2,14 @@
|
|
2
2
|
name: presence
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0.
|
5
|
+
version: 0.0.2
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Jason Wadsworth
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-24 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
prerelease: false
|
@@ -94,6 +94,7 @@ files:
|
|
94
94
|
- lib/presence.rb
|
95
95
|
- lib/presence/commands.rb
|
96
96
|
- lib/presence/errors.rb
|
97
|
+
- lib/presence/listeners/http_notifier.rb
|
97
98
|
- lib/presence/listeners/logger.rb
|
98
99
|
- lib/presence/listeners/mac_list.rb
|
99
100
|
- lib/presence/listeners/mac_printer.rb
|
@@ -102,8 +103,12 @@ files:
|
|
102
103
|
- lib/presence/scanner.rb
|
103
104
|
- lib/presence/version.rb
|
104
105
|
- presence.gemspec
|
106
|
+
- spec/presence/http_notifier_spec.rb
|
107
|
+
- spec/presence/logger_spec.rb
|
105
108
|
- spec/presence/scanner_spec.rb
|
109
|
+
- spec/presence/tracker_spec.rb
|
106
110
|
- spec/spec_helper.rb
|
111
|
+
- spec/support/io_spec_helpers.rb
|
107
112
|
homepage: https://github.com/subakva/presence
|
108
113
|
licenses: []
|
109
114
|
post_install_message:
|
@@ -129,5 +134,9 @@ signing_key:
|
|
129
134
|
specification_version: 3
|
130
135
|
summary: Plays theme music when d8:d1:cb:b3:af:c4 arrives.
|
131
136
|
test_files:
|
137
|
+
- spec/presence/http_notifier_spec.rb
|
138
|
+
- spec/presence/logger_spec.rb
|
132
139
|
- spec/presence/scanner_spec.rb
|
140
|
+
- spec/presence/tracker_spec.rb
|
133
141
|
- spec/spec_helper.rb
|
142
|
+
- spec/support/io_spec_helpers.rb
|