Monday, March 28, 2011

Visiting a Foreign Land

This past week I took a trip to a foreign country, MacLand. I have many friends who either live there or are frequent visitors and they all love it, despite the very high cost of living there. I decided it was time to see what all the fuss was about.

When I first got off the plane and opened up my new 13" MacBook Pro, it was welcoming. I was through security and on the internet in almost no time at all. However, it didn't take long for me to become discombobulated. The customs here are strange, like the fact that they drive on the left side of the road here with their window controls and they don't leave the menus at the tables as I am used to. I'm obviously going to have to learn the local dialect. What is a "Finder" or "Preview" anyway, and what are the funny symbols (arrows and cloverleafs?) I am seeing on many of the menus? And my instincts as to what to do in any given situation tend to be wrong. I am worried that I'll commit a serious faux pas and end up in trouble. Well, I guess I'll just have to trust that my friends that live here will bail me out if that happens.

To achieve some level of comfort, I installed Chrome and Emacs. It's always nice to see a familiar face when abroad. In the lobby of my MacBook there are a number of brochures of local activities, like going on Safari, that I suppose I should try out. When I get up some courage, I guess I will try to branch away from the strictly familiar.

So why did I decide to take this journey? Well some years ago, there was a revolution in MacLand, and the new government, OS X, was based on Unix. I have long preferred Unix to other approaches, but historically, Unix run places were not places for individuals to dwell, unless they had a strong Do It Yourself mentality. My own hobby interests lie elsewhere, I just want my infrastructures to work. My couple personal trips to Unix resulted in more hassles than it was worth to me. With OS X, MacLand promised the power of Unix while also providing a nice friendly infrastructure that just works.

Between this promise, the numerous friends I have who love it, and the fact that Ruby on Rails is supposed to be easier in MacLand, I decided it was time to take the trip. We'll see how I like it, but so far I am cautiously optimistic that this will be a good land.  (as long as there are no betrayals)

Monday, March 21, 2011

Google OpenID via Rails

So you've decided to write a web application. You are probably going to want to allow users to log in. Maybe its because you don't want to mess up password security, or maybe its because you feel the web doesn't need yet one more username/password combination, but you've decided to use OpenID. Here's how I've done it in rails.

For this, I am very much leveraging http://blog.sethladd.com/2010/09/ruby-rails-openid-and-google.html. Rather than repeat everything that Seth Ladd said, I will just include the differences from him.

First, I am starting completely from scratch, so the first step is to create a rails project.

rails new login

Then I need a user model:

rails generate model User identifier_url:string email:string
                          first_name:string last_name:string
rake db:migrate

The above two steps replace step 0 from Seth Ladd's blog post.

For step 1, here is what my Gemfile looks like:
  1 source 'http://rubygems.org' 
  2  
  3 gem 'rails', '3.0.4'
  4 gem 'sqlite3' 
  5 gem 'haml' 
  6 gem 'haml-rails' 
  7 gem 'ruby-openid' 
  8 gem 'rack-openid' 
  9 gem 'mongrel', '>= 1.2.0.pre2'
 10  
 11 group :development do
 12   gem 'rspec-rails' 
 13 end 
 14  
 15 group :test do
 16   gem 'rspec' 
 17   gem 'webrat' 
 18 end
There are only a couple things of note in this Gemfile. As I've mentioned before, I like haml, hence lines 5-6. Lines 7-8 are the lines from Seth's post. Line 10 is because WEBrick causes errors when I try to use the OpenID authentication and the current version of mongrel isn't working for me (Windows platform). Oh, and lines 11-18 are lines I pulled from Ruby On Rails 3 Tutorial by Michael Hartl. Even though I am not doing any testing here, it seems like a good idea to enable it.

Then, of course, I ran:

bundle install

Steps 2-6 I exactly followed Seth's post. For step 7, I basically followed his post, but used Haml rather than Erb. For step 8, I created an action controller:

rails generate controller Actions view

My action controller looks like:
  1 class ActionsController < ApplicationController
  2   before_filter :ensure_signed_in  
  3  
  4   def view 
  5   end
  6 end
and my app/views/actions/view.html.haml looks like:
  1 %p  
  2   = current_user.first_name  
  3   = current_user.last_name  
  4   (  
  5   = current_user.email  
  6   )  
  7 %p= current_user.identifier_url
And with that I have a simple app that uses OpenID for authentication. I test it by running:

rails s

and then going to http://127.0.0.1:3000/actions/view in my browser. As expected, I get redirected to google, and then after logging in, I get sent back, and I can see my information. Doing something useful with this will be left as an exercise for the reader.

Monday, March 14, 2011

When Does X Not Equal X and Other Oddities

In many programming languages:
  • For what value of x is the condition (x == x) false?
  • For what integer value of y is the condition (y < 0) && (-y < 0) true?
i.e. define x on line 3 and y on line 10  in the following Java code sample so that lines 7 and 12 execute:
  1 public class Odd {
  2   public static final void main(String[] arg) {
  3     // define x here 
  4     if (x == x) { 
  5       System.out.printf("Reflexive%n"); 
  6     } else { 
  7       System.out.printf("Not Reflexive%n"); 
  8     } 
  9  
 10     int y = 0; // change 0 to something else
 11     if (y < 0 && -y < 0) { 
 12       System.out.printf("How can they both be negative?%n"); 
 13     } 
 14   } 
 15 }
In SQL:
  • What database value x is the condition (x = x) false?
  • What about values y and z such that ((y = z) OR (y != z)) is false?











These next few lines are intentionally left blank, in case you wan to think about these before viewing possible answers.











Answers
In mathematics, one of the properties of the equality operator is that it is reflexive. It turns out that the Java (and almost every other language) equality operator does not have this property.  If you put the following on line 3:
double x = 0.0/0.0;
and run the program, it'll print out "Not Reflexive".  Why is this?  Well, according to the IEEE standard 754, 0/0 = NaN. What is NaN?  Well, it is not a number, corresponding to the mathematical concept of an indeterminate number. Because it could be any number, the powers that be decided that NaN != NaN.

So what about the second example? Well, in a 2's complement system of encoding numbers, there is room for one more negative number than positive numbers. (e.g. a 16 bit signed value encodes the values -32,768 to 32,767). What this means is that if you try to negate the most negative number (e.g. -32,768), the corresponding positive number won't fit in the memory location, and so you actually get the same number back. This means an answer to line 10 is:
int y = Integer.MIN_VALUE;

What about the SQL questions? Well, it turns out that SQL treats NULL like NaN, i.e. it is an indeterminate value. SQL goes even further than Java.  Not only is the expression (NULL = NULL) result in false, but (NULL != NULL) also results in false. Well, actually that's not quite true. SQL uses three-valued logic, and so both the expressions actually evaluate to undefined, but undefined is treated as false when it comes to matching rows.

Why does this matter?
Have you ever written code where it is assumed that if two numbers have the same value they will be equal? How about that Math.abs() will always return a negative value? Eric Lippert had a blog post showing how this assumption can cause the standard sort functions to fail.

I personally have generated two reports by running queries like:
SELECT * FROM Table WHERE columnA = columnB -- report 1
SELECT * FROM Table WHERE columnA != columnB -- report 2
and then was mightily confused when, at some point during the analysis, I realized that there were rows in my database table that weren't in either report.

These may feel like nitpicky details that only show up on those gotcha type quizzes. However, not understanding these details can lead to bugs in your code. If you are going to be an expert programmer, you have to know and understand the rough edges of your language.

Monday, March 7, 2011

Crop and Upload Image - Server Side

Previously I showed some HTML and JavaScript for cropping and uploading an image. Here is what I did on the server side to support this.

First I make sure I had rails 3 installed, configured for HAML and jquery. I also download the jqueryui and jcrop javascript and css files and add them to my project.  Then, I use the rails scaffolding functionality to get started:

ruby script\rails generate scaffold image content:binary width:integer height:integer name:string

After running:

rake db:migrate

I have a database table for storing an uploaded image, including the binary content, the height and width of the image and a name. Now that I have a place to store the image, I need to modify the generated web pages, so I can upload it.

Since I prefer HAML, the first thing that I do is rename the two erb files that I need to change: views/layouts/applications.html.erb to views/layouts/applications.html.haml and app/views/images/_form.html.erb to app/views/images/_form.html.haml.  The applications.html file is the template for all the pages, and I need to modify that to allow pages to add their own javascript into the header. _form.html is the partial that is used for both uploading new images and editing images.

Converting the applications.html file from erb to haml is just a straightforward transformation. Here is what it looks like when I am done:
  1 !!! 
  2 %html 
  3   %head 
  4     %title Image Upload 
  5     = stylesheet_link_tag :all 
  6     = javascript_include_tag :defaults 
  7     = csrf_meta_tag 
  8     = yield :head 
  9   %body 
 10     = yield
The only real line of note is the named yield on line 8. This is what will allow me to insert custom stylings and javascript into specific pages.

The only thing left is to rewrite _form.html.haml so that it generates HTML and Javascript like discussed in the client side post. Here is what this looks like.
  1 - content_for :head do 
  2   = stylesheet_link_tag 'jquery.Jcrop' 
  3   = javascript_include_tag  'jquery.Jcrop.min'
  4   %style 
  5     -# insert styling described in client post here 
  6   :javascript 
  7     // insert javascript described in client post here
  8 = form_for(@image) do |f| 
  9   - if @image.errors.any? 
 10     #error_explanation 
 11       %h2 
 12         = pluralize(@image.errors.count, "error") 
 13         prohibited this image from being saved:
 14       %ul 
 15         - @image.errors.full_messages.each do |msg|
 16           %li= msg 
 17   .field 
 18     = f.label :name 
 19     %br 
 20     = f.text_field :name 
 21  
 22   Drag and drop image here: 
 23   #cropWidget{:width=>400, :height=>400} 
 24     #surface 
 25     %canvas#canv1{:width=>400, :height=>400} 
 26  
 27   = f.hidden_field :content64 
 28   Uploaded image is here: 
 29   %canvas#canv2{:width=>200, :height=>200} 
 30   .field 
 31     = f.label :width 
 32     %br 
 33     = f.text_field :width, :readOnly=>true 
 34   .field 
 35     = f.label :height 
 36     %br 
 37     = f.text_field :height, :readOnly=>true 
 38   .actions 
 39     = f.submit :id=>"imageSubmit", :onClick=>"uploadSelection();", :disabled=>true;
I left out the specific CSS styles and JavaScript as they are described in the previous post. Notice that lines 1-7 are inserted into the head of the template as described by yield :head on line 8 of the application.html.haml file above. Lines 9-16 are the boilerplate error handling that was generated by the scaffold command. The remainder just generates the HTML for displaying the image and for entering the name of the image.

We are now almost, but not quite, done. If you notice the field that we are storing the image content in is content64, while the database column for the binary data is just called content. We need to take the base64 encoding of the image that is sent over the wire as part of the HTML POST and decode it to binary before saving in the database. To do that, we just need to add new methods in our app/models/image.rb Image class.
  1 require "base64" 
  2 class Image < ActiveRecord::Base 
  3   def content64 
  4     return nil unless content 
  5     return "data:image/png;base64," + Base64.encode64(content); 
  6   end 
  7  
  8   def content64=(c64) 
  9     index = c64.index(','); 
 10     self.content = Base64.decode64(c64[index..-1]); 
 11   end 
 12 end
This way when the content64 attribute is assigned in our controller class, it'll actually write the binary data to the content field so it can be saved to the database. The only clever part of this is that the content64 data that is passed from the HTML form has the initial string "data:image/png;base64,".  Lines 9-10 calculate where this prefix ends and just decodes the remainder of the string. Line 5 adds this prefix back on to the encoded binary content.

Other Work
So the above will allow you to upload a new image that has been cropped and save it to the database. However, to be able to view that image or edit it, more work is needed. Probably the most important thing to do is to add a new action which will return the image's binary content. The url for this action can be used in the src attribute of <img> tags. The other scaffold generated pages for viewing and listing the uploaded images are then modified to display the image rather than showing the binary content. The last step is to modify the javascript described previously to check if an image already exists (i.e. we are editing an existing uploaded image rather than uploading a new image), and preload that. Since this post has gone on long enough, I will leave all of those details as an exercise for the reader.