The Making of VigeTurf - Development

Matt Swasey, Former Viget

Article Category: #Code

Posted on

The making of VigeTurf brought together folks from all our labs.  In this series, we're pulling back the curtain to give a detailed explanation of how it was created. Here, it's all about the application.

When the VigeTurf team came to me asking questions like, "Can we generate unique URLs that have holiday words in them?" and "Can we have a Twitter-like application?", I was pretty stoked.  This was going to be a nice opportunity to test some of the new developments going on inside the Rails community -- and to add my own to it. 

I decided to start by building the Twitter-like application, which ended up having holiday-themed captcha and a curse-word-swap-for-silly-word feature that kept everything clean.  To generate captchas, I used zendesk's captcha plugin.  This would generate random characters using imagemagick libraries through rmagick.  However, we wanted holiday-themed phrases rather than random characters.  So, I edited the image generation script to pull from a random word generator, which further pulled from a list of "holiday" words our office compiled.  Patch to come.

Then, I tackled the VigeTurf creations themselves.  Erik's Flash needed to push two different kinds of data types -- file uploading and file capture -- that the Rails application would intercept and write to the file system.  So we paired for several hours to make it happen.

mig_and_erik_435Here are Erik and I being awesome.

First, when a user chooses to "Upload" a unique background image to create the turf, the Flash has to push that file to the Rails application.  I don't know so much about Flash and the way all this happens, so forgive any slips in terminology.  All I knew was that I was getting some params that had two key values, :Filedata and :Filename.  I simply pushed the :Filedata values into a blob attribute on an ImageFile model instance, and the :Filename to the name attribute.  After that, I would use an after_create hook to write the image to the file system:

In the controller action:

 @image_file = ImageFile.new @image_file.blob = params[:Filedata] @image_file.name = params[:Filename] @image_file.filename = @image_file.name @image_file.directory = APP_CONFIG["upload_dir"] @image_file.save render :nothing => true 

After the grass on the Flash had grown and need to be saved again, the data coming back was much different.  This time, it was in a binary/octet-stream.  The Flash essentially took a snapshot of the image with the grass cut away, and then pushed it as a binary stream to a second end point in the Rails application.  At that point, I dumped the binary stream to a file system.

In the controller:

 @image_file = ImageFile.new(:turf => true) @image_file.blob = request.body @image_file.name = JoyGen.unique_words! @image_file.filename = @image_file.name + ".jpg" @image_file.directory = APP_CONFIG["finished_dir"] @image_file.save render :text => turf_address(@image_file.name) 

As you can see, I'm just taking the incoming requests body and dumping it straight to the file system.  No mime-type checking needed, since I know it's only coming from one place internally.  This would need to be resilient if you were going to accept streams from more than one consistent source.

Also, you might notice that I am setting the image_files name to JoyGen.unique_words!  This is a small library developed to pick three random words from a text file provided.

For those of you interested, here the the JoyGen module: 

 

 module JoyGen WORDS ||= IO.readlines(Rails.root + "/lib/holiday_words.txt") def self.unique_words!(amount = 3, options = {}) delimiter = options[:delimiter] || "-" max_length = options[:max_length] || 8 min_length = options[:min_length] || 3 url = Array.new amount.times { url << scrubbed_word(min_length, max_length) } url.join(delimiter) end private def self.word WORDS[rand(WORDS.length)].chomp end def self.scrubbed_word(min, max) @word = word if @word.length >= min && @word.length <= max @word.gsub(/( *|_*|-*)/, '').downcase else scrubbed_word(min, max) end end end 

 

Take an in-depth look at how the other pieces came together.

Related Articles