lib/whatup/server/server.rb in whatup-0.2.4 vs lib/whatup/server/server.rb in whatup-0.2.5

- old
+ new

@@ -2,37 +2,44 @@ require 'socket' require 'fileutils' require 'securerandom' +require 'sqlite3' +require 'active_record' require 'active_support/core_ext/object/blank' -require 'whatup/server/client' -require 'whatup/server/room' +require 'whatup/server/db_init' +require 'whatup/server/redirection' +require 'whatup/server/models/client' +require 'whatup/server/models/message' +require 'whatup/server/models/room' require 'whatup/cli/commands/interactive/interactive' module Whatup module Server - class Server + class Server # rubocop:disable Metrics/ClassLength include Thor::Shell + include DbInit + include Redirection Client = Whatup::Server::Client - # Used by the interactive client cli - attr_reader *%i[ip port address clients max_id pid pid_file rooms] + attr_reader *%i[ip port address clients pid pid_file rooms] - def initialize port: - @ip = 'localhost' + def initialize ip: 'localhost', port: + @ip = ip @port = port @address = "#{@ip}:#{@port}" @clients = [] @rooms = [] - @max_id = 1 @pid = Process.pid @pid_file = "#{Dir.home}/.whatup.pid" + + DbInit.setup_db! end # Starts the server. # # The server continuously loops, and handle each new client in a separate @@ -60,74 +67,93 @@ def find_client_by name: @clients.select { |c| c.name == name }&.first end + def clients_except client + @clients.reject { |c| c == client } + end + def new_room! clients: [], name: - room = Room.new name: name, clients: clients + room = Room.create! name: name, clients: clients @rooms << room room end private # Receives a new client, then continuously gets input from that client # - # rubocop:disable Metrics/MethodLength, Metrics/AbcSize + # rubocop:disable Metrics/MethodLength def handle_client client client = create_new_client_if_not_existing! client # Loop forever to maintain the connection loop do - handle_chatting(client) if client.chatting? + @clients.reject! &:deleted + Thread.current.exit if client.deleted + + if client.composing_dm? + handle_dm client + elsif client.chatting? + handle_chatting client + end + # Wait until we get a valid command. This takes as long as the client # takes. msg = client.input! unless Whatup::CLI::Interactive.command?(msg) puts "#{client.name}> #{msg}" - # Initialize a new cli class using the initial command and options, - # and then set any instance variables, since Thor will create a new - # class instance when it's invoked. - cmds, opts = Whatup::CLI::Interactive.parse_input msg - cli = Whatup::CLI::Interactive.new(cmds, opts).tap do |c| - c.server = self - c.current_user = client - end - begin # Send the output to the client redirect stdin: client.socket, stdout: client.socket do # Invoke the cli using the provided commands and options. - - # This _should_ achieve the same effect as - # `Whatup::CLI::Interactive.start(args)`, but allows us to set - # instance variables on the cli class. - cli.invoke cli.args.first, cli.args[1..cli.args.size - 1] + run_thor_command! client: client, msg: msg end rescue RuntimeError, Thor::InvocationError, Thor::UndefinedCommandError => e puts e.message client.puts 'Invalid input or unknown command' rescue ArgumentError => e puts e.message client.puts e.message end - msg = nil # rubocop:disable Lint/UselessAssignment + msg = nil end end - # rubocop:enable Metrics/MethodLength, Metrics/AbcSize + # rubocop:enable Metrics/MethodLength + def handle_dm client + msg = StringIO.new + loop do + input = client.input! + puts "#{client.name}> #{input}" + if input == '.exit' + client.puts "Finished dm to `#{client.composing_dm.name}`." + break + end + msg.puts input + end + client.composing_dm + .received_messages << Message.new( + sender: client, + content: msg.string + ) + client.composing_dm = nil + end + def handle_chatting client loop do input = client.input! room = client.room puts "#{client.name}> #{input}" if input == '.exit' client.leave_room! + client.puts "Exited `#{room.name}`." break end room.broadcast except: client do "#{client.name}> #{input}" end @@ -145,38 +171,44 @@ rand_num = SecureRandom.random_number(100).to_s.rjust 3, '0' name = name == '' ? "ANON-#{rand_num}" : name if @clients.any? { |c| c.name == name } client.puts 'That name is taken! Goodbye.' - client.exit! + client.puts 'END' + client.close + Thread.current.exit end - @clients << client = Client.new( - id: new_client_id, + @clients << client = Client.create!( name: name, socket: client ) puts "#{client.name} just showed up!" - client.puts "Hello, #{client.name}!" + client.puts <<~MSG + Hello, #{client.name}! + + Welcome to whatup. + + To get started, type `help`. + MSG client end - # @return A new, unique client identification number - def new_client_id - @max_id += 1 - end + def run_thor_command! client:, msg: + # Initialize a new cli class using the initial command and options, + # and then set any instance variables, since Thor will create a new + # class instance when it's invoked. + cmds, opts = Whatup::CLI::Interactive.parse_input msg + Whatup::CLI::Interactive.new(cmds, opts).tap do |c| + c.server = self + c.current_user = client - # Reroutes stdin and stdout inside a block - def redirect stdin: $stdin, stdout: $stdout - original_stdin = $stdin - original_stdout = $stdout - $stdin = stdin - $stdout = stdout - yield - ensure - $stdin = original_stdin - $stdout = original_stdout + # This _should_ achieve the same effect as + # `Whatup::CLI::Interactive.start(args)`, but allows us to set + # instance variables on the cli class. + c.invoke c.args.first, c.args.drop(1) + end end def exit_if_pid_exists! return unless running?