So I have taken the plunge and spent quite a bit of time playing with RoR (Ruby on Rails), in fact I decided to port my current projects serverside over to ruby to make the Ajax work easier and for the Experience. Along the way I had to write about 15 webservices to use for client server sync. Now if you don't know Ruby on Rails then the rest of this won't make much since so go of and learn a little rails and specifically ActionWebServices, then come back and you will think this is pretty cool :-) For the rest of you read on :-).

For security reasons Ruby on Rails doesn't support passing ActiveRecord objects to and from the client (not by default anyways). The fear is that someone will blindly pass in bad data, and the developer will just save it (without validation) therefore causing all kinds of issues. Now you can get around that by setting a parameter at the top of your ActionWebService::Base. But (at least for me) all that did was trade one problem (security) for another (exceptions being thrown because I don't use Integer ID's).  I thought about it a bit and I realized I would need ActiveWebService::Struct proxies for all of my objects. I looked at my 20 tables and realized that while that would be a pain , it wouldn't be "so" bad.. But hand writing a Struct for each of my objects, then one by one writing a "to" and "from" converter just didn't sound fun. Not to mention that it wasn' a long term solution. I mean what if the next project has 300 tables?

Deep in thought considering the problem I decided what I really needed to do was harness what it means to be a dynamic language. I wanted a module or base class, that would take a ActionWebService::Struct and a ActiveRecord, and dynamically do the translation for me.. Put a ActiveRecord in and get a specific ActionWebService::Struct out, put a Stuct in and get the "right" ActiveRecord back. To that end I wrote WsBase  
module WsBase 
@basedOnType = Class 
def map_from_class (type) 
   if (!type.base_class == (ActiveRecord::Base)) 
       raise(ActionWebServiceError, "ActiveRecord model classes is expected") 
   end 
   @basedOnType = type 
   type.columns.each do |prop| 
      member prop.name, prop.type 
   end 
end 
def fill_base(me) 
   mydata = me.dup 
   #raise @returnval.to_yaml 
   hashVal = Hash.new 
   @basedOnType.columns.each do |attrib| 
      hashVal[attrib.name] = mydata[attrib.name] 
   end 
   returnval = @basedOnType.find(:first, :conditions => ["id = ?", mydata["id"]]) 
   if (returnval == nil) 
      returnval = @basedOnType.new(hashVal) 
      returnval.id = mydata["id"] 
   else 
      hashVal.each do |field, val| 
       eval "returnval." + field + " = val" 
     end 
   end 
   return returnval 
end 
def fill_me(myRec,me) 
   myval = me.dup 
    myRec.attributes.each do |attrib,val| 
     eval "myval." + attrib + " = val" 
   end 
   myval.id = myRec.id 
   return myval 
end 
end 

The idea behind this module is that I create a 3 line proxy class that inherits from ActionWebService::Struct. I then extend it with this module and then add the line map_from_class ActiveRecordClassName.. Thats it. If I want to populate the Struct I pass in my ActiveRecord, If I want to get back my ActiveRecord, I pass in my populated Struct. The class files look something like this

class WsContact < ActionWebService::Struct 
   extend WsBase 
   map_from_class Contact 
end 

And a "read method" in my Webservice controller would look like this

def get_contact(contactId) 
   wcontact = WsContact.new 
   arcontact = Contact.find(:first, :conditions => ["id = ?",contactId]) 
   if (arcontact != nil) 
      returnval = WsContact.fill_me(arcontact, wcontact) 
     return returnval 
   end 
   return nil 
end 

and finally my write method would only need to be this

def save_contact(contact) 
   record = WsContact.fill_base(record) 
   if (record.save) 
      return "Good" 
    end 
   return "Bad" 
end

Now isn't that elegent. And best of all maintance for my model doesn't mean I have to update these classes, they automaticly change because they take on the properties of their ActiveRecord :-).. I even made the code cleaner by making the write function take a param of type, so instead of having 15 write methods I have 1 write method that gets called 15 diffrent ways.. and the write method doesn't change, again taking advantage of rubys dynamic nature, you can call static methods of a parameter :-P..

Anyways, this all qualified as TOO cool not to share in my book.. If anyone has any updates or suggestions on how to write this dynamic code I am all ears, I am a Ruby Neophite so I am sure my fill_base and fill_me functions could be optimized or follow do a better job of following Ruby best practices.