Visa credit card activation upsell

August 4th, 2008

Unfortunately, it seems as though Visa is now upselling identity theft protection with its new card activations. This is doubly upsetting because I shouldn't even have to call their crappy service to activate my new card, but they somehow managed to expose 20,000,000 card numbers. Or maybe that never happened and this is all part of a plan to get unwitting saps to sign up for their crappy protection.

Seriously, though, I entered the last four digits of my new card and waited 2 minutes while a pleasantly automated voice told me she could help protect my identity if I'd just press one. Pause… Really, just press one and all this mess will go away! PLEASE! PRESS ONE!

Why do companies suck so much?

DHL's animated favicon

July 29th, 2008
Whoever came up with the idea to animate DHL's favicon should be shot. Note to designers: unless your favicon plays Defender, keep it static.

Update multiple MySQL rows with a single query

July 9th, 2008

This tip deals largely with sortable lists. When creating a list that will be sorted using an effects library like Script.aculo.us, I usually want to save the results of the sorting to the database. This is a usually simple task due to methods like Sortable.serialize() which take the contents of my list and store them in a form to be appended to my AJAX query string. Here's a simple example:

<ul id="feeds">
  <li id="feed_27">adambondurant.com</li>
  <li id="feed_28">oaklog.com</li></ul>

After creating my sortable and moving around an object from my list (which I won't get into here), I serialize the list and get a query string like this:

feeds[]=27&feeds[]=28...

When I submit those parameters to my server, my parameters might look like this (in PHP):

[feeds] => Array
  (
    [0] => 27
    [1] => 28
    ...
  )

From here, I have a few options for storing the order of the feeds in my database: I can iterate through the array, assigning each position in the array to its corresponding value (feed ID). This option works for small lists, but sometimes I need to update 100 items at a time, and performing 100 UPDATE statements doesn't sit well with me.

Another option is to leverage MySQL's expression syntax to wrap the update into a single query. I can do this using MySQL's CASE statement like so:

UPDATE components
   SET position        = CASE component_id WHEN 27 THEN 0 WHEN 28 THEN 1 END
 WHERE parent_id       = 19
   AND component_type  = 'Feed'
   AND user_id         = 1
   AND component_id   IN (27, 28)

The question I had, however, is which option performs better. In theory, I figured the latter was better because it didn't have to hit the database n times, it was hit only once. This is why profiling exists, so I tried my best to do some testing.

I started with 100,000 rows in the components table, and my test script looped 10,000 times, updating the positions on 80 items each time. I purposefully constructed my SQL queries outside the profiled loop so I could see a comparison of SQL to SQL without much PHP overhead. Granted, I used a database abstraction layer (Zend DB) but this test is not so much about eeking out as much performance as it is comparing two similar approaches.

Here are the numbers I came up with when issuing the CASE-based approach (1 query) 10,000 times:

Lowest:  0.00206017494202
Highest: 0.221321105957
Average: 0.0034388064146

Here are the numbers performing 80 queries, 10,000 times:

Lowest:  0.0084331035614
Highest: 0.227936029434
Average: 0.0145111063004

It's a bit strange to me that the highest time to issue the queries was pretty consistent, but what I really garnered from the testing was that it was about 422% faster to use the CASE-based approach than to issue individual queries. Maybe that was to be expected, but it's still nice to see some data.

Sort by a list of IDs queried using MySQL's IN() function

June 26th, 2008

I run into this problem from time to time, and I've finally put enough energy into it that I want to commit it to memory through the blog. The most common occurrence of this in my world is when performing a search using something like Lucene or Sphinx. Here's what usually happens:

I perform a search using my engine of choice. Sphinx, for example, will give me back a hash of results formatted like this:

  Array
  (
    [matches] => Array
    (
      [10546] => Array
      (
        [weight] => 3
        [attrs] => Array
        (
          [feed_id] => 624
          [published_at] => 1213713545
        )
      )
      [14154] => Array
      (
        [weight] => 3
        [attrs] => Array
        (
          [feed_id] => 583
          [published_at] => 1213801410
        )
      )
      ...
    )
    [time] => 0.000
    ...
  )

Since I do not store my full-text fields in my index, I then have to take the IDs from the matches array and query the database to get at my data. The query is simple:

SELECT * FROM items WHERE id IN (10546, 14154, …);

Unfortunately, that query will not necessarily return the rows in the order I provided. This is an easy fix if I'd like to sort my results on a column in my database, since I can just add a quick ORDER BY clause on say, the date the item was created. But if I searched the database for results sorted by relevance, the database really doesn't know anything about how to sort that way. I need a way to tell MySQL to sort the results in the order I've supplied my IDs, which leads us to at least two functions: FIND_IN_SET and FIELD.

Both functions work the same way: Given a string (or column) as the first argument, they return the 1-based index — or zero if not found — of the argument in either the list of additional arguments (FIELD) or the second argument, a comma-delimited string (FIND_IN_SET). For my purposes, then, my query looks like this:

# Using FIELD
SELECT * FROM items WHERE id IN (10546, 14154, …) ORDER BY FIELD(id, 10546, 14154, …);

# Using FIND_IN_SET
SELECT * FROM items WHERE id IN (10546, 14154, …) ORDER BY FIND_IN_SET(id, '10546,14154,…');

Both functions will return the same results, sorted the same way. I went so far as to profile both, too, just to make sure I was using the best function. Granted, when I profiled this using MySQL's built-in profiler, my query was pretty simple (as it should be, in my opinion). Your mileage may vary:

  +----------+------------+------------------------------+
  | Query_ID | Duration   | Sorting result | Query       |
  +----------+------------+------------------------------+
  |        1 | 0.01572400 | 0.001129       | FIND_IN_SET |
  |        2 | 0.00241400 | 0.00127        | FIELD       |
  |        3 | 0.00194700 | 0.00114        | FIND_IN_SET |
  |        4 | 0.00193100 | 0.001121       | FIELD       |
  |        5 | 0.00192300 | 0.001116       | FIND_IN_SET |
  |        6 | 0.00192100 | 0.001101       | FIELD       |
  +----------+------------+------------------------------+

Note that the query result is a hybrid between show profiles and show profile for query [id], and I've ripped out the actual query because all I care about is the sorting function being used (the query is the same otherwise). The duration on the whole was lower in each subsequent query because of MySQL's query cache, but since I really wasn't interested in that anyway — just the sorting duration — it doesn't really matter.

The profiling I did was ridiculously contrived, but it did go to show me, based on the miniscule differences, that it most likely will not matter which function I use (although FIND_IN_SET ultimately won out over my exhaustive six-query profile).

Awesome recruiter email

June 19th, 2008

Holy hell, this is amazing:

Hello Allan,

I am vara, recruiter form Tekforcecorp.
I saw your resume on Internet and we have a immediate requirement for PHP developer.
please see the requirement and let me know if you are interested. Please get back to me with updated rseume and salary info.This is a fulltime position.

Responsibilities:

  • Develop web applications and services
  • Apply industry best-practice software standards and technology to complex business problems
  • Help set-up and maintain development, test and production environments

Qualifications:

  • Bachelors degree in CS or related field, or equivalent experience
  • 2+ years experience in design and development of commercial web applications on multiple platforms
  • Previous professional experience with PHP required (PHP5 preferred)
  • Current experience with MySQL required
  • Basic understanding of OOP principles and practices is required
  • Experience with Java, Ruby on Rails, and/or object oriented C++ a plus
  • Experience with AJAX a plus
  • Ability to work on a small team, in a start-up environment
  • Ability to handle multiple concurrent activities and have a flexible, positive attitude
  • Excellent verbal, written and communication skills is a must
  • Must be team-oriented, with an interest and willingness to help develop and mentor the engineering team as it grows

Vara
Tekforce Corp
2420 Camino Ramon , Suite 212
San Ramon, CA 94583
925 866 8200 ext. 234
925 866 8219 Fax
vara@tekforcecorp.com

Make Rails' Auto-linker Accept Parentheses

May 21st, 2008

The Rails helper, auto_link is a handy way to scan a block of text, adding HTML links as they are found. I recognized an issue with its regular expression, however, when dealing with files commonly linked to on my last project.

The links in question look like this:

http://www.website.com/assets/screenshotbundle(5-20-08).zip
Unfortunately, Rails was creating links that looked something like this:
http://www.website.com/assets/screenshotbundle(5-20-08).zip
While parentheses aren't necessarily commonplace in URLs, they're still acceptable; Web browsers won't balk when they're encountered. It's clear that we need to patch our helper to accept parentheses.

Delving into the auto_link method in ActionView::Helpers::TextHelper, it's working off a rather large regular expression defined as the constant, AUTO_LINK_RE. To safely change this constant, you need to do so within the initializer block in environment.rb. If you try to define it by putting a file in the config/initializers directory, you'll get a warning that the constant has already been defined. Here's my updated definition:

Rails::Initializer.run do |config|
  module ActionView
    module Helpers
      module TextHelper
        AUTO_LINK_RE = %r{
          (                          # leading text
            <\w+.*?>|                # leading HTML tag, or
            [^=!:'"/]|               # leading punctuation, or
            |                        # nothing
          )
          (
            (?:https?://)|           # protocol spec, or
            (?:www\.)                # www.*
          )
          (
            [-\w]+                   # subdomain or domain
            (?:\.[-\w]+)*            # remaining subdomains or domain
            (?::\d+)?                # port
            (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:][^\s$]))+)?)* # path
            (?:\?[\w\+@%&=.;-]+)?    # query string
            (?:\#[\w\-]*)?           # trailing anchor
          )
          ([[:punct:]]|\s|<|$)       # trailing text
        }x
      end
    end
  end
end

Update: I've created a ticket to correct this behavior.

Recruiters are hilarious

May 8th, 2008
I just got a call from someone (Josh? Jason?) at a recruiter. It took him a few seconds to tell me he was, in fact, a recruiter, but once he did, I realized I didn't want to talk, told him, "No thanks. Bye." and hung up the phone. I could hear him keep talking on the other end as I pulled away my phone, but hung up none-the-less. Not two seconds after I put down my phone did he call back, and as such, went to my voicemail box. I couldn't wait to hear what the message might entail. It went something like this:
Hey, Adam. It's [what's-his-name] again. I'm going to send you a check in the mail for $1.55 for etiquette lessons, because it's obvious your mother never taught you that it's rude to hang up on someone.
Wah wah. Have fun at you crappy recruiting job, Jessie. Or maybe it was Jeff?

A case for methodizing your constants

April 4th, 2008

I was testing a bit of Rails code today and came across a spot where I wanted a unit test to make sure my method was going to properly handle an upper limit on associated objects. Think of it like this:

class Release < ActiveRecord::Base
  has_many :tags
  
  MAX_TAGS = 15
  
  def associate(tag)
    self.tags << tag unless self.tags.size == MAX_TAGS
  end
end

At 15 max tags, either I'm going to clutter up my tests with dummy data, or I'm going to drive myself insane creating needless fixtures. I use Mocha to stub out methods in my tests, so I immediately asked myself, "How can I stub a constant?" You can go ahead and alter the constant in your test case using const_set, but that requires that you set it back at the end of your test. Another option is to methodize the constant and stub it out like you would any other method:

class Release < ActiveRecord::Base
  has_many :tags
  
  MAX_TAGS = 15
  
  def self.max_tags
    MAX_TAGS
  end
  
  def associate(tag)
    self.tags << tag unless self.tags.size == Release.max_tags
  end
end

Now, in your test case, you can just stub out your new class method and, provided you're now accessing the constant in your original method through its new wrapper method, you're all set:

class ReleaseTest < ActiveSupport::TestCase
  def test_associate
    Release.stubs(:max_tags).returns(1)
    
    assert_no_difference 'release(:first).tags.reload.size' do
      release(:first).associate(tags(:new))
    end
  end
end

Start Simple, End With a Flourish

April 3rd, 2008

As I continue developing new sites, I find myself falling into development patterns. I'm trying to foster some of them, but there's one habit in particular I'm trying to break: I realize that when I began my most recent project, it was littered with JavaScript-y goodness in an effort to get the "wow" factor from users. As time has worn on, however, I've found myself ripping out more and more of this chrome, to the point where the majority of my site now functions as a "normal" Web application.

One of the first pieces of AJAX I added to this project was the ability to edit a "media bar" while still browsing the page it was on. You'd click an "edit" link in a vertically floated DIV, and it would scale upwards in sort of a light box effect, while fading out its contents and fading in controls to edit those contents. Now, don't get me wrong. This control looked cool (take my word for it). I still have the code lying around in an old Subversion check-in, because I'm a bit proud of it, actually.

The problem with my editable media bar was that ultimately, it was too stylish for its own good. The novelty wore off after a few uses, and then I was stuck with a difficult-to-maintain piece of JavaScript that never seemed to fit with the rest of the site. But I had spent like three days on it! It was so cool! Which is why it had to go: I had spent so much time on it that I had tunnel vision, and when our designer actually got around to seeing it, he didn't even make a peep; it wasn't even a blip on his radar.

Something I had to realize was that if I really wanted this element on the site, I had to also be comfortable taking it away, and reevaluating it. It may come back in a similar form in the future, but for a version 1.0 release, it wasn't worth the time I spent creating it. I've reverted to a standard link to send users to the media bar edit page, and its functionality is as good, if not better, than my initial flashy solution.

So I'm going to be starting simple on my next project. JavaScript and AJAX can wait. It has its place on The Web, but for most everyday user interface patterns, it's just overkill, and the benefits do not outweigh the development time.

Bagels: Cafe Encore

January 26th, 2008

View Larger Map

I started my search for the perfect bagel near Union Square at Cafe Encore. This little cafe is situated on Post Street at Mason Street, about a block away from my office. Its sign is small and is sandwiched between two other signs, so if you're coming from the East, you might miss it.

The cafe itself is small with a few places to sit, a juice fridge, ATM, and a menu with a healthy assortment of both breakfast and lunch options. The two women I've seen working there are very friendly and are willing to chat with the customers. There is a sign behind the register that advises, "No soup for you! (We're temporarily sold out, sorry)" and people seem to get a kick out of it.

Regarding the bagels, I should start by saying that I'm looking at four things with these reviews:

  • Appearance
  • Price
  • Bagel bread
  • Cream cheese
All bagels will be toasted, plain, with plain cream cheese. Now, onto the review for Cafe Encore.

  • Appearance: The bagel doesn't really look good at all. It's smooshed, and reminds me of Lender's bagels. Even when toasted, I feel like it's just going to ooze cream cheese and not have that crunch I love so much when bitten into.
  • Price: The bagel costs $2.50. I'm used to a $2.06 bagel, but I've come to accept that most everything costs more in Union Square, so I'm not too concerned. I will say that $2.50 is on the high end of what I expect to pay for my bagel, though, and anything more than that will just disappoint, unless it's God's gift to bagels.
  • Bagel bread: The appearance says it all: the bread is squishy and lacks character. There's nothing like biting into a fresh, toasted piece of bread, but this comes nowhere close to perfection.
  • Cream cheese: The coverage on the bagel is spotty at best. I pulled off one quarter of the bagel only to realize that 95% of the cream cheese came with it, leaving the other piece beneath it with very little. I'm not looking forward to eating a piece of bagel with almost no cream cheese.

    Another problem is that the cream cheese is really warm and gooey. I like a firm, cold cream cheese to contrast the toasted bagel, but this stuff doesn't hold up to the heat and threatens to turn into a drippy mess.

While I enjoy the cafe itself, I'm going to have to avoid Cafe Encore's bagels from now on. There are too many things wrong with them to keep going back.