Thursday, April 28, 2011

Rails file uploads: Limiting a user's uploads by space available

Let's say that you want to allow people to upload files to your server. Let's also say that we want to make sure any particular user does not hog up all the space available. Let's also say you want to give each user a variable limit as to how much they can upload (i.e. for tiered services). How would you go about doing that?

Well, from a pseudo code perspective you would do the following.

1) Create a folder for each user based on the user id
2) Make sure all the user's uploads go to that folder
3) Before you do an upload, check against the user's settings to see how much space s/he is allowed
4) Check their folder to see how much space has been used
5) If they have no space left, message the user

So let's see how we do this in practice...

I guess we really want to see how much space is used, so let's start there on the User. In this example the user has a UUID as well as a space_allowed attribute which is set to a default value (and which an admin can change).

def space_used
space = 0
directory = "#{Rails.root}/uploads/#{self.user.uuid}"

Find.find(directory) { |f|
space += File.size(f);
return space

So space_used first sets the directory based on the user's uuid. It then checks to see if that directory exists, and if it does, it will loop through each file and total up the space variable (unfortunately, the Directory object does not have a size variable) to see how much space has been used.

def space_available?
if self.space_used >= self.space_allowed
return false
return true

space_available? checks the space used and compares it to the space_allowed to the user. So in the controller you just need to call space_available? on the user and handle it appropriately


user = User.find(params[:id])
if user.space_available?
... handle upload ...
... handle error ...

Reading AJAX XHR File Uploads in Sinatra

So in my last post I talked about Drag and Drop file uploading with qq.FileUploader (

Anyways, I discovered that qq.FileUploader uses AJAX/XHR to post the file uploads. What I also discovered is that these file uploads need to be handled in a separate manner from a regular file upload form post.

A normal form post (when File upload XHR requests are not available on the client like in Internet Explorer) passes in the following parameters

params: {"qqfile"=>{:type=>"image/png", :head=>"Content-Disposition: form-data; name=\"qqfile\"; filename=\"bb2.png\"\r\nContent-Type: image/png\r\n", :tempfile=>#, :name=>"qqfile", :filename=>"bb2.png"}, "upload_type"=>"rec", "id"=>"24db8cab-285a-abcb-cb47-4daceee977ca"}

Sinatra then reads the :tempfile and :filename parameters to write the file to the server

name = params[:qqfile][:filename]

# create the file path
path = File.join(directory, name)
# write the file, "wb") { |f| f.write(params[:qqfile][:tempfile].read) }

However, if you use a browser that supports XHR uploads, the parameters look a little different

params: {"qqfile"=>"device1.jpg", "upload_type"=>"invoice", "id"=>"24db8cab-285a-abcb-cb47-4daceee977ca"}

So this had me stumped. There was an article I found on the internets ( which talked extensively on how to do the JavaScript side but then at the bottom conveniently left out how to handle the server side (saying it was easy to work out). Well, it wasn't! So that's why I am writing this post

In any case, I won't keep you in suspense any longer...

name = env['HTTP_X_FILENAME']

string_io = request.body # will return a StringIO

data_bytes = # read the stream as bytes

# create the file path
path = File.join(directory, name)

# Write it to disk..., 'w') {|f| f.write(data_bytes) }

The final part is that qq.FileUploader does not give you the option to specify which action to post to depending on the type of upload (XHR vs normal) so I had to put in a fork in the server code to figure out how to handle the request. I am basically checking to see if qqfile is of type String, in which case I handle it as an XHR upload, otherwise I handle it as a normal file upload.

# if qqfile is a string, we are using XHR upload, else use regular upload
if params[:qqfile].class == String
name = params[:qqfile]

string_io = request.body # will return a StringIO

data_bytes = # read the stream as bytes

# create the file path
path = File.join(directory, name)

# Write it to disk..., 'w') {|f| f.write(data_bytes) }
else #regular file upload
name = params[:qqfile][:filename]

# create the file path
path = File.join(directory, name)
# write the file, "wb") { |f| f.write(params[:qqfile][:tempfile].read) }

I haven't tried it in Rails yet, but I am sure it will be similar. I hope this helps...

Wednesday, April 27, 2011

Ajax File Uploading

You know that cool Drag and Drop file uploading they have in Gmail? You know, the one where you can drag a file from a folder onto a webpage and upload it?

Have you looked at the code? Looks complicated huh?

Well, thankfully, for any cool bit of JavaScript functionality out there, someone has created a plug in (most likely in jQuery, but not always...). is (as far as I can tell) a library agnostic JavaScript plug in which factors out all the hard work.

All you have to do is put in a "dummy" element on to which to hook (never end a sentence in a preposition) and then put in a small snippet of code to turn it into a file upload element

<div id="file-uploader">
<p>Please enable JavaScript to use file uploader.</p>

var uploader = new qq.FileUploader({
// pass the dom node (ex. $(selector)[0] for jQuery users)
element: document.getElementById('file-uploader'),
// path to server-side upload script
action: '/server/upload'

Obviously you need to include their library as well...

It has some common backend code to handle uploads as well (php, perl, cgi, java) but no Ruby. Come on guys! Get with it already! Hopefully they will read this and provide...

Tuesday, April 26, 2011


I had a quick look at Node.js. Looks very interesting (kind of like a JavaScript version of Sinatra). Now all I need to do is find out if they have any frameworks...

Wednesday, April 20, 2011

Show Off IO

Just found a great new service called Show Off IO

This is a godsend for Ruby contractors. Basically you port forward the web server on your laptop to showoff and they give you a URL which you can send to a client which allows him to see your website (for a limited time for the free version).

Previous to this, you would have to wait until you had staged the site to show it to your clients.

Good job guys!

RhoMobile - Ruby Development for Mobile

RhoMobile ( offers an open source framework which allows Ruby developers to write mobile apps using web technologies.

I have been using it for about a year now. You can find out the details of it on the website linked above, but I just wanted to go over some of the issues I have found and cover some of the pluses in minuses from a personal perspective to help you decide whether or not this is a technology you will want to exploit on your next project.

So what is it?

At a basic level, a Rhodes ( app comprises of a small Ruby web server/framework and an embedded browser. Server is a little bit of a misnomer because it lives on the client, but essentially it's like a small Rails server which is only used by one client (though in theory you can connect to it from another client if you so choose).

Because the display is done in an embedded browser, you can mark it up in HTML and CSS and even (in some cases) use JavaScript to manipulate the display dynamically.

So why use Rhodes instead of creating a mobile web app?

Well, for one, you can package a Rhodes app and sell it in the app store, android market or app world.

It also (if you need it to) can sync with a sophisticated sync server known as RhoSync ( As of the time of writing, RhoSync is built upon Sinatra and Redis (it used to be Rails and MySQL). This means that a user can manage and manipulate his/her data on their phone without the need for a constant internet connection and that data can be periodically synced with the back end. RhoSync manages all the potential data conflicts and only syncs the differences. It can connect to practically any kind of data store (databases, REST APIs, etc...).

So what are some of the limitations of Rhodes?

So, unfortunately, nothing in life is perfect. One limitation is that because it relies on embedded browsers, if your app does any fancy JavaScript or CSS3 you will have to write another set of HTML only views for BlackBerry (though apparently the latest BlackBerry versions use a WebKit browser. I have yet to try this personally though).

It also really is targeted at the high end smart phones. It is quite well optimised for iOS, but on slower Android devices (anything with less than a 600Mhz processor) it can be quite slow and sluggish.

Another thing to get one's head around is that even though the framework is Rails-like, it is not exactly the same as Rails (it is quite stripped down). The RhoMobile team have been busy adding features however so its ecosystem is growing. When I started using it, there were no models (only controllers and views). Since then, Model support has been added as well as MSpec for testing.

Once your product is done, adding it to the respective app markets is also quite a hassle. This is by no means the fault of RhoMobile or the Rhodes team, but it is something to consider when trying to decide between doing a web based app vs an installable one.

The other limitation is because Rhodes is designed to work with most of the major smart phones on the market, you haven't got access to all the available features on a specific mobile phone. There is limited camera and calendar support, but currently no audio or video recording (it's in the road map, but not yet available). So you are not going to be able to write an app like Shazam or Google Maps using the framework...

If you like Ruby, web technologies AND mobile apps then it's definitely worth taking a look at RhoMobile. It's especially suited for Enterprise App development where you may not need the latest fancy UI enhancements, but you do want to deploy on all the platforms that your team is using with a single code base.

More References

You can find more information on the app I developed at

Responsive Web Design

I wanted to go over Responsive Web Design using CSS.

In the old days of web development, we had to code to common screen sizes (i.e. 800 X 600, 1024 X 768) and we patiently waited for people to upgrade their computers to have a decent amount of screen real estate so we could design things the way we really wanted. We also took on semi stretchy web layouts etc to expand and contract appropriately.

Then about 2 or 3 years ago, Apple released this little device called an iPhone with a 320 X 480 resolution which took the world by storm and suddenly a lot of people were viewing your website on a tiny screen again...

Anyways, as it can be difficult to design a site which looks good on 320 X 480 AND 1680 X 1050, we need to come up with some kind of solution.

One way is to sniff the client and then use an appropriate stylesheet, but then you are mixing CSS with either JavaScript or server side programming and also potentially maintaining a list of appropriate clients and stylesheets. Also, you can miss out new devices/clients as they come along.

The other way is to use the media queries and detect the screen size using CSS.


@media screen and (max-device-width: 480px), screen and (max-width: 480px) {
appropriate iPhone/Android CSS rules go here

You can also "detect" for iPad by using 768 instead of 480.

Things to look out for
I found that although it is possible to move elements around using CSS, there are some limitations with regards to content flow. In one design that I was given by my designer, the logo was to move from the middle to the top of the page depending on the device. What I ended up doing was creating 2 logos and hid/showed them appropriately.

It also helps to break the page down to standard elements (main content, header, footer, etc...) and focus on designing/laying out these elements appropriately. Once you are done with that, you are 80% of the way there and you might only need to do some minor tweaking of the main content section thereafter (i.e. moving form labels from the side to the top for example).

It is important to know your audience as well. In my case, this time around, I am working on a site which is an adjunct to a phone app and it is important for our audience to be able to view the site well on a phone as well as a full size computer. This does involve extra work so there may not be any business to justify it in your case.

Further links