whatup 0.2.5 → 0.3.0

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: 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