whatup 0.3.4 → 0.3.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d9a2ccfe87d331ab0f316dce065f81de75aef2f2d870a1f83ed467d05b52ebdc
4
- data.tar.gz: 990c58f0f6eaf19fe4032c33493d3f18ea7359318e182c15fb14e06390531b1a
3
+ metadata.gz: 4f618bbf5b44743049617a2a4af44444716b2f5caf2f6b698dfded679bd1eff5
4
+ data.tar.gz: 803106014c08fb0d54163d559f7568597c45726c74ba96654b6074d8a4ed27d1
5
5
  SHA512:
6
- metadata.gz: 6a7025e077088cfda1f83ac5587be14628e07deb4c91eb01a66e884baed2523f563b6dcb329d02e408f984ad5aa42d2bb76de8755d060a9aa2f9dd1f86c8d90b
7
- data.tar.gz: 8f0c1ae31f64922a223d23ffcd3e2d8a2555b6cb1e029505cb4389ff0bc0832e1cf406751486195792684fc7ecea654b4906aa08a5ae7ee649356dfa8745efca
6
+ metadata.gz: 3ad1f0ba64cc60f7962fad49e09fa7e926560b7d84c993e4b3550d5cdb41a0173b25f59e0302d54542b67ec9a275faeea04af8418a42d2796ee2ce16eba1d23d
7
+ data.tar.gz: 239e524850f5ca8c8c2591b23a7ed6fa7969ae6847c55c18e4e4e567e3849b5cec7cb8fe986c06eaf2ce8496b0d6217a3a698b99eec537df08b4e98f6c75b36c
@@ -32,7 +32,7 @@ Style/CommentedKeyword:
32
32
 
33
33
  # The default is a bit restrictive
34
34
  Metrics/AbcSize:
35
- Max: 30
35
+ Max: 40
36
36
 
37
37
  # The default is a bit restrictive
38
38
  Metrics/ClassLength:
@@ -43,12 +43,13 @@ Lint/AmbiguousRegexpLiteral:
43
43
  Enabled: false
44
44
 
45
45
  Metrics/BlockLength:
46
+ Max: 40
46
47
  Exclude:
47
48
  - 'whatup.gemspec'
48
49
  - 'spec/**/*_spec.rb'
49
50
 
50
51
  Metrics/MethodLength:
51
- Max: 20 # 10 is a bit too low
52
+ Max: 40 # 10 is a bit too low
52
53
 
53
54
  # Don't type unneccesary ()
54
55
  Style/MethodDefParentheses:
data/README.md CHANGED
@@ -28,8 +28,8 @@ brief instructions.
28
28
  $ whatup
29
29
 
30
30
  Commands:
31
+ whatup -v, --version # Output the version
31
32
  whatup client ... # Perform client commands
32
- whatup hello # Says hello
33
33
  whatup help [COMMAND] # Describe available commands or one specific command
34
34
  whatup server ... # Perform server commands
35
35
  ```
@@ -11,9 +11,15 @@ module Whatup
11
11
  module CLI
12
12
  # Top-level command class
13
13
  class CLI < Thor
14
- desc 'hello', 'Says hello'
15
- def hello
16
- say "Hello!\n", :cyan
14
+ map %w[-v --version] => :version
15
+ option :version,
16
+ aliases: '-v',
17
+ type: :boolean,
18
+ desc: 'Show version',
19
+ default: true
20
+ desc '-v, --version', 'Output the version'
21
+ def version
22
+ say Whatup::VERSION
17
23
  end
18
24
 
19
25
  # Subcommands are defined below, but are implemented in `commands/`
@@ -10,6 +10,7 @@ module Whatup
10
10
  extend ThorInteractive
11
11
 
12
12
  Room = Whatup::Server::Room
13
+ Message = Whatup::Server::Message
13
14
  Client = Whatup::Server::Client
14
15
 
15
16
  desc 'msg [NAME]', 'Send a message to [NAME]'
@@ -29,17 +30,14 @@ module Whatup
29
30
  say "That user doesn't exist!"
30
31
  end
31
32
 
32
- desc 'list', 'List your received messages'
33
+ desc 'list', 'List your direct messages'
33
34
  def list
34
35
  say 'Your direct messages:'
35
- msgs = current_user.received_messages.map do |msg|
36
- <<~MSG
37
- From: #{msg.sender.name}
38
-
39
- #{msg.content}
40
- MSG
41
- end.join('-' * 10)
42
- say msgs
36
+ say \
37
+ Message.where(sender: current_user)
38
+ .or(Message.where(recipient: current_user))
39
+ .map(&:to_s)
40
+ .join('-' * 10)
43
41
  end
44
42
  end
45
43
  end
@@ -51,7 +51,7 @@ module Whatup
51
51
  end
52
52
 
53
53
  desc 'room [NAME]', 'Create and enter chatroom [NAME]'
54
- def room name # rubocop:disable Metrics/AbcSize
54
+ def room name
55
55
  if room = Room.find_by(name: name)
56
56
  current_user.puts <<~MSG
57
57
  Entering #{room.name}... enjoy your stay!
@@ -84,23 +84,6 @@ module Whatup
84
84
  MSG
85
85
  end
86
86
 
87
- desc 'dm [NAME]', 'Send a direct message to [NAME]'
88
- def dm name
89
- if recepient = Client.find_by(name: name)
90
- say <<~MSG
91
- Sending a direct message to #{name}...
92
-
93
- The message can span multiple lines.
94
-
95
- Type `.exit` when you're ready to send it.
96
- MSG
97
- current_user.composing_dm = recepient
98
- return
99
- end
100
-
101
- say "That user doesn't exist!"
102
- end
103
-
104
87
  desc 'exit', 'Closes your connection with the server'
105
88
  def exit
106
89
  current_user.exit!
@@ -8,12 +8,21 @@ module Whatup
8
8
  module CLI
9
9
  # Server commands
10
10
  class Server < Thor
11
- option :port, type: :numeric, default: 9_001
11
+ option :port,
12
+ type: :numeric,
13
+ default: 9_001,
14
+ desc: 'The port to run the server on'
15
+ option :verbose,
16
+ aliases: '-v',
17
+ type: :boolean,
18
+ desc: 'Enable verbose output',
19
+ default: false
12
20
  desc 'start', 'Starts a server instance'
13
21
  long_desc <<~DESC
14
22
  Starts a server instance running locally on the specified port.
15
23
  DESC
16
24
  def start
25
+ ENV['WHATUP_LOG_LEVEL'] = 'DEBUG' if options[:verbose]
17
26
  Whatup::Server::Server.new(port: options[:port]).start
18
27
  end
19
28
  end
@@ -2,35 +2,43 @@
2
2
 
3
3
  require 'whatup'
4
4
  require 'whatup/server/redirection'
5
+ require 'whatup/server/logger'
5
6
 
6
7
  module Whatup
7
8
  module Server
8
9
  module DbInit
9
10
  extend Redirection
11
+ extend WhatupLogger
10
12
 
11
13
  class << self
12
- # Sets up our database, deleting all existing data.
13
14
  def setup_db!
15
+ log.debug { 'Setting up database ...' }
16
+
14
17
  db = "#{Dir.home}/.whatup.db"
15
- SQLite3::Database.new(db) unless File.exist?(db)
18
+
19
+ if File.exist?(db)
20
+ log.debug { "Using existing database `#{db}" }
21
+ else
22
+ log.debug { "Creating new database `#{db}" }
23
+ SQLite3::Database.new(db)
24
+ end
16
25
 
17
26
  ActiveRecord::Base.establish_connection adapter: 'sqlite3',
18
27
  database: db
19
28
 
20
- ActiveRecord::Base.connection.execute <<~SQL
29
+ truncate_sql = <<~SQL
21
30
  DROP TABLE IF EXISTS clients_rooms;
22
31
  DROP TABLE IF EXISTS clients;
23
32
  DROP TABLE IF EXISTS messages;
24
33
  DROP TABLE IF EXISTS rooms;
25
34
  SQL
35
+ log.debug { "Truncating existing data ...\n#{truncate_sql}" }
36
+ ActiveRecord::Base.connection.execute truncate_sql
26
37
 
27
- if Whatup.testing?
28
- # We silence output here, so that tests don't get cluttered
29
- redirect(stdout: StringIO.new) { create_tables! }
30
- return
38
+ StringIO.new.tap do |io|
39
+ redirect(stdout: io) { create_tables! }
40
+ log.debug { "Creating tables ...\n#{io&.string}" }
31
41
  end
32
-
33
- create_tables!
34
42
  end
35
43
 
36
44
  private
@@ -40,14 +48,17 @@ module Whatup
40
48
  create_table :clients, force: true do |t|
41
49
  t.string :name
42
50
  t.references :room
51
+ t.timestamps
43
52
  end
44
53
  create_table :messages, force: true do |t|
45
54
  t.string :content
46
55
  t.references :sender
47
56
  t.references :recipient
57
+ t.timestamps
48
58
  end
49
59
  create_table :rooms, force: true do |t|
50
60
  t.string :name
61
+ t.timestamps
51
62
  end
52
63
  end
53
64
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'English'
4
+ require 'logger'
5
+
6
+ require 'colorize'
7
+
8
+ class Logger
9
+ class Formatter
10
+ def call severity, time, _progname, msg
11
+ color = case severity
12
+ when 'DEBUG' then :light_magenta
13
+ when 'INFO' then :light_green
14
+ when 'WARN' then :light_cyan
15
+ when 'ERROR' then :light_red
16
+ when 'FATAL', 'UNKNOWN' then :red
17
+ end
18
+
19
+ "[#{severity.ljust(5, ' ').colorize color}][#{time}]: #{msg}\n"
20
+ end
21
+ end
22
+ end
23
+
24
+ module WhatupLogger
25
+ # Access a logger, to stdout (for now).
26
+ #
27
+ # Uses logging level ENV['WHATUP_LOG_LEVEL'], which can be WARN, INFO, etc
28
+ def log
29
+ Logger.new(STDOUT).tap do |logger|
30
+ if Logger.constants.map(&:to_s).include? ENV['WHATUP_LOG_LEVEL']
31
+ logger.level = Logger.const_get ENV['WHATUP_LOG_LEVEL']
32
+ else
33
+ logger.level = Logger::INFO
34
+ logger.level = 9001 if Whatup.testing?
35
+ end
36
+ end
37
+ end
38
+ end
@@ -21,7 +21,7 @@ module Whatup
21
21
  end
22
22
 
23
23
  def gets
24
- socket.gets
24
+ socket&.gets
25
25
  end
26
26
 
27
27
  def input!
@@ -55,6 +55,10 @@ module Whatup
55
55
  @deleted = true
56
56
  destroy!
57
57
  end
58
+
59
+ def to_s
60
+ name
61
+ end
58
62
  end
59
63
  end
60
64
  end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'tzinfo'
4
+
3
5
  require 'whatup/server/models/application_record'
4
6
 
5
7
  module Whatup
@@ -7,6 +9,20 @@ module Whatup
7
9
  class Message < ApplicationRecord
8
10
  belongs_to :recipient, class_name: 'Client'
9
11
  belongs_to :sender, class_name: 'Client', foreign_key: 'sender_id'
12
+
13
+ TZ = TZInfo::Timezone.get 'America/Detroit' # Central time
14
+
15
+ def to_s
16
+ <<~MSG.gsub '.exit', ''
17
+ ------------------------------------------------------------
18
+ From: #{sender.name}
19
+ To: #{recipient.name}
20
+ Date: #{TZ.utc_to_local(created_at).to_s :db}
21
+
22
+ #{content&.chomp}
23
+ ------------------------------------------------------------
24
+ MSG
25
+ end
10
26
  end
11
27
  end
12
28
  end
@@ -9,6 +9,7 @@ require 'active_record'
9
9
  require 'active_support/core_ext/object/blank'
10
10
 
11
11
  require 'whatup/server/db_init'
12
+ require 'whatup/server/logger'
12
13
  require 'whatup/server/redirection'
13
14
  require 'whatup/server/models/client'
14
15
  require 'whatup/server/models/message'
@@ -18,9 +19,9 @@ require 'whatup/cli/commands/interactive/interactive'
18
19
  module Whatup
19
20
  module Server
20
21
  class Server # rubocop:disable Metrics/ClassLength
21
- include Thor::Shell
22
22
  include DbInit
23
23
  include Redirection
24
+ include WhatupLogger
24
25
 
25
26
  Client = Whatup::Server::Client
26
27
 
@@ -49,7 +50,7 @@ module Whatup
49
50
  # The server continuously loops, and handle each new client in a separate
50
51
  # thread.
51
52
  def start
52
- say "Starting a server with PID:#{@pid} @ #{@address} ... \n", :green
53
+ log.info { "Starting a server with PID:#{@pid} @ #{@address} ... \n" }
53
54
 
54
55
  exit_if_pid_exists!
55
56
  connect_to_socket!
@@ -57,7 +58,10 @@ module Whatup
57
58
 
58
59
  # Listen for connections, then accept each in a separate thread
59
60
  loop do
60
- Thread.new(@socket.accept) { |client| handle_client client }
61
+ Thread.new(@socket.accept) do |client|
62
+ log.info { "Accepted new client: #{client.inspect}" }
63
+ handle_client client
64
+ end
61
65
  end
62
66
  rescue SignalException # In case of ^c
63
67
  kill
@@ -76,9 +80,9 @@ module Whatup
76
80
  #
77
81
  # @return [Whatup::Server::Room] The created room
78
82
  def new_room! clients: [], name:
79
- room = Room.create! name: name, clients: clients
80
- @rooms << room
81
- room
83
+ Room.create!(name: name, clients: clients).tap do |room|
84
+ @rooms << room
85
+ end
82
86
  end
83
87
 
84
88
  private
@@ -87,15 +91,22 @@ module Whatup
87
91
  #
88
92
  # @param client [Whatup::Server::Client] The client
89
93
  #
90
- # rubocop:disable Metrics/MethodLength
91
- def handle_client client
94
+ def handle_client client # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/LineLength
92
95
  client = create_new_client_if_not_existing! client
93
96
 
94
97
  # Loop forever to maintain the connection
95
98
  loop do
96
99
  @clients.reject! &:deleted
97
100
 
98
- Thread.current.exit if client.deleted
101
+ if client.deleted
102
+ log.debug do
103
+ <<~OUT
104
+ Client `#{client.name}` has been deleted.
105
+ #{'Killing'.colorize :red} this thread."
106
+ OUT
107
+ end
108
+ Thread.current.exit
109
+ end
99
110
 
100
111
  if client.composing_dm?
101
112
  handle_dm client
@@ -106,28 +117,31 @@ module Whatup
106
117
  # Wait until we get a valid command. This takes as long as the client
107
118
  # takes.
108
119
  msg = client.input! unless Whatup::CLI::Interactive.command?(msg)
120
+ log.info { "#{client.name.colorize :light_blue}> #{msg}" }
109
121
 
110
- puts "#{client.name}> #{msg}"
111
-
112
- begin
113
- # Send the output to the client
114
- redirect stdin: client.socket, stdout: client.socket do
122
+ # Send the output to the client
123
+ redirect stdin: client.socket, stdout: client.socket do
124
+ begin
115
125
  # Invoke the cli using the provided commands and options.
116
126
  run_thor_command! client: client, msg: msg
127
+ rescue RuntimeError,
128
+ ArgumentError,
129
+ Thor::InvocationError,
130
+ Thor::UndefinedCommandError => e
131
+ log.info do
132
+ "#{client.name.colorize :red}> #{e.class}: #{e.message}"
133
+ end
134
+ client.puts case e.class.to_s
135
+ when 'RuntimeError'
136
+ 'Invalid input or unknown command'
137
+ else
138
+ e.message
139
+ end
117
140
  end
118
- rescue RuntimeError,
119
- Thor::InvocationError,
120
- Thor::UndefinedCommandError => e
121
- puts e.message
122
- client.puts 'Invalid input or unknown command'
123
- rescue ArgumentError => e
124
- puts e.message
125
- client.puts e.message
126
141
  end
127
142
  msg = nil
128
143
  end
129
144
  end
130
- # rubocop:enable Metrics/MethodLength
131
145
 
132
146
  # Handles inputing direct messages
133
147
  #
@@ -137,18 +151,20 @@ module Whatup
137
151
  msg = StringIO.new
138
152
  loop do
139
153
  input = client.input!
140
- puts "#{client.name}> #{input}"
154
+ log.info { "#{client.name.colorize :light_blue}> #{input}" }
141
155
  msg.puts input
142
156
  if input == '.exit'
143
157
  client.puts "Finished dm to `#{client.composing_dm.name}`."
144
158
  break
145
159
  end
146
160
  end
147
- client.composing_dm
148
- .received_messages << Message.new(
149
- sender: client,
150
- content: msg.string
151
- )
161
+ Message.create!(sender: client, content: msg.string).tap do |m|
162
+ client.composing_dm.received_messages << m
163
+ log.debug do
164
+ "Created new message (id = #{m.id}) from `#{client}` to" \
165
+ "`#{client.composing_dm}`"
166
+ end
167
+ end
152
168
  client.composing_dm = nil
153
169
  end
154
170
 
@@ -163,7 +179,7 @@ module Whatup
163
179
  .select do |c|
164
180
  client.room.clients.pluck(:id).include? c.id
165
181
  end
166
- puts "#{client.name}> #{input}"
182
+ log.info { "#{client.name.colorize :light_blue}> #{input}" }
167
183
  if input == '.exit'
168
184
  client.puts "Exited `#{client.room.name}`."
169
185
  audience.each { |c| c.puts "#{client.name}> LEFT" }
@@ -185,7 +201,18 @@ module Whatup
185
201
  #
186
202
  # @return [Whatup::Server::Client] The created client
187
203
  def create_new_client_if_not_existing! client
188
- name = client.gets.chomp
204
+ log.debug { 'Creating new client' }
205
+
206
+ name = client.gets&.chomp
207
+
208
+ if name.nil?
209
+ log.debug do
210
+ 'New client (currently unknown) has left. ' \
211
+ "#{'Killing'.colorize :red} this thread."
212
+ end
213
+ Thread.current.exit
214
+ end
215
+
189
216
  rand_num = SecureRandom.random_number(100).to_s.rjust 3, '0'
190
217
  name = name == '' ? "ANON-#{rand_num}" : name
191
218
 
@@ -193,6 +220,10 @@ module Whatup
193
220
  client.puts 'That name is taken! Goodbye.'
194
221
  client.puts 'END'
195
222
  client.close
223
+ log.debug do
224
+ "Existing name `#{name}` entered. " \
225
+ "#{'Killing'.colorize :red} this thread"
226
+ end
196
227
  Thread.current.exit
197
228
  end
198
229
 
@@ -200,16 +231,17 @@ module Whatup
200
231
  name: name,
201
232
  socket: client
202
233
  )
234
+ log.info { "Created new client `#{name}` ..." }
203
235
 
204
- puts "#{client.name} just showed up!"
205
- client.puts <<~MSG
206
- Hello, #{client.name}!
236
+ client.tap do |c|
237
+ c.puts <<~MSG
238
+ Hello, #{client.name}!
207
239
 
208
- Welcome to whatup.
240
+ Welcome to whatup.
209
241
 
210
- To get started, type `help`.
211
- MSG
212
- client
242
+ To get started, type `help`.
243
+ MSG
244
+ end
213
245
  end
214
246
 
215
247
  # Initialize a new cli class using the initial command and options,
@@ -233,9 +265,11 @@ module Whatup
233
265
 
234
266
  # Kills the server if a PID for this app exists
235
267
  def exit_if_pid_exists!
268
+ log.debug { "Checking if `#{@pid}` exists ..." }
269
+
236
270
  return unless running?
237
271
 
238
- say <<~EXIT, :cyan
272
+ log.info <<~EXIT
239
273
  A server appears to already be running!
240
274
  Check `#{@pid_file}`.
241
275
  EXIT
@@ -246,14 +280,18 @@ module Whatup
246
280
  # Connect a new socket for this server to start listening on the specified
247
281
  # address and port.
248
282
  def connect_to_socket!
283
+ log.info do
284
+ "#{'Opening'.colorize :blue} TCP socket at `#{@ip}:#{@port}`"
285
+ end
249
286
  @socket = TCPServer.open @ip, @port
250
287
  rescue Errno::EADDRINUSE
251
- puts 'Address already in use!'
288
+ log.error "Address `#{@ip}:#{@port}` is already in use!"
252
289
  kill
253
290
  end
254
291
 
255
292
  # Write this process's PID to the PID file
256
293
  def write_pid!
294
+ log.debug { "Writing PID to `#{@pid}`" }
257
295
  File.open(@pid_file, 'w') { |f| f.puts Process.pid }
258
296
  end
259
297
 
@@ -264,8 +302,12 @@ module Whatup
264
302
 
265
303
  # Kills the server and removes the PID file
266
304
  def kill
267
- say "Killing the server with PID:#{Process.pid} ...", :red
305
+ log.info do
306
+ "#{'Killing'.colorize :red} the server with " \
307
+ "PID:#{Process.pid} ..."
308
+ end
268
309
  FileUtils.rm_rf @pid_file
310
+ log.debug { "Removed `#{@pid_file}`." }
269
311
  exit
270
312
  end
271
313
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Whatup
4
- VERSION = '0.3.4'
4
+ VERSION = '0.3.5'
5
5
  end
@@ -67,7 +67,9 @@ Gem::Specification.new do |spec|
67
67
  'activesupport' => '~> 5.2',
68
68
  'thor' => '~> 0.20.3',
69
69
  'sqlite3' => '~> 1.4',
70
- 'activerecord' => '~> 5.2'
70
+ 'activerecord' => '~> 5.2',
71
+ 'tzinfo' => '~> 1.2',
72
+ 'colorize' => '~> 0.8.1'
71
73
  }.each do |gem, version|
72
74
  spec.add_dependency gem, version
73
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: whatup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mark Delk
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-04-12 00:00:00.000000000 Z
11
+ date: 2019-04-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -178,6 +178,34 @@ dependencies:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
180
  version: '5.2'
181
+ - !ruby/object:Gem::Dependency
182
+ name: tzinfo
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - "~>"
186
+ - !ruby/object:Gem::Version
187
+ version: '1.2'
188
+ type: :runtime
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - "~>"
193
+ - !ruby/object:Gem::Version
194
+ version: '1.2'
195
+ - !ruby/object:Gem::Dependency
196
+ name: colorize
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - "~>"
200
+ - !ruby/object:Gem::Version
201
+ version: 0.8.1
202
+ type: :runtime
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - "~>"
207
+ - !ruby/object:Gem::Version
208
+ version: 0.8.1
181
209
  description: |
182
210
  whatup is a simple server-based instant messaging application using TCP
183
211
  sockets.
@@ -221,6 +249,7 @@ files:
221
249
  - lib/whatup/cli/thor_interactive.rb
222
250
  - lib/whatup/client/client.rb
223
251
  - lib/whatup/server/db_init.rb
252
+ - lib/whatup/server/logger.rb
224
253
  - lib/whatup/server/models/application_record.rb
225
254
  - lib/whatup/server/models/client.rb
226
255
  - lib/whatup/server/models/message.rb