<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-US" xmlns:media="http://www.isaacliu.info" xmlns="http://www.w3.org/2005/Atom">
<id>tag:isaacliu.info,2009:/blog</id>
  <link href="http://isaacliu.info/blog" rel="alternate" type="text/html" />
  <link href="http://isaacliu.info/blog/xml" rel="self" type="application/atom+xml" />
           
  <title>My Blog Feed</title>
  <subtitle>Isaac Liu's Blog Feed</subtitle>
  <link href="http://isaacliu.info/tumblog/" rel="self" />
  <link href="http://isaacliu.info/" />
  <updated>2012-02-21T16:40:41-08:00</updated>
  <author>
    <name>Isaac Liu</name>
    <email></email>
  </author>
  <id>http://isaacliu.info/</id>
  
  <entry>
    <title>Simple Android C2DM</title>
    <link href="/blog/201109/simple-android-c2dm.html" />
    <id>tag:isaacliu.info,2011-09-10:1315644582</id>
    <updated>2011-09-10T08:49:42Z</updated>
    <content type="html">

&lt;p&gt;As I mentioned in my previous post, &lt;a href=&quot;http://ofps.oreilly.com/titles/9781449390501/&quot;&gt;Learning Android&lt;/a&gt; didn&amp;#8217;t cover a couple topics, and one of them is Cloud to Device Messaging, or push notifications. When applications are getting data from the internet, there are two ways to keep the data fresh. First is called polling. It means that the app initiates a connection to the server at set intervals to see if there are new data. The problem is determining the polling frequency. Poll too often, you waste a lot of batteries without always getting new data. Poll too rarely, then you risk not getting new data in a timely fashion. The alternative is called pushing, or push notifications (iphone users should be familiar with the term). Instead of the client initiating a connection, the server sends a data packet to the client whenever there is new data, then the client reacts to it. Because there is some overhead to maintain a persistent connection to the server, rolling out a push notification service isn&amp;#8217;t a trivial task, but google has done that for us! It&amp;#8217;s called Cloud to Device Messaging (C2DM). We piggy back off of the connection that android maintains for gmail, google voice, gchat etc, and register our app to be &amp;#8220;waken up&amp;#8221; only when the server pings us. Note that the server doesn&amp;#8217;t actually send the new data, it only sends an alert that new data is available. This keeps the ping packet small so there is less overhead. It&amp;#8217;s up to the client app to receive the notification and then go to the server to retrieve the fresh data. In this post I will explain how to setup this service for your app. I will assume that you went through &amp;#8220;Learning Android,&amp;#8221; or at least have a basic understanding of the building blocks of Android programming. For the sake of keeping this post under a tolerable reading length, I will only go over the mechanisms. I have checked in a sample c2dm app that&amp;#8217;s commented in my &lt;a href=&quot;https://github.com/iliu/Android-C2DM&quot;&gt;github&lt;/a&gt; repository, so clone the repository to see the whole picture. I also included a small ruby script to mimic a web server sending c2dm packets to the client device, this way you can debug your implementation without needing to setup a server. By the end of this post you should have an app that you can install on your phone or emulator, and then a way to send it push notifications without requiring any server side coding!&lt;/p&gt;
&lt;p&gt;There are a couple requirements before we begin:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The phone must have android 2.2 (froyo) or above installed. The API level should be above 8.&lt;/li&gt;
	&lt;li&gt;The phone that&amp;#8217;s using the client must have at least one google account on the phone (since c2dm piggy backs off of existing google services)&lt;/li&gt;
	&lt;li&gt;You (the developer) must register for to use c2dm &lt;a href=&quot;http://code.google.com/android/c2dm/signup.html&quot;&gt;here&lt;/a&gt; (usually you get approved for developer quota pretty fast)&lt;/li&gt;
	&lt;li&gt;To use the ruby script for debugging, you must have ruby and rubygems install on your computer and install the &amp;#8216;c2dm&amp;#8217; gem (I&amp;#8217;ll show you how to install it)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ok, let us begin! To add c2dm to your app requires work on both the server side and the client side (phone app). The web server&amp;#8217;s job is to send notifications to the phone when new data arrives. But in order to reach the phone, the web server needs to send the messages through the google server, to ensure it goes through the c2dm channels. There&amp;#8217;s obviously a lot of details that goes on behind the scenes here, but this post will focus on the client side code. At the end of the post I&amp;#8217;ll show how to send messages through the google server using a ruby gem called &amp;#8216;c2dm&amp;#8217;. If your interested in the details and the authentication that goes on, you can read more about it &lt;a href=&quot;http://code.google.com/android/c2dm/#push&quot;&gt;here&lt;/a&gt;. From here on I will refer to the app installed on the phone as the client app, and your web service the server app.&lt;/p&gt;
&lt;p&gt;How does the google server know what phone to send the message to? It uses a registration Id that is given to the phones when the client app registers itself with the google server. Thus, the main responsibility of the client app is to register itself with google to obtain the id, and then send the id to your server app. Then just sit back and wait to receive push notifications. To start the registration process for your device, you send an intent to the google c2dm registration server. You can do this any number of ways, in my app i put it in a button onClickListener so it would start registration when the user clicks the button.&lt;/p&gt;
&lt;pre class=&quot;brush: java;&quot; title=&quot;Sending the registration intent&quot;&gt;
// Add an on click listener to the button Register. When it is clicked
// we want to send out the intent to register our device
buttonReg.setOnClickListener(new OnClickListener() {
	@Override
	    public void onClick(View v) {
	    // Create registration intent
	    Intent regIntent = new Intent(&quot;com.google.android.c2dm.intent.REGISTER&quot;);
	    // Identify your app
	    regIntent.putExtra(&quot;app&quot;, PendingIntent.getBroadcast(HomeActivity.this, 0, new Intent(), 0));
	    // Identify role account server will use to send
	    regIntent.putExtra(&quot;sender&quot;, &quot;roleemail@gmail.com&quot;);
	    // Start the registration process
	    startService(regIntent);
	}
    });
&lt;/pre&gt;
&lt;p&gt;It&amp;#8217;s pretty straight forward. Essentially your sending an intent to the google c2dm service running on your phone stating that you want to register this device and this app to receive c2dm messages. Note that you must change the role account to your own role email which you used to register online for the c2dm account (the 3rd requirement listed above). Once this intent is sent out, you will receive an incoming intent that is suppose to assign you a registration id. We will create a BroadcastReceiver to receive that intent and grab the registration id.&lt;/p&gt;
&lt;pre class=&quot;brush: java; highlight:[5,18,28,40]&quot; title=&quot;C2DMReceiver class that extends BroadcastReceiver&quot;&gt;
public void onReceive(Context context, Intent intent) {

    String action = intent.getAction();
    // The action for registration reply is com.google.android.c2dm.intent.REGISTRATION
    if (action.equals(&quot;com.google.android.c2dm.intent.REGISTRATION&quot;)) {
       Log.d(TAG, &quot;Received C2DM Registration Packet&quot;);
       // Call the handleRegistration function to handle registration
       handleRegistration(context, intent);
    } 
}

private void handleRegistration (Context context, Intent intent) {
    // These strings are sent back by google
    String regId = intent.getStringExtra(&quot;registration_id&quot;);
    String error = intent.getStringExtra(&quot;error&quot;); 
    String unregistered = intent.getStringExtra(&quot;unregistered&quot;);
                
    if (error != null) {
       // If there is an error, then we log the error
       Log.e(TAG, String.format(&quot;Received error: %s\n&quot;, error));
       if (error.equals(&quot;ACCOUNT MISSING&quot;)) {
          // ACCOUNT MISSING is sent back when the device doesn't have a google account registered
          Toast.makeText(context, &quot;Please add a google account to your device.&quot;, Toast.LENGTH_LONG).show();
        } else {
          // Other errors
          Toast.makeText(context, &quot;Registration Error: &quot; + error, Toast.LENGTH_LONG).show();
        }
    } else if (unregistered != null) {
      // This is returned when you are unregistering your device from c2dm
      Log.d(TAG, String.format(&quot;Unregistered: %s\n&quot;, unregistered));
      Toast.makeText(context, &quot;Unregistered: &quot; + unregistered, Toast.LENGTH_LONG).show();

      // TODO: send POST to web server to unregister device from sending list

      //Clear the shared prefs
      ((C2DMSampleApplication)context.getApplicationContext()).setRegId(null);
      //Update our Home Activity
      updateHome(context);

    } else if (regId != null) {
      // You will get a regId if nothing goes wrong and you tried to register a device
      Log.d(TAG, String.format(&quot;Got regId: %s&quot;, regId));
      
      // TODO send regID to server in ANOTHER THREAD

      //Store it into shared prefs
      ((C2DMSampleApplication)context.getApplicationContext()).setRegId(regId);
      //Update our Home Activity
      updateHome(context);
    } 
}
&lt;/pre&gt;
&lt;p&gt;We override the onReceive method in BroadcastReceiver to receive intents. On line 5 we see that we need to check for intents with the action &amp;#8220;com.google.android.c2dm.intent.REGISTRATION&amp;#8221;, once we receive the intent, we can process it to grab the registration id. Google passes back 3 strings to give us the status of registration. We grab those on line 14,15,16. If the error string is present, then an error occurred. I create a Toast message to alert the user. If the unregistered string is present, then the intent is returned from a request to unregister the device. If both are null, then we should have the registration id passed back from google. I chose to store this id in the sharedPreference on the phone. The sharedPreference is stored in a separate Application class (you can see the source code &lt;a href=&quot;https://github.com/iliu/Android-C2DM/blob/master/src/com/liuapps/c2dmsample/C2DMSampleApplication.java&quot;&gt;here&lt;/a&gt;), which is what line 36 and 47 are doing. You would also need to send this registration id to your own web server, as the server needs it to send the message to the phone. However, an important but subtle issue here is that you should do the server requests in a separate thread. However, an Async task in this case should not be used because an Async task needs to return to the thread it stemmed from, but a BroadcastReceiver only exists during the call of onReceive, so it an Async task would fail to return. In this case an Intent Service would be a good choice. In my example there is no web service, so i omit the sending to the web server. Instead, i send it out to the log and copy it to clipboard (so if your trying it on a phone you can text or emial it to yourself), you will need it for the server script.&lt;/p&gt;
&lt;p&gt;Once we obtain the registration id, most of the work of registration has been done! Now we have to setup our app to receive a c2dm message. We can use the same BroadcastReceiver and check for the intent with action &amp;#8220;com.google.android.c2dm.intent.RECEIVE&amp;#8221;. Our complete onReceive code looks like this:&lt;/p&gt;
&lt;pre class=&quot;brush: java; highlight:[11]&quot; title=&quot;C2DMReceiver class that extends BroadcastReceiver&quot;&gt;
public void onReceive(Context context, Intent intent) {

    String action = intent.getAction();
    // The action for registration reply is com.google.android.c2dm.intent.REGISTRATION
    if (action.equals(&quot;com.google.android.c2dm.intent.REGISTRATION&quot;)) {
        Log.d(TAG, &quot;Received C2DM Registration Packet&quot;);
        // Call the handleRegistration function to handle registration
        handleRegistration(context, intent);

        // The action for data reply is com.google.android.c2dm.intent.RECEIVE
    } else if (action.equals(&quot;com.google.android.c2dm.intent.RECEIVE&quot;)) {
        Log.d(TAG, &quot;Received C2DM Data Packet&quot;);
        // Call the handleData function to handle c2dm packet
        handleData(context, intent); 
    }
}
&lt;/pre&gt;
&lt;p&gt;You can choose how you want to handle the data packet. Usually you just want to notify the user to open your application activity to retrieve data from the server. I create a notification so it shows up on the phone&amp;#8217;s status bar. The code looks like this:&lt;/p&gt;
&lt;pre class=&quot;brush: java; &quot; title=&quot;Function to handle the c2dm data messages&quot;&gt;
private void handleData(Context context, Intent intent) {
    String app_name = (String)context.getText(R.string.app_name);
    String message =  intent.getStringExtra(&quot;message&quot;);

    // Use the Notification manager to send notification
    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    // Create a notification using android stat_notify_chat icon. 
    Notification notification = new Notification(android.R.drawable.stat_notify_chat, app_name + &quot;: &quot; + message, 0);

    // Create a pending intent to call the HomeActivity when the notification is clicked
    PendingIntent pendingIntent = PendingIntent.getActivity(context, -1, new Intent(context, HomeActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); // 
    notification.when = System.currentTimeMillis();  
    notification.flags |= Notification.FLAG_AUTO_CANCEL; 
    // Set the notification and register the pending intent to it
    notification.setLatestEventInfo(context, app_name, message, pendingIntent); //
                
    // Trigger the notification
    notificationManager.notify(0, notification);
}
&lt;/pre&gt;
&lt;p&gt;Finally, you want to add permissions and register intent filters in your android manifest so your BroadcastReceiver can receive and send intents to the google c2dm service.&lt;/p&gt;
&lt;pre class=&quot;brush: xml; highlight:[9,10,11,12,13,14,15,16,17,18,19,28,29,30,31]&quot; title=&quot;Application Manifest&quot;&gt;
&amp;lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&amp;gt;
&amp;lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
        package=&quot;com.liuapps.c2dmsample&quot; android:versionCode=&quot;1&quot;
        android:versionName=&quot;1.0&quot;&amp;gt;
        &amp;lt;uses-sdk android:minSdkVersion=&quot;8&quot; /&amp;gt;

        &amp;lt;application android:icon=&quot;@drawable/icon&quot; android:label=&quot;@string/app_name&quot;
                android:name=&quot;.C2DMSampleApplication&quot;&amp;gt;
                &amp;lt;receiver android:name=&quot;.C2DMReceiver&quot;
                        android:permission=&quot;com.google.android.c2dm.permission.SEND&quot;&amp;gt;
                        &amp;lt;intent-filter&amp;gt;
                                &amp;lt;action android:name=&quot;com.google.android.c2dm.intent.RECEIVE&quot; /&amp;gt;
                                &amp;lt;category android:name=&quot;com.liuapps.c2dmsample&quot; /&amp;gt;
                        &amp;lt;/intent-filter&amp;gt;
                        &amp;lt;intent-filter&amp;gt;
                                &amp;lt;action android:name=&quot;com.google.android.c2dm.intent.REGISTRATION&quot; /&amp;gt;
                                &amp;lt;category android:name=&quot;com.liuapps.c2dmsample&quot; /&amp;gt;
                        &amp;lt;/intent-filter&amp;gt;
                &amp;lt;/receiver&amp;gt;
                &amp;lt;activity android:name=&quot;.HomeActivity&quot; android:label=&quot;@string/app_name&quot;&amp;gt;
                        &amp;lt;intent-filter&amp;gt;
                                &amp;lt;action android:name=&quot;android.intent.action.MAIN&quot; /&amp;gt;
                                &amp;lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&amp;gt;
                        &amp;lt;/intent-filter&amp;gt;
                &amp;lt;/activity&amp;gt;
        &amp;lt;/application&amp;gt;

        &amp;lt;uses-permission android:name=&quot;android.permission.WAKE_LOCK&quot; /&amp;gt;
        &amp;lt;uses-permission android:name=&quot;android.permission.INTERNET&quot; /&amp;gt;
        &amp;lt;uses-permission android:name=&quot;com.google.android.c2dm.permission.RECEIVE&quot; /&amp;gt;
        &amp;lt;uses-permission android:name=&quot;com.liuapps.c2dmsample.permission.C2D_MESSAGE&quot; /&amp;gt;
        &amp;lt;permission android:name=&quot;com.liuapps.c2dmsample.permission.C2D_MESSAGE&quot;
                android:protectionLevel=&quot;signature&quot; /&amp;gt;
                
                &amp;lt;permission android:name=&quot;com.liuapps.c2dmsample.SEND_NOTIFICATIONS&quot;
                android:label=&quot;@string/sendPermissionLabel&quot;
                android:description=&quot;@string/sendPermissionSummary&quot;
                android:permissionGroup=&quot;android.permission-group.PERSONAL_INFO&quot;
                android:protectionLevel=&quot;normal&quot; /&amp;gt;

        &amp;lt;permission android:name=&quot;com.liuapps.c2dmsample.RECEIVE_NOTIFICATIONS&quot;
                android:label=&quot;@string/receivePermissionLabel&quot;
                android:description=&quot;@string/receivePermissionSummary&quot;
                android:permissionGroup=&quot;android.permission-group.PERSONAL_INFO&quot;
                android:protectionLevel=&quot;normal&quot; /&amp;gt;

        &amp;lt;uses-permission android:name=&quot;com.liuapps.c2dmsample.SEND_NOTIFICATIONS&quot; /&amp;gt;
        &amp;lt;uses-permission android:name=&quot;com.liuapps.c2dmsample.RECEIVE_NOTIFICATIONS&quot; /&amp;gt;
                
&amp;lt;/manifest&amp;gt;
&lt;/pre&gt;
&lt;p&gt;The intent filters register the receiver class to receive broadcast intents from the google c2dm service. Now at this point, the client app is finished and you can start sending messages to it!&lt;/p&gt;
&lt;p&gt;To send a push notification to your phone, the web service sends a HTTP POST message to google with the registration id, and google back end will forward the message to the phone device. The details can be found &lt;a href=&quot;http://code.google.com/android/c2dm/#push&quot;&gt;here&lt;/a&gt;. We just want a quick way to send messages to our phone, so we will use the &lt;a href=&quot;http://rubygems.org/gems/c2dm&quot;&gt;c2dm gem&lt;/a&gt; written by Amro Mousa to send a message. You need to have &lt;a href=&quot;http://www.ruby-lang.org/en/downloads/&quot;&gt;ruby&lt;/a&gt;, &lt;a href=&quot;http://rubygems.org/pages/download&quot;&gt;rubygems&lt;/a&gt;, and the &lt;a href=&quot;http://rubygems.org/gems/c2dm&quot;&gt;c2dm gem&lt;/a&gt; installed. With those installed, we can write a simple ruby script to send messages to the server.&lt;/p&gt;
&lt;pre class=&quot;brush: ruby; highlight: [17,18,19]&quot; title=&quot;sendC2DM.rb&quot;&gt;
#!/usr/bin/env ruby

require 'rubygems'
require 'c2dm'

settings = YAML::parse( File.open( &quot;config.yml&quot; ) ).transform
regId = settings['regId']
email = settings['email']
password = settings['password']

if ARGV.empty? 
  puts 'Usage: ./sendC2DM &quot;message to be sent&quot;'
  exit
end

puts 'Sending C2DM Packet...'
c2dm = C2DM.new(email, password, settings['app_name'])
notification = { :registration_id =&amp;gt; regId, :data =&amp;gt; { :message =&amp;gt; ARGV[0] }, :collapse_key =&amp;gt; &quot;biteme&quot; }
c2dm.send_notification(notification)
&lt;/pre&gt;
&lt;p&gt;A separate &lt;a href=&quot;https://github.com/iliu/Android-C2DM/blob/master/c2dmServer/config.yml&quot;&gt;config.yml&lt;/a&gt; file should be created and saved in the same folder as the script. Is parsed before the c2dm library is used. You need to copy and paste the registration id received from google (you can do that by copying the logcat from your phone, or texting it to yourself when the registration id is copied to the clipboard in the client app). Lines 17, 18, and 19 do the actual work of creating a message and sending it to the server. The collapse key is a key to tell the google server to collapse messages if two messages with the same collapse key arrive. Here&amp;#8217;s how to use this script:&lt;/p&gt;
&lt;pre class=&quot;brush: shell;&quot;&gt;
ruby sendC2dm.rb &quot;this is my message&quot;
&lt;/pre&gt;
&lt;p&gt;If everything was done correctly, you should see a pop up notification from your phone receiving the push notification! Instant gratification!&lt;/p&gt;
&lt;p&gt;The source code of the complete client app and ruby script can be found in &lt;a href=&quot;https://github.com/iliu/Android-C2DM&quot;&gt;this&lt;/a&gt; repository. I mostly covered how to setup the phone client app to receive push notifications. Hopefully we can start seeing better apps that use less battery but get more real-time updates!&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Learning Android App Development</title>
    <link href="/blog/201109/learning-android-app-development.html" />
    <id>tag:isaacliu.info,2011-09-07:1315380871</id>
    <updated>2011-09-07T07:34:31Z</updated>
    <content type="html">&lt;p&gt;I&amp;#8217;ve been following this online book &lt;a href=&quot;http://ofps.oreilly.com/titles/9781449390501/index.html&quot;&gt;Learning Android&lt;/a&gt; which I would highly recommend to anyone that wants to begin learning how to write android apps. It does a great job explaining the concepts of Android development through a non-trivial sample application &amp;#8211; a twitter application. It also goes through the process of building the app step by step, improving the app when introducing more advanced concepts. This helps show how to use the building blocks that&amp;#8217;s learned to improve an application. It goes through all the building blocks of android apps, and even the more advanced concepts like Android Interface Definition Language (AIDL) and using the Native Development Kit (NDK). I have followed along and built the demo apps while publishing the source code in my &lt;a href=&quot;https://github.com/iliu&quot;&gt;github&lt;/a&gt; repositories. If you want to learn how to write Android apps, follow along the book and I guarantee that you will not be disappointed!&lt;/p&gt;
&lt;p&gt;However, I found that there are some concepts that are missing in this textbook, namely:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;context menus&lt;/li&gt;
	&lt;li&gt;android testing framework&lt;/li&gt;
	&lt;li&gt;c2dm (cloud to device messaging)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you do follow along, here are some things to note:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;The &lt;a href=&quot;http://ofps.oreilly.com/titles/9781449390501/Android_System_Services.html&quot;&gt;System Services&lt;/a&gt; chapter has some bugs in the code posted.&lt;/li&gt;
	&lt;li&gt;The &lt;a href=&quot;http://ofps.oreilly.com/titles/9781449390501/AIDL.html&quot;&gt;AIDL&lt;/a&gt; chapter doesn&amp;#8217;t fully explain how to setup the AIDL client app.&lt;/li&gt;
	&lt;li&gt;From a design standpoint there are several design choices that are poorly made for the app in the text. But it does demonstrate how to code the functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Just be sure to read the comments left on the side, as I have left several comments noting the above issues in the text. Enjoy!&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Adding excerpts to Webby</title>
    <link href="/blog/201108/adding-excerpts-to-webby.html" />
    <id>tag:isaacliu.info,2011-08-31:1314808161</id>
    <updated>2011-08-31T16:29:21Z</updated>
    <content type="html">
&lt;p&gt;So after revisiting my blog I realized that descriptive tutorial entries with code segments are helpful, but they are&amp;#8230;long. The front page of my blog was being dragged way too long by those entries. Thus, I needed to add an &amp;#8220;excerpt&amp;#8221; feature that allows me to partially display an entry and add a &amp;#8216;read more&amp;#8230;&amp;#8217; link for the full post. &lt;a href=&quot;http://reinh.com/&quot;&gt;Rien Heinrichs&lt;/a&gt; has done exactly that with his blog created with Webby, and his &lt;a href=&quot;http://github.com/ReinH/reinh-com/tree/master&quot;&gt;github&lt;/a&gt; page contains the source. I took a look and turns out it was a lot easier than i thought. Here is how to do it:&lt;/p&gt;
&lt;p&gt;First you need to choose which part of the article is the excerpt, i.e. what you want to show on the front page. You can easily do that when you are creating your blog entries. In the .txt file, just add a couple lines of embedded ruby as shown below.&lt;/p&gt;
&lt;pre class=&quot;brush: ruby;&quot; title=&quot;your-entry.txt:&quot;&gt;
&amp;lt;%# The entry meta data is above %&amp;gt;
&amp;lt;% @page[:excerpt] = capture_erb do %&amp;gt; 
   Put the text here that you want to display as the excerpt 
&amp;lt;% end %&amp;gt; 
&amp;lt;%= @page.excerpt %&amp;gt;    # we have to insert the excerpt back into the page 

The rest of your long entry text goes below
&lt;/pre&gt;
&lt;p&gt;Now you need to go to your front page (usually index.html) that displays the blog posts, and check if there is a page excerpt. If there is, display the excerpt, or else display the whole entry. The only tricky part here is that the excerpt doesn&amp;#8217;t exist until the page is rendered, so you want to render the page first, then decide whether to display an excerpt or the entry. See the example code below:&lt;/p&gt;
&lt;pre class=&quot;brush: ruby;&quot; title=&quot;index.txt:&quot;&gt;
&amp;lt;% posts = @pages.find(
       :all,
       :content_type =&amp;gt; 'tumblog',
       :sort_by =&amp;gt; :created_at,
       :reverse =&amp;gt; true
   )
   paginate(posts, 4) do |p| -%&amp;gt;
   &amp;lt;% render(p) unless p.excerpt -%&amp;gt;
   &amp;lt;% if p.excerpt -%&amp;gt;
     &amp;lt;%= RedCloth.new(p.excerpt).to_html%&amp;gt;
     &amp;lt;a href='&amp;lt;%= p.url %&amp;gt;'&amp;gt;Read more...&amp;lt;/a&amp;gt;
   &amp;lt;% else -%&amp;gt;
     &amp;lt;%= render(p) %&amp;gt;
   &amp;lt;% end -%&amp;gt;
&amp;lt;% end -%&amp;gt;
&lt;/pre&gt;
&lt;p&gt;You may notice that RedCloth is used to render the excerpt, this is because i use textile to write my entries, so i want to make sure that the textile in the excerpt gets rendered. Be sure to use whatever markup language engine you choose to render the excerpt.&lt;/p&gt;
&lt;p&gt;You can see some examples in my &lt;a href=&quot;https://github.com/iliu/My-Site&quot;&gt;repository&lt;/a&gt; for this site. Look at the &lt;a href=&quot;https://github.com/iliu/My-Site/blob/master/content/blog/201108/adding-excerpts-to-webby.txt&quot;&gt;source of this entry&lt;/a&gt; and also check out this &lt;a href=&quot;https://github.com/iliu/My-Site/blob/master/content/partials/_post.txt&quot;&gt;partial&lt;/a&gt; that i use to display posts.&lt;/p&gt;
&lt;p&gt;And that&amp;#8217;s it, wasn&amp;#8217;t that easy!? No more detailed posts clogging up your blog index.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Added blog tags in Webby</title>
    <link href="/blog/201108/adding-blog-tags-with-webby.html" />
    <id>tag:isaacliu.info,2011-08-30:1314720497</id>
    <updated>2011-08-30T16:08:17Z</updated>
    <content type="html">&lt;p&gt;I just found out that Webby is no long under active development =(. What a sad piece of news. However, with the current Webby version, it&amp;#8217;s still good enough for me to maintain my blog. Since it&amp;#8217;s relatively easy to extend, I decided to add tags to Webby.&lt;/p&gt;
&lt;p&gt;I saved a link to &lt;a href=&quot;https://github.com/aq1018/aaron-blog&quot;&gt;Aarom Qian&lt;/a&gt; blog post about adding tags to Webby a long time ago, but unfortunately the blog is no longer online. I dug around and was able to find his &lt;a href=&quot;https://github.com/aq1018/aaron-blog&quot;&gt;github&lt;/a&gt;  repository and the raw textile/erb entry in there. You can see it here: &lt;a href=&quot;/docs/adding-blog-tags-using-webby.txt&quot;&gt;Adding Blog Tags using Webby&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In general his directions were pretty clear and everything worked well. It explained how to setup the tags framework and how to use rake tasks to generate the tag pages. However, I did need to make some minor hacks to get it working. I&amp;#8217;ll try to add a tutorial blog entry if i have time. I&amp;#8217;d also be more than happy to answer any questions if anyone is having trouble implementing tags.&lt;/p&gt;
&lt;p&gt;In the mean time, try to follow along his post, or check out the source of this blog on my &lt;a href=&quot;https://github.com/iliu/My-Site&quot;&gt;github&lt;/a&gt; account. Enjoy!&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Caching Facebook API Call Results in Ruby on Rails</title>
    <link href="/blog/201009/caching-facebook-api-call-results-in-ruby-on-rails.html" />
    <id>tag:isaacliu.info,2010-09-25:1285439451</id>
    <updated>2010-09-25T18:30:51Z</updated>
    <content type="html">

&lt;p&gt;In my last entry I covered how to integrate facebook api with ruby on rails using fb_graph gem. There is one problem with integrating the facebook api with your website however, and that is if facebook goes down, then your site also crashes. Yesterday facebook went down, along with the graph.facebook.com api. As a result, one of the sites I was managing went down since it kept waiting to authenticate with the facebook graph api. Now there are definitely other solutions around this, but I also found that when using facebook integration on websites, the websites load a little slower because it has to connect to facebook first, fetch the data, then render the webpage, so the performance of the webpage will heavily depend on the speed of the facebook fetch. Thus, I decided to implement a caching scheme that synchronizes a local database with information fetched from facebook. This way my page only needs to access a local database every time it&amp;#8217;s queried, but the database is synced with facebook information, so it&amp;#8217;s pretty much like I&amp;#8217;m accessing facebook!!&lt;/p&gt;
&lt;p&gt;The basic idea is as follows:&lt;/p&gt;
&lt;ol&gt;
	&lt;li&gt;Create a table that stores the information you want from facebook&lt;/li&gt;
	&lt;li&gt;Setup a ruby script that uses the fb_graph gem calls to update the database&lt;/li&gt;
	&lt;li&gt;Setup that ruby script as a cron job on the webserver  and run it as frequent as you like&lt;/li&gt;
	&lt;li&gt;Change your fb_gem calls to local database calls&lt;/li&gt;
	&lt;li&gt;Sit back and let the webserver do the hard work!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;First, lets setup a database table storing the information that you want. I will build on my previous example of the fb_graph gem that polled events from a facebook page using the graph api provided by facebook. For this example we will just pull the events from a facebook page, so we&amp;#8217;ll just create a table storing the event information. We&amp;#8217;ll use the rails generator to generate a model to store the facebook events. This will generate the correct model files and migrations.&lt;/p&gt;
&lt;pre class=&quot;brush: shell;&quot; title=&quot;Run this command in your terminal:&quot;&gt;
% rails g model facebook_event name:string start_time:datetime \
end_time:datetime location:string description:text \
updated_time:datetime identifier:string picture:string
&lt;/pre&gt;
&lt;p&gt;This command will generate a migration file that will setup the table which will store the information pulled from facebook. Change the column names according to whatever information you want cached. We can look at the migration file generated from the above file:&lt;/p&gt;
&lt;pre class=&quot;brush: rails; highlight:[4,5,6,7,8,9,10,11]&quot; title=&quot;db/migrate/#####_create_facebook_events.rb:&quot;&gt;
class CreateFacebookEvents &amp;lt; ActiveRecord::Migration
  def self.up
    create_table :facebook_events do |t|
      t.string :name
      t.datetime :start_time
      t.datetime :end_time
      t.string :location
      t.text :description
      t.datetime :updated_time
      t.string :identifier
      t.string :picture

      t.timestamps
    end
  end

  def self.down
    drop_table :facebook_events
  end
end
&lt;/pre&gt;
&lt;p&gt;The highlighted lines show the columns of the table. Now we run &amp;#8220;rake db:migrate&amp;#8221; to run the migration. Once the table is created, we start writing our ruby script to call the facebook graph api. I created a &amp;#8220;jobs&amp;#8221; folder under my rails root folder to store the ruby scripts. Ruby on rails has a &amp;#8220;runner&amp;#8221; script that runs ruby scripts in the site environment, so we will use that to run our script. First lets move the code to access facebook out of our controller and into the script:&lt;/p&gt;
&lt;pre class=&quot;brush: ruby; first-line: 30&quot; title=&quot;$RAILS_ROOT/jobs/update_facebook.rb:&quot;&gt;
##################
# FACEBOOK ACCESS
##################
config = YAML::load(File.open(&quot;#{RAILS_ROOT}/config/facebook.yml&quot;));
gscc_app = FbGraph::Application.new(config['production']['app_id']);
access_token = gscc_app.get_access_token(config['production']['client_secret']);
page_id = config['production']['page_id'];

page = FbGraph::Page.new(page_id, :access_token =&amp;gt; access_token).fetch;
&lt;/pre&gt;
&lt;p&gt;We need to know which events were updated since our last fetch, so we use the updated_time value from facebook and compare it to the stored updated_time value in our database. Here&amp;#8217;s the code that goes through the database to find the latest updated_time in all the rows. If there are no events, then we set the last update time of the database (lutdatabase) to the earliest possible time (line 24).&lt;/p&gt;
&lt;pre class=&quot;brush: ruby; first-line: 22&quot; title=&quot;$RAILS_ROOT/jobs/update_facebook.rb:&quot;&gt;
query = FacebookEvent.find(:first, :order =&amp;gt; &quot;updated_time DESC&quot;);
if query.nil?
  lutdatabase = Time.at(0);
else
  lutdatabase = query.updated_time;
end
&lt;/pre&gt;
&lt;p&gt;Since the updated_time value for an event isn&amp;#8217;t fetched by default in the connections from page in the fb_graph gem, we need explicitly fetch the event using it&amp;#8217;s identifier (line 44) to access its updated_time. After we fetch each event, we compare the updated_time. If the updated_time is after what&amp;#8217;s in our database, then we know we need to update that event. Line 47 fetches the event from our database. If an event is found, then we update the entry, or else we create a new entry and enter it in our database.&lt;/p&gt;
&lt;pre class=&quot;brush: ruby; first-line: 43; highlight: [44, 47]&quot; title=&quot;$RAILS_ROOT/jobs/update_facebook.rb:&quot;&gt;
page.events.each do |e|
  ie = FbGraph::Event.new(e.identifier, :access_token =&amp;gt; access_token).fetch;
  if ie.updated_time &amp;gt; lutdatabase
    puts &quot;\n**found dirty facebook event - &quot;+ie.name
    edatabase = FacebookEvent.find(:first, :conditions =&amp;gt; [ &quot;identifier = ?&quot;, ie.identifier ]);
    if edatabase
      puts &quot;***found database entry, updating the entry &quot;+edatabase.name
      edatabase.name = ie.name
      edatabase.start_time = ie.start_time
      edatabase.end_time = ie.end_time
      edatabase.location = ie.location
      edatabase.description = ie.description
      edatabase.updated_time = ie.updated_time
      edatabase.identifier = ie.identifier
      edatabase.picture = ie.picture
      edatabase.save
      puts &quot;***updated &quot;+edatabase.name
    else
      puts &quot;***database entry not found, adding to database &quot;+ie.name
      edatabase = FacebookEvent.new( :name =&amp;gt; ie.name, :start_time =&amp;gt; ie.start_time, :end_time =&amp;gt; ie.end_time, :location =&amp;gt; ie.location, :description =&amp;gt; ie.description, :updated_time =&amp;gt; ie.updated_time, :identifier =&amp;gt; ie.identifier, :picture =&amp;gt; ie.picture )
      edatabase.save
      puts &quot;***saved &quot;+edatabase.name
    end
  end
end
&lt;/pre&gt;
&lt;p&gt;However, just updating and fetching events is not enough. If an event is deleted from facebook, the above code would not delete it from our database. So we add the following code to compare the events from our database to the events in facebook. If an event appears in our database but not on facebook, we know that it no longer exists.&lt;/p&gt;
&lt;pre class=&quot;brush: ruby; first-line: 74&quot; title=&quot;$RAILS_ROOT/jobs/update_facebook.rb:&quot;&gt;
all_events = FacebookEvent.find(:all);
page.events.each do |e|
  all_events.delete_if { |ae| ae.identifier == e.identifier }
end
if all_events.size &amp;gt; 0
  puts &quot;Found deleted events! Deleting events from database...&quot;
  all_events.each do |e|
    puts &quot;\n***deleting event &quot;+e.name
    e.delete
    puts &quot;****deleted.&quot;
  end
end
&lt;/pre&gt;
&lt;p&gt;Now we have all of the facebook access and update code, you can actually try running this script by using the &amp;#8220;rails runner&amp;#8221; command. You should see the output and events being added to your database. Now there is one optional step that can be added to ensure that cron won&amp;#8217;t run two instances of the same script. If you setup your script to run every one minute but the one instance of the script takes longer than that, we don&amp;#8217;t want another instance to start up and start overwriting the database. So we can add code to obtain a file lock. The lock won&amp;#8217;t be released until the script finishes, and future instances of the script will need to obtain the lock before it starts (add this to the beginning and end of your code &amp;#8211; see the line numbers below).&lt;/p&gt;
&lt;pre class=&quot;brush: ruby; first-line:3; highlight: [8,9,10,11]&quot;title=&quot;$RAILS_ROOT/jobs/update_facebook.rb:&quot;&gt;
t1 = Time.now
puts &quot;================&quot;
puts Time.now.strftime(&quot;%Y%m%d-%H%M%S&quot;) + &quot; : &quot; + __FILE__ + &quot; starting...&quot;

puts &quot;\ngrabbing file lock.....&quot;
if File.new(__FILE__).flock(File::LOCK_EX | File::LOCK_NB) == false
  puts &quot;*** can't lock file, another instance of script running?  exiting&quot;
  exit 1
end
&lt;/pre&gt;
&lt;pre class=&quot;brush: ruby; first-line:93&quot; title=&quot;$RAILS_ROOT/jobs/update_facebook.rb:&quot;&gt;
t2 = Time.now
puts Time.now.strftime(&quot;%Y%m%d-%H%M%S&quot;) + &quot; : &quot; + __FILE__ + &quot; finished  #{t2 - t1} secs&quot;
puts &quot;================&quot;
&lt;/pre&gt;
&lt;p&gt;You can view the whole source &lt;a href=&quot;http://github.com/iliu/mysite-examples/blob/fb_graph_cache/jobs/update_facebook.rb&quot;&gt;here&lt;/a&gt;.  Great, now the hard part is out of the way, we just need to setup a cron job on the webserver that executes this as often as we want. Lets first write a shell script to wrap around this ruby script so we can call it from the cron job.&lt;/p&gt;
&lt;pre class=&quot;brush: bash; highlight: [16]&quot; title=&quot;$RAILS_ROOT/jobs/wrap_update_facebook.sh&quot;&gt;
#!/bin/bash
pushd $(dirname &quot;${0}&quot;) &amp;gt; /dev/null
basedir=$(pwd -L)
# Use &quot;pwd -P&quot; for the path without links. man bash for more info.
popd &amp;gt; /dev/null
export RAILS_ROOT=`dirname ${basedir}`

# detect if we're on the web server
sname=`basename $RAILS_ROOT`
if [ $sname == &quot;myserver.org&quot; ]
then
    echo &quot;===On web server, changing rails environment===&quot;
    export RAILS_ENV=production
fi

rails runner ${basedir}/update_facebook.rb
&lt;/pre&gt;
&lt;p&gt;Line 16 is what runs the update_facebook script using rails runner. Make sure rails is in your path. Lines 10 to 14 detects if we&amp;#8217;re on the webserver, and changes the rails environment to production. Make sure to change the permissions of the script to executable, then test it out by running the script. Once it is satisfactory, we can setup a cron job. &amp;#8220;cron&amp;#8221; is the linux utility to run a scheduled job. If you are using a windows web host then please search online to see how you can setup a recurring job on the web host. Lets add the job by running crontab:&lt;/p&gt;
&lt;pre class=&quot;brush: shell; &quot; title=&quot;Run this command in your terminal:&quot;&gt;
% crontab -e
&lt;/pre&gt;
&lt;p&gt;Setting up a cron job might seem overwhelming, but it&amp;#8217;s actually really simple. The following syntax applies for each entry&lt;/p&gt;
&lt;pre class=&quot;brush: shell; gutter: false;&quot;&gt;
*     *     *   *    *        command to be executed
-     -     -   -    -
|     |     |   |    |
|     |     |   |    +----- day of week (0 - 6) (Sunday=0)
|     |     |   +------- month (1 - 12)
|     |     +--------- day of        month (1 - 31)
|     +----------- hour (0 - 23)
+------------- min (0 - 59)
&lt;/pre&gt;
&lt;p&gt;Here are some examples jobs:&lt;/p&gt;
&lt;pre class=&quot;brush: shell;&quot; title=&quot;Example cron jobs&quot;&gt;
#runs at 8pm every day
0 20 * * * echo &quot;hello&quot;

#runs every 5 minutes
*/5 * * * * echo &quot;I run every 5 minutes&quot;
&lt;/pre&gt;
&lt;p&gt;So I added the following entry which runs at 9am and 9pm every day:&lt;/p&gt;
&lt;pre class=&quot;brush: shell;&quot;&gt;
0 9,21 * * * &amp;lt;my rails_root&amp;gt;/jobs/wrap_update_facebook.sh
&lt;/pre&gt;
&lt;p&gt;The last step is to modify the controller code and switch out the code that polls from facebook and insert the code to poll from the database:&lt;/p&gt;
&lt;pre class=&quot;brush: ruby; highlight: [17]&quot; title=&quot;$RAILS_ROOT/app/controllers/home_controller.rb:&quot;&gt;
class HomeController &amp;lt; ApplicationController
  def index
    #Load facebook.yml info
    #config = YAML::load(File.open(&quot;#{RAILS_ROOT}/config/facebook.yml&quot;));

    #Instantiate a new application with our app_id so we can get an access token
    #my_app = FbGraph::Application.new(config['production']['app_id']);

    #Actually get the access token
    #acc_tok = my_app.get_access_token(config['production']['client_secret']);

    #Instantiate a new page class using the page_id specified 
    #@page = FbGraph::Page.new(config['production']['page_id'], :access_token =&amp;gt; acc_tok).fetch;

    #Grab the events from the page 
    #events = @page.events.sort_by{|e| e.start_time};
    events = FacebookEvent.find(:all)
    
    #Get the events that are upcoming
    @upcoming_events = events.find_all{|e| e.start_time &amp;gt;= Time.now};

    #Get the events that have passed
    @past_events = events.find_all{|e| e.start_time &amp;lt; Time.now}.reverse;
  end
end
&lt;/pre&gt;
&lt;p&gt;We commented all the facebook access code, and on line 17 added the code to grab events from our database. Everything else stays the same! If your using my previous code, you will also need to remove the line that displays the page name. Now run your rails app and it will poll information from the local database instead of facebook, while your database is synced with facebook!&lt;/p&gt;
&lt;p&gt;The full working example of this code can be found &lt;a href=&quot;http://github.com/iliu/mysite-examples/tree/fb_graph_cache&quot;&gt;here&lt;/a&gt; along with all my example codes. Once you clone the repository make sure to check out the remote branch &amp;#8220;fb_graph_cache&amp;#8221; to get the working source code!&lt;/p&gt;
&lt;pre class=&quot;brush: shell; gutter: false; highlight:[1]&quot; title=&quot;Source code link:&quot;&gt;
http://github.com/iliu/mysite-examples/tree/fb_graph_cache
&lt;/pre&gt;</content>
  </entry>
  
  <entry>
    <title>RoR and Facebook With fb_graph</title>
    <link href="/blog/201009/ror-and-facebook-with-fb-graph.html" />
    <id>tag:isaacliu.info,2010-09-16:1284663600</id>
    <updated>2010-09-16T19:00:00Z</updated>
    <content type="html">

&lt;p&gt;&lt;b&gt;(09/25/10) Edit:&lt;/b&gt; A working version of this source code is uploaded &lt;a href=&quot;http://github.com/iliu/mysite-examples/tree/fb_graph_gem&quot;&gt;here&lt;/a&gt;. Download it and follow the README to try it out!&lt;/p&gt;
&lt;p&gt;While working on our church website, integrating with our church&amp;#8217;s facebook fan page became a desired feature so our site maintenance team can just add an event on facebook and have it synced up to our website.&lt;/p&gt;
&lt;p&gt;After learning about the &lt;a href=&quot;http://developers.facebook.com/docs/api/&quot;&gt;facebook graph api&lt;/a&gt;, I was planning to write a library for ruby that sent out the queries and parsed the results to display on our site. However, I found the &lt;a href=&quot;http://github.com/nov/fb_graph&quot;&gt;fb_graph gem&lt;/a&gt; written by Nov Matake and decided to try it out. The experience was extremely positive and Nov was very helpful and prompt in responding to my emails. Here&amp;#8217;s how I integrated the facebook graph api into a ruby on rails app:&lt;/p&gt;
&lt;p&gt;First things first, lets create a new rails application, and we&amp;#8217;ll name it &amp;#8220;fb_graph_app&amp;#8221;.&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; % rails new fb_graph_app
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With Rails 3.0 it&amp;#8217;s easy to install the required gems. We&amp;#8217;ll go into the Gemfile specify that we need the fb_graph gem:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Gemfile &lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 2&lt;/span&gt; source &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;http://rubygems.org&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 3&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt; 4&lt;/span&gt; gem &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;rails&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;3.0.0&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 5&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt; 6&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Bundle edge Rails instead:&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 7&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# gem 'rails', :git =&amp;gt; 'git://github.com/rails/rails.git'&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 8&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt; 9&lt;/span&gt; gem &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sqlite3-ruby&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;, &lt;span class=&quot;sy&quot;&gt;:require&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;sqlite3&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;#--------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Adding the following line tells rails we need the fb_graph gem installed&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;#--------------------------------------------------------------------------&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;14&lt;/span&gt; gem &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;fb_graph&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;15&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt;16&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;#....truncated the remaining file...&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Once that&amp;#8217;s saved we can install the required gems with the following command:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; % bundle install
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now that fb_graph is installed we can start making facebook graph API! Lets first create a controller and get modify the home page. We will first generate a simple controller called home with one action called index:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; % rails g controller home index
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;With that command the controller and view for &amp;#8220;home&amp;#8221; will be generated. Now we can map the root of the server to this action by changing the config/route.rb file:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;#config/routes.tb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;co&quot;&gt;FbGraphApp&lt;/span&gt;::&lt;span class=&quot;co&quot;&gt;Application&lt;/span&gt;.routes.draw &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 3&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt; 4&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;#...truncated commented stuff...&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 5&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt; 6&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# You can have the root of your site routed with &amp;quot;root&amp;quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 7&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# just remember to delete public/index.html.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 8&lt;/span&gt; root &lt;span class=&quot;sy&quot;&gt;:to&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;home#index&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 9&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# See how all your routes lay out with &amp;quot;rake routes&amp;quot;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;11&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# This is a legacy wild controller route that's not recommended for RESTful applications.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# Note: This route will make all actions in every controller accessible via GET requests.&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;# match ':controller(/:action(/:id(.:format)))'&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;15&lt;/span&gt; &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now log into your facebook account and register for a new app &lt;a href=&quot;http://www.facebook.com/developers/apps.php&quot;&gt;here&lt;/a&gt;. Using the app_id, API key and application secret we are going to create a config file facebook.yml.&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;#config/facebook.yml&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;ke&quot;&gt;development&lt;/span&gt;: &lt;span class=&quot;v&quot;&gt;&amp;amp;defaults&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 3&lt;/span&gt;   &lt;span class=&quot;ke&quot;&gt;app_id&lt;/span&gt;: &lt;span class=&quot;c&quot;&gt;#your app_id&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 4&lt;/span&gt;   &lt;span class=&quot;ke&quot;&gt;client_id&lt;/span&gt;: &lt;span class=&quot;c&quot;&gt;#your api key&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 5&lt;/span&gt;   &lt;span class=&quot;ke&quot;&gt;client_secret&lt;/span&gt;: &lt;span class=&quot;c&quot;&gt;#your client secret&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 6&lt;/span&gt;   &lt;span class=&quot;ke&quot;&gt;perms&lt;/span&gt;: &lt;span class=&quot;s&quot;&gt;offline_access,manage_pages&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 7&lt;/span&gt;   &lt;span class=&quot;ke&quot;&gt;page_id&lt;/span&gt;: &lt;span class=&quot;c&quot;&gt;#the facebook page id that you wish you pull events from&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 8&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt; 9&lt;/span&gt; &lt;span class=&quot;ke&quot;&gt;test&lt;/span&gt;:
&lt;span class=&quot;no&quot;&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class=&quot;cv&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;: &lt;span class=&quot;gv&quot;&gt;*defaults&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;11&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;ke&quot;&gt;production&lt;/span&gt;:
&lt;span class=&quot;no&quot;&gt;13&lt;/span&gt;   &lt;span class=&quot;cv&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;: &lt;span class=&quot;gv&quot;&gt;*defaults&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;For this demo purpose we&amp;#8217;re just going to pull the events from a facebook &amp;#8220;Page&amp;#8221;. Our church has the facebook page &lt;a href=&quot;http://www.facebook.com/developers/apps.php#!/pages/Cupertino-CA/Good-Shepherd-Christian-Community/187141722947?ref=ts&quot;&gt;here&lt;/a&gt;, so i&amp;#8217;ll grab the page_id from the link and insert in facebook.yml for the page_id. With that setup we can now go into our home controller to add in the calls to grab events. So lets add the following code in our home controller:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;#app/controllers/home_controller.rb&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;r&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;cl&quot;&gt;HomeController&lt;/span&gt; &amp;lt; &lt;span class=&quot;co&quot;&gt;ApplicationController&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 3&lt;/span&gt;   &lt;span class=&quot;r&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;fu&quot;&gt;index&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 4&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;#Load facebook.yml info&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 5&lt;/span&gt;     config = &lt;span class=&quot;co&quot;&gt;YAML&lt;/span&gt;::load(&lt;span class=&quot;co&quot;&gt;File&lt;/span&gt;.open(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;idl&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;co&quot;&gt;RAILS_ROOT&lt;/span&gt;&lt;span class=&quot;idl&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/config/facebook.yml&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;));
&lt;span class=&quot;no&quot;&gt; 6&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt; 7&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;#Instantiate a new application with our app_id so we can get an access token&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 8&lt;/span&gt;     my_app = &lt;span class=&quot;co&quot;&gt;FbGraph&lt;/span&gt;::&lt;span class=&quot;co&quot;&gt;Application&lt;/span&gt;.new(config[&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;][&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;app_id&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;]);
&lt;span class=&quot;no&quot;&gt; 9&lt;/span&gt;     acc_tok = my_app.get_access_token(config[&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;][&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;client_secret&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;]);
&lt;span class=&quot;no&quot;&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt;11&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;#Instantiate a new page class using the page_id specified &lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;12&lt;/span&gt;     &lt;span class=&quot;iv&quot;&gt;@page&lt;/span&gt; = &lt;span class=&quot;co&quot;&gt;FbGraph&lt;/span&gt;::&lt;span class=&quot;co&quot;&gt;Page&lt;/span&gt;.new(config[&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;production&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;][&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;page_id&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;], &lt;span class=&quot;sy&quot;&gt;:access_token&lt;/span&gt; =&amp;gt; acc_tok).fetch;
&lt;span class=&quot;no&quot;&gt;13&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt;14&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;#Grab the events from the page &lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;15&lt;/span&gt;     events = &lt;span class=&quot;iv&quot;&gt;@page&lt;/span&gt;.events.sort_by{|e| e.start_time};
&lt;span class=&quot;no&quot;&gt;16&lt;/span&gt;     
&lt;span class=&quot;no&quot;&gt;17&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;#Get the events that are upcoming&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;18&lt;/span&gt;     &lt;span class=&quot;iv&quot;&gt;@upcoming_events&lt;/span&gt; = events.find_all{|e| e.start_time &amp;gt;= &lt;span class=&quot;co&quot;&gt;Time&lt;/span&gt;.now};
&lt;span class=&quot;no&quot;&gt;19&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt;&lt;strong&gt;20&lt;/strong&gt;&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;#Get the events that have passed&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;21&lt;/span&gt;     &lt;span class=&quot;iv&quot;&gt;@past_events&lt;/span&gt; = events.find_all{|e| e.start_time &amp;lt; &lt;span class=&quot;co&quot;&gt;Time&lt;/span&gt;.now}.reverse;
&lt;span class=&quot;no&quot;&gt;22&lt;/span&gt;   &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;23&lt;/span&gt; &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The above code first reads the facebook.yml file to obtain the facebook app information (line 5). It then uses the information to obtain an access token (line 7,8) so we can access events and more information &lt;strong&gt;[Thanks Nov for pointing this out, for the sake of this example you don&amp;#8217;t need an access token because all information accessed from facebook is public. However, if you want to access information that&amp;#8217;s not public (i.e. who rsvped for an event) then you need an access token]&lt;/strong&gt;. Once an access token is obtained, we can use it access information from the page. We store the page information in an instance variable so we can display in the view later. We also use the connections provided by fb_graph to access the events that are posted in for that page (line 15). Once we get the events we sort it by the start time. Then we create 2 instance variables that store the upcoming events and the events that have already passed (line 18 and 21).&lt;/p&gt;
&lt;p&gt;Once we have the event information we can display it in our view.&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;c&quot;&gt;&amp;lt;!-- app/views/home/index.html.erb --&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 2&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Home#index&lt;span class=&quot;ta&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 3&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Find me in app/views/home/index.html.erb&lt;span class=&quot;ta&quot;&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 4&lt;/span&gt; 
&lt;span class=&quot;no&quot;&gt; 5&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;&lt;span class=&quot;c&quot;&gt;&amp;lt;%= @page.name %&amp;gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 6&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;storng&amp;gt;&lt;/span&gt;Upcoming Events:&lt;span class=&quot;ta&quot;&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;br&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 7&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;hr&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 8&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;&amp;lt;% @upcoming_events.each do |event| %&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 9&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;&amp;lt;%= event.name %&amp;gt;&lt;/span&gt; - &lt;span class=&quot;c&quot;&gt;&amp;lt;%= event.start_time %&amp;gt;&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;br&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;&amp;lt;% end %&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;11&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;br&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;12&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;br&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;13&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;storng&amp;gt;&lt;/span&gt;Past Events:&lt;span class=&quot;ta&quot;&gt;&amp;lt;/strong&amp;gt;&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;br&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;14&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;hr&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;15&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;&amp;lt;% @past_events.each do |event| %&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;16&lt;/span&gt;     &lt;span class=&quot;c&quot;&gt;&amp;lt;%= event.name %&amp;gt;&lt;/span&gt; - &lt;span class=&quot;c&quot;&gt;&amp;lt;%= event.start_time %&amp;gt;&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;br&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;/&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;17&lt;/span&gt;   &lt;span class=&quot;c&quot;&gt;&amp;lt;% end %&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now we can see the results by running the web server in rails using the following command:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; % rails server
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;By pointing your browser to &lt;em&gt;localhost:3000&lt;/em&gt; you should now see a simple page with your facebook page name and the events listed from your facebook page in your ruby on rails app! This is a very simple example of using fb_graph to access the facebook graph api to get data from facebook. To learn more about the facebook graph api see &lt;a href=&quot;http://developers.facebook.com/docs/api&quot;&gt;here&lt;/a&gt;. Nov Matake also has a sample facebook app and documentation of the fb_graph api with some examples &lt;a href=&quot;http://github.com/nov/fb_graph&quot;&gt;here&lt;/a&gt;. Note that is a simplified example, and there are several optimizations that could be done. I simply wanted a quick and easy tutorial to get people started with using the fb_graph gem. Hopefully this can help you integrate facebook into your next ruby on rails app!&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Zoom:1 IE Fix</title>
    <link href="/blog/201004/zoom-1-ie-fix.html" />
    <id>tag:isaacliu.info,2010-04-28:1272470829</id>
    <updated>2010-04-28T09:07:09-07:00</updated>
    <content type="html">&lt;p&gt;After playing around with the layout and jQuery for a while, Internet Explorer 8 was really pissing me off. Everything that worked in Firefox, Safari Chrome didn&amp;#8217;t work properly in Internet Explorer 8. I wonder why that browser is still around&amp;#8230; Anyhow, I later found a quick solution that can fix most of the IE problems, and that&amp;#8217;s the zoom:1 fix! All you need to do is add the following line to your css:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ty&quot;&gt;div&lt;/span&gt; {
&lt;span class=&quot;no&quot;&gt;2&lt;/span&gt;     &lt;span class=&quot;ke&quot;&gt;zoom&lt;/span&gt;: &lt;span class=&quot;fl&quot;&gt;1&lt;/span&gt;; 
&lt;span class=&quot;no&quot;&gt;3&lt;/span&gt; }
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;And just that like, every problem that I was having disappeared. I can&amp;#8217;t really explain why, and apparently neither can &lt;a href=&quot;http://www.bennadel.com/blog/1354-The-Power-Of-ZOOM-Fixing-CSS-Issues-In-Internet-Explorer.htm&quot;&gt;this blogger&lt;/a&gt;. But the point is, use it and save yourself some trouble. Microsoft, when are you going to figure it out&amp;#8230;?&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Webby Tricks - JQuery</title>
    <link href="/blog/201004/webby-tricks-jquery.html" />
    <id>tag:isaacliu.info,2010-04-26:1272300292</id>
    <updated>2010-04-26T09:44:52-07:00</updated>
    <content type="html">

&lt;p&gt;Since I&amp;#8217;ve had such a positive experience with &lt;a href=&quot;http://webby.rubyforge.org/&quot;&gt;Webby&lt;/a&gt;, I&amp;#8217;ve decided to blog a little about some stuff I&amp;#8217;ve figured out when using &lt;em&gt;webby&lt;/em&gt;. I know there are some amount of tutorial I&amp;#8217;ve found only for Webby, so I&amp;#8217;ll try to not repeat a lot of them. When I constructed this blog using webby, I wanted the dates to be in the format of &lt;em&gt;&amp;#8220;2 minutes ago&amp;#8221;&lt;/em&gt; or &lt;em&gt;&amp;#8220;1 month ago,&amp;#8221;&lt;/em&gt; instead of absolute dates. This cannot be achieved by using webby alone, since these dates can only be changed when the website is build. Thus, a little java script magic is needed. &lt;a href=&quot;http://jquery.com/&quot;&gt;JQuery&lt;/a&gt; is a javascript library that allows plugins and has several AJAX functions. In particular, I&amp;#8217;m using the &lt;a href=&quot;http://timeago.yarp.com/&quot;&gt;Timeago&lt;/a&gt; plugin for JQuery. What this javascript does is turn any date you wish on your page into the &amp;#8220;timeago&amp;#8221; format. It also auto updates itself, so it&amp;#8217;s perfect to be used with webby. First you want to follow the links above and download both the plugin and JQuery (go ahead and get the min version). In your main layout page (in the &lt;em&gt;layouts&lt;/em&gt; folder), add the following lines between the &amp;lt;head&amp;gt; tags:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;script&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/scripts/jquery.min.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;type&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;2&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;script&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;src&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;/scripts/jquery.timeago.js&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;type&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;script&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;type&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;text/javascript&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;4&lt;/span&gt;   // initialize plugin code
&lt;span class=&quot;no&quot;&gt;5&lt;/span&gt;   jQuery(document).ready(function() {
&lt;span class=&quot;no&quot;&gt;6&lt;/span&gt;   jQuery(&amp;quot;abbr.timeago&amp;quot;).timeago();
&lt;span class=&quot;no&quot;&gt;7&lt;/span&gt;   });
&lt;span class=&quot;no&quot;&gt;8&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;When using webby, I created a &lt;em&gt;scripts&lt;/em&gt; folder in my &lt;em&gt;contents&lt;/em&gt; folder to hold my javascripts. That&amp;#8217;s why the src of the &amp;lt;scripts&amp;gt; tag is &lt;em&gt;/scripts/jquery.*&lt;/em&gt;. After doing this, then the javascript will find all the tags with the following format:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;abbr&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;timeago&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;title&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;2008-07-17T09:24:17Z&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;gt;&lt;/span&gt;July 17, 2008&lt;span class=&quot;ta&quot;&gt;&amp;lt;/abbr&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;into this:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;abbr&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;timeago&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;title&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;July 17, 2008&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;gt;&lt;/span&gt;about a year ago&lt;span class=&quot;ta&quot;&gt;&amp;lt;/abbr&amp;gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;which will yields the following: &lt;strong&gt;&lt;abbr class=&quot;timeago&quot; title=&quot;2008-07-17T09:24:17Z&quot;&gt;July 17, 2008&lt;/abbr&gt;&lt;/strong&gt;. Notice the time format in the &amp;lt;abbr&amp;gt; tag must be in an &lt;a href=&quot;http://en.wikipedia.org/wiki/ISO_8601&quot;&gt;ISO8691&lt;/a&gt; timestamp. When using &lt;em&gt;webby&lt;/em&gt;, since you can use the erb filter, there are several ways to turn dates into the correct format. Here I show one way i used:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt; 1&lt;/span&gt; &lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;idl&quot;&gt;&amp;lt;%&lt;/span&gt;  recent = &lt;span class=&quot;iv&quot;&gt;@pages&lt;/span&gt;.find(
&lt;span class=&quot;no&quot;&gt; 2&lt;/span&gt;        &lt;span class=&quot;sy&quot;&gt;:limit&lt;/span&gt; =&amp;gt; &lt;span class=&quot;i&quot;&gt;6&lt;/span&gt;,
&lt;span class=&quot;no&quot;&gt; 3&lt;/span&gt;        &lt;span class=&quot;sy&quot;&gt;:content_type&lt;/span&gt; =&amp;gt; &lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;tumblog&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;'&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;no&quot;&gt; 4&lt;/span&gt;        &lt;span class=&quot;sy&quot;&gt;:sort_by&lt;/span&gt; =&amp;gt; &lt;span class=&quot;sy&quot;&gt;:created_at&lt;/span&gt;,
&lt;span class=&quot;no&quot;&gt; 5&lt;/span&gt;        &lt;span class=&quot;sy&quot;&gt;:reverse&lt;/span&gt; =&amp;gt; &lt;span class=&quot;pc&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 6&lt;/span&gt;    )
&lt;span class=&quot;no&quot;&gt; 7&lt;/span&gt;    recent.each &lt;span class=&quot;r&quot;&gt;do&lt;/span&gt; |e| &lt;span class=&quot;idl&quot;&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;;
&lt;span class=&quot;no&quot;&gt; 8&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;li&amp;gt;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;idl&quot;&gt;&amp;lt;%=&lt;/span&gt; link_to_page(e); &lt;span class=&quot;idl&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt; 9&lt;/span&gt; &lt;span class=&quot;ta&quot;&gt;&amp;lt;div&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;gt;&lt;/span&gt;[ &lt;span class=&quot;ta&quot;&gt;&amp;lt;abbr&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;class&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;timeago&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;an&quot;&gt;title&lt;/span&gt;=&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;idl&quot;&gt;&amp;lt;%=&lt;/span&gt; e.created_at.utc.strftime(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;%Y-%m-%dT%H:%M:%SZ&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;idl&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;gt;&lt;/span&gt;&lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;idl&quot;&gt;&amp;lt;%=&lt;/span&gt; e.created_at.strftime(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;%b %d, %Y&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;idl&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;lt;/abbr&amp;gt;&lt;/span&gt; ]&lt;span class=&quot;ta&quot;&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;&lt;span class=&quot;ta&quot;&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
&lt;span class=&quot;no&quot;&gt;&lt;strong&gt;10&lt;/strong&gt;&lt;/span&gt; &lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;idl&quot;&gt;&amp;lt;%&lt;/span&gt; &lt;span class=&quot;r&quot;&gt;end&lt;/span&gt; &lt;span class=&quot;idl&quot;&gt;-%&amp;gt;&lt;/span&gt;&lt;/span&gt;  
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The above code is what I used for the right column &lt;em&gt;recent entries&lt;/em&gt; section. I find all pages that are blog entries, and print out the linke and publish date. In particular, I used the following code to turn webby blog date format to the correct timestamp format. As you can see, all the dates on the right column are parsed into the &amp;#8220;timeago&amp;#8221; format.&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;il&quot;&gt;&lt;span class=&quot;idl&quot;&gt;&amp;lt;%=&lt;/span&gt; e.created_at.utc.strftime(&lt;span class=&quot;s&quot;&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;%Y-%m-%dT%H:%M:%SZ&lt;/span&gt;&lt;span class=&quot;dl&quot;&gt;&amp;quot;&lt;/span&gt;&lt;/span&gt;) &lt;span class=&quot;idl&quot;&gt;%&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;As usual, all the source code for this website can be found in my &lt;a href=&quot;http://github.com/iliu/My-Site&quot;&gt;github&lt;/a&gt; repository. Feel free to check it out and play with it.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Birthday Freebies!</title>
    <link href="/blog/201004/birthday-freebies.html" />
    <id>tag:isaacliu.info,2010-04-25:1272210873</id>
    <updated>2010-04-25T08:54:33-07:00</updated>
    <content type="html">&lt;p&gt;My wife and I have the exact same birthday. So every year around this time I start looking for free stuff for our birthdays. Last year was perfect, because Disneyland was giving free admission on your birthday, so my wife and I gladly got to go to Disneyland together, both for free =D. This year although Disneyland isn&amp;#8217;t free on birthdays anymore, there are still a lot of places that offer free stuff on birthdays! This day is becoming the day my wife and I go around and get free stuff! Here are some that I&amp;#8217;ve used:&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;&lt;a href=&quot;https://www.baskinrobbins.com/bdayclub/RegisterInfo1.aspx&quot;&gt;Baskin Robins&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.coldstonecreamery.com/mycs/myCS_pub_home.aspx?ReturnUrl=%2fmycs%2fmyCS_my_bday_acct.aspx&quot;&gt;Cold Stones&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.blackangus.com/PrimeClub.htm&quot;&gt;Black Angus free steak dinner&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.buffet.com/eclub&quot;&gt;Home Town Buffet&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.redlobster.com/club/&quot;&gt;Red Lobster&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;&lt;a href=&quot;http://www.redrobin.com/eclub/default.aspx?AspxAutoDetectCookieSupport=1&quot;&gt;Red Robin&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can definitely find more online, or you can go &lt;a href=&quot;http://www.frugalliving.tv/free-stuff/birthday-freebies.html&quot;&gt;here&lt;/a&gt;. Please post some other good ones if you know of any.&lt;/p&gt;</content>
  </entry>
  
  <entry>
    <title>Crawling up the Ranks</title>
    <link href="/blog/201004/crawling-up-the-ranks.html" />
    <id>tag:isaacliu.info,2010-04-23:1272057410</id>
    <updated>2010-04-23T14:16:50-07:00</updated>
    <content type="html">&lt;p&gt;Finally, I&amp;#8217;m back at the first hit when you google &amp;#8220;isaac liu&amp;#8221;. Because of all the webhost changing and redesigning of my page, my page rank started dropping, until eventually my site completely disappeared from google search when querying &amp;#8220;isaac liu&amp;#8221;! I did some researching and found out that by using the &lt;em&gt;&amp;lt; meta refresh = &amp;quot;&amp;quot; &amp;gt;&lt;/em&gt; tag to do redirection, my page rank took a penalty, and maybe even prevented the google crawler to index my site. This is because normal spam sites use this method to redirect people to their spam sites. Thus, I went out to find how to do redirection the correct way, which was using 301 permanent move. What that means is, the http code that&amp;#8217;s sent back to the requesting client should be 301, which means &amp;#8220;permanently moved site.&amp;#8221; The &lt;em&gt;&amp;lt; meta refresh=&amp;quot;&amp;quot; &amp;gt;&lt;/em&gt; method sends back the code 200, which means &amp;#8220;Request OK,&amp;#8221; and then later does the redirection. That is extremely not search engine friendly. To change this required tweaking of the .htaccess file on my web host, which is using the UC Berkeley EECS department&amp;#8217;s webhost. If your curious, i added the following code to my .htaccess file:&lt;/p&gt;
&lt;div class=&quot;CodeRay&quot;&gt;
&lt;pre&gt;&lt;span class=&quot;no&quot;&gt;1&lt;/span&gt; RewriteEngine on
&lt;span class=&quot;no&quot;&gt;2&lt;/span&gt; RewriteRule  ^index\.html$  /~liuisaac/site/ [R=301,L]
&lt;span class=&quot;no&quot;&gt;3&lt;/span&gt; RewriteRule  ^$  /~liuisaac/site/ [R=301,L]
&lt;/pre&gt;
&lt;/div&gt;&lt;p&gt;This uses the Apache mod_rewrite engine to rewrite all incoming requests to &lt;em&gt;index.html&lt;/em&gt; (line 2 and 3) to the &lt;em&gt;&amp;#8220;site&amp;#8221;&lt;/em&gt; subdirectory. Now all requests will be forwarded and the header message sent back will be 301 (specified by &lt;em&gt;R=301&lt;/em&gt; ), which is search engine friendly. Low and behold, I am back atop the google search for &amp;#8220;isaac liu&amp;#8221;! If you are using &lt;em&gt;&amp;lt; meta refresh=&amp;quot;&amp;quot; &amp;gt;&lt;/em&gt; to do redirection, and don&amp;#8217;t know why your page isn&amp;#8217;t showing up on google, this might be the reason! A more indepth tutorial on mod_rewrite can be found &lt;a href=&quot;http://httpd.apache.org/docs/2.0/mod/mod_rewrite.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
  

 
</feed>

