Code bites

1 note &

Using jQuery Mobile with the Rails asset pipeline

jQuery Mobile is a framework for making mobile web pages that works with a lot of different mobile devices. You use it by creating a relatively simple html page that defines the content and loading the framework that contains a number of javescript, css and image files to ‘magically’ turn it in to a mobile formatted page. You can load the framework files from jQuery’s servers, but if you want to host them yourself this can be done very easily with the new asset pipeline introduced in Rails 3.1.

Just like you use the jquery-rails gem to include jQuery, you can use a gem to include jQuery Mobile. I have been using different gems for this, but one after the other they have stopped being updated so I ended up creating the jquery-mobile-rails-assets gem myself. To use it add the following to the gem file in your Rails 3.1 application:

gem "jquery-mobile-rails-assets"

Run ‘bundle install’. Then add the following to the application.js:

//= require jquery.mobile

And the following to application.css files:

 *= require jquery.mobile

You’re done! However, if your app serves both mobile and regular web pages you probably want to make a separate configuration for mobile. If so you should follow the instructions below before making the changes described above.

First change the following line in application.js:

//= require_tree .

To:

//= require_directory .

And the following line in application.css:

 *= require_tree .

To:

 *= require_directory .

Next make a subdirectory named mobile in both assets/javascripts and assets/stylesheets and copy application.js and application.css there respectively. Then make the the changes to the application.js and application.css files in the mobile subdirectories as described earlier. You can now include jQuery mobile by adding the following to your layout:

  <%= stylesheet_link_tag "mobile/application" %>
  <%= javascript_include_tag "mobile/application" %>

Mobile web pages are speed sensitive so you may want to check out our earlier blog post on using the asset pipeline with Amazon Cloudfront:

Amazon Cloudfront, Nginx and the Rails Asset Pipeline

2 notes &

Amazon Cloudfront, Nginx and the Rails Asset Pipeline

Amazon Cloudfront lets you distribute your assets(images, css, javascript) to locations around the world, giving faster load times for your users and also reducing the load on your servers. The assets generated by the Asset Pipeline introduced in Rails 3.1 are perfect matches for Cloudfront, and using it should be just a matter of pointing it to your server. But in setting it up myself I discovered that there some things to watch:

  • Gzip compression should be turned on for HTTP 1.0
  • If the Last-Modified header is present files may not be updated correctly at the expire time

I have therefore documented my setup here in case someone else could find it useful.

Prerequisites

To use Amazon Cloudfront the way described here your Rails app must be at version 3.1(or higher) and must be using the Rails Asset Pipeline. You also need an account with Amazon Web Services.

Configuring your Nginx webserver

Regardless of using Cloudfront or not I prefer to serve assets from a separate subdomain like for instance assets.example.com. This just feels cleaner to me and makes it easy to ensure that only assets are served through Cloudfront. Here is what the Nginx configuration looks like:

server {
  listen 1.2.3.4:80;
  server_name assets.example.com;

  root /home/user/apps/example/current/public;

  error_log /home/user/logs/error-assets.log notice;
  access_log /home/user/logs/access-assets.log main;

  location / {
    deny all;
  }

  location ^~ /assets/ {
    allow all;
    gzip_http_version 1.0;
    gzip_static  on;
    expires      365d;
    add_header   Last-Modified "";
    add_header   Cache-Control public;
  }

}

The configuration explained:

  • allow all Allow access to everything inside the assets directory, everything else is blocked by the deny all statement above.
  • gzip_http_version 1.0 If you do not include this setting Cloudfront will NOT serve gzipped versions of the files. As a result the files you are serving will be significantly larger, potentially nullifying the speed you gain from using Cloudfront!
  • gzip_static on When Rails precompiles the assets it also generates a gzipped version of css and javascript files. This setting is telling the server to check for these file and serve them in stead of gzipping the file every time someone asks for it. Note that to use this you must include the HttpGzipStaticModule when compiling Nginx. You do this with the —with-http_gzip_static_module flag.
  • expires 365d Indicates that the assets should be cached for one year. So browsers that downloads the assets should not ask for them again before one year has passed. It is my understanding that Cloudfront also uses this to decide how long to store the assets. Normally the assets files are small so the cost of storing them for one year should be minimal, but should you have very large assets that are changed frequently you may want to consider a lower value here.
  • add_header Last-Modified “” This removes the Last-Modified header. We have experienced that when the Last-Modified header is present the file may not be updated correctly when the expires time is reached. This caused the browser to make if-modified requests for the asset every time the page was loaded. Not sure why this is happening, but apparently this header should be removed anyway as, according to among others the Asset Pipeline Rails Guide, it’s presence may cause some browsers to request the file again before the expires time is reached.
  • add_header Cache-Control public This sets Cache-Control to public. This indicates that the assets can be stored in public caches, like for instance in a proxy.

Note that to add the settings related to gzip you have to enable gzip for Nginx in the first place. You do this in the nginx.conf file, and here are the settings that are working for me:

gzip  on;
gzip_http_version 1.1;
gzip_vary on;
gzip_comp_level 6;
gzip_proxied any;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
gzip_buffers 16 8k;
gzip_disable “MSIE [1-6].(?!.*SV1)”;

This configuration should be good also for apps where Cloudfront is not used. The only setting specific to Cloudfront is gzip_http_version 1.0 so this can be removed.

Creating the Cloudfront distribution

Next step is to create the Cloudfront distribution, we are going to go with the defaults of no HTTPS and no logging. Sign in to the AWS Management Console, then click “Create distribution”:

Select “Custom origin”, enter the subdomain configured above and click Continue:

On the next screen enter the domain that should be used to access the assets, then Click continue:

Your distribution has now been created, note that it may take a few minutes before it is ready.

Configuring DNS

The distribution you created has a domain name that looks like xxxxxxxxxxxxxx.cloudfront.net. To make it look a little nicer you can create a CNAME record that points to this for the domain entered when creating the distribution, in our example cloudfront.example.com.

cloudfront.example.com -> xxxxxxxxxxxxxx.cloudfront.net

Note that if the distribution is accessed through HTTPS you should not use a CNAME as this will create security warnings in the browser.

Using it in Rails

To start using the Cloufront distribution add it to production.rb as follows:

config.action_controller.asset_host = "http://cloudfront.example.com"

After you deploy this all requests for assets will be sent to Cloudfront, and Cloudfront directs the request to the location nearest the user. If a copy exists on this location Cloudfront will return it, if not it will forward the request to your server and store the asset returned.

1 note &

Making Admob work with jQuery Mobile(because Adsense won’t)

Adsense blues

In an attempt to add advertising to a jQuery Mobile powered site I started with trying mobile ads from Adsense. They displayed fine when loading the page directly, but not when navigating between pages, sometimes even preventing the content from being displayed. My understanding is that the reason is that the Adsense ads uses document.write(); which breaks the AJAX-driven method that jQuery Mobile uses to navigate between pages.

Talking to Google

I had heard that Google had acquired Admob and assumed that the Adsense mobile ads was a results of Google implementing Admob into Adsense. Recently I attended an event called Google Adsens Partner Day and had the chance to discuss the issue with a Google representative. It turns out that Adsense mobile ads predates the acquisition and she suggested I tried out Admob instead since it uses different codes and is where the future of mobile ads is going for Google.

Implementing Admob

There is currently little integration between Admob and Adsense meaning that you have to sign up for a separate account with Admob even if you already have an Adsense account. But signing up is easy as you can sign in with your Google account and just add a few additional details. The standard Admob ad looks as follows:

<script type="text/javascript">
var admob_vars = {
 pubid: 'PUBLISHER_ID', // publisher id
 bgcolor: '000000', // background color (hex)
 text: 'FFFFFF', // font-color (hex)
 test: true // test mode, set to false to receive live ads
};
</script>
<script type="text/javascript" src="http://mmv.admob.com/static/iphone/iadmob.js"></script>

It works a little better out of the box than Adsense in that it does not break the display of content, but the ad is not displayed when navigating between pages. Luckily Admob has a Wiki that explains the issue and offers some solutions:

Admob Wiki API/Technical Notes

Since my pages uses the listview layout I to put the code in a li tag inside the list, type of tag to use will of course depend on your layout. The important thing is to give it an unique id and add the manual_mode setting. Here is what it looks like:

<li id="admob-UNIQUE_ID">
<script type="text/javascript">
var admob_vars = {
 pubid: 'PUBLISHER_ID', // publisher id
 bgcolor: '000000', // background color (hex)
 text: 'FFFFFF', // font-color (hex)
 test: true, // test mode, set to false to receive live ads
 manual_mode: true
};
</script>
<script type="text/javascript" src="http://mmv.admob.com/static/iphone/iadmob.js"></script>
</li>

Then add the following code that loads the ad at the bottom of the page(but inside the data-role=”page” div):

<script type="text/javascript">
  _admob.fetchAd(document.getElementById('admob-UNIQUE_ID'));
</script>

Make sure your id is unique for every page

As mentioned earlier, when you navigate to a new internal page jQuery mobile loads the content into the page you are on. You must therefore make sure that UNIQUE_ID in the examples above is unique for every page. If not you will end up with several elements with the same id, this may lead to ads not showing on some pages and multiple ads on others. How you do this depends on your system, if you are using a CMS you may already have unique ids for every page. I ended up generating ids based on the page path, in Rails this could look like:

'admob-' + request.path.split('.').first.parameterize

Some example ids generated by this:

/start  -> admob-start
/show/1 -> admob-show-1

Going live

You may have noticed that the examples above contains the line “test: true”. This feature allows you to test display and clicks for the ads to make sure everything works without breaking the terms. When you are finished testing just change this to “test: false” to get real ads. Note that when you do this you may experience that ads are sometimes not displayed. This is normal since Admob will only display ads when it has a relevant ad for a page.

1 note &

Running IE9 using Internet Explorer Application Compatibility VPC Image and Virtualbox

UPDATE: Microsoft has now made available virtualization images created for use with virtualbox, we recommend using these instead of the procedure described here.

modern.IE Download page

The instructions mostly worked for us except under 1 we had to download the .txt file and use it locally with wget instead of the URL directly. And under 3/4 instead of running the .sfx file we did ‘unrar x filename.sfx’.

We will leave this description up in case someone have a use for it, but it will not be updated.

Some time ago Internet Explorer 9 was released. It’s market share is small now but that will probably change quickly. So it should be one of the browsers in your test suit, but unfortunately it needs Vista or newer. So what to do if you don’t want to invest in a new Windows license just to test some websites?

Fortunately Microsoft makes available virtualization images with different browser versions for download. These will only work for a limited time and are intended for the Microsoft Virtual PC. If you are not running Windows at all Virtualbox will support these kind of images, but at least the one with IE9 requires a few tricks to get it to work. This is what will be described in the following article.

The following assumes that you already have Virtualbox running, if not you should have no problems finding resources on how to set this up. I have only tried this under Linux so I can not guarantee that all steps will work on other platforms. If you try it out on a different platform I appreciate it if you leave a comment on how it worked out for you.

Downloading the image

Click here to go the Microsofts download page. You need to download 7 files “Windows_7_IE9.part01.exe”, “Windows_7_IE9.part02.rar” and up to “Windows_7_IE9.part07.rar”. Put them all in one directory and run the following command there:

unrar x Windows_7_IE9.part01.exe

This will create a file called “Windows 7.vhd”. Copy this to the location for your Virtualbox images, in my case “./VirtualBox/VDI”.

Creating a new VirtualBox machine

Launch VirtualBox and click the “New” icon. Name it and select Windows 7 as the operating system:

image

Click “Next” until the “Virtual Hard Disk” page is displayed. Select the image file you downloaded:

image

Click ‘Next’ and then “Create” to complete the setup.

Switching from SATA to IDE to make it work with VirtualBox

Click the “Settings” icon. In the Settings window click “Storage”. Right click “SATA Controller” and select “Remove Controller”:

image

Then right click “IDE Controller” and select “Add Hard Disk”:

image

In the next dialog select “Choose existing disk” and select the image file you downloaded.

Activating and starting Windows

Launch the machine. Sign in as Administrator using password “Password1”. If asked to activate Windows just click cancel. Open a command prompt(Start menu-> All programs-> Accessories-> Command prompt) and enter the command:

slmgr –rearm

This will postpone the activation for 30 days, at which point you will have to repeat the process.(This is all described on Microsofts download page, so you’re not doing anything illegal….)

Restart windows and sign in as Admin using “Password1”. You will be prompted with the normal steps to setup Windows(and maybe another restart) and after that you should be able to run IE9!

0 notes &

Using Google Analytics with jQuery Mobile

jQuery Mobile is a javascript based “Touch-Optimized Web Framework for Smartphones & Tablets”. It is currently in early development with an alpha status but already works reasonably well, at least for the Android and iOS platforms.

To make navigating jQuery Mobile based sites more efficient the framework will not replace the entire page when following an internal link. Instead the page is fetched using an AJAX request and the content is added to the page. Since the asynchronous Google Analytics code is placed in the head part of the page this will not be reloaded and as a consequence only the initial request will show up in the statistics.

The majority of the Google Analytics code is just doing initialization and does not need to be reloaded for every page. The one line of code that should be executed on every page load is this one:

    _gaq.push(['_trackPageview']);

To log all pages viewed we need to move this line from the head to the end of the data-role=”page” div as shown in this example:

<!DOCTYPE html>
<html>
<head>
  <title>Title</title>
  <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.css" />
  <script src="http://code.jquery.com/jquery-1.5.min.js"></script>
  <script src="http://code.jquery.com/mobile/1.0a3/jquery.mobile-1.0a3.min.js"></script>

  <script type="text/javascript">
  var _gaq = _gaq || [];
  _gaq.push(['_setAccount', 'UA-xxxxxx-x']);
  (function() {
    var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
    ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
    var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
  })();
  </script>

</head>
<body>

  <div data-role="page">
    <div data-role="header">
      <h1>Title</h1>
    </div>
    <div data-role="content">
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    </div>
    <div data-role="footer">
      <h4>Footer</h4>
    </div>

  <script type="text/javascript"
    _gaq.push(['_trackPageview']);
  </script>

  </div>

</body>
</html>

After this change all page views are reported, but not necessarily with the correct page information. To get correct stats of which pages are accessed you have to include the page like this:

     _gaq.push(['_trackPageview', '/page1']);

If you are using server side scripting you can of course automate this. For example in Ruby on Rails you can do:

    _gaq.push(['_trackPageview', '<%= request.path %>']);