Hello Facebook World

Posted by dave

Facebook

With the hype growing every day, I figured I was being left behind by the cool kids in not having my very own Facebook application!

Overall I like what I see there, they do not allow any custom JavaScript in FBML apps but do give you some reasonable tags to do AJAX callbacks and stuff (Note to self: I need to test how badly these degrade) and basic hide/show markup stuff which is useful.

However I just had to blog about the pain I have had with the rFacebook Gem. From what I can gather, the latest version of the gem simply doesn’t work as every time I redirected via Facebook to login (a necessary step) I got the following error:

NoMethodError (You have a nil object when you didn't expect it! You might have expected an instance of Array. The error occurred while evaluating nil.length):

/usr/local/lib/ruby/gems/1.8/gems/rfacebook-0.6.4/lib/facebook_rails_controller_extensions.rb:37:in `fbparams'
/usr/local/lib/ruby/gems/1.8/gems/rfacebook-0.6.4/lib/facebook_rails_controller_extensions.rb:60:in `fbsession'
/usr/local/lib/ruby/gems/1.8/gems/rfacebook-0.6.4/lib/facebook_rails_controller_extensions.rb:130:in `require_facebook_login'
[snip...]

Much Googling, wailing and gnashing of teeth later I found someone talking about this issue being specific to 0.6.4 of the gem (the latest) and sure enough downgrading to the 0.6.2 version of the gem and everything is sweetness and light.

Anyway, a morning of frustration out of the way and I am proud to present…

Hello Facebook World

Apart from the gem issues the development process is fairly smooth. The FBML Test Console is nice enough and is very useful to prevent hundreds of checkin/deploy cycles to test out your markup as your site (obviously) has to be deployed to be accessed through Facebook.

I am hoping to build a little app for fun (more than “Hello World”) so expect a few more posts here as that develops…

Slides from my Presentations

Posted by dave

Since the presentations section of the Shiny site is gone for now as we have a reshuffle over there and I haven’t got around to putting up a presentations section here yet, I thought I would add a quick post tonight to make the slides available from the various presentations I have given recently.

If you have any questions about any of these, please contact me.

Note: I would love to put these on SlideShare but it really seems to bork on Keynote PDF’s :(

Is the market ready for Abobe Flex?

Posted by dave

Adobe Flex

I have been thinking of doing some work with Flash and/or Flex on afeeda recently and one thing that has always held me back from considering Flex is its dependence on v9 of the flash player.

Now we have a decent amount of traffic for afeeda, I thought it would be worth taking a look at the version distribution across flash players for our visitors.

Flash version graph

That is 89% of visitors ready to run Flex content, that is pretty reasonable. I am not planning on being dependent on any Flex so I think that is the answer I need to go ahead with investigation/development with some Flex now.

The only worrying thing is why people have such a huge distribution of v9.xxx players, I thought the flash player had an auto-update in it after a certain version so what is up with that?

Silverlight will really have to hustle to get that kind of penetration.

Disclaimer: These are the stats for afeeda only, your visitors may differ to ours.

Deploy Rails using Capistrano without access to Subversion

Posted by dave

I have been doing some work for a company recently who wanted to deploy their rails application using capistrano but had a problem in that their remote servers were firewalled from their development network, which included their Subversion server.

The solution still seemed absolutely ripe for automation, it just meant we had to dive into writing our own capistrano task to basically do the following:

  • Check out latest version of code from subversion (svn export)
  • Zip exported source up into a file
  • Transfer the zipped source to the deployment server(s)
  • Unzip into the relevant releases folder on the deployment server(s)

So, I spent a few hours on Sunday morning tidying up what we did and making it a little more generic and I thought I would share it on here in case anyone else would find this useful.

The capistrano task is as follows:

desc "Export source from SVN, Upload it to an SFTP location, Download it to the release path" 
task :export_svn_deploy, :roles => :app do
  tmp_path = "/tmp" # A path to a *local* temporary directory
  sftp_server = "your.sftp.server" # The SFTP/SSH server for storage of the zipped source
  sftp_user = "sftp_user" # The username for the storage server
  sftp_path = "deploy_tmp" # The remote path on the storage server
  tmp_tgz = "exported_src.tar.gz" # Temporary file name for the zipped source

  # Export from SVN, zip and upload to the remote storage server
  system "svn export -q --force #{repository} #{tmp_path}/src/" 
  system "cd #{tmp_path}/src/ && tar czf #{tmp_path}/#{tmp_tgz} ./" 
  Net::SFTP.start(sftp_server, sftp_user) do |sftp|
    sftp.put_file("#{tmp_path}/#{tmp_tgz}", "#{sftp_path}/#{tmp_tgz}")
  end
  system "rm -rf #{tmp_path}/src && rm -f #{tmp_path}/#{tmp_tgz}" 

  # On each server, scp the file from the remote location and unzip it to the release_path
  run "mkdir #{release_path}" 
  run "scp -q #{sftp_user}@#{sftp_server}:#{sftp_path}/#{tmp_tgz} #{release_path}/#{tmp_tgz}" 
  run "cd #{release_path} && tar xzf #{tmp_tgz} && rm #{tmp_tgz}" 
end

Notes

  • The remote_path on the SFTP storage server must exist or the SFTP commands will fail.
  • It is assumed that you are using ssh keys and no pass phrase to connect to the SFTP server from both the client machine and also all of the remote servers, this way both the Net::SFTP and the scp commands will not need passwords to be sent.
  • The mkdir #{release_path} before the scp should not be needed if this is being used in a real deployment, let the regular capistrano tasks take care of that
  • I chose to use Net::SFTP on the client to upload the file because this script was originally developed for a client using Windows and Net::SFTP works whereas shelling out to do an scp, wouldn’t. The rest of this cleaned up task however would need some modification to work on Windows as it stands as the tar commands would fail.
  • This blog post needs less notes ;)

Next Steps

I am pretty sure that the best way to implement this would be as a custom source control plugin for capistrano so by doing:

set :scm, :svn_via_sftp

and have some extra options to specify SFTP server name and user account then this process could be completely seamless with a regular cap deploy.

I would be really interested to see if this is a common scenario, I suspect it will be with larger clients who do not want their SCC systems exposed to the web. Is anyone in this situation?

Twitter page views, for real?

Posted by dave

Twitter Logo

So like everyone else I have been reading the back and forth between Alex from Twitter and DHH about scaling Rails up to the level of traffic Twitter is seeing at the moment.

However, I was thinking about this tonight and the numbers just don’t sit right with me:

At that time they were fielding spikes of up to 11,000 requests per second across some 16 cores with very little caching thrown into the mix to mitigate. No wonder their site had been feeling slow.

11,000 requests per second!! That seemed very high so I did a little digging for stats from some other sites:

MySpace 15,000req/sec (40billion/month – source)
Twitter 11,000req/sec
YouTube 1,500req/sec (4 billion/month – source)
 

Even if you take the Twitter number as a peak against the others which are averaged out across a month, it just seems way too high, YouTube could spike to 7x its average traffic before it came close to the twitter peak. I know it has become very popular (and I am a fan, in case you think I am just biased against the service) but I find it hard to believe these numbers.

If these numbers are true, then it is great news for Rails on MySQL. If Twitter can just about scale to this level of traffic across only 16 cores with one back end MySQL server and “very little caching” then that pretty much blows any performance complaints about Rails out of the water.

However, even if those numbers are out by an order of magnitude or even two, I still think that this is good news for Rails scaling because it shows that it follows exactly the same scaling pattern as other web frameworks and you will eventually become database bound and that is the hard problem to solve. Even better I think it shows the power of Ruby/Rails that as a response to this argument Nic Williams has released a gem which implements multiple database server pooling in ~75 lines of Ruby and about a day or so of work. Using a master write and multiple read database cluster can scale a massive amount more than a single DB or an Active/Passive set-up.

Note: The above quote is taken from DHH’s article, I am sure that the original interview article mentioned 11,000req/sec and you can see plenty of references to 11,000req/sec in the comments it seems to have been edited now, maybe I am right to doubt the numbers.

Finding a bug in Rails with your first ever line of code!

Posted by dave

Ruby on Rails

So my cousin-in-law (if there is such a word!), Simon Wheatley is starting to learn Rails and after writing his very first line of Rails code and struggling for a while eventually IM’ed me for some help. I must admit I assumed it would be a bug in his code, I mean who gets it right the very first time? However a little investigation revealed it was indeed a bug.

What was the line of code?

scaffold :nursery

Turns out singularisation was at fault, ActiveRecord and scaffolding use pluralize and singularize all over the place and “nursery” was not being singularised correctly.

>> "nurseries".singularize
=> "nurseries"

This was being caused by the regex in the rule for correctly serialising “series” to “series” being too loose and catching any word that ended in “series”.

So I spent a little time last night creating a patch for this. I hope it gets accepted but I fear it may not because additions/changes to singularisations have been rejected in the past. I am keeping my fingers crossed though because I have kept this patch very focused and it does fix a rule which is currently too loose.

That brings up an interesting point though, at what point do the inflector functions graduate from being utility functions for use in things like DB table name pluralisation into a full featured linguistic framework? Obviously a full linguistic framework needs to cope with multiple languages and maybe even context and I would agree this is way out of the intended scope of the current inflector. However I do feel that it could be expanded a little without straying into that territory.

For example while investigating this last night I came across a few examples that I feel should go in:

>> "foot".pluralize     => "foots" 
>> "feet".singularize   => "feet" 

>> "tooth".pluralize    => "tooths" 
>> "teeth".singularize  => "teeth" 

>> "goose".pluralize    => "gooses" 
>> "geese".singularize  => "geese"

I feel these, along with “nurseries”, “miseries” and the others that my patch fixes have as much right to be in there as “oxen” -> “ox” and “octopi” -> “octopus” which already have special cases in there. If this patch gets accepted, I might try my luck with this (patch here).

Anyway stick with it Simon, I am sure you won’t find a bug on every subsequent line :)

Take a REST with Rails at NWRUG

Posted by dave

North West Ruby User Group

So I finally got around to finishing off the video from the NWRUG in February, this video is of Rob Lee (http://kodefoo.com, http://monkeyhelper.com) talking about REST in Rails.

I have to say I am still not convinced about REST as a general design principle (although I must say this is not because of Rob’s presentation!) and I just cant see many benefits over how I already write Rails applications, maybe this makes me a Rails luddite and if so, i don’t really care ;)

Now, if I was implementing a specific API I would be all over doing it with REST and I am more than happy to consume other peoples API’s with REST and ActiveResource.

We have Graham Pengelly (http://goingspare.wordpress.com/, http://www.mainlineconsultants.com/) to thank for the fabulous intro/ident that starts the video… it really makes us look way more professional than we are ;)

Finally, just a quick plug for next weeks NWRUG meeting (on the 22nd March), anyone unlucky enough to be going will have to watch me talk about Unobtrusive JavaScript but all is not lost because after that, Paul Robinson will be doing a “Mystery Ruby” session.

Anyway, on with the video from February! Enjoy.

ActiveRecord Delegation

Posted by dave

Ever wanted to be able to delegate ActiveRecord fields between objects when using belongs_to relationships?

What do I mean? Well, lets say you have a really simple normalised database for storing people and the companies they work for:

People and Companies Tables
class Person < ActiveRecord::Base
  belongs_to :company
end
class Company < ActiveRecord::Base
  has_many :people
end

Lets say you want to write the persons address out in a view, you would obviously have to traverse the relationship and might end up with code like this:

<%= @person.company.address %>

Not a massive hassle, but you also need to check if the person actually belongs to a company before you do that or you will get a NoMethodError complaining that company is nil.

<%= @person.company.address unless @person.company.nil? %>

This can pretty quickly become tiresome and clutter up those clean and tidy views you have been striving towards. Wouldnt it be nice to cut out some verbosity and be able to do this?

<%= @person.address %>

At first delegate looks like the answer but and sure enough it would allow an address method to delegate to company but it still causes problems if company is nil. Also it gets kinda ugly when you want to delegate a whole load of fields as was the case with the app I was working with today.

Enter AR-Delegation

Plug-in time! Wouldn’t it be nice to do something like this?

class Person < ActiveRecord::Base
  belongs_to :company
  has_columns :from => :company, :except => "id" 
end

and get a fully nil safe set of delegated methods out of it so you can just call the fields like they belong to the model doing the delegation.

Hopefully these other examples will be self explanatory as to what this little plug-in can do:

has_columns :from => :source, :except => "id" 
has_columns :from => :source, :only => [ "title", "name" ], :prefix => "src_" 
has_column :col_name, :from => :source, :as => :new_name
has_column :col_name, :from => :source

It does allow fields to be prefixed which might be useful if you are bringing in fields from multiple other models or would just like to prefix the fields.

Source

I have popped it up on RubyForge so to install it would be:

script/plugin install svn://rubyforge.org/var/svn/ar-delegation

I would be really interested to see if anyone else finds this useful, I will be updating it with tests and stuff as and when I have the time but I would really love to hear some feedback on it.