The Devel!

choonkeat on programming & software

Ruby for Rails Beginners

I've been doing this exercise for a few Rails beginners (or non-rubyists who glanced at Rails a bit before) and the general feedback is that they learn Rails but not Ruby, and this is new to them. So I suppose I should just write it down to save future effort.

If you already know Ruby you'd want to stop reading here.

"Computer, book me on the cheapest flight to Mexico for tomorrow!"

The unfortunate thing is, most folks' first contact with Rails will be some short code snippets like

class Comment < ActiveRecord::Base
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :comments
  validates_uniqueness_of :username, :case_sensitive => false
end

Very succinct. Looks like english and could even appear friendly to non-programmers. It doesn't look "real" and has "toy" written all over it. The jaded programmer will see "config file", "no real syntax", "fragile", "abitrary subset", "haml" (zing!) or "not powerful" as if a guy in grey suit just demoed how he told his computer what to do verbally - "How sustainable can such fake syntax be?". And perhaps beginners might go, "Rails lets me write english-ish code! Wow wee!"

Let's start somewhere else

In mainstream OO languages like Java, you can't really write Java code anywhere you like. "Huh?" Yes, you just don't usually think about it this way. For example you can't simply insert Java code anywhere, say…

System.out.println("here?");
public class Hello {
  public static void main(String [] argv) {
    System.out.println("world!");
  }
  System.out.println("or here!");
}

It's not allowed and you'd get errors

$ javac Hello.java 
Hello.java:1: class, interface, or enum expected
System.out.println("here?");
^
Hello.java:6: <identifier> expected
  System.out.println("or here!");
                    ^
Hello.java:6: illegal start of type
  System.out.println("or here!");
                     ^
3 errors

In Ruby, however, you can write Ruby in weird places:

puts("here?")
class Hello
  def self.main(argv)
    puts("world!")
  end
  puts("or here!")
end
Hello.main(ARGV)

Which runs like this instead

$ ruby hello.rb 
here?
or here!
world!

So? Big deal

Taking another step back, let's look at this Ruby class

class Hello
  def an_instance_method()
    puts("This is inside an_instance_method")
  end
  def self.a_class_method()
    puts("This is inside a_class_method")
  end
end

If you're a programmer you'd have ascertained def defines a method (or function). Now, the difference between def an_instance_method() and def self.a_class_method() is that a_class_method is a class method (or Java programmers like to say "static method") and is used like this

Hello.a_class_method()

which prints This is inside a_class_method whereas an_instance_method is an instance method that you can call on instances of the Hello class,

x = Hello.new()
x.an_instance_method()

So? Big deal

Say we define our Ruby class like this, with a puts statement at the bottom

class Hello
  def an_instance_method()
    puts("This is inside an_instance_method")
  end
  def self.a_class_method()
    puts("This is inside a_class_method")
  end
  puts(self)
end

Running it would produce

$ ruby hello.rb 
Hello

Notice puts(self) has printed the name of our class Hello. This means we are referring to the Class which we're still in process of defining! And since we can refer to it, we can also use it (as much of it as we've defined so far)

class Hello
  x = self.new()
  puts(x)

  def an_instance_method()
    puts("This is inside an_instance_method")
  end
  x.an_instance_method()

  def self.a_class_method()
    puts("This is inside a_class_method")
  end
  self.a_class_method()
end

Running it would produce

$ ruby hello.rb 
#<Hello:0x0000010084f778>
This is inside an_instance_method
This is inside a_class_method

Notice how instance x obtains an instance method after the fact! Let's clean up our class: rename it as User, and rename the class method to validates_uniqueness_of, and add some arguments to the class method…

class User
  def self.validates_uniqueness_of(what, options)
    puts("This is inside validates_uniqueness_of #{what} and #{options}")
  end
  self.validates_uniqueness_of('username', Hash['case_sensitive',false])
end

The #{blah} syntax is string interpolation, allowing Ruby code to run within a string, like "Five plus One is equal to #{5 + 1}". So, running this file would produce

$ ruby hello.rb
This is inside validates_uniqueness_of username and {"case_sensitive"=>false}

In Ruby, Hash objects (or associative arrays) can be defined literally as {'case_sensitive'=>false}; (brackets) and {curly braces} are largely optional; self is implied; You can also use :symbols to denote things you'd usually use enums or constants for

class User
  def self.validates_uniqueness_of(what, options)
    puts "This is inside validates_uniqueness_of #{what} and #{options}"
  end
  validates_uniqueness_of :username, :case_sensitive => false
end

We can use the < syntax to denote inheritance and define the class method elsewhere

class Base
  def self.validates_uniqueness_of(what, options)
    puts "This is inside validates_uniqueness_of #{what} and #{options}"
  end
end

class User < Base
  validates_uniqueness_of :username, :case_sensitive => false
end

We could stash more methods into our Base class

class Base
  def self.has_many(what)
    # some code
  end
  def self.validates_uniqueness_of(what, options)
    # some code
  end
end

Or we could use Mixins to organize them neatly into standalone modules and compose them together

module Relation
  def has_many(what)
    # some code
  end
end

module Validation
  def validates_uniqueness_of(what, options)
    # some code
  end
end

class Base
  extend Relation
  extend Validation
end

Either way, we can now have something that looks familiar

class User < Base
  has_many :comments
  validates_uniqueness_of :username, :case_sensitive => false
end

So you see, the toy looking code is not Rails-specific, nor is it some limited-capacity syntax for PPT & luring beginners.

Came for Rails? Stay for Ruby

And that's it. If this has piqued your interest, you might want to investigate how Ruby lets Rails get away with the syntax of config/routes.rb and how Ruby makes has_many and validates_uniqueness_of possible to implement.

Giving Feedback on a Design You Are Paying For

Disclaimer: I’m not knowledgeable in this area, but am interested to know what’s up. Please feel free to correct my misconceptions.

“Make this blue”, “Too big”, “Bigger”, “Darker”, “Too narrow”, “Put the logo here”

I find it hard to comprehend why anyone paying good money for design, would review in this manner. Over and over, round after round. Sounds more like a person trying to work Photoshop with voice control. Shouldn’t we let the skilled worry about their craft? If your designer doesn’t know design better than you, why there is a business relationship?

On the back of such encounters (as an onlooker), I had concluded [mistakenly] that having multiple rounds of design reviews was just masturbatory and that a customer should only focus to communicate the whys clearly and leave designers to do their job.

Yesterday, 37signals posted another “Behind the scenes” article on their blog. As I was reading their recap of multiple iterations and feedback, it occurred to me that my conclusion was misdirected. There’s nothing wrong with the fact that there were multiple rounds of review. My issue was with the nature of the critiques.

The feedback for each round were about story, intent, clarity. Not pixel pushing. Seems to me, that is how a money paying customer should be reviewing to get his money’s worth.

Loremipsumizer

I had the need to show some folks example screens of webapps I’m working on recently. Using those screenshots, the other party could have a better feel of things, we could have a clearer discussion. But the irritation [for me] is always the setup

  1. The app I’m working on is still in flux, i.e. design & data attributes will likely change, often
  2. The content I have is often imported from real data sources, and hence is somewhat private & sensitive
  3. I’m too lazy to think up fake names, fake scenarios, fake photos to fill my database, just so I could have a fragile, demo screenshot (I’d need to redo / update later)

What I’d really like is

  1. To be able to use my current webapp, as-is
  2. To be able to use my current data, as-is
  3. To protect private & sensitive data

Smells like a bookmarklet? So here it is Loremipsumizer.js. After installing it, I can anonymize any webpage instantly (then take a screenshot, annotate or print & discuss with somebody else, without worrying about the sensitivity of content)

Click here to try it on this webpage!

The Loremipsumizer will anonymize all text, scramble all numbers, replace images, flash embeds (& video tags?) with wireframe-ish cross-boxes (via data uri). Unfortunately I didn’t have an elegant solution to anonymize background images (css sprinting, image size, etc) so I’ll need suggestions there.

In any case, here are a few screenshots of webpages I’ve anonymized. No prizes for guessing which websites:

Note: Loremipsumizer does not require any JS library, and have not been tested on irritating browsers. Patches are welcome.

FOAF Rewards

The Joneses is an interesting idea: if you push product placements all the way to 11, you'd have it done in real life. But how do you attribute a [growth in] sale of product P, to the "Joneses" team member? Target market of product P + geography of the sale? And what if the network effects is far reaching and has no geographical boundary (i.e. internet)? So, no two "Joneses" teams should pimp the same product? No good.

What if we try something more pre-emptive, using something more direct - FOAF. So when customer Charlie buys Product P, your CRM picks up that amongst other customers who bought P, customer Betty is in Charlie's FOAF network (Facebook, Twitter, email, blog, flickr, etc).

With this knowledge, you could directly credit Betty a referral reward and hope to gain an army of MLM team. But this risks displacing Betty's original reason of buying your product with a weaker, extrinsic motivation.

Alternatively, you could credit Betty with a discount on her next purchase. This approach is good and already exist informally. But it is also unsurprisingly inefficient in its current state: the connection happens if you manage to make the sales assistant remember you and they bother to inquire about referrals when serving a new customer.

Wouldn't it be great if a hypothetical "iCashRegister (now with FOAF)" exist to make this more efficient?

To cap the potential hemorrhage of "discount credits" on heavily connected customers, if there are several customers connected to this new customer, they'd only each get a slice of the credits. Also, for some business, it might make sense to attribute a referral based on the transaction of the same product. For other businesses, it might make sense to attribute a referral based on purchase recency.

So now any customer connected to a subsequent customer gets discount credits. And all this can happen regardless of whether (you) the business owner is servicing the customer, or if it was just your part-time sales assistant in the shop.

To hell with wimpy loyalty cards. Why isn't your business using "iCashRegister (now with FOAF)"?

Update: Do check out Anafore if you're interested in this topic!

You're Doing It Wrong - ACM Queue

What good is an O(log2(n)) algorithm if those operations cause page faults and slow disk operations? For most relevant datasets an O(n) or even an O(n^2) algorithm, which avoids page faults, will run circles around it.
the results coming out of the CS department would be so much more interesting and useful if they applied to real computers and not just toys
- Web annotation on You’re Doing It Wrong - ACM Queue

Since @replies Can Come From Anybody

Someone, let's say Yahoo, could roll out their own twitter-like service (let's call it yatter). And all yahoo mail users "become" yatter users automatically, like how they became Openid accounts.

  1. when a yatter user "joe" yatts, "@twitter/charlie what are you doing?"
  2. yatter's twitter bot called "yatter" relays the tweet, "yatter/joe: @charlie what are you doing?"
  3. twitter user "charlie" sees the message in his @replies stream, and can respond, "@yatter/joe hitting my refresh button again and again"
  4. a yatter.com bot called "twitter" relays the message, "twitter/charlie: @joe hitting my refresh button again and again"
  5. yatter user "joe" sees that in his @replies stream in yatter.com.. (repeat from the top)

I'd guess it makes sense for yatter apis to be fully compatible with twitter.com, in the ma.gnolia.com vs del.icio.us kind of way. So twitter clients just need to add a new config, "hostname", and we're done.

I think yatter.com could technically allow @yatter/joe to follow @twitter/charlie. The question is, would twitter.com allow their @charlie user to follow @yatter/joe?

Yatter could use the age-old hotmail trick and append the latest status message of the mail sender to the email footer. That should pique interests of recipients. Besides, "a signature" is an existing, understood concept that users can easily see why they'd would want to update their "yatter status"..

An iPhone Optimized Web Page

[UPDATE: Proof of concept no more, RssPaper is now in the store!]

The defacto way of making a web page "optimized for iPhone" is to slap on iUI (or variants) to simulate the look & feel of a native app (i.e. the UITableView). You can click on an item in the list, and the page slides sideways to reveal another list, or more details… etc.

However, the starting point of trying to mimic meant we're aiming for the web site's experience is to be worse-or-almost-as-good compared to if it was a native app. e.g. m.cnn.com Can aim to be different instead?

In trying to mimic a native app, the iUI approach locks down the browser's width & scaling capability. I actually liked that part of browsing on the iPhone – its automatic zoom! To zoom-in, you simply double-tap on an area of the page. To zoom out, double-tap again. (There's pinching, but it is manual & I don't like tedious things)

Unfortunately, most layouts don't make it easy for iPhone to automatically zoom-in. The kicker is, a good & fluid CSS layout actually hinders! e.g. the text is still small after double-tapping on jnd.org

Turns out, the automatic zoom is controlled [only] by the width of the page segment.. meaning, the page's font size can be infinitesimally small – it doesn't matter. As long as the containing width is also small (e.g. contains 5 words across 1 row), the user can still comfortably zoom in and get big text with sharp font of 5 words across the entire screen width!

Taking this to an extreme, we could have all our site's content render in tiny little font inside narrow columns on 1 single page and the iPhone visitor can still effortlessly zoom in and read everything! No click, ajax, network lag, read content, back, click another, .. etc.. i.e. instead of content hierarchy behind deep navigation, we could have it based on font sizes.

Sounds like a newspaper huh?

To try this out, I'd hacked up this proof-of-concept a few days ago, and have been using it to read my RSS feeds on my bus rides the past few days. While the app itself is not there yet, the zoom interface experiment has really been working out very well!

screenshots

Have an iPhone/iPod touch? Try it and lemme know?

PS: A more extreme sell for a zoom-based UI is Microsoft's famous Seadragon demo.
PS: this whole experiment was sparked off when I tried to read the beautiful hacker-newspaper.gilesb.com on my iPhone.

9.99 Reasons to Love IE6

A friend showed me this website today: http://www.bringdownie6.com/ "Bring down!" Such angry words. There's so much negativity on the Internet regarding a browser.

Instead of 1) getting good folks to put feeble badges on websites, or 2) resigning to fate & cursing whenever you have to fire-up IE6 and check, or 3) getting bigger, better tools to do stupid things faster… Maybe we can be more constructive.

Here's an idea:

Dear Developers & Designers,

Save your sanity: At $0.00 cost to you, we make your website work in IE6!

Step 1. Register your website with us
Step 2. Just copy-paste this into your website's <head> section: <script src="http://weloveinternetexplorer.com/bff.js"></script>
Step 3. *chuckle* There's no Step 3!

How this'll work is that, all your IE6 site visitors will be gently redirected to our payment page, explaining nicely:

We're sorry but Website XYZ no longer works with your old, antique Internet Explorer browser. BUT for just US$9.99/month (1 movie ticket!) the hardworking engineers at weloveinternetexplorer.com will fixup the website for YOU, and make this problem go away for you! It'll just take 30 seconds! We accept all major credit cards & Paypal (maybe even 淘宝! Alipay (correction by @xtinegoh) )

But you choose not to :-( you may install any of these free, modern browsers and use Website XYZ through them: [firefox logo] [safari logo] [google chrome logo]


WeLoveInternetExplorer.com Team

If the IE6 compatibility work has not been complete yet, the above pay-wall will not be shown. Instead, we'd display a cool 1990s construction worker icon that these folks will be extremely familiar with!

Construct2_e0

All paid users can of course transparently continue to use the web site (and all other websites that uses this service).

PS: Designers & developers that would like to do Internet Explorer compatibility work (yes, all 3 of you out there, 尤其是中国的同胞朋友, Hội lập trình viên Việt Nam, and сотрудник российских программистов !!!!) Please contact me