Watsonbox

Developer's notes.

Fork me on GitHub

GooglePlacesAutocomplete CocoaPod

GooglePlacesAutocomplete is a simple Google Places API autocompleting address entry view for iOS devices, written in Swift.

There are already a couple of solutions out there for this. GooglePlacesAutocomplete is different because it is 100% Swift, and aims to provide the simplest possible method of entering validated, autocompleted addresses.

It’s available as a CocoaPod – installation instructions here. Feedback and contributions welcome!

Screenshots

Ruby Speech Recognition With Pocketsphinx

pocketsphinx-ruby is a high-level Ruby wrapper for the pocketsphinx C API. It uses the Ruby Foreign Function Interface (FFI) to directly load and call functions in libpocketsphinx, as well as libsphinxav for recoding live audio using a number of different audio backends.

The goal of the project is to make it as easy as possible for the Ruby community to experiment with speech recognition, in particular for use in grammar-based command and control applications. Setting up a real time recognizer is as simple as:

1
2
3
4
5
6
7
8
configuration = Pocketsphinx::Configuration::Grammar.new do
  sentence "Go forward ten meters"
  sentence "Go backward ten meters"
end

Pocketsphinx::LiveSpeechRecognizer.new(configuration).recognize do |speech|
  puts speech
end

This library supports Ruby MRI 1.9.3+, JRuby, and Rubinius. It depends on the current development versions of Pocketsphinx and Sphinxbase – there are Homebrew recipes available for a quick start on OSX.

Mean.io on Dokku

Here’s how I set up the Mean.io stack for deployment with Dokku.

  • Set up MongoDB on the Dokku host and create a database. I used Jeff Utter’s single container plugin for this.
1
2
3
$ dokku mongodb:start
$ dokku mongodb:create <app> <database>
$ dokku mongodb:link <app> <database>
  • Modify config/env/production.js to use ENV var for MongoDB URL:
config/env/production.js
1
2
3
4
5
6
'use strict';

module.exports = {
  db: process.env.MONGO_URL,

// [REST OF FILE ELIDED]
  • Configure the application to run in the production environment:
1
$ dokku config:set <app> NODE_ENV=production
  • Set the unsafe-perm npm configuration option to allow npm to operate under the root account. More information here. Create .npmrc as follows:
.npmrc
1
unsafe-perm = true
.env
1
BUILDPACK_URL=https://github.com/mbuchetics/heroku-buildpack-nodejs-grunt.git
  • Add Heroku build task without environment to Gruntfile.js since Dokku doesn’t read config during build steps:
Gruntfile.js
1
2
3
4
5
6
7
8
9
  // [REST OF FILE ELIDED]

  // For Heroku users only.
  // Docs: https://github.com/linnovate/mean/wiki/Deploying-on-Heroku
  grunt.registerTask('heroku:production', ['cssmin', 'uglify']);

  // Dokku does not set ENV vars during build steps:
  grunt.registerTask('heroku:', ['cssmin', 'uglify']);
};

Getting Started With Dokku

UPDATE: Some recent changes to ohardy/dokku-mariadb have resolved the problems with losing DB links on reboot.

I decided to jump in and set up my own mini-Heroku with Docker and Dokku as seems to be the fashion at the moment. Here are my thoughts and a few of the issues I hit along the way. Using Dokku v0.2.3.

I installed Dokku on Digital Ocean. It doesn’t have to be Digital Ocean but they certainly make it easy. Then added some swap space, too. Save some typing by setting up an alias:

1
$ alias dokku='ssh -t dokku@yourvps.tld

Configuration Variables

Dokku allows applications to be configured using enviornment variables as described in the twelve-factor app:

1
$ dokku config:set acme company_name="Acme Corporation"

That won’t work though, because Dokku’s command line app doesn’t support spaces in config variables. You can get around this by setting the configuration directly in /home/dokku/acme/ENV:

/home/dokku/acme/ENV
1
export company_name="Acme Corporation"

They won’t be correctly reported by dokku config acme, but they’ll work.

Configuration Backup

You can backup Dokku’s configuration (including application configuration) with:

1
$ dokku backup:export [FILE]`

This will place the backup in /home/dokku/[FILE].

Multiple Domains and Basic Auth

I installed a couple of plugins which worked well out of the box:

https://github.com/matto1990/dokku-secure-apps
Secures an individual app with HTTP Basic authentication. Dead simple to use and useful when deploying apps for testing or private use only.

https://github.com/neam/dokku-custom-domains
Configure additional custom domains for your dokku app.

Persistent Storage

Dokku has plenty of plugins for different data stores. I decided to work with ohardy/dokku-mariadb since on a VPS with farily limited memory, I preferred to only run one container and a single instance of MariaDB.

The main problem I faced here was keeping database connections working after a reboot. On reboot, docker containers are restarted and will receive a new private IP. This means that the DATABASE_URL used by the apps, e.g. mysql://[PRIVATE_IP]:3306/[database] will change and break database connections. Despite some discussion and attempted fixes, the multiple container dokku MariaDB plugin (kloadut/dokku-md-plugin) uses a dynamic port for each container, which also changes on reboot, so suffers from the same problem.

I fixed this issue in watsonbox/dokku-mariadb:reboot-persist-connections by patching ohardy/dokku-mariadb to use a fixed IP address. It also uses a fixed port (3306), since only one container/instance is created.

Worth noting that ohardy/dokku-mariadb stores persistent DB data on the host in /home/dokku/.o_mariadb (kloadut/dokku-md-plugin in /home/dokku/.mariadb).

Aggregation for Ember Data Async Relationships

Ember.js has computed properties for aggregating data which are automatically re-calculated when the source data set changes. But what if we want to display aggregate data from an async relationship in a list view? We might have the following:

app/assets/javascripts/models/invoice.js.coffee
1
2
3
4
5
App.Invoice = DS.Model.extend
  lineItems: DS.hasMany('lineItem', { async: true })

  lineItemAmounts: Ember.computed.mapBy('lineItems', 'amount')
  amount: Ember.computed.sum('lineItemAmounts')

Each App.Invoice has a bunch of App.LineItems, and calculates its own amount by summing up their individual amounts. However, each time an invoice is rendered, all of the line items will be fetched from the server in order to make this calculation.

What we can do instead is send the aggregate figure from the server along with the invoice. It might be calculated on the fly (with a group by query, for example), or maintained along with the invoice. Here’s an approach that does exactly this, but will switch to using the real ember data objects once they have been loaded:

app/assets/javascripts/models/invoice.js.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
App.Invoice = DS.Model.extend
  subtotal: DS.attr('number')
  lineItems: DS.hasMany('lineItem', { async: true })

  lineItemAmounts: Ember.computed.mapBy('lineItems', 'amount')
  lineItemAmountsSum: Ember.computed.sum('lineItemAmounts')

  # Uses subtotal until async lineItems become available
  amount: (->
    if @cacheFor('lineItems')
      @get('lineItemAmountsSum')
    else
      @get('subtotal')
  ).property('lineItems.@each.amount')

In this case subtotal is the aggregated amount from the server.

Lazy and Partial Data Loading With Ember.js and Rails

While learning Ember.js, I couldn’t find all the info laid out clearly in one place on these subjects, so thought I’d write up my findings. I’m using Ember.js 1.5.1, Ember Data 1.0.0-beta.7+canary.f482da04 (!!), and Rails 4.1.0.

Lazy Loading Relationships

Most of the Rails/Ember guidelines out there suggest that related data should normally be sideloaded, which is great and helps reduce the number of HTTP requests required, or data duplication (in the case of embedded data). To sideload data, set up the relationship and Rails serializer as follows:

app/assets/javascripts/models/project.js.coffee
1
2
3
4
App.Project = DS.Model.extend
  name: DS.attr('string')
  description: DS.attr('string')
  invoices: DS.hasMany('invoice')
app/serializers/project_serializer.rb
1
2
3
4
class ProjectSerializer < ActiveModel::Serializer
  attributes :id, :name
  has_many :invoices, embed: :ids, include: true # Sideload relationship
end
Example JSON Response
1
2
3
4
5
6
7
8
9
{
  "projects": [
    { "id": 1, "name": "Project 1", "description": "...", "invoice_ids": [1, 2] }
  ],
  "invoices": [
    { "id": 2, "reference": "INV-002", "date": "2014-06-11", "project_id": 1 },
    { "id": 1, "reference": "INV-001", "date": "2014-04-04", "project_id": 1 }
  ]
}

Async Loading

Often we’d prefer to lazily load the associated data only when it’s referenced. Ember Data calls this an async relationship. Simply modify the above by omitting the include serializer option and adding async to the relationship:

app/assets/javascripts/models/project.js.coffee (partial)
1
invoices: DS.hasMany('invoice', { async: true })
app/serializers/lazy_project_serializer.rb (partial)
1
has_many :invoices, embed: :ids
Example JSON Response
1
2
3
4
5
{
  "projects": [
    { "id": 1, "name": "Project 1", "description": "...", "invoice_ids": [1, 2] }
  ]
}

When the invoices relationship is accessed, Ember Data will automatically make a request to /invoices?ids[]=1&ids[]=2 (or presumably wherever that route is defined), so the Rails InvoicesController must be set up to restrict returned data based on the ids parameter.

Update 30/12/14: Since Ember Data v1.0.0-beta.9 has many coalescing has become opt-in. This means setting coalesceFindRequests: true on the REST adapter for the above behavior. Thanks to CamonZ for pointing this out.

Note that if an association is set to async, but sideloaded data exists in the server response, Ember Data will simply use that data and not attempt to make another request. This is useful, allowing data to be sideloaded for a detail view because we know we’re going to need it, but not for a list view where it might not be used. When moving from list to detail, the invoices will be loaded, but when arriving directly on the detail page, only the project will be loaded. Specify a different serializer in Rails for each action:

app/controllers/projects_controller.rb
1
2
3
4
5
6
7
8
9
10
11
class ProjectsController < ApplicationController
  respond_to :json

  def index
    respond_with Project.all, each_serializer: LazyProjectSerializer
  end

  def show
    respond_with Project.find(params[:id])
  end
end

Async Loading From Links

Another possibility, which gives more control over association endpoints, and avoids having to pass a bunch of IDs around, is to provide links for the relationships in the JSON response. For example:

app/serializers/project_serializer.rb
1
2
3
4
5
6
7
class ProjectSerializer < ActiveModel::Serializer
  attributes :id, :name, :links

  def links
    { invoices: project_invoices_path(id) }
  end
end

Loading Partial Models

Imagine that we have a dropdown list of projects in the page navbar. In addition to lazy loading related data, we might also want to omit attributes we know we’re not going to need yet. In this case a large project description might be a candidate for ommission from the list view, especially if the list of projects is large. The most comprehensive description of this problem I was able to find is here, and includes links to related discussions.

Firstly we need to identify the list data as partial by adding a partial attribute and setting it in a PartialProjectSerializer. We also ommit the invoices and description:

app/assets/javascripts/models/project.js.coffee
1
2
3
4
5
App.Project = DS.Model.extend
  name: DS.attr('string')
  description: DS.attr('string')
  partial: DS.attr('boolean')
  invoices: DS.hasMany('invoice', { async: true })
app/serializers/partial_project_serializer.rb
1
2
3
4
5
6
7
class PartialProjectSerializer < ApplicationSerializer
  attributes :id, :name, :partial

  def partial
    true
  end
end

We now have two problems to solve:

  1. Reload a complete model for the detail view if we have only a partial model.
  2. Don’t allow partial list data to overwrite a complete model if it comes in afterwards. You can simulate this in your dev environment using a threaded web server such as Puma and setting a delay on the resource index.

For the first we can use setupController on the project route. Modifying the model hook won’t work when a model is passed for example to link-to because it doesn’t get called.

app/assets/javascripts/routes/project_route.js.coffee
1
2
3
4
5
6
7
8
Facture.ProjectRoute = Ember.Route.extend
  model: (params) ->
    @store.find 'project', params.project_id

  setupController: (controller, model) ->
    # If the model is partial, we'll refresh it (from the full project resource)
    model.reload() if model.get 'partial'
    controller.set 'model', model

It would be nice if we could solve the second problem by asking Ember Data to retrieve and merge data from the server (using something like Store#find_and_update), but that doesn’t appear to be possible. It is however possible to update individual records using Store#update.

I’ve been getting these projects in the ApplicationRoute, which now looks like this:

app/assets/javascripts/routes/application.js.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
Facture.ApplicationRoute = Ember.Route.extend
  setupController: (controller) ->
    $.getJSON '/projects.json', (data) =>
      # Update records in the store
      projects = data['projects'].map (project) =>
        # Don't merge partial=true
        if existing_project = @store.getById('project', project.id)
          project.partial = false unless existing_project.get('partial')

        @store.update 'project', project

      # Set all records on the controller
      controller.set 'projects', projects

Confreaks XBMC Addon 0.3.0

I finally got around to updating xbmc-confreaks to work properly with XBMC Frodo and Gotham. This time I’ve also submitted it to the official XBMC Addons repository so as of yesterday you should be able to install it through ‘Get Addons’ in XBMC itself. Be aware that you might need to refresh the repository (right click) first.

FB Canvas Apps #2: Fixed Positioning

Recently I was asked to add a fixed position ‘tab’ to the left side of a Facebook canvas app, for example like the Get Satisfaction ‘Community’ tab.

Normally this would be a simple case of using position: fixed, but not in this case because Facebook canvas apps live inside iFrames. Facebook resizes this iFrame to fit its content, meaning that the main browser scrollbar is used to scroll around the page rather than the iFrame’s scrollbar. However, fixed position elements are fixed relative to the iFrame, not the main document. Since the iFrame never actually scrolls, the fixed element stays put.

The solution is to use a little javascript to scroll the fixed element relative to the Facebook scroll position at a fixed interval. This gives a slightly different user experience but is the best approach I’ve found so far.

CoffeeScript/jQuery: pseudoFixed Method and Invocation
1
2
3
4
5
6
7
8
9
$.fn.pseudoFixed = ->
  top = this.position().top

  window.setInterval(
    => FB.Canvas.getPageInfo((info) => this.animate({ top: info.scrollTop + top })),
    1000
  )

$('[data-behavior~=pseudo-fixed]').pseudoFixed()
Example HTML Tab Element
1
<div data-behavior="pseudo-fixed" style="position: fixed; left: 0; top: 150px">...</div>

FB Canvas Apps #1: Dialog Positioning

Having worked on several Facebook applications, I’ve built up a few tricks to solve some common problems. This one shows how to get a dialog (jQuery UI, in this case) to appear centered in the browser rather than centered in the canvas iFrame.

The problem is caused by Facebook resizing the canvas iFrame to accommodate the entire page. When a dialog is displayed inside the canvas application, it is displayed vertically centered inside that iFrame, which can cause the current browser scroll position to jump up or down accordingly.

The fix is to call Facebook’s FB.Canvas.getPageInfo JS API method, to position the dialog relative to the user’s current browser scroll position.

Here is an implementation which uses CoffeeScript and extends the jQuery UI dialog widget.

Facebook Initialization
1
2
3
4
5
6
7
8
$ ->
  # Async Facebook initialisation
  window.fbAsyncInit = ->
    FB.init({ appId: 'APP_ID', status: true, cookie: true, xfbml: true })
    FB.Canvas.setAutoGrow()

    # Allows other components to hook into this event with $(document).on('fb:initialized', -> ...)
    $(document).trigger('fb:initialized')
The Dialog Widget
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
CanvasDialog =
  # Some example default dialog options
  options: {
    width: 600,
    modal: true,
    autoOpen: true
  }

  _init: ->
    if @_inCanvas
      if this._fbLoaded()
        this._showDialogInCanvas()
      else
        $(document).on 'fb:initialized', => this._showDialogInCanvas()
    else
      this._showDialog()

  # Displays the dialog positioned correctly inside the FB canvas iFrame
  _showDialogInCanvas: ->
    FB.Canvas.getPageInfo (info) =>
      @options.position = ['center', info.scrollTop + 50]
      this._showDialog()

  # Calls the jQuery UI dialog's original init method
  _showDialog: -> $.ui.dialog.prototype._init.apply(this, arguments)

  # True if dialog is being loading inside the FB iFrame (any iFrame for that matter)
  _inCanvas: top != self

  # True if FB javascript API has been loaded
  _fbLoaded: -> typeof FB != 'undefined'


$.widget "ui.canvasDialog", $.ui.dialog, CanvasDialog
Example Usage
1
$('#canvasDialog').canvasDialog()

Notes

  • By triggering an event when Facebook is initialized, the CanvasDialog is able to wait for the Facebook JS API to become available if it has not yet been loaded.

  • In development it can be useful to work outside the Facebook environment. The CanvasDialog detects this and shows the dialog without attempting to call the Facebook API.

  • In the past I’ve often used the excellent and well maintained ColorBox library for dialogs. I note that it too has been updated with similar functionalilty.

Rails & PHP Session Sharing #2

Back in May I posted on how I set up shared sessions between Rails and a legacy PHP app. Since then I’ve upgraded memcache-client to Mike Perham’s replacement, Dalli. I took this opportunity to tidy up the PHP session store as well. Here is what I have now:

This is cleaner than what I had before and should be much more resistant to changes in Dalli, too.