Ruby Pipe and C Extensions - ruby

I have some ruby code (1.9) like
#rd,#wd = IO.pipe
def callback()
puts #wd.class
# do stuff
end
pid = fork do
#rd.close
register_callback(:callback)
end
#wd.close
# do some stuff in parent process
register_callback is a C extension that makes a blocking system call, and upon certain conditions will call the ruby function associated with the symbol passed in.
However, #wd is of type NilClass according to the message I get when I run this program and it tries to access #wd in the callback function, which doesn't make any sense to me. Any help is appreciated.

after you call the register_callback method. The rest of the code continues to execute (as you do the register_callback method call inside fork). So #wd.close runs, before your callback is made. Hence when the callback() method is finally called. #wd is nil (which is the result of #wd.close).

Related

RSpec: Expecting a method to be called causes that method to not actually be called

I have some code that could be represented in very simple terms as:
def method_a(key)
hash = method b(key)
hash.delete(key)
end
def method_b(key)
return { key => 1 }
end
and then an rspec test like
it 'calls method_b'
expect(someClass).to receive(:method_b).with(key)
method_a(key)
end
However I then get an error in the second line of method_a because it's trying to call delete on a nil object. When I debug, I can see that the logic inside method_b is never actually being invoked. It's not failing somewhere in method_b, it's literally not calling it at all. If I get rid of the expect statement in the test, this error goes away. It seems like the expect statement is causing it to just skip over the actual call to method_b, leaving me with a nil value instead of the hash I'm expecting.
Is there a way I can stop it from skipping over method_b, or at least terminate execution once the expect statement is successful, so I don't run into the error on the next line?
When you set a message expectation, it overrides the original code, unless you explicitly tell RSpec not to:
expect(someClass).to receive(:method_b).with(key).and_call_original

Is there any way to set an RSpec expectation to receive a method call as an argument?

Ie
expect(my_class).to receive(:method_b).with(:calling_method_b_here)
(it's not the return value I want, but specifically to test that method_b is called in this way)
?
This should work for you if I understand correctly:
it "should call #method_b when I call #method_that_calls_b" do
expect(my_class).to receive(:method_b).with(anything) #anything means I don't care explictly what is sent in just that it was called
my_class.method_that_calls_b
end
This will test that method_that_calls_b actually does call method_b with any arguments. If you know what arguments it is supposed to receive then replace anything with what you expect it to be called with. e.g.
it "should call #method_b with 'hello'" do
expect(my_class).to receive(:method_b).with('hello')
my_class.method_b('goodbye') #this will fail
my_class.method_b('hello') #this will pass without the above line
end

Is there a way to get a stack trace from rspec when a method is unexpectedly called more times than specified?

I setup a mock object and told it to expect a check for nil and to return false:
status = double('status')
status.should_receive(:nil?).and_return(false)
I only expect the call to nil? to occur once, but I got an error in my rspec test, saying that status received nil? twice.
Is there a way to get rspec to show where/how each call occurred?
adding the '--backtrace' option did not work.
Try something like this:
status.should_receive(:nil?).twice { puts caller; false }
This tells rspec to allow two invocations and call the associated block each time. Thecaller method generates a full backtrace which you should be able to analyze onstdout. We also returnfalse to stay on the code-path we're testing.
If the two backtraces are hard to distinguish and you're only interested in the second (unexpected) invocation, then set up two successive expectations:
status.should_receive(:nil?).and_return(false)
status.should_receive(:nil?) { puts caller; false }
Here the double will return false on the first invocation and call the block on the second.
Reference for setting responses on expectations:
https://github.com/rspec/rspec-mocks#setting-responses

Replacement in 1.9 for Socket's ready? method

For some time I have been using an old Ruby distribution (I think it was 1.8.6) on which I coded with the socket library. The old library had a method called ready?, which checked whether there was still data to be received without blocking. What would be the best replacement for that in 1.9?
The reason why I require it is as I have a program structured like this:
def handle_socket_messages
while true
break unless messages_to_send
sent_messages
end
while #s and #s.ready?
#read messages
readStr = #s.recv(0x1024)
...
end
end
(I then have another loop which keeps executing the handle_socket_messages method and then uses a sleep, so that the loop doesn't spin too fast, along with some other methods.
As you can see, I need to check whether I will receive data using #s.ready? (#s is a socket), otherwise the loops hang at readStr = #s.recv(0x1024), where the socket keeps wanting to receive data which the server doesn't send (It's waiting for data when it shouldn't).
What would be the best replacement for this method?
The solution was:
class Socket
def ready
not IO.select([self], nil, nil, 0) == nil
end
end
I've been using the ready? method successfully in Ruby 2.2.2 by requiring io/wait. There is a bit more info in this SO answer: https://stackoverflow.com/a/3983850/2464

IronRuby: Cannot call method on a COM Object with one or more arguments

When I try and call any method on a COM Object that takes one or more arguments, I get the following error on the last argument:
Could not convert argument 0 for call to Open. (ArgumentError)
Everything works fine when calling a method that takes no arguments, or getting/setting a property. Here is the code that gives me the error above:
def new_com_object(prog_id)
System::Activator.CreateInstance(System::Type.GetTypeFromProgID(prog_id))
end
xls = new_com_object('Excel.Application')
xls.Visible = true
xls.Workbooks.Open('c:\\Book1.xls')
Looks like I need to use String#to_clr_string when calling methods. Right now IronRuby.net documentation is borked, so it's hard to figure that out.

Resources