[rspec-users] RSpec wire protocol Was:Re: describe "RSpec's documentation" do

Ed Howland ed.howland at gmail.com
Fri Dec 11 17:29:21 EST 2009

On Fri, Dec 11, 2009 at 9:03 AM, Matt Wynne <matt at mattwynne.net> wrote:
> Yes we did, briefly - probably before the discussion went online on the
> lighthouse ticket. I think there were a couple of reasons why not REST:
> (1) simplicity - we wanted the wire server to be as low-tech as possible so
> it would be easy and lightweight to implement on any platform (think
> Cuke4EmbeddedDevice). Everyone's got a TCP stack but not everyone's got a
> simple web server like sinatra - I'm not even sure there's an especially
> simple one for .NET
> (2) flexibility - as the protocol has emerged, it's become clear that the
> relationship is pretty much client / server, but when we first started we
> weren't sure how much chatter would need to go back & forth. Obviously a
> REST server can't start making requests back to a client (unless that client
> also starts offering a web server), but if you've got two peers talking over
> a socket you can have two-way comms.
>> If anyone has any thoughts on this, I'd be interested.
> If you're using your protocol to effectively get remote control over
> objects, have you considered writing something to support DRB[1] on the
> server side? Otherwise you could also look at SLIM[2], which is the protocol
> Fitnesse uses in much the same way as Cucumber uses the wire protocol. We
> did consider using slim in Cucumber - it's widely supported with server
> implementations on many platforms, but since SLIM has much more flexibility
> that we needed, we decided to keep things simple.

Thanks, Matt.
[2] has some really good ideas and some that I've already thought of.
I kinda like the multiple instructions per transaction. I don't know
if I need that yet. I considered [1] but wasn't sure how easy it would
be to write a server for DRb in another language.

All I'm doing is representing the Ruby object protocol (object.send
:message, args) in REST.

JSON suffices for just about any object serialization and you can
customize it for your class. I currently don't need to layer any
command structure on top of JSON. Only the message's arguments payload
and return values get serialized/deserialized. JSON is built-in or
supported via a standard lib or module in all of my targeted
languages. .Net/PHP/Perl and Python.

REST works to supply the basics of the command layer.

Resources are classes, objects and bare functions.

E.g. To create a new object from a class.
obj=MyObject.new => POST: http://server/class/MyObject
obj-MyObject.new(1,2) POST: http://server/class/MyObject  'args=[1,2]'

These return an object id along with json_cerate args.

To call a method on said object:

obj.f(:a=>10) => PUT : http://server/object/1/msg/f 'args={"a":1,"b":2}'
.. and returning results as above.

To call a class level method, return a single object or a list of them, use GET.

obj=MyClass.get(1) => GET http://server/class/MyClass/msg/get?args=1
 -- raw method --
obj =JSON.parse  GET http://server/object/1

List (via a query):
array = MyClass.query('where active = ?', true) =>
  GET http://server/class/MyClass/msg/query?'args=["WHERE active =
?",true]'  # with URL encoding

-- raw list of all objects of a class --
GET http://server/class/MyClass/*

-- raw list of all objects --
GET http://server/object/*

... and so on

All returned objects on the Ruby side are just handles that only hold
the id. They don't contain any marshaled data from the target language
side. The reasoning is that all variable access must be done via
accessor methods since that is the case in Ruby. However, given a REST
call like this in a target server:
PUT http://server/object/2/msg/a
the implementation is free to decide that that is a variable access,
and assuming it is public, return its value. Setting the contents of a
variable is just the same, but with Ruby conventions that the target
language has to obey:

obj.a=1  => PUT http://server/object/3/a=  'args=[1]'

The motivation behind this is to use both Cuke and RSpec to test
legacy (or BDD develop new) projects w/o having to install and learn
another BDD/xUnit framework.

I think this suffices for all RSpec use cases, but it may be
incomplete. If you are anyone think of anything I have missed, let me

Already you can:

sudo gem install <sexy name goes here>
(in ../spec_helper.rb: require '<sexy name>')

describe MyObject
    @obj = MyObject.new
it "should be valid"
   @obj.should be_valid  #  would PUT/ object/<id>msg/valid?
it "should have x == ['avg', 10.0]" do
  @obj.x.should == ['avg', 10.0]

Note there is no need to declare a class on the RSpec side, or to
include a module or inherit from some other class. In PHP to get the
specs to pass:
--- my_object.php   --  <a naming convention>
class MyObject {
  var x;
  function MyObject() {x=array("avg", 10.0);}
  function isValid() { return true; } // another convention

You get in to a R-G-R rhythm just as quickly as with native Ruby code.

The server code for this would need to reflect on MyObject to
determine variable access and discover methods that start with 'isXXX'

Currently this is very primitive. There is no support for passing
objects to other objects. I don't know about class level methods in
all target languages yet. I thought the user could provide class
factory classes for those if they don't exist, like in PHP4.

It requires an object store on the server side, each implementation is
free to decide on that. In Sinatra I it is just a class variable for
now, the object ids are the indexes.

>> Should we :
>> o continue to use web proxies for objects and method calls?
>> o -or- switch to running the matchers (should ==, etc.) over the wire
>> protocol, more like cucumber?
> I'm not really clear what the goal of your app is, but to go more into line
> with what cucumber's doing, I think what you'd do would be to allow RSpec to
> remotely invoke entire examples ("it blocks") remotely. You could then write
> those examples entirely in the native language.

I mentioned the goal above. Writing those examples in the target
language is not what I had in mind. I only want  code in the actual
SUT to be written in that SUT's language. You'd have to port ALL of
RSpec's matchers there or use another framework's assertion lib. It'd
be interesting but __way__ beyond the scope of what I am doing. Also,
I am not sure what an it block would look like it it were sent over
the wire. I imagine it queries the SUT wire server for any matching
"it"s and any left over are just reported as unimplemented.

BTW, does Cuke's wire protocol allow for a mixture of Ruby side and
SUT side step definitions?

>> BTW, anyone working on a Cuke4PHP?
> That's the first time I've heard anyone even suggest it :) I'd be happy to
> collaborate with someone if they wanted some help with that.

I seriously need this. And am willing to help. Currently we are using
Cuke+Webrat+Mechanize to test only the web facing surface of the app.
But to get things like 'Given I have a user named "Ted" with password
"Secret"'  to work and insert into the legacy DB is tricky. If that
step could execute on the PHP side, life would be sweet!

AFAIK, there seems to be simple REST servers in at least .Net and PHP.
Fitzgerald for PHP and just using IIS and writing handlers in .Net.


> [1]http://segment7.net/projects/ruby/drb/introduction.html
> [2]http://fitnesse.org/FitNesse.UserGuide.SliM

Ed Howland

More information about the rspec-users mailing list