On Fri, Dec 11, 2009 at 9:03 AM, Matt W. [email protected] wrote:
REST server can’t start making requests back to a client (unless that client
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
know.
Already you can:
sudo gem install
(in …/spec_helper.rb: require ‘’)
describe MyObject
before(:each)
@obj = MyObject.new
end
it “should be valid”
@obj.should be_valid # would PUT/ object/msg/valid?
end
it “should have x == [‘avg’, 10.0]” do
@obj.x.should == [‘avg’, 10.0]
end
end
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 –
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.
Ed
[1]http://segment7.net/projects/ruby/drb/introduction.html
[2]FitNesse.UserGuide.SliM
Ed Howland
http://twitter.com/ed_howland