Shoulda You abandon Test::Unit?

I was not very keen on RSpec. Well to be honest I had some problems with writing tests at all. Fortunately old and bad times are gone. I mean writing test is now much more natural and has become second nature. That way I don’t need to force myself to write them. You know, attacking new ideas is much more sexier than boring (most of time) tests ;)

As freelancer I do often jump into some established project, with long history and different tools used. This has advantage I can watch other developers how theirs code looks, and learn from it how things can be done (there are always exceptions).

In one of such projects I’ve found Shoulda plugin. This plugin adds to Test::Unit bunch of nifty helpers, making live much easier.

First, stop writing those damn underscores and make tests much more readable:

context "Facebook app" do
    context "when removed " do
      setup do
         #some init code
      end
      
      should "accept only HTTP post" do
          #request and assertions
      end
      
      should "clear FB bindings" do
         #request and assertions
      end
    end
end

context allow to group tests and provide some common setup phase. Naming tests is much more easier and allow DRY, making test titles much more compact. However when running tests it will output names of tests (when some errors/failures) perpending them with all context names.

"Facebook app when removed should accept only HTTP post"
"Facebook app when removed should clear FB bindings"

More readable? You bet! But that is not all! Helpers to make even more less typing, this time with assertions. Just check docs for this plugin (note there is also non-rails version, Shoulda gem, which provides cut-down features, but is not coupled with Rails, so can be used in other projects using Test::Unit).

For me, RSpec is still not test framework of choice. Maybe the other day I will drink RSpec’s Kool-Aid, but for now I prefer Test::Unit + Shoulda plugin.

If there are some RSpec zealots, what is reason for You to choose it over Test::Unit?

Quick fix for strange Ruby errors

Well, maybe not fix but tip how to find some clues what next.

Quick Fix
on CC license

When You encounter strange errors like terminate called after throwing an instance of 'int' this is probably something with roots down into Ruby executable or some binary library (and ruby binary dump – ruby.core file in current directory). AFAIK this is error message from GCC, that means something wrong happened on low level – low from Ruby perspective.

It is not very informative message, so to get know what is really happening usually is enough to enable debug:

$DEBUG=true

In You Ruby application. With debugging enabled, all exceptions (event those catched) are dumped to STDERR, so usually last one will give some hint, at least in which area is problem.

For me it was:

Exception `RuntimeError' at test/functional/receiver_controller_test.rb:9 - FreeImage exception for type JPEG: JPEG parameter struct mismatch: library thinks size is 464, caller expects 428
DIRECTORY/vendor/plugins/attachment_fu/lib/technoweenie/attachment_fu/processors/image_science_processor.rb:14: [BUG] Segmentation fault
ruby 1.8.6 (2007-09-24) [i386-freebsd6]

No, I don’t know yet what is really happening, I hope that ongoing FreeImage upgrade will do the trick.

Uploading photos to Facebook with RFacebook

I’m quite busy recently, and as result I write posts on my blogs with smaller frequency as I usually did… But there are few interesting topics I want to write about. First – RFacebook and photos upload.

I’m using RFacebook gem to handle calls to Facebook API. But recently I found out, that probably there is no way to upload photos to Facebook with RFacebook. The reason is that API call is special case and as far as I know, there is no code for that in RFacebook.

So we need to extend RFacebook by ourself. Problem with facebook.photos.upload is that need to be multi-part encoded post, that means MIME encoding. Since this is one way post, we don’t need any specialized library to handle MIME thus we will prepare own code. To make HTTP request Net::HTTP Ruby library is obvious choice, however it does not support multiparts.

First – Google, and I found this post describing how to make multiparts with Net::HTTP.

Next we need to extend RFacebook::FacebookWebSession providing photos_upload method. RFacebook uses method_missing to handle calls to API, providing own method photos_upload will skip default processing flow:

module RFacebook
  class FacebookWebSession
    
    def photos_upload(params = {})
      params = (params || {}).dup
      params[:method] = "facebook.photos.upload"
      params[:api_key] = FACEBOOK["key"]
      params[:v] = API_VERSION
      params[:session_key] = session_key
      params[:call_id] = Time.now.to_f.to_s
      file = params.delete :file
      params[:sig] = signature(params)
      
      boundary = 'some_long_and_random_string_which_wont_show_in_data_stream'
      params_as_arr = []
      params.each {|k,v| params_as_arr << text_to_multipart(k,v)}
      params_as_arr << file_to_multipart('filename', 'somefile', 'image/jpg', file)
      qry = params_as_arr.collect {|p| 
        '--' + boundary + "\r\n" + p
       }.join('') + "--" + boundary + "--\r\n"
      handle_xml_response(
        Net::HTTP.start(API_HOST).post2(
          API_PATH_REST,
          qry,
          "Content-type" => "multipart/form-data; boundary=" + boundary).response.body.to_s
      )
    end
  end
  
  private
  
    def text_to_multipart(key,value)
      return "Content-Disposition: form-data; name=\"#{key.to_s}\"\r\n" + 
        "\r\n" + 
        "#{value}\r\n"
    end

    def file_to_multipart(key,filename,mime_type,content)
      return "Content-Disposition: form-data; name=\"#{key.to_s}\"; filename=\"#{CGI::escape(filename)}\"\r\n" +
        "Content-Transfer-Encoding: binary\r\n" +
        "Content-Type: #{mime_type}\r\n" + 
        "\r\n" + 
        "#{content}\r\n"
    end

end

Now assuming that fbsession is valid Facebook session we use it providing :file with raw data and :caption for description to Facebook:

fbsession.photos_upload :file => File.read("SOME_PATH_TO_FILE.jpg"),
  :caption => 'some caption'

As You can see this is very ‘rough’ solution, just to give You idea where to head next (error checking, handle other MIME types than image/jpeg, etc).

Please give me You thoughts on that code – if there will be some response I will feel obligated to prepare some more quality code to submit it as patch to RFacebook :)))