Yen's Blog

Lens, Wheels, Skates, Keyboard

Sports I Would Watch

…if I had an unlimited amount of free time, bathroom breaks and no desire to have a life.

  1. Olympic men’s hockey
  2. Bruins hockey
  3. NHL hockey
  4. IIHF tournament hockey such as the World Juniors
  5. Top-flight European professional hockey (KHL, Swedish Elite League, Finnish Elite League)
  6. Olympic women’s hockey. It’s enjoyable but less exciting than the men’s game due to the lack of strong competition; basically it’s a 2-horse race between Canada and USA. Also there’s no checking and it’s slower.
  7. College hockey
  8. AHL hockey
  9. High school hockey
  10. Tour de France
  11. Other Summer and Winter Olympic sports
  12. Bandy. I’ve never seen this Swedish specialty, but this hockey-soccer hybrid is tantalizing. Putting the players on skates is one way to make soccer more exciting.
  13. Pick-up hockey in a rink or on a pond
  14. Roller hockey
  15. Canadian shinny
  16. European indoor roller hockey. They play on rollerskates. Seriously. I’ve only seen it a few times. It’s exciting and weird.
  17. Figure skating
  18. Midget/Bantam/Squirt/Mite hockey. Midget is an official youth hockey designation, it doesn’t mean the game is played by midgets, Borderlands-style.
  19. Midget hockey, Borderlands-style
  20. Street hockey
  21. A couple of guys practicing with sticks and puck
  22. Soccer
  23. Volleyball (not beach)
  24. Tennis

… and many sports later

Football
Basketball
Baseball

Using Angular Root Scope for Communication Between Controllers

Angular makes it simple to create standard index and details views. Using the ubiquitous task list as an example, I can set up routes using the $route service as follows:

1
2
3
4
5
$routeProvider
  .when('/tasks', { templateUrl: '_tasks.html', controller: 'TasksIndexCtrl' })
  .when('/task/:id', { templateUrl: '_task.html', controller: 'TaskDetailsCtrl' })
  .otherwise({ redirectTo: '/tasks' });
}

The routes definitions specify the Angular controller and HTML template to use when the user navigates to a particular path. Here we have 2 controllers, one for the index page and one for the details page.

The index controller retrieves the task list as a JSON array via the $http service:

1
2
3
4
5
6
7
function ($scope, $http) {
  $http.get('tasks.json').success(
    function (data) {
      $scope.tasks = data;
    }
  );
}

The index view renders each task with a link which embeds the task’s id. When clicked, the link redirects to the task’s details view.

1
<a href='#/task/{{task.id}}'>{{task.name}}</a>

The details controller also retrieves task data as JSON via $http, after extracting the id value from $routeParams.

1
2
3
4
5
6
7
function ($scope, $http, $routeParams) {
  $http.get('task-' + $routeParams.id + '.json').success(
    function (data) {
      $scope.task = data;
    }
  );
}

View the first version in Plunker

So far it’s pretty run-of-the-mill. But let’s say the task list also contains sufficient data to show each task’s details (the index page simply displays fewer information due to space constraint). There’d be no need for a separate request to pull data for the details page. The details controller can just search in-memory, within the task list which is already retrieved. However, since each controller has its own scope and can’t access another’s scope, there has to be another way to share data between controllers.

Enters the root scope, which acts as the parent scope for all other scopes. If a property can’t be found in a scope, Angular searchs its parent scope and so on until it reaches the root scope (as a consequence of prototypical inheritance). Putting the task list in the root scope would allow Angular to find it in the index controller and, because all scopes share the same root scope, also allow the details controller to access it.

Modifying the index controller to save the task list to the root scope:

1
2
3
4
5
6
7
8
function ($rootScope, $http) {
  $rootScope.tasks = [];
  $http.get('tasks.json').success(
    function (data) {
      $rootScope.tasks = data;
    }
  );
}

Instead of making an HTTP request, the details controller can search for a task by id within the root scope. When it finds the tasks, it saves it to its own scope. It’s probably a good idea to not pollute the root scope any more than necessary.

1
2
3
4
5
6
7
8
9
10
11
12
13
function ($rootScope, $scope, $routeParams) {
  var getTask = function (id) {
    id = parseInt(id, 10);
    for (var i = 0; task = $rootScope.tasks[i]; ++i) {
      if (task.id === id) {
        return task;
      }
    }
    return task;
  };

  $scope.task = getTask($routeParams.id);
}

Each time the user returns to the index page, a fresh request is made to retrieve the task list. I could optimize it further by retrieving the task list only if it has changed. To do so, I’d need to keep track of the modification time. Here, I’m only making the simple check to see whether the task list has already been populated.

1
2
3
4
5
6
7
8
9
10
function ($rootScope, $http) {
  if (typeof $rootScope.tasks === 'undefined') {
    $rootScope.tasks = [];
    $http.get('tasks.json').success(
      function (data) {
        $rootScope.tasks = data;
      }
    );
  }
}

View the second version in Plunker

Updating SQL Server Primary Key

A requirement recently came up to update the primary key of a SQL Server table. We get user data from a third-party single sign-on service and store them locally. When the SSO software was upgraded, user IDs were changed, so we need to update our local users but retain existing data. I suppose one way to do so would be to drop any existing foreign-key constraint and update all related tables. Unfortunately, the User table is related to practically every other table in the schema. Updating a bunch tables manually, without the safety net of relational integrity, would be a bit dodgy. Fortunately, there’s a less painful approach using the ON UPDATE CASCADE clause.

In a simple example, I have a LineItem table, whose userId field references User’s Id field. Since the existing FK relationship doesn’t have ON DELETE CASCADE, I’d have to “alter” the constraint to add it. But because is no “alter contraint” in T-SQL, I’d have to drop and re-add the constraint.

1
2
3
4
5
6
7
8
9
10
11
12
/* drop existing constraint */
ALTER TABLE [dbo].[LineItem] DROP CONSTRAINT [FK_LineItem_User]
GO

/* add constraint */
ALTER TABLE [dbo].[LineItem]  WITH CHECK ADD  CONSTRAINT [FK_LineItem_User] FOREIGN KEY([UserId])
REFERENCES [dbo].[User] ([Id])
ON UPDATE CASCADE
GO

ALTER TABLE [dbo].[LineItem] CHECK CONSTRAINT [FK_LineItem_User]
GO

Thereafter when I update an Id in the User table, the change would be cascaded to LineItem and all other related tables.

Top 10 Archive

A long time ago, before I settled into a comfortable life in suburbia and write dry articles about computer, I was in grad school and belonged to a student association. Yes, that sounds much more exciting. But we had lots of gatherings, movie nights, BBQ, parties, in general lots of opportunities to revel in our collective nerddom. When I moved out with roommmates, we indulged in more of the same. I loved top 10 lists and always had one for most of the events. Here are of the top 10 lists over the years, scraped from old emails. Most probably won’t make any sense without some context. But they may be worth a chuckle or two for those who were named, or were unfortunately enough to be at those events.

Top 10 Reasons to go to the Star Wars party at Tan’s

  1. Tan needs friends.
  2. It behooves you to test your Star Wars knowledge against the best.
  3. At long last, our ‘96 Star Wars skit will begin to make some sense.
  4. Tan’s place and Yen’s cooking sure beat Yen’s place and Tan’s cooking.
  5. Help Tan finish up those jars and jars of lychee jelly.
  6. Tan promised no Cajun Vietnamese food.
  7. Experience the spectacle of Tan’s room in person, in case you missed it on The X-Files.
  8. Experience Yen’s cooking in person, in case you missed it on FOX’s World’s Most Dangerous Stunts.
  9. Tan will give a tour of his closet full of dresses, sorry outfit.
  10. Find out what Obi Wan means by “a most wretched hive of scum and villainy”.

Top 10 reasons why you should go to the VSA party at McCormick’s

  1. None of us will be cooking
  2. No weird guys trying to put a move on you/your sibling
  3. DJ recently lost CDRs containing his disco collection :(
  4. Get a good laugh out of VSA guys trying bust a move
  5. Betty will bring her Spice Girl/‘N Sync/Backstreet Boys albums
  6. Help Yen feel better about splurging much $$ for the system
  7. If the music sux and the crowd is boring, well at least it’s better than your 8.01 problem set.
  8. David will show off his break-dancing skills
  9. Nothing good on MIT cable
  10. We love you guys!

Top 10 reasons our team will win… VSA volleyball match

  1. Betty can really spank… the ball
  2. Tan is the funniest thing you’ve ever seen. Hey, it distracts the other team.
  3. Binh’s jump serve can really knock the net off the post
  4. Anh Hien’s no-look pass, which leads to…
  5. Yen’s nothing but net shot
  6. Our Pokemon-in-the-hole, be Nguyet
  7. Van’s exquisite drop shot
  8. We’re gonna try some of the mind-bending tricks we learn in the Matrix
  9. Binh has a strong incentive: if we win, he won’t have to dress up as a girl for Senior Dinner.
  10. The rest of us has a strong incentive: If we lose, we’ll have to watch Binh…

TOP 10 REASONS YOU SHOULD TO GO THE PARTY

  1. Tan still needs friends
  2. After this you won’t have to see the graduates ever again
  3. Be the first to put Tan’s kitchen to good use
  4. Tan promised he’d spend half a week cleaning the house before we even set foot there
  5. Tan’s living room: the only one equipped with a pingpong table
  6. We need fresh meat for multiplayer N64
  7. We made Binh promise he won’t go anywhere near the kitchen
  8. Yen finally learned to decipher Betty’s recipe (e.g., half a tablespoon, not cup, of salt)
  9. We know you love to eat
  10. Food will be catered by the Iron Chefs!

Top 10 Reasons you should go to Yen’s House-warming party at 7 Seckel Street

  1. Listed by the Boston Phoenix as one of the hottest spots in Cambridge.
  2. Will never be sold out like Crouching Tiger @ Kendall Cinema.
  3. Plenty of seatings on the brand-new hardwood floor.
  4. We’ve had enough time to recover from Tan’s cooking at last year’s party.
  5. If things get too slow, we’ll go crash Diem’s apartment just down the street.
  6. TV will not pick up MIT Cable.
  7. House does not have pingpong table nor N64 like Tan’s, but will have something much better… me.
  8. Landlord’s dog will “only bark, not bite”.
  9. Food will be catered by Iron Chef – Vietnamese
  10. Be witness to a rare event that happens only once every millennium: Tan may bring a date!

Top 10 reasons you should go to the July 4th BBQ at McCormick’s

  1. No need to fight for a piece of real estate with a zillion sweaty, smelly people.
  2. Check out McCormick’s sparkling new men’s room (sorry ladies)
  3. Beach volleyball!!!
  4. We won’t feel embarrassed by the Brazilians about our lack of rhythm
  5. McCormick’s reputation as MIT’s party central
  6. Weather should be hot enough that the chicken wings will cook themselves.
  7. Pity the poor writhing masses below from our vantage point
  8. Betty will bring her luau outfit, complete with lei, straw skirt and coconut half-shells (or is it Tan???)
  9. Your personal fireworks show (“Hey Betty, I didn’t know these lighter fluids come in gallon cans?” )
  10. You’ll be entertained

TOP 10 REASONS YOU SHOULD COME: Patriots Superbowl Game, Feb 1, 2003

  1. Big men in very tight tights
  2. The Patriots cheerleaders
  3. Experience the actions exploding out of our, uh, medium-screen TV
  4. Leftover goodies from last week
  5. If you don’t know your false starts from your touchbacks, well you’d know as much as most of this bunch :)
  6. After this the next chance to party will be (drum roll please) President’s Day!
  7. This week won’t be my turn to clean the house
  8. Half-time show will feature Janet Jackson
  9. Half-time show will not feature *NSYNC
  10. Beracah promised no more BBB (Beracah’s Burned Brownies)

Top 10 reasons you should come: Thanksgiving party, Nov 27, 2003

  1. We just finished clearing up the mess from Halloween
  2. Yen already put away his leather whip
  3. The dreaded toilet paper-man will not be making an appearance
  4. Yen will not be allowed in the kitchen while cooking is in progress
  5. It’s not Yen’s turn this week to clean the house :)
  6. We live across from McDonald’s, if that becomes a necessity
  7. We should be thankful that we’re eating turkey instead of being in Turkey.. okay that was kinda lame
  8. The more bodies, the warmer it is inside
  9. This being East Cambridge, there are at least 3 liquor stores within a half-mile radius
  10. This line intentionally left blank

Top 10 Reasons you should come: Halloween Party, Oct 31, 2003

  1. Sebastian has been spending time in the gym to prepare for this event, for what we’re afraid to ask.
  2. Iva’s “spanking” new leather outfit
  3. Beracah wants to scare someone besides us
  4. Yen just needs friends
  5. You’ve just spent a fortune on feather boas at the Garment District and want to show SOMEBODY
  6. Our BLOOD-RED Chamber of Horror, mwahahaha
  7. Our landlady doesn’t live within earshot
  8. Need you a reason to drink yourself silly?
  9. Ergo, there’ll be lots of drunk strangers in a darkened room… on second thought, let’s not go there
  10. It’s Halloween, we need a good scare, and you’re the best :)

TOP 10 REASONS YOU SHOULD COME to the BBQ: April 25, 2004

  1. Our chief meteorologist, Beracah’s knee, informs us that Sunday should be more or less sunny
  2. Our second-floor deck is finally all thawed out
  3. The occupants are finally all thawed out
  4. Our friendly neighborhood liquor stores sent us a reminder that we haven’t visited them lately
  5. In preparation for this event, Beracah has been abstaining from McDonald’s
  6. Yen hasn’t had a chance to set anything on fire in a while
  7. The fire department is just down the street
  8. Iva would like to try out her new Gucci luau outfit
  9. We haven’t consumed vast quantities of alcohol in a month, and Sebastian is getting restless
  10. No actual eyeball will be on the menu

TOP 10 REASONS YOU SHOULD COME: VSA Monthly Dinner Mar 13, 2004

  1. Yen is just providing the space and will not do any actual cooking
  2. We made Tan promise that he would not cook
  3. Since Nhut’s SO lives in California, his sole hobby for the past few years has been cooking and cooking some more
  4. If worse comes to worst, there’s a McDonald’s across the street
  5. East Cambridge has the cheapest liquor in town
  6. With all the spare change we won from the Tet party we can finally afford to deck up the place a bit
  7. The only thing green to be consumed will be the beer
  8. Contrary to appearance our kitchen does not date from the Bronze Age
  9. My roommates are quite normal people and will not try to eat you
  10. St Patty’s day party to follow!!!

TOP 10 REASONS YOU SHOULD COME: Saint Patrick’s Day Party Mar 13, 2004

  1. No one showed up for our George Washington’s Birthday party
  2. We’ll hide the extra rolls of TP so Beracah can’t use them for his costume again
  3. Drinking green beer helps save water and demonstrate your environmental sensitivity
  4. Yen will not go as a Leprechaun (even though he’s got the right height)
  5. Iva’s been practicing moves at flamenco and bellydancing shows
  6. Test your drinking mettle against the best of East Cambridge, and I’m not talking about Yen, Iva or Beracah
  7. We will not play any song from Sebastian’s death-metal collection
  8. Our neighbor seems to be hard-of-hearing
  9. There’ll be a bunch of drunk people wearing masks in a dark room… on second thought let’s not go there
  10. We’ve got enough beads to cover (no pun intended) all of your Mardi Gras-style antics

TOP 10 REASONS YOU SHOULD COME TO THE BBQ Sunday July 4, 2004

  1. Be first-hand witness to a rare event that only happens once in a lifetime: Iron Chef Polish vs. Iron Chef McDonald’s
  2. Our friendly neighborhood liquor store is offering discounts to frequent customers (or so Sebastian tells us)
  3. This being our 4th BBQ, we have the grilling thing down to an art. Quit your snickering over there.
  4. Since Iva is out of town, Sebastian will do his best to impersonate her with a coconut-shell luau outfit
  5. Betty promises a preview of her Miss New England posedown. BYOO.
  6. After “Supersize me”, Beracah no longer wants to eat hamburgers at McDonald’s. Only at home.
  7. The city of East Cambridge recently made us honorary Portuguese… meet Sebastiao, Beracaho and Tran
  8. Fighting a zillion sweaty people for a piece of personal real estate is so much more entertaining on our balcony than on the Esplanade
  9. It’ll be safe, as aliens invaders have all been eradicated by Will Smith
  10. No wardrobe malfunction anticipated at half-time show

TOP 10 REASONS YOU SHOULD COME: Party September 25, 2004

  1. We’re between-hurricanes
  2. This week is official “Imbibe-yourself-into-a-stupor” week in East Cambidge
  3. Our friendly neighborhood liquor store is having a going-out-of-business sale
  4. There’s no hockey on Saturday night (or this season)
  5. Beracah spent the entire summer working on his no-lines tan
  6. Betty is looking for volunteer “oil-persons” to help during her body-building competition
  7. Last chance for Sebastian to try out his revealing summer outfit
  8. Our amorous next-door neighbors asked if we could shake their bed with the subwoofer
  9. Forget Miss America, Iva will show off her Miss East Cambridge catwalk routine
  10. We have absolutely no good reason whatsoever

TOP 10 REASONS YOU SHOULD COME TO THANKSGIVING 2004 DINNER (NOV 24, 2004)

  1. If we said last Saturday was the last chance to see the real Nhut before he turns into California surfer dude… we lied! THIS will be absolutely the last chance
  2. We will be disclosing the censored photos from Betty’s bodybuilding competition
  3. Last chance to experience our cooking in person before the restraining order from Zagat arrives
  4. We recently cleaned the living room and discovered all the missing cooking wares
  5. Once in a life time chance to experience the curious and tempting possibilities of Vietnamese-Polish-Bulgarian fusion cuisine… after this, you may not want to again
  6. The turkey we’re about to consume came with a legitimate organ donor card
  7. Beracah promised that he’d continue to keep his “secret” recipe a secret
  8. Meet my, ahem our, hot new roommate, Miss Betty Junior
  9. Sebastian promised that he’ll be sober while cooking
  10. We’ll make sure you won’t be sober enough to think the food isn’t great!

SUPERBOWL 2005, FEB 6, 2005

  1. Last time we gathered to watch the Superbowl, they won!
  2. Last year’s Justin & Janet brouhaha will be reenacted by Yen and his inflatable girlfriend
  3. Have an imaginary housewarming party with an imaginary Betty (you can ask her to explain this one :)
  4. Catch the explosive actions out of our imaginary big-screen TV
  5. The Pats cheerleaders will be cold, just like Beracah likes
  6. For those not well versed in the intricacies of the sport, we will continue last year’s lesson in football rules and terminology. This week’s topic: What “tight end” refers to instead of the player’s physique.
  7. It’s a long, cold, dark winter night and we’re scared
  8. We’d like to be reminded what you all look like
  9. We’ve still got some liquor left. Come get some before Sebastian discovers it.
  10. There’s no point in watching the Michael Jackson trial cuz he’s guilty :)

TOP 10 REASONS YOU SHOULD COME TO THE TET POTLUCK/MARDI GRAS PARTY sat FEB 12, 2005

  1. Since this potluck is for Tet, there’ll be plenty of luck and plenty of… pot. That didn’t come out right.
  2. Sebastian promises that the only dip he’ll be making is for potato chips
  3. We promise no leftover eggnogs
  4. Our elderly next door neighbor is complaining that we haven’t played any good gangsta rap lately
  5. Since Michael Jackson cannot make an appearance due to certain, ahem, legal commitment, Beracah will do his best to impersonate the gloved one (on-stage, that is)
  6. Sebastian recently uncovered scientific evidence that the best cure for the winter blues is to drink yourself into a stupor, or so he told us
  7. Even though Yen doesn’t own a six-pack, he plans to buy a few
  8. Iva will preview her catwalk routine for the East Cambridge’s Next Top Model audition
  9. Help us trash the place so we can get on Queer Eyes for the Straight Guys
  10. We’ve lived in East Cambridge so long, we’ve turned Brazilian. Pronto ao partido e começa quente em mim?

TOP 11 REASONS YOU SHOULD COME TO THE JULY 4TH, 2005 PARTY

  1. Sebastian was recently elevated to Gold Member status at E Cambridge’s very own Liquor-4-Everyone store and gets access to the best stuff
  2. We have a visiting professional hiphop dancer from Flensburg, Germany, the capital of hiphop
  3. Boston.sidewalk.com recently gave us a 9/10 for “Hotspot You’ve Never Heard of in a Place You’d Never Go”
  4. Beracah has been practicing moves on Monday nights at a as-yet-unnamed club, where “the guys are really good”
  5. Our place is certified by Mass Dept of Public Health as “fit for grooving”
  6. The nice little old lady next door let us borrow her “great-for-parties” collection of gangsta rap
  7. Yen recently broke up with his inflatable girlfriend, so… guys, she’s available!
  8. Iva will impart hot tips on how to strut like America’s Next Top Model without breaking your ankles
  9. Recent inquiry about dress code as to whether “thongs are okay” were voted 3-1, in favor of
  10. Come celebrate Michael Jackson’s complete acquittal! Sorry, there are no kids here.
  11. The Queer Eyes for the Straight Guys have never been here

Top 10 reasons you should come to the Year of the Dog Dinner Party:

  1. The year of the dog doesn’t necessarily imply anything about the menu
  2. Menu will be prepared by chefs with culinary degrees from the internet
  3. If the menu doesn’t work out, there’s Banh Mi Ba Le across the street
  4. There’s enough alcoholic libations to keep a Polish guy happy
  5. Kitchen is Swedish-themed, but fortunately the cuisine won’t be
  6. Cold weather means no guest will be tempted to come in thongs
  7. Beracah would appreciate friendly faces to try out new pickup lines
  8. Of all the hip spots in Dorchester, Fields Corner is the Hippest, according to FieldsCornerIsTheBest.com
  9. Landlady lives on premise, but fortunately she’s cool with parties
  10. The chefs misplaced their vegetarian recipe book :)

Top 10 reasons you should come to the July 4th BBQ:

  1. We’ve had the urge to set something on fire since watching Xmen 3
  2. Instead of fighting for a piece of personal real estate a zillion sweaty people on the Esplanade, you could be doing that on our patio
  3. Lots of guys will be sporting 6-packs. Beer, that is.
  4. Recent inquiry about dress code as to whether “thongs are okay” were voted 3-1, in favor of
  5. Balanced menu with sundry grilled options means that this will not simply be a sausage fest
  6. Grilling will be expertly handled by chefs with culinary degrees from the Internet
  7. The arcane secret of burning everything to a crisp has been lost since GrillMastah B left on vacation
  8. Come celebrate Germany’s recent victory in the world cup. What, nobody cares about soccer? Well, hmm, they do drink lots of beer in Germany.
  9. We will light the BBQ with the electro-shock thingy on our R2-D2 unit
  10. You’re getting sick of eating health food

TOP 10 REASONS YOU SHOULD COME: Happy Turkey Day Gobble Gobble II (Nov 23, 2006)

  1. We seemed to have misplaced our favorite cookbook, 101 Ways to Prepare Tofu and Leeks
  2. Getting here doesn’t involve dodging illegal fireworks and avoiding domestic disturbance spilled onto the streets
  3. Having lots of people over helps us save on heating cost
  4. Menu will be prepared by chefs with culinary degrees from the Culinary Institute of the Internet
  5. No easy access to liquor stores within 2 blocks as in E Cambridge, but hopefully Sebastian will come…
  6. You seriously need a break from your daily diet of chocolate, coca-cola, buffalo wings, fried chicken, chicken pakora, a good steak, all at once.
  7. Turkey is a staple of Vietnamese cuisine (pineapple turkey, lemon turkey, coconut turkey, pepper turkey, turkey stew, turkey salad, turkey and potatoes, you get the idea)
  8. Boston.sidewalk.com gave us a 9/10 for “Place You Should Go on Thanksgiving When There’s Nowhere Else to Go”
  9. Roast a turkey in honor of the outgoing Republican senators and congressmen.
  10. We miss you guys!

Top 10 signs Ozgur may be a robot

  1. His favorite movies are Terminator, AI, and that Robin Williams movie.
  2. He can talk really fast
  3. He doesn’t need any sleep
  4. He doesn’t eat all day long
  5. “Ozgur Topcu” written backwards is “Ucpot Rugzo”, which of course in Turkish stands for “I Robot”
  6. Robots have no use for bowls
  7. He consumes vast quantities of coffee without any apparent effect
  8. When he’s “talking to himself”, he’s actually holding a conversation with his computer
  9. Have you ever seen him do the Robot?
  10. He actually understands File-Up

Top 10 Reasons To Come To Our New Year 2008 Party

  1. We finally finished the leftovers from Thanksgiving
  2. You can go sledding down the slope in front of the lawn
  3. Having people around helps cure SAD (Seasonal Anti-social Disorder)
  4. No turkey, but we’ll have a Turkish person
  5. If the weather gets cold enough you can go ice skating in the backyard.
  6. Crowding around our 13-inch TV helps keep you warm
  7. Thanks to old age, you forgot when Christmas was and slept through it
  8. One of your New Year’s resolutions is “spend more time in Arlington”
  9. This is the year of the rat, and the rat from Ratatouille will be doing the cooking
  10. I know you covet the Star Trek chess set I got for Christmas.

Lunch Crew 5/30/2008: Top 10 Reasons Why You Should “Do The ‘Crew”

  1. Discover first-hand whether Silver Dust will work on werewolves
  2. TS is running a very important study on how many SoftArtisans employees can fit around the peanut table
  3. You love musical chairs
  4. Dan may surprise us by wearing a non-Portugal or -Chelsea shirt.
  5. You never know when Tim may forget that he ordered food and drop by
  6. You are dying to find a killer recipe for Squirrel Stew and it’s not on Wikipedia… yet.
  7. Exercise your brain by participating in the weekly trivia game. The weekly topic: Where in the world does Ozgur eat lunch? (Hint: Starts with Crown and ends with Cafe)
  8. The entertainment will involve thought-provoking questions such as, “Does my half-eaten mozzarella sandwich belong to you”?
  9. You love Thai food and want to eat at Amarin every other week
  10. It’s too quiet upstairs and we need more conversations

TOP 10 REASONS YOU SHOULD COME TO OUR JULY 4 2009 BBQ

  1. No tornadoes in the forecast
  2. It’ll be a nice sunny day because we performed an ancient Indian rain ritual and these things never work
  3. No need to go down to the Esplanade… plenty of hot and scantily clad people right here
  4. Grillmaster has a PhD in culinary science from the Internet
  5. Karaoke machine is broken… intentionally.
  6. You like juicy melons. Watermelons, that is.
  7. Fresh corn was on sale and we bought a few… hundreds
  8. Wii wiill provide you wiith abundant wiine and wiitty entertainment. Okay that was lame, but don’t be such be a wiise-ass.
  9. Beracah has honed to perfection a new force power possessed by no other Jedi or Sith master—Force Burn
  10. Be the first to handle our virgin grill

2010: TOP 10 REJECTED RETREAT RECOMMENDATIONS

  1. Sprints to include actual sprint on the river path
  2. JIRA will now track amount of coffee consumed
  3. Foster relationship with customers by taking them on fishing trips to the footbridge above the dam
  4. To improve posture, desks should be removed and employees encouraged to type while in yoga pose
  5. Bugs should be ranked by number of K-cups it would take to resolve
  6. SA logo to be changed to unicorn dueling narwhal
  7. Whenever there’s an exception, SilverDust should play Pacman game instead of showing actual error message
  8. Stand-up meetings taking too long, cutting into productivity. Recommending stand-upside-down meetings, which should drastically cut down meeting duration due to severe fatigue.
  9. To promote team identity, dev should wear hair in a pony tail, pre-sales Mohawk and TS the Ozgur-perm
  10. Buy OfficeWriter and receive free autographed drawing of your pet, or random animal if you don’t have one
  11. Bug priority to be decided by result of foosball match
  12. SilverDust error messages to be generated by Random Chuck Norris Fact generator

2009: TOP 10 OVERHEARD QUOTES AT THE SHAREPOINT CONFERENCE IN LAS VEGAS

  1. I was working on DMH till 2am on purpose to prepare for this trip
  2. Simply put SharePoint is the best product ever invented. I would use it over and over again. It is pure joy to behold. I’m just so excited about SharePoint… What? Of course you can quote me on that… My name? Ben Jones.
  3. Is this the line for the poker tournament?
  4. Is this the line for the buffet?
  5. Just checking one last time. Are you absolutely sure that what happens in Vegas stays in Vegas?
  6. Just for this year, the show will start daily at 8pm and goes till 5 in the morning
  7. Hey check it out, the water fountain dispenses booze!
  8. So nobody remembers a thing about last night? Oh, has anyone seen Jim?
  9. People on the street are so friendly here. They keep wanting to go back to my room to get to know me better.
  10. I just hit the jackpot for $50 billion! Unfortunately they told me that the slot machine was imported from Zimbabwe.
  11. There’s a tiger in the bathroom!
  12. Why do people keep asking us for directions to the Stardust?
  13. Wanna see the pictures from my wedding last night?

2008: WHAT DID YOU LIKE ABOUT LAST YEAR’S RETREAT?

Last year’s retreat on Martha’s Vineyard was a success. We figure that will try capture the spirit if not the formula of last year’s retreat. To get some ideas, we wrote down some of the quotes we overheard. Disclaimer: The quote and/or ascription of speaker may be mostly to completely apocryphal.

“Sitting by the shimmering pool in the soft moonlight, pouring our hearts out about life and relationship, proving that romanticism is very much alive. It reminds me of… oh never mind.” – Andrey

“Participating in a softball game is huge part of my immersion in American culture—it’s so full of excitement and camaderie. However, I found it strange that Americans would enjoy such a painful sport.” – Tamar

“I’m proud of how everyone, when given a challenge, rose to the occasion and performed beyond their normal ability. This year we doubled the beer and there was no problem whatsoever.” – Dan

“I don’t know how I did it, but I think I just set off the alarm back at the office” – George Sass

“Um, the lobsters were great” – Richard

“I didn’t think anyone can eat 5 lobsters until now” – Ron

“Stooping in a tiny kitchen with 25 other people, in a room intended for 2, reminds me of college. And just as in college, the free beer made it worth every bit of trouble.” – Scott

“RPI has parties? I mean, besides LAN parties…” – George

“Uh, I don’t remember much. Just that I met someone cool at the bar. I think his name were Daniels… not Anthony, Jack. Jack Daniels.” – Ozgur

“Skinny-dipping in the pool” – Anonymous

“I was a little worried when my roommate Mark disappeared for half of the night” – Yen

“Everything from the setting to the activities were beyond my expectation. I just wish my sweetie were here. We could have played all sort of casual fun games like tennis, bowling, boxing, you know. We’d have to hook her up to the TV first, of course.” – Kevin

So Long WordPress, Hello Octopress

I’ve been using WordPress since 2011. It works well enough, but for me there are a few drawbacks.

  • It’s slow. It takes time to do anything from creating a post to previewing to deploying it. I prefer to edit text in a proper text editor, with the full array of keyboard shortcuts, so I usually end up copying and pasting anyways. But using the UI to do everything is cumbersome. Of course I could install a local instance of WordPress so I can preview locally. However, I would have to deploy everything, posts and images, every time. This seems inefficient for just adding a new post.

  • It’s hard to backup. I could export the WordPress content as an XML file, but this is only useful to WordPress. For a full manual backup, I would use Control Panel to export the database, then FTP into the site and download the entire WordPress content folder as an archive. This is time-consuming and a far cry from proper version control.

  • It’s hard to customize. There are probably a dozen plugins for anything I will ever want to do. While I could view and edit the plugin source, without an easy way to test it’s effectively a blackbox. Further, I wouldn’t want to have deal with PHP just to customize a plugin.

So it’s an easy to decision to put WordPress out to pasture. I looked at Jekyll and deploying on Heroku. Jekyll seems like a good choice because it’s used by GitHub, so it’s more likely to be developed and less likely to go away. I also checked out Ruhoh, which is created by a Jekyll developer to address some of its shortcomings. I really liked Ruhoh for the customizability, Mustache markup syntax and incremental page generation. I may go back to Ruhoh at some point. I finally settled on Octopress. It adds a theming engine and a host of productivity enhancements to Jekyll as well as beautiful syntax highlighting. It makes it trivially simple to deploy to GitHub Pages.

There are many good articles written about using Octopress. The documentation, for one, is a very good place to start. Here are just some of the issues I ran into along the way.

Importing from WordPress

I used Jekyll’s migration tools, specifically hpricot to process a WordPress export XML file to generate posts and pages. The process worked quite well, although as expected some manual cleanups were necessary.

Setting up DNS

This is probably a no-brainer for many people, unfortunately I’m not among them. Since my blog is now hosted by GitHub Pages, I had to configure my DNS to point my domain to the new IP address. The process is quite clearly laid out in the documentation. In my case, I had to create an A record for my top-level domain, yentran.org, to point to 204.232.175.78. I also created a CNAME record for the subdomain www.yentran.org as an alias which points to the same address. I was concerned that my email address xxx@yentran.org would be broken. It turns not to be the case. Email forwarding relies on a MX record, which continues to point to my ISP’s IP address. There’s also a wildcard record, which catches any unknown subdomain for which there is no matching A or CNAME record.

Images

Previously I used NextGen gallery to manage images. NextGen stores images in separate folders, so it’s simple to copy them (rather than searching for them on my hard drive).

I now use Dropbox’s Public folder to host images. This allows me to easily manage images. In Lightroom (or any other image organizing software), I can export the images directly to Dropbox’s local sync folder and the images immediately becomes available for linking. If I want to modify images—such as resizing them—I can work directly with the Dropbox sync folder without having to make changes to the site.

Image gallery

I used Lightbox 2 jQuery plugin to display an image gallery. However, it doesn’t work with hotlinked images. I decided to roll my own.

I wrote a small Ruby + Thor utility to generate the necessary markup for images based on the Dropbox folder and append it to posts. Thor is awesome.

Blogging with Octopress

Everytime you add or modify a post or page, Jekyll regenerates the entire site. This is so that it can properly update the site metadata. However, this can take a long time, particularly if you have many posts and pages. Octopress provides a useful optimization tool. Type rake isolate["post name"] moves all other posts except the specified post into a _stash folder, so that regeneration is significantly faster. Once you’re done, type rake integrate to move the other posts back; just don’t forget to do this before deploying.

Categories

Octopress doesn’t provide an out-of-the-box way to display categories, but this can be easily done. I want display them as an aside (Octopress-speak for a sidebar plugin or partial). I based mine on Octostrap3’s Category List aside.

1
2
3
4
5
6
7
8
9
10
11
<section>
  <h1>Categories</h1>
  <ul id="categories">
    {% for category in site.categories %}
     {% capture category_url %}{{ site.category_dir }}/{{ category | first | slugize | downcase | replace:' ','-' }}{% endcapture %}
      <li data-category="{{ category | first }}">
        <a href="{{ root_url | append:'/' | append:category_url }}">{{ category | first }}</a> <em>{{ category | last | size }}</em>
      </li>
    {% endfor %}
  </ul>
</section>

Each category in the site.categories collection is an array, the first element being the category name and the last containing the pages in the category. Unfortunately Jekyll’s Liquid markup language doesn’t have a way to sort a collection of arrays. I opted to use JavaScript. This isn’t ideal, but some of Octopress’s own asides use the same approach.

1
2
3
4
5
6
7
8
9
10
11
$(function() {
  var categories = [];
  $('[data-category]').each(function() {
    categories.push(this);
  });
  categories.sort(function (a, b) {
    return a.attributes['data-category'].value.toLowerCase() <= b.attributes['data-category'].value.toLowerCase()
          ? -1 : 1;
  });
  $('#categories').html(categories);
});

Breadcrumbs

This would be another nice-to-have for Octopress. There are a few solutions to display breadcrumbs using Liquid. However, the more I work with Liquid, the less I like it. I would much prefer Mustache. So I again went with a JavaScript solution, which would still work if later on I decide to go with another blogging framework.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
function breadcrumbs() {
  var url_parts, $html, $li, $content, href, text, url, i;

  url = window.location.pathname;
  url_parts = $.grep(url.replace(/index\.html/, '').split(/\//), function (x) { return x.length > 0; });

  if (url_parts.length === 1) { // if at the top level
    return;
  }

  $html = $('<ol class="breadcrumbs">');
  for (i = 0; i < url_parts.length; ++i) {
    text = url_parts[i];
    href = '/' + url_parts.slice(0, i + 1).join('/');
    $content = (i < url_parts.length - 1) ? $('<a>').prop('href', href).text(text) : text;
    $('<li>').append($content).appendTo($html);
  }
  $html.insertAfter('#breadcrumbs-js');
}

$(function () {
  breadcrumbs();
});

Here’s the CSS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
.breadcrumbs {
  font-family: PT Sans, helvetica, sans-serif, arial;
  margin: 0;
  padding: 30px 15px 0 55px;
}
.breadcrumbs li {
  display: inline;
  list-style: none;
  padding-left: 15px;
}
.breadcrumbs li:not(:last-child):after {
  padding-left: 15px;
  content: "/";
}
.breadcrumbs li:first-child {
  padding-left: 0;
}
/* Octopress classic theme */
#content .breadcrumbs + div > {
  margin-top: 15px;
}
#content .breadcrumbs + div > article > header {
  padding-top: 0;
}

I created a partial consisting of the JavaScript and the necessary CSS, then included it in the pages.html layout.

1
{% render_partial _includes/custom/breadcrumbs.html %}

Navigation

On my travel pages, I want to have a hierarchy of links to different destinations:

USA
  Arizona
    Grand Canyon National Park
  Florida
    Cape Canaveral

It should be set up so that if I add a new destination, I wouldn’t have to hunt down all the pages containing the links in order add a new one. Luckily, with Jekyll all of the site’s metadata is stored in a single YAML file and accessible through the site variable. In order to capture the navigation hierarchy, I added the following to _config.yml:

travel:
  usa:
    - name: "Arizona"
      url:  az
      places:
        - name: "Grand Canyon National Park"
          url:  grand-canyon-national-park
    - name: "Florida"
      url:  fl
      places:
        - name: "Cape Canaveral"
          url:  cape-canaveral

YAML is a really compact, highly readable format. In the block above, indentation denotes nesting, : denotes a key-value pair and - denotes a collection. The parser turns the above YAML into the following hash:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{
  "travel" => {
    "usa" => [
      {
        "name" => "Arizona", "url" => "az", "places" => [
          {"name" => "Grand Canyon National Park", "url" => "grand-canyon-national-park"}
        ]
      },
      {
        "name" => "Florida", "url" => "fl", "places" => [
          {"name" => "Cape Canaveral", "url" => "cape-canaveral"}
        ]
      }
    ]
  }
}

After making modifications to _config.yml, I could quickly examine them by firing up the interactive Ruby shell, irb, then typing the following:

1
2
require 'yaml'
site = YAML.load_file('_config.yml')

I can access the navigation metadata as follows:

  • site["travel"] returns a hash containing country objects
  • site["travel"]["usa"] returns an array of state objects
  • site["travel"]["usa"][0] returns the first element of the array, which is Arizona
  • site["travel"]["usa"][0]["name"] returns the text “Arizona” and site["travel"]["usa"][0]["url"] the corresponding URL
  • site["travel"]["usa"][0]["places"] returns an array of locations in Arizona
  • site["travel"]["usa"][0]["places"][0] returns the object for Grand Canyon National Park, and so on

Jekyll makes it even nicer to work with the hash by turning keys into instance methods. So instead of writing site["travel"]["usa"][0]["places"][0], I could also write site.travel.usa[0].places[0].

In order to generate the following HTML for the navigation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<ul>
  <li><a href="/travel/usa">USA</a>
    <ul>
      <li><a href="/travel/usa/az">Arizona</a>
        <ul>
          <li><a href="/travel/usa/az/grand-canyon-national-park">Grand Canyon National Park</a></li>
        </ul>
      </li>
      <li><a href="/travel/usa/fl">Florida</a>
        <ul>
          <li><a href="/travel/usa/fl/cape-canaveral">Cape Canaveral</a></li>
        </ul>
      </li>
    </ul>
  </li>
</ul>

I used the following Liquid code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<ul>
  <li><a href="{{ root_url }}/travel/usa">USA</a>
    <ul>
      {% for state in site.travel.usa %}
        <li><a href="{{ root_url }}/travel/usa/{{ state.url }}">{{ state.name }}</a>
          <ul>
            {% for place in state.places %}
              <li><a href="{{ root_url }}/travel/usa/{{ state.url }}/{{ place.url }}">{{ place.name }}</a></li>
            {% endfor %}
          </ul>
        </li>
      {% endfor %}
    </ul>
  </li>
</ul>

Now if I want to add another destination, I’d only have to modify the YAML.

Conclusion

Octopress/Jekyll makes it easy and fun to tweak, hack and do so efficiently. I only wish I’d made the switch earlier!

Sigma 50mm f/1.4 Lens for Nikon AF

I’m fond of landscape photography. However when there’s a human subject, applying the same kind of composition can come out looking too formal or posed. I’d like to achieve more of the natural, spontaneous, spur-of-the-moment feel. For that I want to have a lens that’s fast, focuses quickly and has good bokeh. A prime lens would have those qualities at a much lower price point than a telephoto lens. With that settled, I had to decide between 35, 50 and 85mm. From what I’ve read, the general opinion is that the wide-angle 35mm prime lens is good for landscape or street scenes, while 85mm is good for an intimate, close-up portrait. That leaves the 50mm, which can do either almost as well and happens to be the (only) lens on my first camera, a Canon AT-1. That it’s famously associated with Henri Cartier-Bresson can’t be a bad thing either.

Nikon offers two 50mm lenses at f/1.4 and f/1.8. I chose the Sigma 50mm f/1.4 over the Nikon counterparts. For one, the reviews for the Sigma are universally outstanding. Even though bokeh quality is somewhat subjective, the general consensus is that the Sigma is better. For almost the same price as the Nikon 50mm f/1.4, it looks and feels much more solid than the plasticky-looking Nikon equivalent. Furthermore, it has the professional-standard 77mm filter size instead of the 58mm and 52mm filter sizes for the Nikon lenses.

The Sigma is very solid with a nice heft. It’s plastic, but manages to feel upscale and not cheap. It comes with a canvas pouch, a lens hood and a lens cover. It’s made in Japan, which is nice for a lens at this price range. Even my much more expensive Nikon 24-120mm f/4 zoom is made in Thailand. Strangely, there’s no marker on the lens to line up with the camera body, so I had to fiddle around a bit to get it to screw in.

Now onto the test images. All photos are taken with a Nikon D600 in either aperture priority or manual mode with auto ISO sensitivity. The first shoot is a walkaround our neighborhood.

This is a rather cliché shot. The subject is flat so all of it can be in focus. I thought the sharpness is very good.

Exposure 1/1000 @ f/1.8, 100 ISO

Here Kate is inspecting our walkway. In daylight the lens focuses easily. I think focusing works best at this distance. Depth of field is good with the lens wide open and so is bokeh. The 50mm focal length is perfect for portraits that include the upper body or shoulder.

Exposure 1/1250 @ f/1.8, 100 ISO

I love this shot, and not just because of the subject. Because of the closeness, I had to stop down the aperture a bit while focusing on the eyes. Otherwise the other parts of the face would be out of focus.

Exposure 1/50 @ f/5, 1800 ISO

The second shoot was at a ballroom competition at Tufts University where my friends were competing. Here I put the D600 in continuous servo AF mode with 9 focusing points. I shot in manual mode with the shutter speed set at a sufficiently high value to prevent blurring. I also set the AE/AL button to AF-on so that shutter release button just, well, release.

This is a rather flashy couple that clearly dominated the rest of the field. They were quite adept at showmanship. Not surprisingly, they took first place.

Exposure 1/400 @ f/2.2, 720 ISO

Here they are practicing in the corridor above the main floor. The setting is very nice, as is the photogenic couple. This has almost an Instagram feel to it.

Exposure 1/400 @ f/1.8, 3200 ISO

This is at the maximum aperture. Again sharpness is excellent at a range of about 3m. I had several long-distance shots from around 20-40m with continuous servo AF that didn’t turn out very good. The subject was blurry while the background was in sharper focus. Either the autofocus wasn’t working well, or I just didn’t have the right setting. I switched from 9- to 21-point focusing mode and got somewhat better results. In general I think this lens suits close-ups better.

Exposure 1/400 @ f/1.4, 2800 ISO
Exposure 400 @ f/2.2, 3200 ISO

The last shoot is at a friend’s house. I had to use continuous focusing again because the subject was rather, ahem, mobile. Some shots turned out quite blurry even with high shutter speed. I’ll have to look into this more. This shows the nice subject isolation possible with the ridiculously large aperture.

Exposure 1/60 @ f/1.4, 450 ISO
Exposure 1/160 @ f/2.2, 3200 ISO
Exposure 1/160 @ f/2.2, 2800 ISO

In conclusion, I love this lens! I like the ability to just point and shoot without worrying too much about composition. Being so used to carefully composing a shot, I’ve had to resist that impulse a bit and just focus on capturing the moment as it happens. The results look and feel natural and effortless. Needless to say this lens will get a lot more work in the future.

Fixing Windows Media Center No Tuner Available Error With HDHomeRun Prime

I have the HDHomeRun Prime network tuner. Recently, after a Verizon FiOS tech rebooted the ONT to fix a network issue, I got the dreaded Viewing or Listening Conflict. No tuner available to satisfy the current request error when trying to view Live TV in Windows Media Center. When I tried Settings > Windows Media Center setup, I got the error:

Tuner Not found. The TV signal cannot be configured because a TV tuner was not detected. If you have a tuner, ensure it is installed correctly. To find out how to watch TV on your PC, visit http://www.windows.com/pctv.

Since I was able to view cable TV using the bundled Quick TV application, the tuners seemed to be working properly. I tried upgrading the SiliconDust software, re-installing PlayReady for WMC, rebooting the router, turning off the Windows Firewall, none of which made any difference. I opened the Services panel and verified that the Windows Medica Center Receiver Service and the HDHomeRun Service are started.

All this resulted in lost time and furthered the perception that MS just can’t seem to make simple tasks as effortless as Apple. Finally I figured it out — Network Discovery had to be turned on. I’m not sure why it was turned off in the first place, but it had to be running for WMC to detect the tuners.

Simply open Windows Explorer and select the Network tab in the sidebar. You should see the HDHomerun Live DRIxxxx devices under the Media Devices section. If you don’t, Windows will ask if you want to turn on Network Discovery. Do this. If you’re lucky, once you’ll have done this WMC will immediately detect the tuner and you’re done. Otherwise, you can turn on Network Discovery in the Network and Sharing Center > Advanced sharing settings.

If you’ve swapped out the CableCARD then done the above and still get the same error, you may need to set up Windows Media Center again to update its settings. Go to Settings > General > Windows Media Center Setup > Set Up TV Signal. Completing the wizard should configure Windows Media Center to work with the new tuners.

Helper Method to Generate Nested Tags From Block

I’m still pretty new to Ruby. Hopefully one day I’ll know enough to be able to look back and slap myself on the forehead for struggling with these issues.

Right now our views generate a menu from a simple set of links:

1
2
<%= link_to "foo", "#" %>
<%= link_to "bar", "#" %>

I’d like to convert them to a drop-down menu that looks like this:

1
2
3
4
5
6
7
<div class="dropdown">
  <a class="dropdown-toggle" href="#"><i class="caret"></i></a>
  <ul class="dropdown-menu">
    <li><%= link_to "foo", "#" %></li> 
    <li><%= link_to "bar", "#" %></li>
  </ul>
</div>

I’d like to minimize the changes to the markup as much as possible. Initially I tried using a partial:

1
2
3
4
5
6
7
8
9
<!-- /shared/_dropdown.html.erb -->
<div class="dropdown">
  <a class="dropdown-toggle" data-toggle="dropdown" href="#"><i class="caret"></i></a>
  <ul class="dropdown-menu">
    <li>
      <%= yield %>
    </li>
  </ul>
</div>

The view would then include the partial:

1
2
3
4
<%= render :layout => "shared/dropdown" do %>
  <%= link_to "foo", "#" %>
  <%= link_to "bar, "#" %>
<% end %>

However, this put both links inside a single <li> tag. It looked fine with some CSS styles, but not generating the proper markup bothered me a bit. So I tried using a helper method which should offer some more flexibility. The helper method would be taking a block, decompose it into individual anchor tags and and wrap them in the proper markup.

I started off with:

1
2
3
def dropdown(&block)
  content = capture(&block)
end

The capture helper method captures the block and stores it in a variable that I can process. More importantly, it also works for strings within the block. This is an important distinction between capture and a similar helper method, with_output_buffer.

If the block is empty, there is nothing to do. Otherwise, I’d turn them into links.

1
2
3
4
(content = capture(&block)) && anchors = content.split(/\n/).reject { |a| a.empty? }
if anchors.present?
  ...
end

To generate the top-level div is pretty straight-forward:

1
2
content_tag(:div, :class => 'dropdown') do
end

If this were in a view, I could just merrily include other content_tags in the block argument. However, in a helper mehod, the content is stored inside an output buffer. I would have to use concat to add it to the output buffer.

1
2
3
content_tag(:div, :class => "dropdown") do
  concat link_to(content_tag(:i, "", :class => "caret"), "#", :class => "dropdown-toggle", :data => { :toggle => "dropdown" })
end

Next I wanted to add a ul tag and pass its content inside a block. I would also have to use concat as before. In my initial attempt, I tried to do the following:

1
2
3
concat content_tag(:ul, :class => 'dropdown-menu') do
...
end

This resulted in a syntax error. The content_tag is correctly treated as the first argument to concat. However because of Ruby’s order of precedence, the block is intepreted as belonging to concat, not content_tag as intended. To be able to use concat with the do..end syntax, I would have to wrap concat’s arguments inside parentheses:

1
2
concat( content_tag(:ul, :class => 'dropdown-menu') do
end )

This looked quirky and not very ruby-ish. Fortunately, it turned out that the other block syntax using curly braces has higher precedence than do..end (source). This let me eliminate the redundant parentheses:

1
concat content_tag(:ul, :class => "dropdown-menu") { ... }

Finally the links were added inside li tags:

1
anchors.collect { |a| concat content_tag(:li, a.html_safe) }

Here’s the full method which turned out to be quite short:

1
2
3
4
5
6
7
8
9
def dropdown(&block)
  (content = capture(&block)) && anchors = content.split(/\n/).reject { |a| a.blank? }
  if anchors.present?
    content_tag(:div, :class => "dropdown") do
      concat link_to(content_tag(:i, "", :class => "icon-caret"), "#", :class => "dropdown-toggle #{toggleClass}", :data => { :toggle => "dropdown" })
      concat content_tag(:ul, :class => "dropdown-menu") { anchors.collect { |a| concat content_tag(:li, a.html_safe) } }
    end
  end
end

Bruins Took Down Sabres in Costly Victory

It was game 1 of the World Series. Yawn. Since our pickup game was canceled in part due to the aforementioned sport—eh recreational—event, it was going to be either a work night or an early night. Fortunately, the Bruins came to the rescue by taking on the Sabres. It’s a good thing it was on NBCSports/Versus instead of NESN, where viewers would have been bombarded with a constant barrage of WS updates, even though the network TV announcers are downright annoying. The Bruins welcome the undefeated Sharks tomorrow at home, but tonight would be a Bear-Buffalo grudge match.

The game was billed as a Wednesday Rivalry Game. The Bruins’ record was 5-2 and the Sabres 1-8-1, so these were clearly two teams headed in different directions. In fact, it hasn’t been much of a rivalry since they last faced each other in the 2nd round of the 2009-10 playoffs. The Sabres have not made the playoffs in the last two years. They have the second worst record in the league. The only other team with a similarly dismal record, the Broadstreet Bumblers, got their coach fired just 3 games into the season. Nevertheless, the Sabres were one of the few teams with a winning record against the Bruins last season and, like the Canadiens, always seem to find ways to beat the Bruins.

But it wouldn’t happen tonight. Just from this game, it was clear why they’ve been struggling. They were terrible in their zone. They got beat to the puck. They got beat in one-on-one battles. They weren’t strong enough on the puck carrier to get it back. On offense, they weren’t playing as a team. Skilled guys tried to make plays by themselves. Cody Hodgson tried to take on 4 Bruins defenders on the power play—that doesn’t work even in peewee hockey. Guys were taking themselves off-sides. They were a terribly disorganized team who seemed unfocused and unconfident. And then there was John Scott, a 6’8, 260-lb monster who looks vaguely like Liev Schreiber. By NHL standards, he can barely skate (even though he’s better than every normal person and probably most minor leaguers). He’s there to intimidate and even terrorize. Just ask Phil Kessel. Admittedly, in hockey, particularly “old-time hockey”, being intimidating can sometimes have as much of an impact as being skillful.

The lone bright spot for the Sabres had to be Ryan Miller. The Bruins had odd-man rushes all night. Despite the lopsided score, it would have been much worse if it weren’t for him. He made several outstanding saves that would have gotten by lesser goalies. The most sparkling save was on a tic-tac-toe play that started on one side of the net and ended on the other with with David Krejci catching the puck on his backhand and quickly flipping it with his forehand, only to be robbed by Miller’s glove. Of the goals he gave up, three were perfect backdoor plays that he had no chance on. One he would have stopped if his stick hadn’t been pushed out of position by a falling teammate. Only the last one was somewhat saveable, but by then the game was out of reach.

The Bruins’ 5-2 victory was marred by an ugly incident. Long after he had released the puck, Loui Eriksson was blindsided by Scott with a hit to the head that spun him around and knocked him to the ground. He struggled to get up but stumbled, clearly disoriented. This is a scary hit that was eerily reminiscent of the Matt Cooke hit on Marc Savard that ended Savard’s promising career. Eriksson was woozy and had to be helped off the ice. One can only hope that his long-term health isn’t compromised. He’s undoubtedly lost for tomorrow night’s game, if not longer. This type of hits, where the “head is the principal area of contact”, is something the league has been trying to stamp out. Already several multi-game suspensions have been handed out this season for similar plays. It’s quite expected that the league will come down hard on Scott.

The Bruins responded with Adam McQuaid tackling Scott and wrestling him down. Perhaps that was enough. I’m not one to demand that the Bruins respond to a dirty play with dirty plays of their own. It’s important to many hockey fans that the team stand up for their teammates. After the Savard incident, the Bruins were criticized by some for their lack of response. Maybe Shawn Thornton will take on Scott the next time they meet (provided Scott is playing), but it wouldn’t be good for Thornton given that Scott is in a totally different weight class. Maybe someone will engage Steve Ott, another dirty Sabre (his sucker punch on Krejci went unnoticed by the refs). I really think a more measured response is to make them pay on the scoreboard. The Bruins did just that, by scoring a goal on the PP resulting from Scott’s major penalty. If Scott’s action was a intended as “message-sending”, it already failed, because experience has shown that when faced with adversity, this team becomes stronger.

Simple Table Sorting With Angular

View in Plunkr

I have a page with tabular data which I want to be able to sort without having to go back to the server. Also a nice-to-have would be the ability to filter the displayed items. Since I’m trying to learn Angular, this seems like an excellent opportunity to try it out.

In the controller, the variable items holds the data to be sorted/filtered, while filteredItems is initialized to an empty array. I also specify the initial sort field and direction.

1
2
3
4
5
6
function tableSortCtl($scope, $filter) {
  $scope.items = $.parseJSON($('#json').html());
  $scope.filteredItems = [];
  $scope.sortField = "firstName";
  $scope.descending = false;
}

The data are inserted as a JSON string into the page inside a script tag. I could have made a separate request to retrieve the data. But since the data are already available and used elsewhere on the page, I decided to save the extra request. The JSON string is parsed into an array of objects with the help of jQuery.

1
2
3
<script id="json" type="application/json">
[ { "id" : 1, "firstName" : "Alice", "lastName" : "Krige", "birthdate" : "1954-06-28", "address" : "123 Main Street", "phone" : "111-222-3333" }, { "id" : 2, "firstName" : "Bob", "lastName" : "Probert", "birthdate" : "1965-06-05", "address" : "23 Elm Street", "phone" : "359-324-1494" }, { "id" : 3, "firstName" : "Charlie", "lastName" : "Darwin", "birthdate" : "1809-02-12", "address" : "65 Finch Alley", "phone" : "782-624-6038" } ]
</script>

Here’s an excerpt of the markup:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 <table class="table table-striped" id="tag-list" ng-show="items.length > 0">
  <thead>
    <tr>
      <th id="firstName">
        <a ng-click="sort('firstName')">First name <i class="icon-sort"></i></a>
      </th>
    </tr>
  </thead>
  <tbody>
    <tr ng-repeat="item in filteredItems | orderBy:sortField:descending">
      <td></td>
    </tr>
  </tbody>
</table>

The ng-repeat directive specifies that a new table row should be created for each item. With the parameterized orderBy filter, sorting is accomplished simply by assigning a value to the sort field and toggling the sort direction flag. This is impressively straight-forward and elegant. The rest of the function is DOM-manipulation to display the proper sort arrow.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$scope.sort = function (newSortField) {
  if ($scope.sortField == newSortField)
    $scope.descending = !$scope.descending;

  $scope.sortField = newSortField;

  $('th i').each(function () {
    $(this).removeClass().addClass('icon-sort');  // reset sort icon for columns with existing icons
  });
  if ($scope.descending)
    $('#' + newSortField + ' i').removeClass().addClass('icon-caret-down');
  else
    $('#' + newSortField + ' i').removeClass().addClass('icon-caret-up');
};

Here’s the markup for the text filter. A change to the value of the textbox triggers the search.

1
<input type="text" ng-model="query" ng-change="search()" class="input-large" placeholder="Filter" />

The search function simply iterates through all the fields of each item and attempts to find a simple (case-insensitive) match. This works for numbers and strings but unfortunately not, for example, on the month name for a date field, for which we’d have to do some additional processing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$scope.search = function () {
  $scope.filteredItems = $filter('filter')($scope.items, function (item) {
    for (var field in item) {
      if ($scope.match(item[field], $scope.query))
        return true;
    }
    return false;
  });
};
$scope.match = function(fieldValue, searchTerm) {
  if (!fieldValue) return false;
  if (!searchTerm) return true;
  return fieldValue.toString().toLowerCase().indexOf(searchTerm.toLowerCase()) >= 0;
};