Connecting a Windows VM to a Mac-based localhost Ruby on Rails site (Passenger)

Posted: January 20th, 2011 | Author: | Filed under: posts | Tags: , , , | 5 Comments »

Supporting Internet Explorer for your web app is a pain. At least, trying to test your Ruby on Rails (or other localhost-based site) in IE while developing on the Mac should be easy, given the prevalence of virtual machines. Right?? You’d think. However, trying to setup our front-end developer to access a local instance of Passenger running on Mac OS X from a Windows VM proved to be more of an issue than it needed to be. Adding to this, most of the top Google results for relevant keywords for this configuration made the process more overly difficult than it needed to be.

So, to help ease your pain in testing your Mac-based Rails site from a Windows virtual machine, here is an easy configuration for the three main VMs.

We need to do two main things:

  1. Trying to serve port-based or subdomain-based sites over to the Windows VM is going to be more challenging. Let’s make things easy – we’ll set the default http://localhost site for our Mac to our Rails site.
  2. We need to get the VM’s network to communicate with the Mac’s network correctly so that we can access this site.

On my team, we’re using Passenger on the Mac (running on top of the Mac’s base install of Apache). We’re using Passenger Pane to configure Apache easily. We also have this hooked into RVM, but that won’t be relevant to what we’re doing here. For the purposes of this post, I’ll assume that you have Passenger successfully serving a site that you can access via http://somedomain.local I’ll also assume that for whichever VM you’re opting to use, you have Windows installed and running in it.

** Do yourself a favor. Make sure that VM Ware Tools, Parallel Tools, or VirtualBox Guest Additions are installed – this ensures that the network connectivity will work as expected.

 

Let’s get http://localhost serving our Rails site

At the bottom of /etc/apache2/httpd.conf when using Passenger Pane, you’ll find the following configuration:

<IfModule passenger_module>
  NameVirtualHost *:80
  <VirtualHost *:80>
    ServerName _default_
  </VirtualHost>
  Include /private/etc/apache2/passenger_pane_vhosts/*.conf
</IfModule>

 

To make our Passenger site the default localhost site, go into passenger_pane_vhosts/, view the Apache config file for the site you want to be the default, and you’ll see something along the lines of the following:

$ cat yoursite.local.vhost.conf
<VirtualHost *:80>
  ServerName yoursite.local
  DocumentRoot "/Users/username/rails/yoursite/public"
  RackEnv development
  <Directory "/Users/username/rails/yoursite/public">
    Order allow,deny
    Allow from all
  </Directory>
</VirtualHost>

 

Copy the lines from inside the VirtualHost block and paste them back into /etc/apache2/httpd.conf so that it looks like the following:

$ cd ..
$ sudo mate httpd.conf
# Added by the Passenger preference pane
# Make sure to include the Passenger configuration (the LoadModule,
# PassengerRoot, and PassengerRuby directives) before this section.
<IfModule passenger_module>
  NameVirtualHost *:80
  <VirtualHost *:80>
    ServerName _default_
	####
	DocumentRoot "/Users/username/rails/yoursite/public"
	RackEnv development
	<Directory "/Users/username/rails/yoursite/public">
	  Order allow,deny
	  Allow from all
	</Directory>
	####
  </VirtualHost>
  Include /private/etc/apache2/passenger_pane_vhosts/*.conf
</IfModule>

 

Restart Apache by going to System Preferences > Sharing and uncheck & recheck Web Sharing.

Also, if you’re using VMWare Fusion or Parallels, note the IP address that’s shown here.

Configuring VirtualBox

Let’s start with Virtual Box, which was the biggest pain to configure. Not because there’s a lot of work, but because they one critical detail doesn’t seem to be well known (most of the posts you’ll read online try to guide you through using the command-line network configuration tool). I should note that this was my first real opportunity to use Virtual Box, having only used Parallels and VMWare Fusion beforehand. It’s a little rough around the edges in a few places, but overall very impressive.

1) First, ensure that network settings for VirtualBox should be set to NAT – no port forwarding needed

 

2) Now, here’s what took a while to dig up: VirtualBox connects http://10.0.2.2 to the Mac’s localhost (thanks to this forum post) That is, just typing that into IE within the VM should connect you to http://localhost running on the Mac.

 

Configuring Parallels

1) For Parallels, you want to make sure that your VM is running on a Shared Network.

 

2) If you’re setup with this network configuration, you should be able to directly access the local IP address of your Mac, as shown in the System Preferences > Sharing screen:

 

Configuring VMWare Fusion

1) VMWare Fusion is much like Parallels. Configure your VM to “Share the Mac’s network connection” via NAT.

 

2) If you’re setup with this network configuration, you should be able to directly access the local IP address of your Mac, as shown in the System Preferences > Sharing screen, as with Parallels.

Hopefully, if you’ve made it to this point, for your respective VM, you have IE accessing your Passenger-based site. Success!

 

Hopefully, at least now you’ll be able to spend your time on all of the issues that poor users confined to IE are suffering through and get bug fixing quickly.


Another quick tip for server bottlenecks – prevent against cron job overrun

Posted: May 19th, 2010 | Author: | Filed under: posts | Tags: , , , , , | 1 Comment »

As I wrote the previous post on WordPress performance, I remembered another tip that we used to solve an issue on our FanGamb servers and wanted to do another technical post to cover it.

Because FanGamb is a very data-intensive site (lots of constantly updating odds, games, results, etc.), much of the software powering the site doesn’t directly tie to the web interface and instead interacts with our database. Last winter, we had an issue where as usage increased on the site, resource usage increased faster than it should have been and systems started locking up.

Digging through the processes that were running, we finally noticed a confluence of issues. First, one of our data scripts was running away, loading a bunch of duplicated games. This wasn’t good, but the vendor was able to fix it easily. However, this first issue led to a second problem. As the number of games increased in the database, the script that processed these games kept running longer and longer to deal with the increasing number of dups. As game results update quite frequently, our cron jobs trigger in fairly close succession. What began to happen was as the first cron job took longer and longer to complete, the subsequent cron job would kick off before the first completed. So, we had cron job after cron job stacking up on the server, quickly leading to issues, as you would suspect.

The fix for this was quite simple, as well, and has since become a standard practice for us. There’s a utility that EngineYard (our host) pointed us to that implements “locking”, so that one task can’t kick off while the other is already operating – it’s called Lockrun. It uses a temporary file and system ‘flock’ing to implement this, so it’s incredibly simple to install. One little utility and a big issue solved – the best kind of solution.

If this is your cron job:

/usr/bin/php script.php > log.log

Using this utility, just change to:

/usr/bin/lockrun –lockfile=/data/path/JOBNAME.lockrun — sh -c “/usr/bin/php script.php > log.log”

Download the utility here: Lockrun


Why is the user experience for adding Facebook apps so poor?

Posted: January 12th, 2010 | Author: | Filed under: posts | Tags: , , , | No Comments »

A friend linked me to a random promotions app on Facebook the other day and it stopped me in my tracks for a moment.  Unlike most apps I’ve seen, the first screen was something very interesting – it was actually inside the application itself and was not the not the lovely “Allow Access” screen that Facebook users are accustomed to.

This made me wonder – pretty much most of the biggest Facebook apps I can think of ask for permission before letting you do anything.  I understand why Facebook is protecting profile information, but as a developer, you can configure your initial app screen so as not to require permission to access content.  The promotions app did this and we”ve set it up for our application.

So, the question is – why are so many huge Facebook app developers happy with the poor user experience of having the “Allow Access” screen being the first thing that users encounter with their application?  Why not show a screen that gives you a better idea of what the app is and then if the users actually wants to use the app, then they would request access.  Is this simply a ploy for the publishers to get more “active users” and thus boost themselves in the AppData standings?  Or are Facebook users quickly becoming numb to releasing their private information and the age of privacy is really over, as Facebook’s Zuckerberg recently claimed?

FYI – for Rails developers using Facebook, it’s pretty easy to not require the Allow Access screen on your welcome controller or whatever serves as your landing screen.  Just skip the Facebooker ensure_app_installed before_filter and don’t make calls that try to access the user’s information:

skip_before_filter :ensure_app_installed

Collaborative Team Facebook Development

Posted: January 10th, 2010 | Author: | Filed under: posts | Tags: , , , , | No Comments »

For as much as has been written regarding developing Facebook applications with Rails (which isn’t that much considering the staggering popularity of Facebook and Rails), there is even less written on developing Facebook applications in a team environment.

There are a couple of potential strategies. One of the few articles out there describes the challenges in developing apps as a team:

This sets up some challenges for a development team. First off, your application probably relies on Facebook services, meaning it can only be fully tested from within Facebook. This implies that the external Facebook server needs to be connected to your development server. Since many Rails developers run off their own development machine behind a firewall, this is potentially awkward. Similarly, the fact that the Facebook application has a single URL mapping makes it difficult for multiple developers to work on the same application without tripping over each others” work.

The author goes on to discuss the solution that their team is using, consisting of a series of Rake tasks and a staging server to facilitate testing in a team environment.

The other option, which is supported natively by Facebooker, is using an SSH tunnel from the local development workstation to an intermediary server and having the Facebook app point at that server. (This necessitates having an understanding IT team that doesn’t lose that much sleep over some open ports..) The SSH tunnel is discussed by many of the Facebooker tutorials available (here, here, and here).

We adapted the SSH tunnel strategy to our team environment. Each developer will have a development Facebook app and then will point that to their assigned port on our staging server. Then they can tunnel into the staging server from their local workstation. In this way, everyone can be testing their work independently without needing to coordinate deploys through to a staging and central test application. The below diagram illustrates this.

Ok, so we have our plan, but how to go about setting this up? In particular, the other tutorials don”t talk about what you do or do not have to do on the server side. In truth, the tunnel is fairly easy to configure and seems to be working well.

On the server side, the key is making sure that the ports you need aren”t blocked and are accessible for purposes of the tunnel. No need to configure virtual hosts or mongrels – the staging server is a simple relay. You will probably need to configure SSH to turn on GatewayPorts. That”s just a couple of quick steps:

  • sudo vi /etc/ssh/sshd_config
  • Find the line GatewayPorts if it”s there.
  • Change or add to: GatewayPorts yes
  • Restart SSH: sudo /etc/init.d/ssh restart

(based on other blog posts I came across, be sure to indicate GatewayPorts yes and not GatewayPorts clientspecified)

Now, via Facebook”s Developers application, each developer should configure an application for testing purposes. Set the Canvas Callback URL to http://your.stagingserver.com:####/ (Facebook requires that you have a trailing slash) and you should be good to go. Depending on your application, you may need to configure other settings (other callback URLs or iFrame/FBML, but the canvas callback URL is the primary setting. Make a note of the API key and the secret key.

Finally, we can setup the tunnel. Go into the Rails app that you pulled down from your Git repp and edit config/facebooker.yml. Add in the API key and secret key from your development application you just configured. For the canvas page name, enter the name of your application, as configured on Facebook. Then change the callback URL to the same URL the app is configured for: http://your.stagingserver.com:####/ (again, trailing slash!). Then enter the SSH username and hostname for your staging server, save the file, and you should be good to go.

At this point, you should be able to start your development server (ruby script/server) and then start the tunnel (rake facebooker:tunnel:start). If you’re on Windows, be sure to run these commands through Cgywin. At this point, you should be able to go to http://apps.facebook.com/yourappname and see your development app. While the app is loading, you should also see the SSH command window and the Ruby Webrick/Mongrel window showing the incoming connection and the Rails system handling the requests.


Rails Facebook Development – Sharing some code for tracking active/inactive users…

Posted: January 9th, 2010 | Author: | Filed under: posts | Tags: , , , , | 14 Comments »

The next version of FanGamb will be a Facebook app.  It’s not a 1:1 translation of the current game to Facebook – we’ll be incorporating many of our game design learnings from running the first version of the game for NFL/NCAA football this fall. Since FanGamb is a Rails app, we’ve been working on setting up a collaborative development process around building Facebook apps in a team environment.  I’ll post more on this process to come, but for now, I wanted to contribute some code from our Rails Facebooker-based (that’s the main Rails Facebook API plugin) app.

Documentation on Facebooker is a little light, so there isn’t a whole lot to go on.  There is a fairly active Google group, though, and a list of the methods, so we’re not totally in the dark.  But as far as hunting for best practices and example code, it’s been a lot of trial and error.

One of the things that we wanted to implement in our application is making sure we know which users have our app installed.  It’s pretty common to store the user ids in the app’s database – you need to be able to link it to other data elements that you’re storing for them.  However, Facebooker doesn’t by default help you keep that user model in sync with which users actually have the app installed on Facebook.  You can tell which of a user’s friends have the app, but not a master list.  When you have an active Facebook user session, you can look at User#has_added_app, but we want to be able to tell if a user has the app added even if they aren’t on the app at that moment (for things like leaderboards, etc.).

So, we need to be able to track when a user uninstalls the application and set their user record to be inactive.  Facebook has the post_authorize and post_deauthorize call backs for this tracking.  As I said, there isn’t a lot of sample Facebooker code out there, so if you’re looking to implement the same thing, here are our callback actions for you to utilize.

The first thing you’re going to want to do is probably put your callback actions in a separate controller, so that you can limit which filters are run on the controller.  Facebooker has a before filter ensure_app_installed (suggested for your application_controller) which basically forces a user trying to use the app to install it.  This won’t work for your post_deauthorize action, so you need to turn that off:

skip_before_filter :ensure_app_installed, :except => :post_authorize

Also, because Facebook posts to these URLs, Rails treats it as a form submission and will try to verify the authenticity token.  So you need to turn that off for both post_authorize and post_deauthorize:

skip_before_filter :verify_authenticity_token

With this setup, you’re able to create the rest of the methods.  Here’s the stub code:

class CallbacksController < ApplicationController
  skip_before_filter :ensure_app_installed, :except => :post_authorize
  skip_before_filter :verify_authenticity_token
  def post_authorize
    if request.post?
      #do something here
      render :nothing => true
    end
  end
  def post_deauthorize
    if request.post?
      #do something here
      render :nothing => true
    end
  end
end

One important point – the above code will work, but we can’t forget to verify the signature to ensure that the request is actually from Facebook, since these actions are outside of the Facebooker plugin (which typically handles this validation).

Looking at the Facebook development wiki, you can see some sample code for RFacebook on how to validate this.  Implementing this, our callback controller now looks like the following:

class CallbacksController < ApplicationController
  skip_before_filter :ensure_app_installed, :except => :post_authorize
  skip_before_filter :verify_authenticity_token
  def post_authorize
    if request.post?
      if verify_uninstall_signature
        #set user's has_app indicator to 1 to indicate active
      end
    end
    render :nothing => true
  end
  def post_deauthorize
    if request.post?
      if verify_uninstall_signature
        #set user's has_app indicator to 0 to indicate inactive
      end
    end
    render :nothing => true
  end
  private
      #based on http://wiki.developers.facebook.com/index.php/Post-Remove_URL
      def verify_uninstall_signature
        signature = ''
        keys = params.keys.sort
        keys.each do |key|
          next if key == 'fb_sig'
          next unless key.include?('fb_sig')
          key_name = key.gsub('fb_sig_', '')
          signature += key_name
          signature += '='
          signature += params[key]
        end
        signature += FACEBOOK_YAML['secret_key']
        calculated_sig = Digest::MD5.hexdigest(signature)
        if calculated_sig != params[:fb_sig]
          logger.warn "\nWARNING :: potential spoofing :: expected signatures did not match"
          logger.info "\nSignature (fb_sig param from facebook) :: #{params[:fb_sig]}"
          logger.info "\nSignature String (pre-hash) :: #{signature}"
          logger.info "\nMD5 Hashed Sig :: #{calculated_sig}"
          #check to see if ip variables are nil
          if not request.env['HTTP_X_FORWARDED_FOR'].nil? and not request.env['HTTP_X_REAL_IP'].nil?
            ip = request.env['HTTP_X_FORWARDED_FOR'] || request.env['HTTP_X_REAL_IP']
          else
            ip = request.remote_ip
          end
          logger.info "\nRemote IP :: #{ip}"
          return false
        else
          #logger.warn "\n\nSUCCESS!! Signatures matched.\n"
        end
        return true
      end
end

Get the code in a Gist here.

Note the one line that references FACEBOOK_YAML[‘secret_key'].  This should refer to your secret Facebook key.  We put this into the initializer block in environment.rb:

#load facebooker configuration for usage
facebook_config = File.join(RAILS_ROOT, 'config', 'facebooker.yml')
FACEBOOK_YAML = YAML::load(ERB.new(File.read(facebook_config)).result)[RAILS_ENV]

Phew!  That should do it – you should now be able to track whether your users have the app installed or not via the post callbacks.  Hopefully, this will help someone looking to keep track of active/inactive users in their Rails Facebook app!

Big props to my co-founder Alan deLevie who’s figured out a big chunk of this Facebooker stuff for the FanGamb team.

EDIT: typo fixed.  Thanks Sting Tao!