Larry Price

And The Endless Cup Of Coffee

The Wisdom of Confucius - Now Available in an API

| Comments

I make a lot of silly things while learning new technology. In this case I have tapped into the almighty power of Confucius, a Chinese philosopher from 2500 years ago credited for writing or editing many Chinese classic texts. Growing up as an American child, I associated Confucius with proverbs, and I associated proverbs with that classic after-Chinese-dinner dessert the fortune cookie.

Fortune cookies are a pure delight: a message in a bottle from the restaurant’s proprietors for you to enjoy as you kindly vacate the facility.

Most fortune cookies that I’ve opened recently have three parts: a fortune (generally a proverb), a lesson in simplified Chinese, and a lottery number. So many secrets wrapped up in such a small, golden treasure.

Ever left a Chinese restaurant, gotten in your car, and felt the need to break something only to realize you left your precious fortune cookie on the table? I have built a solution.

The Fortune Cookie API is a simple, RESTful API built to generate fortune cookie data. The root URL shows documentation built using Apiary.io.

How does it work? I need fortunes now!

There are options. You can get fortunes from the /v1/fortunes endpoint, lessons from the /v1/lessons endpoint, and lottery numbers from the /v1/lottos endpoint. By default you get 100 of any model, but all endpoints include a limit (max 1000), skip, and page parameter to facilitate getting all the lessons and fortunes. For lottery numbers, we approximately build Powerball numbers except we currently ignore the rule for red balls, which means there are something ike 42 billion different possibilities. Due to the high number of potential lottery numbers, the lottos endpoint also includes a firstId parameter that lets you start from anywhere.

But there’s no need to get the individual models (unless you’re into that kind of thing)! I also created a /v1/cookie endpoint to retrieve a random fortune, lesson, and lottery number as a single object. Woohoo! You can specify the number of cookies (max 100) with the limit parameter.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
GET http://fortunecookieapi.com/v1/cookie

{
  "fortune": {
    "id": "53ffcf1d4ea4f76d1b8f223e",
    "message": "This fortune intentionally left blank"
  },
  "lotto": {
    "id": "001000200030004000500006",
    "numbers": [10,20,30,40,50,6]
  },
  "lesson": {
    "id": "53ffcf1d4ea4f76d1b8f2241",
    "chinese": "因特网",
    "pronunciation": "yintewang",
    "english": "internet"
  }
}

Now you can fill that hole in your heart where the fortune cookies are missing. If you’re interested in the code, you can check it out on Github.

UPDATE: I’ve registered the domain fortunecookieapi.com for the love of fortune cookies. Enjoy.

Fetching Random Mongoose Objects the Simple Way

| Comments

As I venture through the land of NodeJS, I’ve found the wonder and magic of NPM, a package management tool for Javscript similar to ruby’s gems. Although there are nearly 100,000 packages on the main npmjs site (94,553 at time-of-writing), it seems there are still niches to be filled.

Recently, while working on a top secret side-project, I wanted to grab a random object from a MongoDB collection. I used the highly-extensible mongoose to set up my models and just needed to find a package somewhere with the desired functionality. I found such a package called mongoose-random, but, unfortunately, I was never able to get this plugin to work correctly. The plugin in question also needed to insert new columns on your tables, which I didn’t really want. So I decided to create a new package.

mongoose-simple-random is an incredibly easy way to include a random accessor on your mongoose models. All that’s required is adding the plugin to the schema before compiling the model:

test.js
1
2
3
4
5
6
7
8
var random = require('mongoose-simple-random');

var s = new Schema({
  message: String
});
s.plugin(random);

Test = mongoose.model('Test', s);

Now I can ask the model for a single random element of the Test model with a single call to findOneRandom:

find_one.js
1
2
3
4
5
6
var Test = require('./test');

Test.findOneRandom(function(err, element) {
  if (err) console.log(err);
  else console.log(element);
});

Need to find more than one? Use findRandom to get an array:

find_five.js
1
2
3
4
5
6
var Test = require('./test');

Test.findRandom({}, {}, {count: 5}, function(err, results) {
  if (err) console.log(err);
  else console.log(results);
});

Zowee! Just like the default find methods, you can pass in optional filters, fields, and options:

find_five_with_optionals.js
1
2
3
4
5
6
7
8
9
var Test = require('./test');

var filter = { type: { $in: ['education', 'engineering'] } };
var fields = { name: 1, description: 0 };
var options = { skip: 10, limit: 10, count: 5 };
Test.findRandom(filter, fields, options, function(err, results) {
  if (err) console.log(err);
  else console.log(results);
});

Given 1000s of objects, performance is excellent. I haven’t tested it on larger-scale databases, but I wouldn’t mind seeing some performance tests in the future.

The (Honest) Truth About Dishonesty

| Comments

The Idea

Everyone lies. Everyone cheats. From Marcy down the street cheating on her diet to Bernie Madoff committing massive finanical fraud, we’re all a little dishonest from time to time. With the power of experimentats and research, Dan Ariely seeks to learn what leads humans to act dishonestly in The (Honest) Truth About Dishonesty.

The Gooey Center

Since reading Predictably Irrational earlier this year, I’ve really liked reading Dan Ariely; his writing style is familiar yet authoritative, making for highly readable books filled with high-quality experiments and meaningful insights. The (Honest) Truth About Dishonesty is a similar read to Predictably Irrational, but with a tight focus on what drives the common man to lie and cheat.

The main takeaway is, given the chance, most people will cheat just a little given the opportunity. Ariely’s experiments found that very few people cheated heavily, even in situations where the likelihood of being caught is minimal. Rounding up your billable hours on a timesheet is easy to do without a shred of guilt, and the likelihood of getting caught is approximately nil. On the other hand, fudging an hour or two in a single day is not something anyone I know would take lightly. Unfortunately, if you round up billable hours by 10 minutes a day for a whole week, you’ve fudged nearly an hour of billable work (without the guilt).

Day-to-day living makes us less honest. If you’ve had a long day at work, you’re more likely to cheat on your diet when you get home because your defenses have been torn down. You’re more likely to put things off in favor of sitting around watching television.

Ariely finds that collaboration also leads to more dishonesty: as humans, we are more willing to bend the rules if it will benefit someone else, especially if we have befriended that someone else.

Who Would Like This

Those interested in behavioral economics should get a kick out of this book, especially if they enjoyed Ariely’s other works. Reading this book can also make you think more critically about some of the “little white lies” you tell yourself and others throught the day.

Hotfixing a Bug in Trello's client.js

| Comments

I’m a huge fan of Trello, and I recently created an app to analyze Trello data called Ollert. Ollert makes heavy use of the Trello API. I’ve written previously about using Trello’s client.js to connect to the Trello API.

During some ad-hoc testing of opening/closing the Trello authorization popup, I found that clicking “Deny” on the popup, reopening it, and then clicking “Connect” resulted in me not being able to connect. I spent some time looking into workarounds and I even contacted Trello support. After studying client.coffee, I eventually found the problem: the Trello client keeps around an anonymous object called ready that stores some session data. When a user clicks ‘Deny’, this ready object remembers. I was able to fix the Deny/Allow issue by pulling down a local copy of client.js and making an interface change to the module:

client.js
1
2
3
clearReady: function() {
  ready = {}
}

From another issue I was having, I already call Trello.deauthorize() before my application attempts to contact Trello, so now I also make a call to Trello.clearReady(). My authorize() function looks like this:

trello-controller.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var authorize = function (expires, callback) {
  Trello.deauthorize();
  Trello.clearReady();

  Trello.authorize({
    name: "Ollert",
    type: "popup",
    interactive: true,
    expiration: expires,
    persist: false,
    success: callback,
    scope: {
      read: true,
      write: true
    }
  });
}

Since I’ve pulled client.js locally, I’m no longer sending my developer token when I load the file. I manually set my key when the page loads, but this could just as easily be done in my authorize method above.

layout.haml
1
2
3
4
:javascript
  $(document).ready(function() {
    Trello.setKey("#{ENV['PUBLIC_KEY']}")
  });

You can find a copy of my modified client.js in this Gist.

Testing Through a Trello Connection With Capybara and Webkit

| Comments

During the hardening of Ollert, a Trello data analysis tool I wrote, I started writing acceptance tests. I quickly ran into an issue where the meat of my application requires opening pop-up window, signing into Trello, and allowing my application access.

I created a test user on Trello with a few varied boards to allow for proper testing. In doing this, I store the user’s login information in my .env file. For the most part, I can use the steps provided in this common web_steps.rb.

Connecting.feature
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Feature: Landing

Background:
  Given I am on the landing page

@javascript
Scenario: Deny connecting to Trello
  Given I follow "Connect to Get Started"
  And I press "Deny" on the Trello popup
  Then I should be on the landing page

@javascript
Scenario: Allow connecting to Trello
  Given I follow "Connect to Get Started"
  When I authorize with Trello as the test user
  Then I should not see "Connecting..."
  And I should not see "Redirecting..."
  And I should be on the boards page

When the Trello popup appears, we have to specify the window we’re going to use. Since I’m using capybara-webkit, I’m going to go ahead and do all of my Trello popup activities in one step, which saves me from writing a lot of unnecessary steps.

trello_popup_steps.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
When /^I press "(.*?)" on the Trello popup$/ do |button|
  trello_popup = windows.last
  page.within_window trello_popup do
    click_button button
  end
end

When /^I authorize with Trello as the test user$/ do
  trello_popup = windows.last
  page.within_window trello_popup do
    click_link "Log in"

    fill_in "user", with: ENV['TEST_USER_TRELLO_USERNAME']
    fill_in "password", with: ENV['TEST_USER_TRELLO_PASSWORD']

    click_button "Log In"
    click_button "Allow"
  end
end

Straightforward so far. We grab the window handle and we click links, fill in fields, and press buttons within that window.

Note that I’m using capybara-webkit, a headless web driver, to run my Javascript. Although the first test (“Deny”) will pass, the “Allow” test fails ambiguously. This is because capybara-webkit is not recognized as a supported browser by the Trello popup.

Anecdotally, I contacted Trello support about this and received the following response:

Currently it is not possible to test this with a headless browser as you are looking to do without getting the unsupported browser message.

So I guess we should just give up, right? …Or we could manipulate the headers we send to load the Trello popup such that Trello thinks we are Google Chromium.

trello_popup_steps.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
When /^I authorize with Trello as the test user$/ do
  trello_popup = windows.last
  page.within_window trello_popup do
    page.driver.header(
      "User-Agent",
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/34.0.1847.116 Chrome/34.0.1847.116 Safari/537.36"
    )

    click_link "Log in"

    fill_in "user", with: ENV['TEST_USER_TRELLO_USERNAME']
    fill_in "password", with: ENV['TEST_USER_TRELLO_PASSWORD']
    
    click_button "Log In"
    click_button "Allow"
  end
end

Fantastic. Now my tests pass. I can’t sleep at night, but my tests pass.

Unfortunately, that won’t be the case if I add more tests to this .feature file. Hidden somewhere deep in the browser’s cache or cookies or somethings, Trello is remembering that we logged in sometimes. Sometimes it even remembers that someone else has logged in. The UI of the Trello popup changes based on whether it thinks you’ve already logged in. In order to keep things consistent, I like to add an if-statement to take care of this case.

trello_popup_steps.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
When /^I authorize with Trello as the test user$/ do
  trello_popup = windows.last
  page.within_window trello_popup do
    page.driver.header(
      "User-Agent",
      "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/34.0.1847.116 Chrome/34.0.1847.116 Safari/537.36"
    )

    if page.has_content? "Switch Accounts"
      click_link "Switch Accounts"
    else
      click_link "Log in"
    end

    fill_in "user", with: ENV['TEST_USER_TRELLO_USERNAME']
    fill_in "password", with: ENV['TEST_USER_TRELLO_PASSWORD']
    
    click_button "Log In"
    click_button "Allow"
  end
end

Edge cases addressed. Now I can make connections to Trello and test my application. Be warned, I’ve already had these tests break once when Trello updated the UI behind the Trello popup. If Trello ever stops supporting Chromium 34.0, these tests are also likely to stop working. These tests are most useful during development, when we have the potential to break the Trello connection ourselves, and so I think they are well worth the pain of potential future maintenance.