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
         |