How to fix this error message? syntax error, unexpected tIVAR - ruby

I'm trying to run some tests on my code, but I'm running into a problem. For a very simple line of code, I get a weird error message. This test is to make sure my server can receive info from one of my clients.
The tests and file run fine without this line:
client_1.#socket.puts("This gives an error.")
Including that bit of code gives this error:
macowner:WAR3 macowner$ ruby ServerTests1.rb
ServerTests1.rb:160: syntax error, unexpected tIVAR, expecting '('
client_1.#socket.puts("Output for Server to receive.") #Error
^
Help is greatly appreciated. I get this error fairly frequently, but I have no idea what it means (even with searching for it).
enter code here
require 'minitest/autorun'
require 'socket'
require_relative 'WarGame_Class.rb'
require_relative 'ModifiedPlayer_Class.rb'
require_relative 'DeckClass.rb'
class WarServer
def initialize(host, port)
#socket_server = TCPServer.new(host, port)
#players = [Player.new, Player.new]
#deck = CardDeck.new
#deck.deal_cards(#players[0].cards, #players[1].cards)
game = WarGame.new
#clients = {} # keys are sockets, values are players
end
def client_keys(key)
#clients.keys[key] # this should work
end
def input #input reader function
#input
end
def close
#socket_server.close
end
def capture_input ##input client to get what they wrote
#input = #clients.keys[0].read_nonblock(1000) # arbitrary max number of bytes
end
def accept_client
#Hash here to link client to player? (or game?)
client = #socket_server.accept
#clients[client] = #players[#clients.size]
# puts "clients key 0: #{#clients.keys[0]}"
puts
# puts "clients values: #{#clients.values}"
if #clients.size == 2
start_game#####################!!!! Starts game if two clients can put client messages in start game
end
end
def start_game ##############!!!
#clients.keys[0].puts "Welcome to War. Please press enter to play your card"
#clients.keys[1].puts "Welcome to War. Please press enter to play your card"
end
end
class MockWarClient
def initialize
#socket = TCPSocket.new('localhost', 2012)
end
def output
#output
end
def input
#input
end
def capture_output #need to add (socket)? How else read from specific socket?
#output = #socket.read_nonblock(1000) # arbitrary max number of bytes
rescue
#output = "capture_output error."
end
def write_input
#input = #war_server.client_keys.write_nonblock(1000)
end
end
class WarServerTest < MiniTest::Unit::TestCase
def setup #This would be like our INITIALIZE Function
#anything is available through out all tests (i.e., instance vars)
#war_server = WarServer.new('localhost', 2012)
end
def teardown
#war_server.close
end
def test_server_capture_output_from_client
client_1 = MockWarClient.new
#war_server.accept_client
client_2 = MockWarClient.new
#war_server.accept_client
client_1.#socket.puts("Output for Server to receive.") #Line I need to fix
#SOCKETforSERVER
#clien_n.SOCKETforSERVER.puts''
#Replace puts with write_nonblock, perhaps
end
end

If you're trying to access the #socket instance variable then you just need another accessor method in MockWarClient:
def socket
#socket
end
and the say client_1.socket.puts(...) to use it. You could also use attr_reader to simplify things:
class MockWarClient
attr_reader :socket, :input, :output
def initialize
#socket = TCPSocket.new('localhost', 2012)
end
def capture_output #need to add (socket)? How else read from specific socket?
#output = #socket.read_nonblock(1000) # arbitrary max number of bytes
rescue
#output = "capture_output error."
end
def write_input
#input = #war_server.client_keys.write_nonblock(1000)
end
end
That will create the three accessor methods for you.

You cannot call a method named #socket in a usual way as you are trying in client_1.#socket. That method name is very unusual because it is confusing with an instance variable, and it is doubtful that you actually have such method, but if you do, then the way to call such method is:
client_1.send(:#socket)

Related

Rspec - Stubbing gets.chomp out of the constructor

I'm writing a Mastermind game in Ruby and in the constructor of the class 'Game' I want to use gets.chomp to ask the user for its name. Pretty easy, but where I run into trouble is when testing this class in RSpec, but I can't seem to properly stub out 'gets' and 'puts', because they are in the constructor not a regular method.
class Game
def initialize
puts "Please enter your name:"
#player = Player.new(gets.chomp)
end
end
describe Game do
Game.stub(:gets).and_return("Create Code AI")
Game.stub(:puts)
subject(:game) { Game.new }
describe "#new" do
its("player.name") { eql("Create Code AI") }
end
end
class Player
attr_reader :name
def initialize(name)
#name = name
end
end
I've also tried putting the stubs into 'before' and 'let' blocks amongst other things, but nothing seems to work. Any help is appreciated!
I have a method which captures stdin and stdout to help with the testing in cases like these:
require 'stringio'
module Kernel
def capture_stdout(console_input = '')
$stdin = StringIO.new(console_input)
out = StringIO.new
$stdout = out
yield
return out.string.strip
ensure
$stdout = STDOUT
$stdin = STDIN
end
end
Now, let's assume I want to test a method that interacts with stdin/stdout:
def greet
name = gets
puts "Welcome, #{name}!"
end
I would write the following test:
require 'rspec/autorun'
RSpec.describe '#say_name' do
it 'prints name correctly' do
input = 'Joe'
result = capture_stdout(input) do
greet
end
expect(result).to eql 'Welcome, Joe!'
end
end
I presented the example above to illustrate how to test both console input and output.
In your case, the test could look like this:
describe Game do
subject(:game) do
capture_stdout('Create Code AI') { return Game.new }
end
describe "#new" do
its("player.name") { eql("Create Code AI") }
end
end
Note: In order for this to work, #player should be an accessible member of Game. So, you may want to add this to your Game class:
attr_reader :player

Why is my class variable still nil after I assign it? (Ruby)

According to my test, client_1 should be a client class which I have used to see if I can output and put it. I need to use #output variable in my client class is STILL NIL, but I assign it with the capture_output method in my client class.
#war_server.client_keys(0).puts("Hello Frodo") #works
temp_holder = client_1.capture_output #store output from server to temp holder
puts "Here is the captured input from client_1! : #{temp_holder}"#works
puts #client_1.output #DOES NOT WORK. But it should because I assin it in my class and use a reader definition
Here's the code for my classes and test. Thanks!
require 'minitest/autorun'
require 'socket'
require_relative 'WarGame_Class.rb'
require_relative 'ModifiedPlayer_Class.rb'
require_relative 'DeckClass.rb'
class WarServer
def initialize(host, port)
#socket_server = TCPServer.new(host, port)
#players = [Player.new, Player.new]
#deck = CardDeck.new
#deck.deal_cards(#players[0].cards, #players[1].cards)
game = WarGame.new
#clients = {} # keys are sockets, values are players
end
def client_keys(key)
#clients.keys[key] # this should work
end
def input #input reader function
#input
end
def close
#socket_server.close
end
def capture_input ##input client to get what they wrote
#input = #clients.keys[0].read_nonblock(1000) # arbitrary max number of bytes
end
def accept_client
#Hash here to link client to player? (or game?)
client = #socket_server.accept
#clients[client] = #players[#clients.size]
# puts "clients key 0: #{#clients.keys[0]}"
puts
# puts "clients values: #{#clients.values}"
if #clients.size == 2
start_game#####################!!!! Starts game if two clients can put client messages in start game
end
end
def start_game ##############!!!
#clients.keys[0].puts "Welcome to War. Please press enter to play your card"
#clients.keys[1].puts "Welcome to War. Please press enter to play your card"
end
end
class MockWarClient
def initialize
#socket = TCPSocket.new('localhost', 2012)
end
def output
#output
end
def input
#input
end
def capture_output #need to add (socket)? How else read from specific socket?
#output = #socket.read_nonblock(1000) # arbitrary max number of bytes
rescue
#output = "capture_output error."
end
def write_input
#input = #war_server.client_keys.write_nonblock(1000)
end
end
class WarServerTest < MiniTest::Unit::TestCase
def setup #This would be like our INITIALIZE Function
#anything is available through out all tests (i.e., instance vars)
#war_server = WarServer.new('localhost', 2012)
end
def teardown
#war_server.close
end
def test_server_capture_output_from_client
client_1 = MockWarClient.new
#war_server.accept_client
client_2 = MockWarClient.new
#war_server.accept_client
#can output #war_server.client_keys, though, if I take out the argument to pass in.
#puts "Test_Server_output #client keys #{#war_server.client_keys(player)}" #cient_1?
puts "Test_Server_output #client keys 0 #{#war_server.client_keys(0)}"
puts "Test_Server_output #client keys 1 #{#war_server.client_keys(1)}"
#war_server.client_keys(0).puts("Hello Frodo")
temp_holder = client_1.capture_output
puts "Here is the captured input from client_1! : #{temp_holder}"
#puts #war_server.input
puts #client_1.output
end
end
client_1 is a local variable, distinct from #client_1. I don't see anywhere in this code where you assign #client_1. Unless there is some code you're not sharing which does this, then #client_1 will evaluate to nil.
#client_1 is an instance variable, which is a variable on the instance of a class and exists for the duration that the object is in existence. In order to assign a value to an instance variable, you must denote it by preappending the variable name with #.
In contrast, client_1 is a local variable, which exists only within a single method or block. In your snippet, you have assigned a value to the client_1 local variable, but not to the#client_1 instance variable, which is nil until assigned.

Need to access ONE KEY/SOCKET in a hash for my my main program

Okay, so I'm building a server to play the game of war as an assignment. My server needs to get input from two different sockets, and that's where I'm stuck. I've stored my sockets as keys and my players as values in a hash, but I can't get the sockets in my main program.
I can puts the hash in my main program, but I can't just get ONE socket so that I can tell my program to read from it. Here's my classes and the test I'm running:
require 'minitest/autorun'
require 'socket'
require_relative 'WarGame_Class.rb'
require_relative 'ModifiedPlayer_Class.rb'
require_relative 'DeckClass.rb'
class WarServer
def initialize(host, port)
#socket_server = TCPServer.new(host, port)
#players = [Player.new, Player.new]
#deck = CardDeck.new
#deck.deal_cards(#players[0].cards, #players[1].cards)
game = WarGame.new
#clients = {} # keys are sockets, values are players
end
def read_client_keys
#clients.key[0] #does not work
end
def close
#socket_server.close
end
def capture_input(player) ##input client to get what they wrote
#input = #clients.keys[0].read_nonblock(1000) # arbitrary max number of bytes
end
def accept_client
#Hash here to link client to player? (or game?)
client = #socket_server.accept
#clients[client] = #players[#clients.size]
# puts "clients key 0: #{#clients.keys[0]}"
puts
# puts "clients values: #{#clients.values}"
if #clients.size == 2
start_game#####################!!!! Starts game if two clients can put client messages in start game
end
end
def start_game ##############!!!
#clients.keys[0].puts "Welcome to War. Please press enter to play your card"
#clients.keys[1].puts "Welcome to War. Please press enter to play your card"
end
end
class MockWarClient
def initialize
#socket = TCPSocket.new('localhost', 2012)
end
def output
#output
end
def capture_output #need to add (socket)? How else read from specific socket?
#output = #socket.read_nonblock(1000) # arbitrary max number of bytes
rescue
#output = "capture_output error."
end
def capture_input
end
end
class WarServerTest < MiniTest::Unit::TestCase
def setup #This would be like our INITIALIZE Function
#anything is available through out all tests (i.e., instance vars)
#war_server = WarServer.new('localhost', 2012)
end
def teardown
#war_server.close
end
def test_server_capture_output_from_client
client_1 = MockWarClient.new
#war_server.accept_client
client_2 = MockWarClient.new
#war_server.accept_client
#can output #war_server.read_client_keys, though, if I take out the argument to pass in.
#puts "Test_Server_output #client keys #{#war_server.read_client_keys(player)}" #cient_1?
puts "Test_Server_output #client keys #{#war_server.read_client_keys}"
# I NEED TO JUST BE ABLE TO GET *ONE* KEY SO I CAN READ FROM THE SOCKET
#warserver.capture_input
refute(#war_server.input.empty)
end
end
You must use #keys[0] (or #keys.first) to select the first key of the Hash.
The function #key(val) does something different, returning the key corresponding to an occurrence of val in the hash.
http://www.ruby-doc.org/core-2.0.0/Hash.html#method-i-key-3F
def read_client_keys
#clients.keys[0] # this should work
end

How do I test a function with gets.chomp in it?

I have a simple function using gets.chomp like this:
def welcome_user
puts "Welcome! What would you like to do?"
action = gets.chomp
end
I'd like to test it using ruby's built in TestCase suite like this:
class ViewTest < Test::Unit::TestCase
def test_welcome
welcome_user
end
end
The problem is, when I run that test, the gets.chomp stops the test because it needs the user to enter in something. Is there a way I can simulate user inputs using just ruby?
You could create a pipe and assign its "read end" to $stdin. Writing to the pipe's "write end" then simulates user input.
Here's an example with a little helper method with_stdin for setting up the pipe:
require 'test/unit'
class View
def read_user_input
gets.chomp
end
end
class ViewTest < Test::Unit::TestCase
def test_read_user_input
with_stdin do |user|
user.puts "user input"
assert_equal(View.new.read_user_input, "user input")
end
end
def with_stdin
stdin = $stdin # remember $stdin
$stdin, write = IO.pipe # create pipe assigning its "read end" to $stdin
yield write # pass pipe's "write end" to block
ensure
write.close # close pipe
$stdin = stdin # restore $stdin
end
end
You first separate the 2 concerns of the method:
def get_action
gets.chomp
end
def welcome_user
puts "Welcome to Jamaica and have a nice day!"
action = get_action
return "Required action was #{action}."
end
And then you test the second one separately.
require 'minitest/spec'
require 'minitest/autorun'
describe "Welcoming users" do
before do
def get_action; "test string" end
end
it "should work" do
welcome_user.must_equal "Required action was test string."
end
end
As for the first one, you can
Test it by hand and rely that it won't break (recommended approach, TDD is not a religion).
Get the subverted version of the shell in question and make it imitate the user, and compare
whether get_action indeed gets what the user types.
While this is a practical answer to your problem, I do not know how to do 2., I only know how to imitate the user behind the browser (watir-webdriver) and not behind the shell session.
You could inject the IO dependency. gets reads from STDIN, which is class IO. If you inject another IO object into your class, you can use StringIO in your tests. Something like this:
class Whatever
attr_reader :action
def initialize(input_stream, output_stream)
#input_stream = input_stream
#output_stream = output_stream
end
def welcome_user
#output_stream.puts "Welcome! What would you like to do?"
#action = get_input
end
private
def get_input
#input_stream.gets.chomp
end
end
Tests:
require 'test/unit'
require 'stringio'
require 'whatever'
class WhateverTest < Test::Unit::TestCase
def test_welcome_user
input = StringIO.new("something\n")
output = StringIO.new
whatever = Whatever.new(input, output)
whatever.welcome_user
assert_equal "Welcome! What would you like to do?\n", output.string
assert_equal "something", whatever.action
end
end
This allows your class to interact with any IO stream (TTY, file, network, etc.).
To use it on the console in production code, pass in STDIN and STDOUT:
require 'whatever'
whatever = Whatever.new STDIN, STDOUT
whatever.welcome_user
puts "Your action was #{whatever.action}"

Ruby threads: somehow class fields become nil

I'm trying to achieve a socket server on Ruby with multiply rooms, where people can send chat messages to each other, for example.
The code is pretty simple:
Main file:
require 'room'
require 'socket'
room = Room.new
### handle connections
Thread::abort_on_exception=true
server = TCPServer.open(2000) # Socket to listen on port 2000
puts 'Open socket at 2000 port'
loop { # Servers run forever
room.handle_player server.accept
}
room.rb
require 'player'
class Room
#players = []
attr_accessor :players
def initialize
end
def handle_player(connection)
puts ' New client connected!'
Thread.start(connection, self) do |client, room|
player = Player.new connection, room
#players.push player
end
end
def broadcast(message)
puts "Players is #{#players.class}" #NilClass !
end
end
player.rb
class Player
def initialize(connection, room)
#room = room
while line = connection.gets
puts 'got line ' + line
room.broadcast line
end
end
end
The problem is that when I call Room#broadcast from a player – it tells me that #players is nil, but it isn't! How that can be?
I'm not sure I'm doing everything correct (my feelings tell me that player should not have direct link to room), but it simplifies the example.
This has nothing to do with threads.
You should put that initialization #players = [] into the initialize of Room.
The way you have it, you do not initialize an instance variable of your object but an instance variable of the class Room.
You set #players as a Room's class instance variable.
Instead of this, you should put this in your initialize method:
def initialize
#players = []
end

Resources