Thursday, April 28, 2011

At home he's a tourist: FFI for Ruby internals

FFI stands for "foreign function interface". That describes the most common use case: Creating Ruby bindings to external native libraries. But it's also possible to use FFI to spelunk Ruby's internals. Consider:

require 'ffi'
module RubyInternals
extend FFI::Library
ffi_lib(FFI::CURRENT_PROCESS)
end
view raw ffi1.rb hosted with ❤ by GitHub

Foreign? Far from it. You've now got access to all of C-ruby's innards. For example, if you're using Ruby 1.9:

module RubyInternals
attach_variable :ptr_vm_core, :rb_mRubyVMFrozenCore, :pointer
end
view raw ffi2.rb hosted with ❤ by GitHub

With this you've got a handle on the otherwise inaccessible RubyVM core object. As a pointer, it's not all that useful. But with a little more mischief...

module RubyInternals
attach_function :const_def_pointer, :rb_define_const,
[:pointer, :string, :pointer], :void
attach_variable :ptr_object_class, :rb_cObject, :pointer
const_def_pointer(ptr_object_class, "RubyCore", ptr_vm_core)
end
view raw ffi3.rb hosted with ❤ by GitHub

... now you can treat the RubyVM core like any other object. One interesting (if unwieldly-named) method that it offers is "core#define_singleton_method". It accepts an InstructionSequence -- compiled YARV bytecode -- and uses it to set up a singleton method in the object where it is invoked. Let's make it a little easier to use:

module RubyInternals
def define_iseq_singleton_method(mod,sel,iseq)
mod.module_eval do
RubyCore.send("core#define_singleton_method",mod,sel,iseq)
end
end
end
view raw ffi4.rb hosted with ❤ by GitHub

Now it is easy to create methods using bytecode sequences:

module Greeter
extend RubyInternals
iseq = RubyVM::InstructionSequence.compile <<-RUBY
puts "Hello, world."
RUBY
define_iseq_singleton_method(self, :greet, iseq)
end
Greeter.greet
view raw ffi5.rb hosted with ❤ by GitHub

Saturday, April 2, 2011

Good Reasons to Use RSpec

It's been recently suggested that RSpec owes its popularity to fashion, and that people don't understand the alternatives. My experience is different.

I've been using RSpec to test Ruby code for about five years. Before RSpec, I used Ruby's Test::Unit, as well as a number of other xUnit-style frameworks for Perl and Java. I was never happy with the earlier frameworks, but I was convinced that unit testing was worthwhile so I made do with what was available.

I switched to RSpec because in very little time I found it more helpful than other frameworks I had used. The reason is this: RSpec helps me to alternate between writing code and writing tests without having to switch contexts.

By using the description of behavior as the model of testing, RSpec answers the question of what to test before you have to ask. It's more than syntactic sugar. It's conceptual sugar. I find that not having to think so much about the testing framework, I'm freed up to think about the tests themselves, so I write better tests and more of them.

RSpec has "no discernible benefit" over other frameworks? It depends on who is doing the discerning. I find plenty of good reasons to use RSpec, and I have a lot of company.