jcmuller/traffic_light_controller

View on GitHub
lib/traffic_light_controller/server.rb

Summary

Maintainability
A
25 mins
Test Coverage
module TrafficLightController
  class Server

    class << self
      def run
        server = Server.new
        server.work
      end
    end

    def initialize(config = Config.new)
      Thread.abort_on_exception = true
      @config = config
      @server = TCPServer.new(config.server.address, config.server.port)
      @current_path = ""
    rescue Errno::EADDRINUSE
      address_in_use_error(config.server)
    rescue Errno::EADDRNOTAVAIL, SocketError
      address_not_available_error(config.server.address)
    end

    def work
      while(true)
        start_thread_and_do_work
      end
    end

    private

    attr_reader :server, :config, :board, :current_path

    def start_thread_and_do_work
      Thread.start(server.accept) do |request|
        accept_request(request)
      end
    end

    def accept_request(request)
      process_request(request)
    rescue => e
      STDERR.puts "Got #{e.class}: #{e}!

#{e.backtrace.join($/)}"
    ensure
      request.close
    end

    def process_request(request)
      path = path_from_request(request)

      if config.arduino.lights.has_key?(path)
        if path != current_path
          @current_path = path
          change_pins(path)
          respond_200(request, path)
        else
          respond_304(request, path)
        end
      else
        respond_404(request, path)
      end
    end

    def path_from_request(request)
      path = ""
      while(line = request.readline) do
        break if line == "\r\n"
        if match = line.match(%r{GET /+(?<path>\w*) HTTP/1\.})
          path = match[:path]
        end
      end

      path
    end

    def change_pins(path)
      init_board
      board.turnOff
      board.setHigh(config.arduino.lights[path]) unless path == "off"
      board.close
    end

    def respond_200(request, path)
      request.print "HTTP/1.1 200 OK\r\nContent-type:text/plain\r\n\r\n"
      request.puts path
    end

    def respond_304(request, path)
      request.puts "HTTP/1.1 304 Not Modified\r\nContent-type:text/plain\r\n\r\n"
    end

    def respond_404(request, path)
      request.print "HTTP/1.1 404 Not Found\r\nContent-type:text/plain\r\n\r\n"
      request.puts "The requested path doesn't exist"
    end

    def init_board
      @board = Arduino.new(config.arduino.port)
    rescue Errno::ENOENT
      STDERR.puts "The port #{config.arduino.port} doesn't exist"
      exit
    rescue ArgumentError
      STDERR.puts "#{config.arduino.port} is not a serial port"
      exit
    end

    # ERRORS
    def address_in_use_error(server)
      STDERR.puts <<-EOT
There appears that another instance is running, or another process
is listening on the same port (#{server.address}:#{server.port})

      EOT
      exit
    end

    def address_not_available_error(address)
      STDERR.puts <<-EOT
The address configured is not available (#{address})

      EOT
      exit
    end
  end
end