Posts from life.i.think...

GetText, Rails, Authentication, Japanese Firefox, Setting the Language Manually

There occurs an interesting problem when you are using Ruby’s GetText to translate a site that requires authentication.

Most likely, you have a before_filter that calls a method that authenticates the user for certain methods. Within this filter you probably have something like:


user = (attempt_oauth or
  attempt_basic_auth or
  attempt_session_auth or
  attempt_cookie_auth or
  (@authentication_attempt = nil))

If the user has clicked “Remember Me” and stored an cookie locally, than we’re going to validate the user using attempt_cookie_auth. But what if the user has a default language set (stored most likely in the database). We’ll assume there is a method user.lang that returns the default language or nil.

Your first attempt at setting this here may be this one.

NOTE: THIS IS WRONG!


# If the user has a default lang, set it here.
if u && u.lang && (LANGUAGE_CODES.include?(u.lang))
  cookies[:lang] = u.lang unless cookies[:lang]
end

That sets the cookie for the user, but the GetText stack has already been invoked, so the language will be that sent over by the browser. Refreshing the page will cause GetText to pick up the cookie value and render the proper language.

Next, we try using set_locale (GetText.set_locale) to set the language manually, which seems like a perfectly reasonable option.

NOTE: THIS IS WRONG!


# If the user has a default lang, set it here.
if u && u.lang && (LANGUAGE_CODES.include?(u.lang))
  cookies[:lang] = u.lang unless cookies[:lang]
  set_locale u.lang
end

Why is this bad? The set_locale method persists for the life of the Ruby instance (in this case, Mongrel), not the session. This means that there will be a literal battle for contention over which language to use.


User A sets the language to JA and the page renders JA.
User B sets the language to EN and the page renders JA.
User A sets the language to JA and the page renders EN.
You see the problem! We want a clean slate at the beginning of each request so GetText has no pre-conceived notions about what the language should be. Hrm … let’s try something.

NOTE: THIS IS WRONG!


# If the user has a default lang, set it here.
if u && u.lang && (LANGUAGE_CODES.include?(u.lang))
  cookies[:lang] = u.lang unless cookies[:lang]
  set_locale u.lang
else
  set_locale nil
end

Note … to be thorough, and because the authentication stack is not called for all methods, we also add this in our application.rb


  before_init_gettext :set_default_locale
  def set_default_locale; set_locale nil; end

Here, everything seems to work! We can even write a test to verify that the contention above doesn’t occur.


def test_lang_should_be_set_on_a_per_session_basis
  bob.lang = 'ja'
  assert bob.save
  bob.reload

  assert_equal 'ja', bob.lang
  post '/sessions', {:username_or_email => bob.screen_name, :password => 'foo'}
  assert_response :redirect
  follow_redirect!
  assert_response :redirect
  follow_redirect!
  assert_response :success
  assert_equal 'ja', Locale.current.language

  phoenix.reload
  assert_equal nil, phoenix.lang
  post '/sessions', {:username_or_email => phoenix.screen_name, :password => 'foo'}
  assert_response :redirect
  follow_redirect!
  assert_response :redirect
  follow_redirect!
  assert_response :success
  assert_equal 'en', Locale.current.language
end

But alas, download the Japanese version of Firefox and visit the page.

The browser is hungry for UTF-8 data. As you can see, the part of the page in which we set the locale manually using set_locale is being pushed as SHIFT-JIS. The blue highlighted area is actually outside of the application.rb controller stack, so is sent via GetText’s default assumptions: UTF-8.

But this isn’t a problem in Safari or Internet Explorer. Why? Let’s look at the value of HTTP_ACCEPT_LANGUAGE.


Firefox :   "HTTP_ACCEPT_CHARSET" => "EUC-KR,utf-8;q=0.7,*;q=0.7" 
Safari  :   "HTTP_ACCEPT_CHARSET" =>  ???

Safari actually doesn’t pass one, while Firefox gives precedence EUC-KR (effectively SHIFT-JIS) over UTF-8.

Sigh. Things are looking grim. Back to the drawing board. Let’s look at the order of precedence for how the Rails GetText integration determines the langauge.


The language passed to GetText.bindtextdomain.
The lang query param. ( url?lang=foo )
The lang cookie.
The value of HTTP_ACCEPT_LANGUAGE passed by the browser.
The default (English).

Aha! GetText.bindtextdomain! Looking at the RDocs and source, this is not only called within the init_gettext method, but can be set on a per session basis without mucking with default language settings!

Let’s try!

NOTE: THIS IS CORRECT! CELEBRATE!


# If the user has a default lang, set it here.
if u && u.lang && (LANGUAGE_CODES.include?(u.lang))
  cookies[:lang] = u.lang unless cookies[:lang]
  GetText.bindtextdomain("Twitter", :locale => u.lang)
end

So finally, we can set the language on a request manually after the GetText stack has already been invoked. Whew!

Scripting CONSTANTS in environment.rb {rails}

You can script the declaration of constants easily enough with Module#const_set.

Sometimes you want to do this from environment.rb when you are loading the Rails stack. It’s not immediately clear which module this is encapsulated in. Turns out it’s not contained within anything specifically … just plain ol’ Object.


foo.each do |bar|
  Object.const_set("CONSTANTLY_#{bar.upcase}", 42)
end

Trailing Whitespace in Textmate

So yes, it’s true. I’ve been using Textmate lately. I’m still using Debian and the trusty Thinkpad (and eyeballing the sexy X300), but also have picked up a MacBook Air, and am dabbling in the world of Mate.

One thing that has bothered me about my typing in Mate (other than the lack of using modes) is that something I’m doing is leaving trailing whitespace. This, of course, is a problem. Eventually I’ll figure out what muscle memory reflex is causing this, but there are a few things I’ve found along the way to help identify these vile and unnecessary bytes in my documents.

Add Syntax Highlighting for Trailing Whitespaces

First we must at a rule to match trailing spaces in our language of choice.
{Q: Can we do this for all languages?}

Secondly, assign a color to this property in your theme of choice.

Keyboard Shortcut to Remove all Trailing Whitespaces

TextMate comes with a command to remove all trailing spaces from a document. Why not assign a keyboard shortcut to it?
{Q: I’d love to do this on-save of all documents.}

A Better GMail Labeling System

The other day Cameron and I were talking about a better way of labeling and prioritizing your mail in GMail. It is a system of his friend’s devise, who’s name I’ve forgotten (I’ll update this when I get it.)

The system is simply to create 4 labels, and then apply filters to the emails you receive on a regular basis.

1. Now (Mail you need to look at RIGHT NOW!)
2. Soon (Mail that you should probably read by the end of the day)
3. Later (Mail to read when you find that free time you’ve been looking for)
4. Never ( crickets .. I mean maybe you might want to read them … sometime …)

So for example, Twitter direct messages auto go to Now. An email about that fancy pants party you’re going to in 2 days would go under Soon. Facebook messages would be filed under Later. Financial statements might go to Never.

It’s important that you preface the words (Now, ...) with numbers so GMail orders them properly. This works fantastically with Mihai Parparita’s wonderful GMail Macros Greasemonkey script.

Update: The aforementioned friend who devised this brilliant scheme is Ted Grubb.

Back from RailsConfEurope07

5 1/2 hour train from Heidelburg to Berlin.
Arrived at the closed Tegel Airport at 1 AM and sat on a bench till they opened at 4 AM.
Waited for 7:30 AM flight to London.
Layover, and then ~12 hour flight to San Diego.
Through customs and flight to San Jose.
Bus to Cal Train Station.
1 1/2 hour Cal Train to the Bart Station.
Bart to 24 and Guerrero.
Home! Whew! What a ride.

Germany was absolutely beautiful. The conference was a blast, and had the best food I’ve ever had at a lunch break. The talk went well I think, and you can see the slides, which have a few cool rails tricks embedded, here:

s.rb: X60, Compiz, & Xrandr.

Continuing with Ruby scripts that should probably be written in Bash, here’s one for the eyecandy inclined.

Situation:

A couple of notes. Unfortunately, you cannot trigger an xrandr script on VGA plug/unplug events (yet). See this post. Also, if you are not running an accelerated desktop, or your 2 monitors fit within the bounds defined by a few X components (see this post), you can use your laptop as a secondary monitor.

For now, I’m just toggling between my two monitors via this small Ruby script.


#!/usr/bin/env ruby

def lappy; exec "xrandr --output VGA --off --output LVDS --auto";  end
def external; exec "xrandr --output LVDS --off --output VGA --auto"; end

case ARGV.first
  when 'l' then lappy
  when 'e' then external
  else
    external if `xrandr -q`.include?('VGA connected')
    lappy
  end

Download.

And before all you MacHeads out there smile to yourselves about all this work; You thought I forgot about you! I so vary rarely tinker with my desktop setup. This truly is the oddity! Oh, and http://twitter.com/blaine/statuses/153159902

f.rb: An awk script.

Also known as “the most the most useful piece of software [someone else] wrote in [a] calendar year,” rewritten.

Today Mark Dominus blogged about f, a wholly fantastic and tiny Perl script that changes:

ps aux | awk ‘{print $2}’ to ps aux | f 2.

Of course there’s the deeply embedded urge of every geek to take something like this and reproduce it in all possible languages (plus there was a syntax error running the Perl script on my end), so here we have it in Ruby!


#!/usr/bin/ruby

($stderr.print "f.rb usage: fieldnumber\n"; exit) if ARGV.empty?
$stdin.each { |l| puts l.split[ARGV.first.to_i-1] }

Download.

Addi's Present Moment

As we walk down the street, she bounds into the air, biting at the leash. With joy in her face she pulls and skips. She flips in the air, much to the amusement of passersby.

You are not in the present moment! [1]”, I think to myself. You are missing the enveloping smell of the Angel Trumpets to our left; The smile of the child’s face to our right. But then I realize that to Addi, in that moment, I am the present. I’ve spent the day at the office, as she’s spent the day on the couch, my bed, or whichever comfortable spot she deems appropriate in my tiny studio apartment.

She becomes the present moment for me. I no longer am distracted by what would normally grab my attention. I love her wholly and this moment is a precious one. I remember now words given to me that remind us that focusing our energy towards the present allows us to fully embrace all that we care about; my dog included. And me to her.

[1] “You shouldn’t chase after the past or place expectations on the future. What is past is left behind. The future is as yet unreached. Whatever quality is present you clearly see right there, right there.” —Buddha

Making Happiness, Not Money

So many people seem to judge success based on how much money one makes. This is a terrible misconception though, as success is not defined by monetary gain, but by happiness. This inspires an entirely different way of thinking about our lives; in particular our jobs. What if we didn’t strive to make money, but instead to make happiness?

When I first iterated over this article, I went back and forth on whether or not to include bullet points refining the concept. I found it difficult to articulate, because I was quick to inject my own inspirations for contentment. Everyone has a different definition of “happiness,” but there are a various ways you can look at your job in which are most encouraging. I believe I’ll continue to post on this concept, but here are a few things I find important and that could lend themselves to others as well.

1) Layout a base set of criteria that makes you happy. Like profiling an application for performance gain, you can’t know you’ve reached your goal unless you’ve set one. But how does one determine what happiness means? It’s actually fairly easy. Slow down, take a deep breath, and think about what makes you smile (smiling on the inside counts as well). What makes your entire body reverberate with that indescribable feeling?

2) Make just enough money to be able to provide these things for yourself.

3) If you make more money than you need to be happy, use it to inspire happiness in others.

4) Ensure that your means of making money contributes to your overall happiness. Sacrificing your well-being for delayed reward is a broken model. Some would argue this point, saying that there are necessary evils; Someone has to do the dirty work. But one can be content in their work, glamorous or not, based on their perception of the work itself and its benefit to the greater good.

5) Love unconditionally.

6) Add pictures of cats to whatever projects you work on. This guarantees, without the glimmer of a doubt, the happiness of not only yourself but your users as well.

Ok ok, the last one was a joke, but seriously … who doesn’t like cat pictures.

Twitter Takes the Blog Category at SxSW!



Be sure to check out our new visualizer we have on display there if you haven’t yet!

Recent Twitter Links

Twitter and Cali-forn-i-a

Dear friends, family, close relations, guys, gals, second cousins twice removed, and the web a large,

I’m moving. Moving to sunny, sunny California. I’m kinda flying by the seat of my pants right now, but I’ll initially be in Palo Alto for about 3 months, staying with Jesse, and then it’s a hop, skip, and a jump into SF.


I’m very excited to announce that I’ve joined the team at Obvious, who of course made something called Odeo, to work on Twitter. I landed at Twitter for a number of reasons, but most importantly because I love and use it, as well as truly believe in what we’re working on. Twitter allows me (and you!) to talk about all the oddities of life, of which there are most definitely plenty. It provides an outlet for that strange little voice in your head that normally lies silent. The one that should at times stay quiet, but often wants to jump up and down and scream something like “HEY! Why is the mall Santa running through my neighborhood naked with a bottle of scotch!?”

Twitter the site aside, I’ll be joining an absolutely stunning team of passionate and creative people. I’ll also be leaving the same here in Lexington. I truly love the people at Voltaic, and words escape me in trying to describe the time I’ve spent there. I will miss them very much.

Anyway, enough for now. Lots of exciting stuff coming up!

Programmatically Naming and Creating Variables with Ruby

This has been covered elsewhere. I did it in the past, but of course life has a way of kicking out old info to make space for the new.

Without being too terribly long winded, I will say that the reason for making this post is due to the fact that when I wanted to figure out how to dynamically create variables with ruby, I did a google search for a number of derivations of: ruby programmatically creating variables. This came up with a lot of nothing. I quickly realized this was turning into one of those dreaded cases of having to know the method name you’re looking for before you can find it. I’m a bit embarrassed to say I didn’t check this one, but thanks to blink on #ruby-lang, I’m posting just incase someone else searches with the same terms.

So at its very simplest:


  eval "ans = 42" 
Read more about eval at Jim Weirich’s always excellent blog.

Furthermore, instance variables of a class can be set via set_instance_variable.


class DeepThought
end

question = DeepThought.new
question.instance_variable_set("@ans", 42)

def question.what_is_the_answer
  return @ans
end

question.what_is_the_answer
=> 42

Giveth before you Taketh

It’s commonality on the web. A site asks you for something you consider sacred (it’s a strong word, I know): your email, your username, your password, before it gives something useful back to you. Sure, it may give you view-only access to information, but in order to actually participate in a community, you have to supply them with something first.

But what if this didn’t have to be the case? What if it shouldn’t be the case? A web application should be giving, loving, and excited about its users, even if they aren’t already defined by a particular identity. The web, at its heart, is largely anonymous. Sure, it knows things about you without your consent: your browser, your IP, your location, whether or not you have flash, etc, etc. But the web doesn’t know you; you as a person. If you are most webapps (you know who you are), then you ask the user to give this up, oftentimes before showing them what exactly they’ll be getting in return.

You are standing at a bar, hanging out with a variety of webapps.

Sitting next to you are some people you have heard of before (hey, they have a rep!): Digg, Gmail, Flickr. You’re an outgoing person, and you’ve heard about how amazing these people are, so you introduce yourself.

But to your left is a newcomer. Maybe they’re respectable looking, maybe they’re not. Maybe you’re immediately intrigued by their mystique, instantly wanting to get to know them. Maybe you’re not. Now in a perfect world, everyone would introduce themselves to everyone and immediately get along famously, becoming instant friends. Unfortunately, that’s not the world we live in, and nor is it the web at large. There are people who want to know you for their own benefit. So you’re jaded, and quietly sit there sipping your appletini.

But what if they turned to you and and started telling you a fantastic story, something about themselves that you would have never gleamed at first sight, especially in a bar as big as this one. You talk back and forth for a while, entertaining each-other with common interests and tales of the good-ol’-days, and eventually it comes out.

“Hey, by the way, my name is Britt.”
“Oh hi, I’m Twitter.”

And there it is. The beginning of a beautiful relationship.

Use what you develop, love what you develop.

If you don’t already read Kathy Sierra’s Creating Passionate Users, you most probably should!

Her Valentines day post (linked above), was on “Loveocracy” and how what’s good for the user is good for you. I’d like to add something that she implied in the post, but didn’t mention specifically.

Not only do you want passionate users, but passionate developers. It seems obvious, and should go without saying, but is very important. Love the software you write. USE the software you write! In an ideal world we, as developers, could love every application we create. Unfortunately for some, especially in the case of contract development, this isn’t always the case. But whenever possible, one is blessed with working on an application that they not only enjoy developing, but using as well. When you care about something, you nurture it. In the software world, this means more creative and intuitive applications, which are ultimately well written with fewer bugs.

In the end, don’t simply create passionate users, but be one yourself.

html2comatose - Importing HTML into the Comatose MicroCMS

Well, you don’t see this every day.

One client of ours wants to add some shopping cart jazz to a few of his websites. Nothing new here, especially since we’ve developed our own kick ass shopping cart. The oddity is that he wants most of the static content of some very old-school sites imported in pure HTML form to a CMS for online editing. No layouts or cleanup, just the old, pure HTML. Actually, one of the sites used image maps for navigation (the whole page is solely images!), so we are doing a bit of cleanup there.

Comatose is a nice ‘microCMS’ that sits happily ontop of an existing Rails app. Today I whipped up a script to import a static site into comatose.

It’s not much … you can download it here. Usage is simple:


html2Comatose: Import HTML into the Comatose MicroCMS

        Usage: ./html2comatose app-root html_pages_root [additional extensions you'd like to import]
        Example: html2Comatose ~/devel/myapp ~/var/www/httpdocs php rhtml

After the import is complete, you’ll want to put something like this in your /config/routes.rb ..


map.comatose_admin
map.comatose_root '/site', :layout => nil
map.connect '', :controller => 'comatose', :action => 'show', :index => '', :page => 'index'

Haha, I’m not sure if anyone will every find a need for this, but let me know if you end up using it for anything.

Userscripts.org ... 2!

They grow up so fast! After much hubbub and a bit of general hurah, the first stage in the major Userscripts.org rewrite has been released!

Thanks to all that helped make this possible!

life.i.think

“You are not here merely to make a living. You are here in order to allow the world to live more amply, with greater vision, with a finer spirit of hope and achievement. You are here to enrich the world, and you impoverish yourself if you forget the errand.”

- Woodrow Wilson.

GD on OSX and Ruby (stringFT)

After going through the process of building GD in OSX with all the relevant fonts, libraries and what-have-you, I was running into a terribly frustrating problem with Ruby-GD, specifically when trying to use the Captcha gem.


undefined method `stringFT' for GD::Image:Class

Luckily, this can be fixed very simply by:


sudo port install rb-gd

Javascript Conditionals in RJS

Every so often one has the desperate need beat RJS into evaluating javascript conditionals, say, if you don’t know if an element is going to necessarily be on the page or not.

For this one particular use case, luckily, Enrico Franchi pointed out a workaround for this.


page.select('element').each do |element|
  page.replace_html element, replacement
end

But what if we want to compare equality, or any other type of conditional? We can use the unbelievably, ridiculously, mind bogglingly ugly hack:


page << 'if (el.value == 10) {'
  page['block'].replace_html replacement
page << '} else {'
  page['block'].replace_html replacement
page << '}'

Imagine if we needed both of these previous methods combined? My head is spinning!

Dann Webb provides a plugin that implements ejs (erb based javascript) that provides a very cool and interesting mesh between javascript and erb.

But tell me. Why, oh why, wasn’t this patch accepted?

next page »