Scaling a Rails Application – Thinking About the Full Stack

I found a great presentation about scaling web applications that I wanted to share with the world. Enjoy!
Scaling a Rails Application from the Bottom Up

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Software | Tagged , , , | Comments closed

Getting FancyUpload to work with Rails

FancyUpload is a slick looking file uploader with a progress bar. However, it’s not completely obvious how to get it working with your Rails application. I searched long and hard and found 2 pretty good examples here and here. The problem is that the former only works with Amazon S3, and the latter just doesn’t seem to work. If you are using Amazon S3 check out the first link which will be more useful than this article. I will show you how to get a great file uploading interface up and running in minutes.
FancyUpload with Rails 1
FancyUpload with Rails 2
FancyUpload with Rails 3
FancyUpload with Rails 4

After some wrestling with the code in both examples, I was able to get a sample Rails app up and running. The app is hosted on github for your downloading pleasure.

I don’t think it’s worth going through the fancyuploader code here because you can view examples and usage on the official website, but I will mention the trick to getting it working in Rails:

Step 1. Disable protect_from_forgery for the upload action
This is as simple as:

class UploadsController < ApplicationController
  protect_from_forgery :except => 'upload'
  def upload
...

Step 2. Copy the file somewhere useful and return 200

class UploadsController < ApplicationController
  protect_from_forgery :except => 'upload'
  def upload
    FileUtils.mv params[:file].path, RAILS_ROOT+"/data/#{params[:Filename]}"
    return render :nothing => 200
  end
end

Step 3. Set the URL param of FancyUploader properly

...
var up = new FancyUpload3.Attach('uploader_file_list', '#upload_link', {
      path: 'http://#{request.host_with_port}/javascripts/fancyupload/source/Swiff.Uploader.swf',
      url: 'http://#{request.host_with_port}/uploads/upload',
...

I highly recommend checking out the sample app from my git repository and poking around in the code. There is very little. Feel free to contact me if you cannot get fancyupload to work with Rails after trying my sample app.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Software | Tagged , , , , , | Comments closed

How to change a UIBarButtonItem in the iphone toolbar

I recently needed to add a custom button to the iPhone toolbar but it needed to have state. Specifically, I needed the ability to enable and disable it based on some conditions. I implemented this using two images for the button – one for enabled and one for disabled.

First, add the button to the toolbar:

UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
UIImage *shuffleButtonDisabledImage = [UIImage imageNamed:@"shuffle_button_disabled.png"];
UIButton *shuffleButton = [UIButton buttonWithType:UIButtonTypeCustom];
[shuffleButton setImage:shuffleButtonDisabledImage forState:UIControlStateNormal];
 
shuffleButton.frame = CGRectMake(0, 0, shuffleButtonDisabledImage.size.width, shuffleButtonDisabledImage.size.height);
[shuffleButton addTarget:self action:@selector(shuffleSelected) forControlEvents:UIControlEventTouchUpInside]; 
UIBarButtonItem *shuffleBarItem = [[UIBarButtonItem alloc]	initWithCustomView:shuffleButton];
 
toolbarItems = [NSMutableArray arrayWithObjects:flexibleSpace,shuffleBarItem,flexibleSpace,nil];
[toolbarItems retain];

I did this in my init function.

Next you need to implement the logic for disabling and enabling:

if(someFlag){
        shuffleActivated = YES;
        [(UIButton *)[[toolbarItems objectAtIndex:1] customView] setImage:[UIImage imageNamed:@"shuffle_button_small.png"] forState:UIControlStateNormal];
}else{
        shuffleActivated = NO;
	[(UIButton *)[[toolbarItems objectAtIndex:1] customView] setImage:[UIImage imageNamed:@"shuffle_button_small_disabled.png"] forState:UIControlStateNormal];
}

Update:
There is actually a better way to set enabled and disabled view states for a button anywhere in your iPhone application. You can specify which image to display based on the enabled or disabled state of the button, and then just manage the button state. One benefit to this is your disabled button won’t bounce like it is enabled. You also don’t have to create an image every time the button state changes. Of course you could have avoided both of these issues with a little more code, but this is a nice way to avoid both of those issues with a little less code. Here is what creating the toolbar will look like now:

UIBarButtonItem *flexibleSpace = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemFlexibleSpace target:nil action:nil];
// this code is copied which is bad, should put in a utility class
UIImage *shuffleButtonDisabledImage = [UIImage imageNamed:@"shuffle_button_disabled.png"];
UIImage *shuffleButtonEnabledImage = [UIImage imageNamed:@"shuffle_button.png"];
UIButton *shuffleButton = [UIButton buttonWithType:UIButtonTypeCustom];
shuffleButton.adjustsImageWhenDisabled = YES;
[shuffleButton setEnabled:NO];
[shuffleButton setImage:shuffleButtonEnabledImage forState:UIControlStateNormal];
[shuffleButton setImage:shuffleButtonDisabledImage forState:UIControlStateDisabled];
 
shuffleButton.frame = CGRectMake(0, 0, shuffleButtonDisabledImage.size.width, shuffleButtonDisabledImage.size.height);
[shuffleButton addTarget:self action:@selector(shuffleSelected) forControlEvents:UIControlEventTouchUpInside]; 
UIBarButtonItem *shuffleBarItem = [[UIBarButtonItem alloc]	initWithCustomView:shuffleButton];
 
toolbarItems = [NSMutableArray arrayWithObjects:flexibleSpace,shuffleBarItem,flexibleSpace,nil];
[toolbarItems retain];

Again, this is done in my init function.

Your enabling/disabling logic would look like this:

if((numSelected == 1)&&(!shuffleActivated)){
	[(UIButton *)[[toolbarItems objectAtIndex:1] customView] setEnabled:YES];
}else if((numSelected == 0)&&(shuffleActivated)){
	[(UIButton *)[[toolbarItems objectAtIndex:1] customView] setEnabled:NO];
}
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Software | Tagged , , | Comments closed

B’more on Rails synopsis – April 2010

B’more on Rails is a community of Baltimore folks who use the Ruby programming language and the Ruby on Rails application framework. Check out our MeetUp page. Below is a brief synopsis from our April 2010 meetup. You can also download the slides here.

Bundler is great for gem management. Among other things, it solves the issue where gems are required to load your environment but the environment needs to be loaded for the bootstrapping code that installs those gems to execute.

Cucumber seems to be the most agile solution for BDD style integration tests. Writing out the features before you write code seems like a lot of work, but it will yield better productivity as it forces you to keep the business requirement paramount during development. It’s overkill for unit tests in which case RSpec, Shoulda, or TestUnit (shipped with Rails) will serve you well. Also, it can be quite painful at first, but the key is to start simple and you will be naturally drawn to advanced usage.

Cucumber should be used in conjunction with Capybara or Webrat for browser simulation. Also the following testing tools were mentioned:

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Bmore On Rails Review, Software | Tagged , , , , , , , , , , , | Comments closed

Install MySQL on Mac OS X 10.6 and add StartupItem

Installing MySQL on Mac OS X 10.6 took a bit of digging around. Here is a consolidated tutorial. I am leaving out sudo commands for convenience. Use them where you don’t have permission.

# create the directory where you will keep your MySQL binary distribution
mkdir ~/src
cd ~/src
 
# download mysql binary distribution from http://dev.mysql.com/downloads/mysql/
# then move the distribution into your src directory
mv /path/to/mysql/download.tar.gz ~/src
# make sure you are in ~/src (use pwd if unsure) then untar
gunzip < /path/to/mysql-VERSION-OS.tar.gz | tar xvf -
 
# create the MySQL group
dscl . create /Groups/mysql
# give it some group id
dscl . create /Groups/mysql gid 296
# create a MySQL user
dscl . -create /Users/mysql
# make the MySQL user a member of the MySQL group
dscl . append /Groups/mysql GroupMembership mysql
# change ownership to mysql
chown -R mysql .
chgrp -R mysql .
 
# install mysql
scripts/mysql_install_db --user=mysql
# make a symlink in the /usr/local directory or you can customize the location in support-files/mysql.server if you prefer
mkdir /usr/local
cd /usr/local/
ln -s /full/path/to/your/mysql/install/directory mysql
 
# you should now be able to start the server
support-files/mysql.server start
 
# add mysql to your PATH
# in ~/.bash_profile
export PATH=/usr/local/mysql/bin:$PATH
source ~/.bash_profile
# should bring up the mysql CLI
mysql
# exit 
mysql> exit
 
# set mysql server to auto start upon boot
mkdir /Library/StartupItems/MySQLCOM
cd /Library/StartupItems/MySQLCOM/
ln -s /usr/local/mysql/support-files/mysql.server MySQLCOM
 
# test for success
/Library/StartupItems/MySQLCOM/MySQLCOM stop
/Library/StartupItems/MySQLCOM/MySQLCOM start
 
# create a root password
mysqladmin -u root password your_root_password
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Software | Tagged , , , | Comments closed

Recovering off screen windows in Mac OS X

I constantly lose windows on OS X. I think it’s because sometimes I am connected to an external monitor so OS X gets confused when I am not. I found a great solution online that I would like to re-post here so that I never have to look for it again and I can help share it with the world. All credit goes to the author of the original article.

The solution is to pop open your Script Editor and run this Apple script:

-- Example list of processes to ignore: {"xGestures"} or {"xGestures", "OtherApp", ...}
property processesToIgnore : {}
 
-- Get the size of the Display(s), only useful if there is one display
-- otherwise it will grab the total size of both displays
tell application "Finder"
  set _b to bounds of window of desktop
  set screen_width to item 3 of _b
  set screen_height to item 4 of _b
end tell
 
tell application "System Events"
  set allProcesses to application processes
  set _results to ""
  repeat with i from 1 to count allProcesses
    set doIt to 1
    repeat with z from 1 to count processesToIgnore
      if process i = process (item z of processesToIgnore) then
        set doIt to 0
      end if
    end repeat
 
    if doIt = 1 then
      tell process i
        repeat with x from 1 to (count windows)
          set winPos to position of window x
          set _x to item 1 of winPos
          set _y to item 2 of winPos
 
          if (_x < 0 or _y < 0 or _x > screen_width or _y > screen_height) then
 
            set position of window x to {0, 22}
 
          end if
        end repeat
 
      end tell
    end if
  end repeat
end tell
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Random, Software | Comments closed

Converting Table Data to YAML for Testing in Ruby on Rails

One of my clients has a huge application with no test suite. I am helping the company migrate to Rails 2.3 as well as remove some bottlenecks from their code. I needed to convert around 10 look-up tables to YAML’s so that I could load the data as fixtures and test the application properly as I make changes. I scraped together pieces of code from the web and came up with this:

namespace :db do
  namespace :fixtures do
 
    desc 'Create YAML test fixtures from data in an existing database.  
    Defaults to development database.  Set RAILS_ENV to override.'
    task :dump => :environment do
      sql  = "SELECT * FROM %s"
      skip_tables = ["schema_info"]
      ActiveRecord::Base.establish_connection(RAILS_ENV)
      tables=ENV['TABLES'].split(',')
      tables ||= (ActiveRecord::Base.connection.tables - skip_tables)
 
      tables.each do |table_name|
        i = "000"
        File.open("#{RAILS_ROOT}/test/fixtures/#{table_name}.yml", 'w') do |file|
          data = ActiveRecord::Base.connection.select_all(sql % table_name)
          file.write data.inject({}) { |hash, record|
            hash["#{table_name}_#{i.succ!}"] = record
            hash
          }.to_yaml
        end
      end
    end
  end
end

To use, put this code in a file somewhere like RAILS_ROOT/lib/tasks/util/table_to_yml.rake and execute something like:

rake db:fixtures:dump TABLES=lookup_table_1,lookup_table_2,...,lookup_table_n
Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Software | Tagged , , , , , , , | Comments closed

Getting jQuery, Rails, and Authenticity Tokens to play nice

For anyone that uses the jQuery AJAX library to send POST requests to their Rails application, you have probably found that the built in Rails CSRF protection does not play nice with your jQuery POST requests. By the way, if you don’t know what CSRF is, check out my Ruby on Rails security presentation. You need to send an Authenticity Token with each POST request and jQuery does not do that for you auto-magically. This post explains how I got jQuery, Rails, and Authenticity Tokens to play nice together preventing the dreaded error:

 ActionController::InvalidAuthenticityToken (ActionController::InvalidAuthenticityToken)

Step 1: Set up an AUTH_TOKEN Javascript variable
Add this to your application’s layout file (in the head):

<%= javascript_tag "var AUTH_TOKEN = #{form_authenticity_token.inspect};" if protect_against_forgery? %>

Step 2: Send the request with an AUTH_TOKEN

$.ajax({ 
            type: "POST",
    	    data:"authenticity_token="+$.URLEncode(AUTH_TOKEN),
    	    url: "http://"+HOST+"/users/login", 
    	    success: function(html){
    	        alert("woohoo!");
    	    }    	
});

One thing to note is the $.URLEncode function. I started using this jQuery plugin when I noticed “escape” and “encodeURIComponent” functions were not properly encoding the “+” character. I forgot where I found the plugin so I’ll just post it right here since it’s really lite:

$.extend({URLEncode:function(c){var o='';var x=0;c=c.toString();var r=/(^[a-zA-Z0-9_.]*)/;
  while(x<c.length){var m=r.exec(c.substr(x));
    if(m!=null && m.length>1 && m[1]!=''){o+=m[1];x+=m[1].length;
    }else{if(c[x]==' ')o+='+';else{var d=c.charCodeAt(x);var h=d.toString(16);
    o+='%'+(h.length<2?'0':'')+h.toUpperCase();}x++;}}return o;},
URLDecode:function(s){var o=s;var binVal,t;var r=/(%[^%]{2})/;
  while((m=r.exec(o))!=null && m.length>1 && m[1]!=''){b=parseInt(m[1].substr(1),16);
  t=String.fromCharCode(b);o=o.replace(m[1],t);}return o;}
});

This is the bare minimum involved to play nicely with Rails but I prefer to keep things DRY.

Step 3: DRY up your jQuery POST requests
Any good Rails programmer will notice a flaw with this solution – you have to include the authenticity token in each request. This is not DRY. You can obviously create a wrapper function but I prefer the following method:

$(document).ajaxSend(function(event, request, settings) {
    if (typeof(AUTH_TOKEN) == "undefined") return;
    if (settings.type == 'GET' || settings.type == 'get') return;
    settings.data = settings.data || "";
    settings.data += (settings.data ? "&" : "") + "authenticity_token=" + $.URLEncode(AUTH_TOKEN);
});

ajaxSend is a Global jQuery event that allows you to modify the request before it gets sent. There is one issue with the method above that caused a bit of debugging on my end. When no data is provided in your post request:

$.ajax({ 
            type: "POST",
    	    //data:"some_data="+some_data,
    	    url: "http://"+HOST+"/users/login", 
    	    success: function(html){
    	        alert("woohoo!");
    	    }    	
});

jQuery will not properly set the Content-Type of the request header. If we look at line 3522 of jQuery (1.3.2), we see the following:

if ( s.data )
				xhr.setRequestHeader("Content-Type", s.contentType);

This says that if no data is in the request, jQuery will not set the Content-Type header and a quick Firebug will show you that the browser defaults to
” text/plain; charset=UTF-8 ”
which results in Rails not decoding the authenticity token properly.

Step 4: Set the Content-Type before you POST
The easy fix is to set the Content-Type in your ajaxSend global event handler resulting in the function shown below:

$(document).ajaxSend(function(event, request, settings) {
    if (typeof(AUTH_TOKEN) == "undefined") return;
    if (settings.type == 'GET' || settings.type == 'get') return;
    settings.data = settings.data || "";
    settings.data += (settings.data ? "&" : "") + "authenticity_token=" + $.URLEncode(AUTH_TOKEN);
    request.setRequestHeader("Content-Type", settings.contentType);
});

Enjoy!

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Software | Tagged , , , , | Comments closed

Ruby on Rails – Skipping validations based on where object is created

I ran into an issue today where I wanted to skip validations based on where an object was created. To be more specific, I wanted to validate strictly if a “venue” was created through my web interface but I want to skip validations when importing venues. The reasoning is pretty simple, I want to require that a user provides a zip code, state, etc. if they use my web interface. However, if I import venues from a website with CRON at some frequency, I want to gather as much venue data as possible and ask users to update it later if needed.

This is not very straightforward in Rails. After all, I do not want to skip validations entirely. I ended up creating a virtual attribute for validations I wanted to skip and checking the attribute before validating:

validates_presence_of :name
validates_presence_of :country_id, :unless => "!validations_to_skip.nil? and validations_to_skip.include?('country')"
validates_presence_of :region_id, :unless => "!validations_to_skip.nil? and validations_to_skip.include?('region')"
validates_presence_of :city, :unless => "!validations_to_skip.nil? and validations_to_skip.include?('city')"
validates_presence_of :zip, :unless => "!validations_to_skip.nil? and validations_to_skip.include?('zip')"
 
validates_existence_of :user
 
# array of validations to skip
attr_accessor :validations_to_skip
attr_protected :validations_to_skip

You can put something like that at the top of your model file. Be sure to protect the virtual attribute so an attacker cannot set validations_to_skip from a web request in cases where you mass assign the model attributes.

To skip validations just do something like this when you create a venue:

v = Venue.new
v.validations_to_skip = ["country","region","city","zip"]
v.name = "A Venue"
v.user = some_user
v.save

What do you think? Anyone have a better way?

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Software | Tagged , , , , | Comments closed

Back links for your Ruby on Rails application

Here is a nice little helper I wrote to display back links:

def back_link
    link_to "&laquo; Go Back", request.env["HTTP_REFERER"].blank? ? "/" : request.env["HTTP_REFERER"]
end

If you just use the HTTP_REFERER without checking if it’s blank, weird things can happen if someone lands on your page from an email, for example.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google
  • MySpace
  • Slashdot
  • StumbleUpon
  • Technorati
  • TwitThis

If you enjoyed this post, make sure you subscribe to my RSS feed!

Posted in Software | Tagged , , | Comments closed