The year 2009 is said to be the year of the API. Not the dusty CORBA/SOAP/RMI kind but the new kid on the block, REST.
Many good people have written about what REST is. I will not pain you with yet an other explanation. In this post I will tell you how you can use a RESTful service as a programmer.
As a programmer you want to build reliant applications. You also know that users are strange beings. They do the most unfathomable things to your carefully crafted code. You cast exceptions and write exception handlers. RESTful services should provide you, the programmer, with just that information. When manipulating or creating a resource the remote service should tell you how that went in a response code. The bare minimum of response codes a service should sent back are:
- 200, which tells you, the sender, the request was accepted and processed.
- 404, familiar from the web, the resource could not be found.
- 500, something went wrong on the receiving end.
I believe anything less is just broken and implementing only these three is bad practice in my honest opinion. So for the sake of it let’s pretend the webservice is well behaving and a bit more complete.
Context is king
It is important the remember that you are in a conversation with the service. Request and response are intimately linked. “43” isn’t much of an answer if you don’t know the question. So when a response code is 403 the service understood the request but refused to fulfill it. Why it refused to do so should be in the body of the response but it could be any number of reasons. What’s important is that the sender of the request understands that the request is not fulfilled and should act accordingly. How is should act is of no concert to the RESTful service.
When implementing a client for a RESTful service always be aware that the server could return response code XYZ. If you want special handling for a particular situation, for example a 404, match that response code. Treat 4xx and 5xx response codes as an exception and code handlers for that.
An exception uncaught in your code will break your application. The same goes for uncaught 4xx and 5xx response codes.
Rails 2.3 ships with an upgraded memcache client. Great stuff! Memcache is at the moment my favourite library. Certainly in combination with the awesome Starling library. Easy queuing!
When I upgraded the stack no longer worked. Starling reported a can't dup NilClass error. I quickly found the offending line. Starling inherits from the main MemCache class. This class just changed and broke the Starling subclass. I believe the breakage was warranted. Starling directly accessed the, now removed, buckets instance variable. This clearly breaks encapsulation and is in my humble opinion bad style. Luckily for me someone on Github already worked out a solution. This is just a hasty patch and I'm eagerly awaiting the new Starling release.
My app relies heavily on JSON. The JSON parser in ActiveSupport was enhanced in the new release to parse and convert strings which represent dates to actual Date objects. The method responsible for this is rather long. In my case some of my parsers suddenly died with an Invalid JSON string error. Long story short the code which extracts the date also remembers where the delimiting double quotes are for removal later. However this does not correctly work and it ends up removing other characters entirely. This obviously does not sit well with the downstream parser as this now get an invalid JSON string. I created a (ticket)[http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/2273] describing this behaviour further.
I worked around the problem by removing certain character sequences in the input stream.
If you just like me upgraded your application from Rails 2.2 to 2.3 you might have run into some issues. I ran into three, that's why this three part installment of Upgrading Rails.
My application polls some status message. In order to ensure some level of security this polling is based on the session id. This session id was read from the cookie using javascript. Suddenly when I upgraded to 2.3 this no longer worked. The javascript consistently returned nothing. In Safari things worked just fine! Weird. After digging thought commits and googled the hell out of it I found the answer. Session cookies are set with an http_only flag. This means that javascript can no longer be use to access the cookie. This makes Cross Site Scripting a lot harder as attackers lose one way of obtaining the cookie with the session id. Good move Rails team!
i removed the responsible javascript code and worked around it by creating a salt. Works like a charm again!
This developer section is powered by adva (lowercase!) and Passenger! Two pieces of superb software. In this short post I'll take you through the steps needed to setup adva in combination with Passenger.
Rails 2.3 got released last week and we'll be using that for it's support for engines and templates. Just create a new rails app with the adva template as described on the adva github page. From there on the situation got a bit less clear. Sure you can run script/server and have the stuff run on localhost but that is hardly what you want. We want a Git repo which we can deploy with Capistrano!
To this end I initialized a new git repository. To my surprise this didn't include the vendor/adva directory. That is because installing adva via the template based solution leaves a git repo in vendor/adva. Which makes kind of sense. You want to be able to update. Maybe git submodules would be a better fit but there are not known for their clarity.
What I ended up doing, and this is what the guys in the IRC channel suggested, was to remove the vendor/adva directory altogether and use the same approach Rails/Capistrano use as to link the log/system/etc directories. So I cloned the adva git repository from github and switched to the 0.1.2 branch. Then I modified the deploy.rb to create the symlink after updating the code:
task :finalize_update, :except => { :no_release => true } do
run "chmod -R g+w #{latest_release}" if fetch(:group_writable, true)
# mkdir -p is making sure that the directories are there for some SCM's that don't
# save empty folders
run <<-CMD
rm -rf #{latest_release}/log #{latest_release}/public/system #{latest_release}/tmp/pids &&
mkdir -p #{latest_release}/public &&
mkdir -p #{latest_release}/tmp &&
ln -s #{shared_path}/log #{latest_release}/log &&
ln -s #{shared_path}/system #{latest_release}/public/system &&
ln -s #{shared_path}/pids #{latest_release}/tmp/pids &&
ln -s #{shared_path}/adva #{latest_release}/vendor/adva
CMD
if fetch(:normalize_asset_timestamps, true)
stamp = Time.now.utc.strftime("%Y%m%d%H%M.%S")
asset_paths = %w(images stylesheets javascripts).map { |p| "#{latest_release}/public/#{p}" }.join(" ")
run "find #{asset_paths} -exec touch -t #{stamp} {} ';'; true", :env => { "TZ" => "UTC" }
end
end
Basically all I did was copy paste the original task and added the last symlink creation. I'm sure there is a far more elegant way of doing this. Please do let me know!
There was one final hiccup. When creating the initial Rails project the template linked all the Rails engines in the vendor/plugins directory. These symlinks are not valid on an other machine. I removed them and symlinked them by hand using the relative paths.
That's it! Enjoy the new adva install!