whatup 0.2.5 → 0.3.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0ace83a0aa8e24fb44a1f6900f8cdcaf201e7d8d9661883d1bb0ec8fdadf7fd6
4
- data.tar.gz: 56b91c405097272ca64cbf9d78b4c6c02fd8749ce883838369b933b618c53552
3
+ metadata.gz: 974cf5162247e51601cda75af330d68aa1b9a77a73eb394228a5c4db17966445
4
+ data.tar.gz: 1a9113486f862c0ff3fc324458572f0e98055cd3459a126b84fc61ae44c90b77
5
5
  SHA512:
6
- metadata.gz: 1664ad2e6d346c84d4509fe166744e2f863889d59a968f21db9188989eabf87e4e927045530926a51dc80e2ffdd6734f252642fb0b645139b177319399eb97c6
7
- data.tar.gz: 4ee3b633248639ff80215487a2d2820fc7e01a5b6b8923574f464b9255de77443febae039cb9e6cfa8444a2c2e649c7a3597ad82de66127039c6e373a2158168
6
+ metadata.gz: c9c93d48307ac342e04efa0b43a43c7d84d6208be74e87702b2f35c30eae0c01be4d6fca302ee6c691b2e6f4c41af6ea1d64d024f09d735cd503c35419744b1c
7
+ data.tar.gz: 1fcce685d6b140cb51452fdf299b0190212c42c036aa9821fc9f062ad8387c06d617f9068c52c37c8b877708ec072f01cc070ede433df061cfa161300da02370
data/.rubocop.yml CHANGED
@@ -3,6 +3,10 @@
3
3
  AllCops:
4
4
  TargetRubyVersion: 2.4 # Modern Ruby
5
5
 
6
+ # !!defined?(RSpec)
7
+ Style/DoubleNegation:
8
+ Enabled: false
9
+
6
10
  # attr_reader *%i[a b c]
7
11
  Lint/UnneededSplatExpansion:
8
12
  Enabled: false
@@ -17,6 +21,15 @@ Style/FrozenStringLiteralComment:
17
21
  Exclude:
18
22
  - 'spec/system_spec.rb'
19
23
 
24
+ # For long specs.
25
+ #
26
+ # describe 'some spec'
27
+ # ...
28
+ # end # describe 'some spec'
29
+ Style/CommentedKeyword:
30
+ Exclude:
31
+ - 'spec/system_spec.rb'
32
+
20
33
  # The default is a bit restrictive
21
34
  Metrics/AbcSize:
22
35
  Max: 30
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+ require 'whatup/cli/thor_interactive'
5
+
6
+ module Whatup
7
+ module CLI
8
+ # Server commands
9
+ class Dm < Thor
10
+ extend ThorInteractive
11
+
12
+ Room = Whatup::Server::Room
13
+ Client = Whatup::Server::Client
14
+
15
+ desc 'msg [NAME]', 'Send a message to [NAME]'
16
+ def msg name
17
+ if recepient = Client.find_by(name: name)
18
+ say <<~MSG
19
+ Sending a direct message to #{name}...
20
+
21
+ The message can span multiple lines.
22
+
23
+ Type `.exit` when you're ready to send it.
24
+ MSG
25
+ current_user.composing_dm = recepient
26
+ return
27
+ end
28
+
29
+ say "That user doesn't exist!"
30
+ end
31
+
32
+ desc 'list', 'List your received messages'
33
+ def list
34
+ 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
43
+ end
44
+ end
45
+ end
46
+ end
@@ -1,34 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'thor'
4
+ require 'whatup/cli/thor_interactive'
4
5
 
5
6
  require 'whatup/client/client'
7
+ require 'whatup/cli/commands/interactive/dm'
6
8
 
7
9
  module Whatup
8
10
  module CLI
9
- # Any methods of class `Whatup::CLI::Interactive` that rely on instance
10
- # variables should be included here
11
- COMMANDS = %i[
12
- room
13
- list
14
- exit
15
- dmlist
16
- dm
17
- ].freeze
18
-
19
- require 'whatup/cli/commands/interactive/setup'
20
-
21
11
  # Interactive client commands that are available after connecting
22
12
  #
23
13
  # This class is run on the server
24
14
  class Interactive < Thor
25
- prepend InteractiveSetup
15
+ extend ThorInteractive
26
16
 
27
17
  Room = Whatup::Server::Room
28
18
  Client = Whatup::Server::Client
29
19
 
30
- attr_accessor *%i[server current_user]
31
-
32
20
  no_commands do
33
21
  # Checks whether a given input string is a valid command
34
22
  def self.command? msg
@@ -39,7 +27,7 @@ module Whatup
39
27
 
40
28
  # Parses a client's input into a format suitable for Thor commands
41
29
  #
42
- # @param [String] msg - the client's message
30
+ # param [String] msg - the client's message
43
31
  def self.parse_input msg
44
32
  # Split user input at the first "option"
45
33
  cmds, opts = msg&.split /-|--/, 2
@@ -48,8 +36,6 @@ module Whatup
48
36
  cmds = cmds&.split(/\s+/)
49
37
  opts = opts&.split(/\s+/)
50
38
 
51
- # `Whatup::CLI::Interactive.new(cmds, opts)` expects arrays, and
52
- # a final options hash
53
39
  cmds = [] if cmds.nil?
54
40
  opts = [] if opts.nil?
55
41
 
@@ -57,23 +43,17 @@ module Whatup
57
43
  end
58
44
  end
59
45
 
60
- # Don't show app name in command help, i.e, instead of
61
- # `app command desc`, use `command desc`
62
- def self.banner task, _namespace = false, subcommand = false
63
- task.formatted_usage(self, false, subcommand).to_s
64
- end
65
-
66
46
  desc 'list', 'Show all connected clients'
67
47
  def list
68
48
  say 'All connected clients:'
69
- @server.clients_except(@current_user).each { |c| say " #{c.status}" }
70
- say "* #{@current_user.status}"
49
+ server.clients_except(current_user).each { |c| say " #{c.status}" }
50
+ say "* #{current_user.status}"
71
51
  end
72
52
 
73
53
  desc 'room [NAME]', 'Create and enter chatroom [NAME]'
74
54
  def room name
75
55
  if room = Room.find_by(name: name)
76
- @current_user.puts <<~MSG
56
+ current_user.puts <<~MSG
77
57
  Entering #{room.name}... enjoy your stay!
78
58
 
79
59
  Type `.exit` to exit this chat room.
@@ -83,40 +63,27 @@ module Whatup
83
63
  "- #{client.name}\n"
84
64
  end.join}
85
65
  MSG
86
- @current_user.update! room: room
66
+ current_user.update! room: room
87
67
 
88
- room.broadcast except: @current_user do
68
+ room.broadcast except: current_user do
89
69
  <<~MSG
90
- #{@current_user.name} has arrived! Play nice, kids.
70
+ #{current_user.name} has arrived! Play nice, kids.
91
71
  MSG
92
72
  end
93
73
 
94
- room.clients << @current_user
74
+ room.clients << current_user
95
75
  return
96
76
  end
97
77
 
98
- room = @server.new_room! name: name, clients: [@current_user]
78
+ room = server.new_room! name: name, clients: [current_user]
99
79
 
100
- @current_user.puts <<~MSG
80
+ current_user.puts <<~MSG
101
81
  Created and entered #{room.name}... invite some people or something!
102
82
 
103
83
  Type `.exit` to exit this chat room.
104
84
  MSG
105
85
  end
106
86
 
107
- desc 'dmlist', 'List your received messages'
108
- def dmlist
109
- say 'Your direct messages:'
110
- msgs = @current_user.received_messages.map do |msg|
111
- <<~MSG
112
- From: #{msg.sender.name}
113
-
114
- #{msg.content}
115
- MSG
116
- end.join('-' * 10)
117
- say msgs
118
- end
119
-
120
87
  desc 'dm [NAME]', 'Send a direct message to [NAME]'
121
88
  def dm name
122
89
  if recepient = Client.find_by(name: name)
@@ -127,7 +94,7 @@ module Whatup
127
94
 
128
95
  Type `.exit` when you're ready to send it.
129
96
  MSG
130
- @current_user.composing_dm = recepient
97
+ current_user.composing_dm = recepient
131
98
  return
132
99
  end
133
100
 
@@ -136,8 +103,16 @@ module Whatup
136
103
 
137
104
  desc 'exit', 'Closes your connection with the server'
138
105
  def exit
139
- @current_user.exit!
106
+ current_user.exit!
140
107
  end
108
+
109
+ desc 'dm ...', 'Perform direct message commands'
110
+ long_desc <<~DESC
111
+ Perform direct message commands.
112
+
113
+ See `dm help [CMD] for more info about specific commands.
114
+ DESC
115
+ subcommand 'dm', Dm
141
116
  end
142
117
  end
143
118
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thor'
4
+
5
+ # Allows us to use variables passed into a Thor class's `config[:locals]`
6
+ # Allows us to call the variables passed into `config[:locals]` as methods
7
+ # in our cli classes
8
+ module ThorInstanceVariableHook
9
+ def method_missing method, *args, &block
10
+ var = instance_variable_get(:@_initializer).last[:locals][method]
11
+ return var unless var.nil?
12
+
13
+ super
14
+ end
15
+
16
+ def respond_to_missing? name, include_private = false
17
+ super
18
+ end
19
+ end
20
+
21
+ module ThorInteractive
22
+ # Don't show app name in command help, i.e, instead of
23
+ # `app command desc`, use `command desc`
24
+ def banner task, _namespace = false, subcommand = false
25
+ task.formatted_usage(self, false, subcommand).to_s
26
+ end
27
+
28
+ # Include the instance variable to method hook in our cli classes
29
+ def self.extended base
30
+ base.send :include, ThorInstanceVariableHook
31
+ end
32
+ end
@@ -9,6 +9,7 @@ module Whatup
9
9
  extend Redirection
10
10
 
11
11
  class << self
12
+ # Sets up our database, deleting all existing data.
12
13
  def setup_db!
13
14
  db = "#{Whatup.root}/db/whatup.db"
14
15
  SQLite3::Database.new(db) unless File.exist?(db)
@@ -23,7 +24,13 @@ module Whatup
23
24
  DROP TABLE IF EXISTS rooms;
24
25
  SQL
25
26
 
26
- redirect(stdout: StringIO.new) { create_tables! }
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
31
+ end
32
+
33
+ create_tables!
27
34
  end
28
35
 
29
36
  private
@@ -4,6 +4,9 @@ module Whatup
4
4
  module Server
5
5
  module Redirection
6
6
  # Reroutes stdin and stdout inside a block
7
+ #
8
+ # @param [IO#write] stdin - will redirect stdin to this
9
+ # @param [IO#write] stdout - will redirect stdout to this
7
10
  def redirect stdin: $stdin, stdout: $stdout
8
11
  original_stdin = $stdin
9
12
  original_stdout = $stdout
@@ -26,6 +26,10 @@ module Whatup
26
26
 
27
27
  attr_reader *%i[ip port address clients pid pid_file rooms]
28
28
 
29
+ # @param ip [String] The ip address to run the server on
30
+ # @param port [Integer] The port to run the server on
31
+ #
32
+ # @return [Whatup::Server::Server] The created server
29
33
  def initialize ip: 'localhost', port:
30
34
  @ip = ip
31
35
  @port = port
@@ -53,26 +57,24 @@ module Whatup
53
57
 
54
58
  # Listen for connections, then accept each in a separate thread
55
59
  loop do
56
- Thread.new(@socket.accept) do |client|
57
- case handle_client client
58
- when :exit
59
- client.puts 'bye!'
60
- Thread.kill Thread.current
61
- end
62
- end
60
+ Thread.new(@socket.accept) { |client| handle_client client }
63
61
  end
64
62
  rescue SignalException # In case of ^c
65
63
  kill
66
64
  end
67
65
 
68
- def find_client_by name:
69
- @clients.select { |c| c.name == name }&.first
70
- end
71
-
66
+ # @param client [Whatup::Server::Client] The client to not retrieve
67
+ #
68
+ # @return [Array<Whatup::Server::Client>] All currently connected clients
69
+ # except for `client`
72
70
  def clients_except client
73
71
  @clients.reject { |c| c == client }
74
72
  end
75
73
 
74
+ # @param clients [Array<Whatup::Server::Client>] Room's inital clients
75
+ # @param name [String] The room's name
76
+ #
77
+ # @return [Whatup::Server::Room] The created room
76
78
  def new_room! clients: [], name:
77
79
  room = Room.create! name: name, clients: clients
78
80
  @rooms << room
@@ -83,6 +85,8 @@ module Whatup
83
85
 
84
86
  # Receives a new client, then continuously gets input from that client
85
87
  #
88
+ # @param client [Whatup::Server::Client] The client
89
+ #
86
90
  # rubocop:disable Metrics/MethodLength
87
91
  def handle_client client
88
92
  client = create_new_client_if_not_existing! client
@@ -125,6 +129,10 @@ module Whatup
125
129
  end
126
130
  # rubocop:enable Metrics/MethodLength
127
131
 
132
+ # Handles inputing direct messages
133
+ #
134
+ # @param client [Whatup::Server::Client] `client` is the sender of
135
+ # the message, and `client.composing_dm` is the recipient.
128
136
  def handle_dm client
129
137
  msg = StringIO.new
130
138
  loop do
@@ -144,6 +152,10 @@ module Whatup
144
152
  client.composing_dm = nil
145
153
  end
146
154
 
155
+ # Handles chatting.
156
+ #
157
+ # @param client [Whatup::Server::Client] The client. `client` is assumed
158
+ # to already belong to a room
147
159
  def handle_chatting client
148
160
  loop do
149
161
  input = client.input!
@@ -166,6 +178,10 @@ module Whatup
166
178
  # If no username is provided (i.e, blank), it assigns a random, anonymous
167
179
  # username in the format `ANON-xxx`, where `xxx` is a random number upto
168
180
  # 100, left-padded with zeros.
181
+ #
182
+ # @param client [TCPSocket] The client connection
183
+ #
184
+ # @return [Whatup::Server::Client] The created client
169
185
  def create_new_client_if_not_existing! client
170
186
  name = client.gets.chomp
171
187
  rand_num = SecureRandom.random_number(100).to_s.rjust 3, '0'
@@ -194,22 +210,26 @@ module Whatup
194
210
  client
195
211
  end
196
212
 
213
+ # Initialize a new cli class using the initial command and options,
214
+ # and then set any instance variables, since Thor will create a new
215
+ # class instance when it's invoked.
216
+ #
217
+ # This achieve the same effect as
218
+ # `Whatup::CLI::Interactive.start(args)`, but allows us to set
219
+ # instance variables on the cli class.
220
+ #
221
+ # @param client [Whatup::Server::Client]
197
222
  def run_thor_command! client:, msg:
198
- # Initialize a new cli class using the initial command and options,
199
- # and then set any instance variables, since Thor will create a new
200
- # class instance when it's invoked.
201
223
  cmds, opts = Whatup::CLI::Interactive.parse_input msg
202
- Whatup::CLI::Interactive.new(cmds, opts).tap do |c|
203
- c.server = self
204
- c.current_user = client
205
-
206
- # This _should_ achieve the same effect as
207
- # `Whatup::CLI::Interactive.start(args)`, but allows us to set
208
- # instance variables on the cli class.
209
- c.invoke c.args.first, c.args.drop(1)
210
- end
224
+ cli = Whatup::CLI::Interactive.new(
225
+ cmds,
226
+ opts,
227
+ locals: {server: self, current_user: client} # config
228
+ )
229
+ cli.invoke cli.args.first, cli.args.drop(1)
211
230
  end
212
231
 
232
+ # Kills the server if a PID for this app exists
213
233
  def exit_if_pid_exists!
214
234
  return unless running?
215
235
 
@@ -221,21 +241,26 @@ module Whatup
221
241
  kill
222
242
  end
223
243
 
244
+ # Connect a new socket for this server to start listening on the specified
245
+ # address and port.
224
246
  def connect_to_socket!
225
- @socket = TCPServer.open @port
247
+ @socket = TCPServer.open @ip, @port
226
248
  rescue Errno::EADDRINUSE
227
249
  puts 'Address already in use!'
228
250
  kill
229
251
  end
230
252
 
253
+ # Write this process's PID to the PID file
231
254
  def write_pid!
232
255
  File.open(@pid_file, 'w') { |f| f.puts Process.pid }
233
256
  end
234
257
 
258
+ # @return [Bool] Whether or not a PID for this app exists
235
259
  def running?
236
260
  File.file? @pid_file
237
261
  end
238
262
 
263
+ # Kills the server and removes the PID file
239
264
  def kill
240
265
  say "Killing the server with PID:#{Process.pid} ...", :red
241
266
  FileUtils.rm_rf @pid_file
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Whatup
4
- VERSION = '0.2.5'
4
+ VERSION = '0.3.0'
5
5
  end
data/lib/whatup.rb CHANGED
@@ -8,7 +8,13 @@ module Whatup
8
8
  class Error < StandardError
9
9
  end
10
10
 
11
+ # @return [String] The full path to application root
11
12
  def self.root
12
13
  Dir.pwd
13
14
  end
15
+
16
+ # @return [Bool] Whether or not the app is running in a test environment
17
+ def self.testing?
18
+ !!defined?(RSpec)
19
+ end
14
20
  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.2.5
4
+ version: 0.3.0
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-10 00:00:00.000000000 Z
11
+ date: 2019-04-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -215,9 +215,10 @@ files:
215
215
  - lib/whatup.rb
216
216
  - lib/whatup/cli/cli.rb
217
217
  - lib/whatup/cli/commands/client.rb
218
+ - lib/whatup/cli/commands/interactive/dm.rb
218
219
  - lib/whatup/cli/commands/interactive/interactive.rb
219
- - lib/whatup/cli/commands/interactive/setup.rb
220
220
  - lib/whatup/cli/commands/server.rb
221
+ - lib/whatup/cli/thor_interactive.rb
221
222
  - lib/whatup/client/client.rb
222
223
  - lib/whatup/server/db_init.rb
223
224
  - lib/whatup/server/models/application_record.rb
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Whatup
4
- module CLI
5
- # Implements a `before` hook to set the correct instance variables
6
- # before any command methods.
7
- #
8
- # This is needed, since Thor creates another cli class instance when it is
9
- # called with `invoke`, and we need to reassign any variables to the new
10
- # cli instance.
11
- #
12
- # TODO: grab commands dynamically
13
- module InteractiveSetup
14
- Whatup::CLI::COMMANDS.each do |cmd|
15
- define_method cmd do |*args|
16
- cli = instance_variable_get(:@_initializer).last[:shell].base
17
- @server = cli.server
18
- @current_user = cli.current_user
19
- super *args
20
- end
21
- end
22
- end
23
- end
24
- end