Using named_scope for object filtering

One of the cool new features in Ruby on Rails is the named_scope.  You can put a named_scope in your model as a way to do a customized find by just calling a name.  Here is an example of a simple named_scope.  This named scope gets all the widgets that are blue.  You can call it like this: Widget.blues

class Widget < ActiveRecord::Base
  named_scope :blues, :conditions=> "color='blue'"
end

Thats pretty cool huh? Very DRY if you are going to be using this quite a bit. Here is a more complex example where you can insert a parameter using lambda.

  named_scope :above_price, lambda { |*args| {:conditions => ["price>?",args.first] } }

  #get all widgets with price above $25
  Widget.above_price(25)

Even cooler…
Now here is something I like even better that I was not expecting, but it seems to work. I am able to chain these together to filter out different widgets based on criteria. This is useful in an application that needs to allow users to filter out certain content based on criteria. Lets say you have a list full of widgets of all kinds, but the user only wants to see widgets that are blue and cost more than $25. You can add some check boxes and inputs to your view with a submit button and let the user choose different criteria so they can find the widget they are looking for. Then you can filter out the widgets with named scopes in your controller

widgets = Widget.all
if params[:is_blue]
  widgets = widgets.blues
end
if params[:above_price]
  widgets = widgets.above_price(params[:above_price])
end
#now you would have all the blue widgets above whatever price the user entered in the field "above_price"

#If I remember correctly, you can also call them by chaining them together if you need to
widgets = widgets.blue.above_price(25)

I think this is a pretty elegant solution for doing object filtering in Rails. Go named_scope! You crazy!

Creating a Facebook (iFrame) app using Ruby on Rails & Facebooker

I recently had the pleasure of creating a Facebook app with Ruby on Rails. At first I thought I might try to write my own code to interact directly with the Facebook API, but after looking at it a bit, I decided I better find some existing software to save me some time. Really the only good choice is Facebooker. rFacebook is also another choice, but it looks like it was abandoned a while ago. Apparently Facebook has made quite a few changes to the API in the last year or so. After diving into development I found that Facebooker is not exactly up to date either. A workaround is pretty easy to code, so I will show you that here.

First thing you want to do when creating a facebook app is to log on to Facebook Developer -> http://www.facebook.com/developers/. They have 2 sites for this and dont ask me why. The other one is developer.facebook.com. Do not use that one, because there is no way to edit your app from that page.

From http://www.facebook.com/developers/, click “Set up New Application” and enter an app name. After that you will go to the “Edit” screen for your application. There are a ton of different things you can configure, like the icon, etc.., but for now you just need to worry about your canvas settings. Click “Canvas” in the left side menu. Enter a name for your app: http://apps.facebook.com/your-app-name-goes-here . Next is the most important part, the “Canvas Callback URL”. For this, you need to enter the URL of your application. Since this is going to be an iFrame application, your app will be retrieved from your servers. So I entered: http://www.myapp.com/facebook/canvas. Next make sure you select “iFrame” under canvas render method. Save your changes and its time to start coding with Rails

Here is a screenshot of my settings:
Picture 3

You will need to install facebooker as a plugin. Follow the instructions on github – http://github.com/mmangino/facebooker

Rails Stuff
1. Create a controller called “facebook_controller.rb”
2. Add an action for your canvas page. This will be like the welcome page for your application
3. Add “ensure_authenticated_to_facebook” to the top of your controller. I will talk about this in more detail later.

4. Set up your view (canvas.html.erb) with whatever you want in it. “Hello world”

class FacebookController < ApplicationController
  ensure_authenticated_to_facebook

  def canvas
  end
end

Since you are the developer and you set up the app in facebook, it will already be installed in your facebook account. You can test it by going to http://apps.facebook.com/name-of-your-app-goes-here
You should see your app render in the iframe. Now lets move on to where the app actually does something. Create a link in your view to go to another action that will count your friends

canvas.html.erb


<%=link_to "count_friends", :action=>"count_friends" %>

Now you need to add the action to your facebook_controller.rb:

def count_friends
  @count = 0
  #this session variable is set automatically by the facebooker plugin
  fbsession = session[:facebook_session]
  #iterate thru your friends.  fbsession.user grabs your user account and then .friends pulls in all your friends
  fbsession.user.friends.each do |user|
    @count += 1
  end
end

count_friends.html.erb

You have <%=@count%> friends

Woopty doo! You have a working facebook app using Ruby on Rails and Facebooker.

Now if you try to send this to your friends it will not work, because of some changes in the facebook api that were not changed in Facebooker. The main problem lies in the install_url. When your friend tries to install the facebook app, it will send them to your website (no iFrame, just your site outside facebook..no good), instead of directing them to your canvas page which contains your website in an iFrame. To fix this I just hardcoded :canvas=>”true” in this method:

starting at line #189 in vendor/plugins/facebooker/lib/facebooker/rails/controller.rb. I commented out one line and hard coded in a value for canvas. Not sure if this breaks anything else, but it seems fine so far. I am guessing facebook changed something with the fb_sig_in_cavas parameter.

      def create_new_facebook_session_and_redirect!
        session[:facebook_session] = new_facebook_session
        next_url = after_facebook_login_url || default_after_facebook_login_url
        #top_redirect_to session[:facebook_session].login_url({:next => next_url, :canvas=>params[:fb_sig_in_canvas]}) unless @installation_required
        top_redirect_to session[:facebook_session].login_url({:next => next_url, :canvas=>"true"}) unless @installation_required
        false
      end

Push this change to your server and restart your app and this should make everything show up in the iframe after the facebook user installs your app.

On a side note: if you try to use “ensure_application_is_installed_by_facebook_user” in your controller, instead of “ensure_authenticated_to_facebook”, you will get the opposite effect when you click on a link within your application. It will turn into an infinite iframe loop. Every time you click it will create another page inside the iFrame

Change a column data type with Rails migration

I always forget the syntax for this and I have to look it up, so I am going to post it here.  Maybe this will help some others out.  Basically, what I need to do is change the data type of a column in my database table.  The table “widgets” contains a column/field named “count”.  I want to change count from an integer to a float.  So first I create a rails migration with the following command.

>script/generate migration change_data_type_for_widget_count

Then I edit the migration file:   app_root/db/migrate/20091007151516_change_data_type_for_widget_count.rb.  Here is the migration syntax to change the data type of a column:

class ChangeDataTypeForWidgetCount < ActiveRecord::Migration
  def self.up
    change_table :widgets do |t|
      t.change :count, :float
    end
  end

  def self.down
    #.....
  end
end

Then you just run the rake task “db:migrate” and it will change the data type in the database table.

>rake db:migrate

Using Ruby Threads

I have a fairly simple Ruby script that downloads 2 very large files from 2 different locations.  Long story short.. it is cheaper for me from a bandwidth perspective (b/c of 95th percentile billing) to download both of the files at the same time.  I can’t exactly do this in my Ruby script if I write it that executes sequentially.  So I thought back to my Java days (YUCK!)  and decided to see if Ruby had a similar mechanism as Java for threading.  Turns out it does and its pretty easy to use.  My requirements are to download both files and continue with the script only after both files are downloaded.  So here is what I did:

#create a thread to download first file
t1 = Thread.new do

system(“/usr/local/bin/ruby /path/to/my/file/start_download.rb”)

end

#start the 2nd download

start_download2

while t1.status!=false

sleep 1

end

#continue

…….

Easy enough. I start by firing off a thread to download the first file, then I start download the 2nd file within the main script.   Then I check the status of the thread to see if its complete.  If so, I continue the execute of the main script.