presence 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/.simplecov ADDED
@@ -0,0 +1,14 @@
1
+ class SimpleCov::Formatter::QualityFormatter
2
+ def format(result)
3
+ SimpleCov::Formatter::HTMLFormatter.new.format(result)
4
+ File.open('coverage/covered_percent', 'w') do |f|
5
+ f.puts result.source_files.covered_percent.to_f
6
+ end
7
+ end
8
+ end
9
+ SimpleCov.formatter = SimpleCov::Formatter::QualityFormatter
10
+
11
+ SimpleCov.start do
12
+ add_filter '/spec/'
13
+ add_group 'CLI', 'lib/duple/cli'
14
+ end
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in presence.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jason Wadsworth
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Presence
2
+
3
+ Monitors the local network for presence of network clients by MAC address.
4
+
5
+ ## Installation
6
+
7
+ Install arping (Mac OS X)
8
+
9
+ $ brew install arping
10
+
11
+ Install arping (Debian/Ubuntu)
12
+
13
+ $ sudo apt-get install arping
14
+
15
+ Install the gem
16
+
17
+ $ gem install presence
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env rake
2
+
3
+ require 'bundler/gem_tasks'
4
+
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ begin
9
+ require 'cane/rake_task'
10
+
11
+ desc "Run cane to check quality metrics"
12
+ Cane::RakeTask.new(:cane) do |cane|
13
+ cane.abc_max = 15
14
+ cane.style_measure = 100
15
+ cane.style_glob = '{lib}/**/*.rb'
16
+ cane.gte = {'coverage/covered_percent' => 50}
17
+ end
18
+ rescue LoadError
19
+ warn "cane not available, quality task not provided."
20
+ end
21
+
22
+ task :default => [:spec, :cane]
data/bin/presence_scan ADDED
@@ -0,0 +1,21 @@
1
+ #! /usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ if File.exists?(File.join(File.expand_path('../..', __FILE__), '.git'))
6
+ lib_path = File.expand_path('../../lib', __FILE__)
7
+ $:.unshift(lib_path)
8
+ end
9
+
10
+ require 'presence'
11
+ # Presence::CLI::Root.start
12
+
13
+ # s = Presence::Scanner.new
14
+ # s.register_listener(Presence::Logger.new)
15
+ # s.register_listener(Presence::Tracker.new)
16
+ # s.scan
17
+
18
+ Presence::Scanner.scan_loop do |s|
19
+ s.register_listener(Presence::Logger.new)
20
+ s.register_listener(Presence::Tracker.new)
21
+ end
@@ -0,0 +1,18 @@
1
+ module Presence
2
+ # Wrapper class for shell command execution.
3
+ class Commands
4
+ def arping(ip)
5
+ result = run("sudo arping -c 1 #{ip}")
6
+ [cmd, result]
7
+ end
8
+
9
+ def local_ip
10
+ result = run('ifconfig | grep broadcast')
11
+ result.scan(/[\d\.]+/).first
12
+ end
13
+
14
+ def run(command)
15
+ `#{command}`
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,7 @@
1
+ module Presence
2
+ # Raised when the presence command is executed in an unsupported environment.
3
+ class InvalidEnvironment < StandardError; end
4
+
5
+ # Raised when an invalid listener is registered with the Presence::Scanner.
6
+ class InvalidListener < StandardError; end
7
+ end
@@ -0,0 +1,65 @@
1
+ require 'logger'
2
+
3
+ module Presence
4
+ # Scanner listener that sends descriptions of scan events to a log. By
5
+ # default, it uses a Logger instance that writes to stdout. To enable more
6
+ # verbose output, set:
7
+ #
8
+ # logger.level = ::Logger::INFO
9
+ #
10
+ # or:
11
+ #
12
+ # logger.level = ::Logger::DEBUG
13
+ #
14
+ # To write to a file or some other outlet, pass an instance of Logger to the
15
+ # constructor.
16
+ class Logger
17
+ attr_accessor :logger
18
+
19
+ def initialize(log = nil)
20
+ if log.nil?
21
+ log = ::Logger.new(STDOUT)
22
+ log.level = ::Logger::WARN
23
+ log.formatter = proc do |severity, datetime, progname, msg|
24
+ "#{msg}\n"
25
+ end
26
+ end
27
+ self.logger = log
28
+ end
29
+
30
+ def listener_registered(listener, scanner)
31
+ logger.info "Registered listener: <#{listener.class}> for: #{scanner}"
32
+ end
33
+
34
+ def scan_started(ip_prefix, range)
35
+ logger.info "Checking range: #{ip_prefix}.#{range.first} to #{ip_prefix}.#{range.last}"
36
+ end
37
+
38
+ def scan_finished(ip_prefix, range)
39
+ logger.info "Scan finished."
40
+ end
41
+
42
+ def ip_scanned(ip, cmd, result)
43
+ logger.debug " - Checked #{ip} with #{cmd}"
44
+ result.split("\n").each do |l|
45
+ logger.debug " #{l}"
46
+ end
47
+ end
48
+
49
+ def localhost_found(ip, mac)
50
+ logger.debug " * Found localhost!"
51
+ end
52
+
53
+ def mac_found(ip, mac)
54
+ logger.debug
55
+ logger.debug " * Found #{mac} at #{ip}"
56
+ logger.debug
57
+ end
58
+
59
+ def mac_not_found(ip)
60
+ logger.debug
61
+ logger.debug " * No MAC found at #{ip}"
62
+ logger.debug
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,15 @@
1
+ module Presence
2
+ # Scanner listener that stores a hash of MAC address to IP address for all
3
+ # MACs found by the scanner.
4
+ class MACList
5
+ attr_accessor :macs_found
6
+
7
+ def initialize
8
+ @macs_found = {}
9
+ end
10
+
11
+ def mac_found(ip, mac)
12
+ @macs_found[mac] = ip
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module Presence
2
+
3
+ # Scanner listener that prints all MACs found by the scanner to stdout.
4
+ class MACPrinter
5
+ def mac_found(ip, mac)
6
+ puts "#{ip} : #{mac}"
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ module Presence
2
+
3
+ # Scanner listener that executes and prints the output from nmap for all MACs
4
+ # found by the scanner.
5
+ class NMapper
6
+ def localhost_found(ip, mac)
7
+ @local_ip = ip
8
+ @local_mac = mac
9
+ end
10
+
11
+ def mac_found(ip, mac)
12
+ return if ip == @local_ip
13
+
14
+ result = `sudo nmap -O #{ip}`
15
+ puts '*' * 50
16
+ puts "* IP: #{ip}"
17
+ result.split("\n").each do |l|
18
+ puts " #{l}"
19
+ end
20
+ puts '*' * 50
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,53 @@
1
+ require 'presence/listeners/mac_list'
2
+
3
+ module Presence
4
+
5
+ # Scanner listener that tracks all MACs found by the scanner. This listener is
6
+ # intended to be used when scanning in a loop. The tracker will maintain a
7
+ # list of MAC addresses on the network and detect when a new client connects
8
+ # or when a connected client disconnects.
9
+ class Tracker
10
+ def initialize
11
+ @mac_history = {}
12
+ @current_list = Presence::MACList.new
13
+ end
14
+
15
+ def listener_registered(l, scanner)
16
+ scanner.register_listener(@current_list) if l == self
17
+ end
18
+
19
+ def mac_found(ip, mac)
20
+ if @mac_history[mac].nil?
21
+ mac_connected(mac, ip)
22
+ elsif @mac_history[mac] != ip
23
+ mac_changed(mac, @mac_history[mac], ip)
24
+ end
25
+ @mac_history[mac] = ip
26
+ end
27
+
28
+ def scan_finished(ip_prefix, range)
29
+ macs_left = @mac_history.keys - @current_list.macs_found.keys
30
+ macs_left.each do |mac|
31
+ old_ip = @mac_history[mac]
32
+ mac_disconnected(mac, old_ip)
33
+ @mac_history.delete(mac)
34
+ end
35
+ @current_list.macs_found.clear
36
+ end
37
+
38
+ def mac_connected(mac, ip)
39
+ puts " ** #{mac} connected as #{ip}"
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.
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,108 @@
1
+ require 'presence/errors'
2
+
3
+ module Presence
4
+
5
+ # Scans a network for connected clients, captures the MAC address, and
6
+ # dispatches related events to any registered listeners.
7
+ class Scanner
8
+ PAIR = '[0-9a-f]{2}'
9
+ SEP = '\:'
10
+ MAC_REGEXP = Regexp.new("(#{6.times.map {PAIR}.join(SEP)})")
11
+
12
+ attr_accessor :listeners, :options
13
+
14
+ def initialize(options = nil)
15
+ options ||= {}
16
+ self.options = {
17
+ ip_prefix: nil,
18
+ octet_range: (1..255)
19
+ }.merge(options)
20
+ self.listeners = []
21
+ @commands = Presence::Commands.new
22
+ end
23
+
24
+ def register_listener(l)
25
+ raise Presence::InvalidListener.new("Listener cannot be nil") if l.nil?
26
+ listeners << l
27
+ dispatch(:listener_registered, l, self)
28
+ end
29
+
30
+ def localhost_ip
31
+ unless @localhost_ip
32
+ @localhost_ip = @commands.local_ip
33
+ end
34
+ @localhost_ip
35
+ end
36
+
37
+ def ip_prefix
38
+ unless options[:ip_prefix]
39
+ options[:ip_prefix] = localhost_ip.split('.')[0,3].join('.')
40
+ end
41
+ options[:ip_prefix]
42
+ end
43
+
44
+ def octet_range
45
+ options[:octet_range]
46
+ end
47
+
48
+ def scan
49
+ dispatch(:scan_started, ip_prefix, octet_range)
50
+ octet_range.each do |i|
51
+ scan_ip("#{ip_prefix}.#{i}")
52
+ end
53
+ dispatch(:scan_finished, ip_prefix, octet_range)
54
+ end
55
+
56
+ def scan_ip(ip)
57
+ cmd, result = @commands.arping(ip)
58
+ dispatch(:ip_scanned, ip, cmd, result)
59
+ process_scan_result(ip, cmd, result)
60
+ end
61
+
62
+ def process_scan_result(ip, cmd, result)
63
+ if result =~ MAC_REGEXP
64
+ mac = $1
65
+ dispatch(:localhost_found, ip, mac) if ip == localhost_ip
66
+ dispatch(:mac_found, ip, mac)
67
+ else
68
+ dispatch(:mac_not_found, ip)
69
+ end
70
+ end
71
+
72
+ def dispatch(event, *args)
73
+ listeners.each do |l|
74
+ l.send(event, *args) if l.respond_to?(event)
75
+ end
76
+ end
77
+
78
+ def to_s
79
+ "<#{self.class} ip_prefix: '#{self.ip_prefix}' octet_range: (#{self.octet_range})>"
80
+ end
81
+
82
+ class << self
83
+ def check_env
84
+ commands = Commands.new
85
+ if commands.run('which ifconfig').size == 0
86
+ raise Presence::InvalidEnvironment.new("Unsupported platform: ifconfig not found.")
87
+ end
88
+ if commands.run('which arping').size == 0
89
+ raise Presence::InvalidEnvironment.new("Unsupported platform: arping not found.")
90
+ raise Presence::InvalidEnvironment.new("Install arping first: brew install arping")
91
+ end
92
+ end
93
+
94
+ def scan_loop(&block)
95
+ scanner = self.new
96
+ yield(scanner) if block_given?
97
+
98
+ while(true)
99
+ begin
100
+ scanner.scan
101
+ rescue Interrupt
102
+ break
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,3 @@
1
+ module Presence
2
+ VERSION = "0.0.1"
3
+ end
data/lib/presence.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'presence/version'
2
+ require 'presence/errors'
3
+ require 'presence/commands'
4
+ require 'presence/scanner'
5
+ require 'presence/listeners/logger'
6
+ require 'presence/listeners/mac_list'
7
+ require 'presence/listeners/mac_printer'
8
+ require 'presence/listeners/nmapper'
9
+ require 'presence/listeners/tracker'
data/presence.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'presence/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "presence"
8
+ gem.version = Presence::VERSION
9
+ gem.authors = ["Jason Wadsworth"]
10
+ gem.email = ["jdwadsworth@gmail.com"]
11
+ gem.description = %q{Monitors the local network for presence of network clients by MAC address.}
12
+ gem.summary = %q{Plays theme music when d8:d1:cb:b3:af:c4 arrives.}
13
+ gem.homepage = "https://github.com/subakva/presence"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ # gem.add_dependency('thor')
21
+
22
+ gem.add_development_dependency('rake', ['~> 0.9.2'])
23
+ gem.add_development_dependency('rspec', ['~> 2.11.0'])
24
+ gem.add_development_dependency('cane', ['~> 2.3.0'])
25
+ gem.add_development_dependency('simplecov', ['~> 0.7.1'])
26
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ describe Presence::Scanner do
4
+ let(:local_ip) { '10.0.1.30' }
5
+ let(:local_mac) { '8d:1d:bc:3b:fa:4c' }
6
+
7
+ let(:ip) { '10.0.1.31' }
8
+ let(:mac) { 'd8:d1:cb:b3:af:c4' }
9
+ let(:arping_cmd) { "sudo arping -c 1 #{ip}" }
10
+
11
+ let(:commands) {
12
+ commands = stub(Presence::Commands)
13
+ Presence::Commands.should_receive(:new).and_return(commands)
14
+ commands
15
+ }
16
+ let(:listener) {
17
+ listener = mock(Object)
18
+ scanner.register_listener(listener)
19
+ listener
20
+ }
21
+ let(:octet_range) { (30..32) }
22
+ let(:scanner) { Presence::Scanner.new(octet_range: octet_range) }
23
+
24
+ before {
25
+ commands.stub(:local_ip).and_return(local_ip)
26
+ commands.stub(:arping).and_return([arping_cmd, 'No such jeaun'])
27
+ commands.stub(:arping).with(local_ip).and_return([arping_cmd, local_mac])
28
+ commands.stub(:arping).with(ip).and_return([arping_cmd, mac])
29
+ }
30
+
31
+ describe '#scan' do
32
+ it 'dispatches a scan_started event' do
33
+ listener.should_receive(:scan_started).with('10.0.1', octet_range)
34
+ scanner.scan
35
+ end
36
+
37
+ it 'dispatches a scan_finished event' do
38
+ listener.should_receive(:scan_finished).with('10.0.1', octet_range)
39
+ scanner.scan
40
+ end
41
+
42
+ it 'scans all IPs in the range' do
43
+ listener.should_receive(:ip_scanned).with('10.0.1.30', arping_cmd, local_mac)
44
+ listener.should_receive(:ip_scanned).with('10.0.1.31', arping_cmd, mac)
45
+ listener.should_receive(:ip_scanned).with('10.0.1.32', arping_cmd, 'No such jeaun')
46
+ scanner.scan
47
+ end
48
+ end
49
+
50
+ describe '#scan_ip' do
51
+ it 'executes arping to check for a mac address' do
52
+ commands.should_receive(:arping).with(ip).and_return([arping_cmd, mac])
53
+ scanner.scan_ip(ip)
54
+ end
55
+
56
+ it 'dispatches an ip_scanned event' do
57
+ listener.should_receive(:ip_scanned).with(ip, arping_cmd, mac)
58
+ scanner.scan_ip(ip)
59
+ end
60
+
61
+ it 'dispatches a mac_found event' do
62
+ listener.should_receive(:mac_found).with(ip, mac)
63
+ scanner.scan_ip(ip)
64
+ end
65
+
66
+ context 'with a localhost IP' do
67
+ it 'dispatches a localhost_found event' do
68
+ listener.should_receive(:localhost_found).with(local_ip, local_mac)
69
+ scanner.scan_ip(local_ip)
70
+ end
71
+ end
72
+
73
+ context 'with a non-existent IP' do
74
+ before {
75
+ commands.stub(:arping).with(ip).and_return([arping_cmd, 'No such jeaun'])
76
+ }
77
+
78
+ it 'does not dispatch a mac_found event' do
79
+ listener.should_not_receive(:mac_found)
80
+ scanner.scan_ip(ip)
81
+ end
82
+
83
+ it 'dispatches an ip_scanned event' do
84
+ listener.should_receive(:ip_scanned).with(ip, arping_cmd, 'No such jeaun')
85
+ scanner.scan_ip(ip)
86
+ end
87
+
88
+ it 'dispatches a mac_not_found event' do
89
+ listener.should_receive(:mac_not_found).with(ip)
90
+ scanner.scan_ip(ip)
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,18 @@
1
+ lib_path = File.expand_path('../../lib', __FILE__)
2
+ $:.unshift(lib_path)
3
+
4
+ require 'simplecov'
5
+ require 'presence'
6
+ Dir['spec/support/**/*.rb'].each { |f| require File.expand_path(f) }
7
+
8
+ RSpec.configure do |config|
9
+ config.treat_symbols_as_metadata_keys_with_true_values = true
10
+ config.run_all_when_everything_filtered = true
11
+ config.filter_run :focus
12
+
13
+ # Run specs in random order to surface order dependencies. If you find an
14
+ # order dependency and want to debug it, you can fix the order by providing
15
+ # the seed, which is printed after each run.
16
+ # --seed 1234
17
+ config.order = 'random'
18
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: presence
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Jason Wadsworth
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ prerelease: false
16
+ type: :development
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: 0.9.2
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ version: 0.9.2
29
+ name: rake
30
+ - !ruby/object:Gem::Dependency
31
+ prerelease: false
32
+ type: :development
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ~>
37
+ - !ruby/object:Gem::Version
38
+ version: 2.11.0
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: 2.11.0
45
+ name: rspec
46
+ - !ruby/object:Gem::Dependency
47
+ prerelease: false
48
+ type: :development
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 2.3.0
55
+ version_requirements: !ruby/object:Gem::Requirement
56
+ none: false
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ version: 2.3.0
61
+ name: cane
62
+ - !ruby/object:Gem::Dependency
63
+ prerelease: false
64
+ type: :development
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ~>
69
+ - !ruby/object:Gem::Version
70
+ version: 0.7.1
71
+ version_requirements: !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 0.7.1
77
+ name: simplecov
78
+ description: Monitors the local network for presence of network clients by MAC address.
79
+ email:
80
+ - jdwadsworth@gmail.com
81
+ executables:
82
+ - presence_scan
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - .rspec
88
+ - .simplecov
89
+ - Gemfile
90
+ - LICENSE.txt
91
+ - README.md
92
+ - Rakefile
93
+ - bin/presence_scan
94
+ - lib/presence.rb
95
+ - lib/presence/commands.rb
96
+ - lib/presence/errors.rb
97
+ - lib/presence/listeners/logger.rb
98
+ - lib/presence/listeners/mac_list.rb
99
+ - lib/presence/listeners/mac_printer.rb
100
+ - lib/presence/listeners/nmapper.rb
101
+ - lib/presence/listeners/tracker.rb
102
+ - lib/presence/scanner.rb
103
+ - lib/presence/version.rb
104
+ - presence.gemspec
105
+ - spec/presence/scanner_spec.rb
106
+ - spec/spec_helper.rb
107
+ homepage: https://github.com/subakva/presence
108
+ licenses: []
109
+ post_install_message:
110
+ rdoc_options: []
111
+ require_paths:
112
+ - lib
113
+ required_ruby_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ! '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ none: false
121
+ requirements:
122
+ - - ! '>='
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 1.8.23
128
+ signing_key:
129
+ specification_version: 3
130
+ summary: Plays theme music when d8:d1:cb:b3:af:c4 arrives.
131
+ test_files:
132
+ - spec/presence/scanner_spec.rb
133
+ - spec/spec_helper.rb