Alexa on Rails – how to develop and test Alexa skills using Rails


Alexa is awesome and I think that conversational software is the future. This post documents what I set myself as a technical learning challenge:

  • Host the skill locally, to allow a fast development feedback cycle prior to pushing code.
  • To find a way to automated tests (unit, functional and end-to-end), as most demos refer to manual testing.
  • To use something other than JS (like most of the demos do)
  • To write an Alexa skill that’s backed by a data store
  • To be able to handle conversations.

The way Alexa services interact with apps is the following:

User->Echo: “Alexa, …”
Note right of Echo: Wakes on ‘Alexa’
Echo->Amazon: Streams data spoken
Amazon->Rails: OfficeIntent
Rails->SkillsController: POST
SkillsController->Amazon: reply (text)
Amazon->Echo: reply (voice)
Echo->User: Speaks

The skill

The skill is a data retrieval one, giving information about the company’s offices and the workers there.

Alexa, Rails, git, ngrok and an Amazon account

I bought a dot and set up an Amazon account to register the skill on.

Install Rails and git for your OS. You’ll also need a data-store, easily using sqlite, or mysql gems.

ngrok is a nifty tool that will tunnel Alexa calls in to our local server.

Get the code

Fork or clone the repo for a head-start, or read along taking only pieces you need from this post.

Set up the app

  • Setting some environment variables

The database connection use the following environment variables:

  • Setting up the database
rake db:create db:migrate db:seed spec

This will create and setup the database tables, seed the development tables and run the unit and integration tests.

  • Running tests

Will run all tests excluding the audio tests, which I’ll describe below. Make sure all tests pass.

Connecting to the real thing

When a user invokes your skill, Amazon will route requests to an endpoint listed on the Alexa site. In order for this to function, you must first configure the skill there. It’s straightforward, but must be manually uploaded to the skill’s configuration page on Amazon’s site.

Intent schema

This is where you define the intents the user can express to your skill. I think of ‘intents’ as the skill’s ‘methods’, if you think of the skill as an object.


Permutations on the intent’s syntax. For example:

Bookit for vacant rooms between {StartDate} and {EndDate}
OfficeWorkers who the {Staff} from {Office} are

Slot types

Here are the slot types for our skill, defining synonyms for our slots, being the parameters for intents. If you think this is complex, please remember that I am only the messenger here…


Now that you have configured the skill’s interfaces, we now need to route communications from Amazon to our local server running Rails as we develop and debug. This is easily done using ngrok, explained below.


ngrok is a service, with a free tier, that will redirect traffic from outside your home/office’s firewall into your network. Once configured, it will route traffic from Amazon to our http://localhost:3000, essential for our aspired fast development cycle.

Run it using:

ngrok http 3000

Your configuration may vary, depending on whether you are paying customer or not, so change ‘endpoint’ accordingly.

You’ll see something like this once you run it:


Add your endpoint to Amazon’s skill page under configuration:


Generating a certificate

Once you’ve settled on the endpoint URL, you’ll need to create or reuse a certificate for Amazon to use when communicating with your server process.

genrsa 2048 > private-key.pem
openssl req -new -key private-key.pem -out csr.pem
openssl req -new -x509 -days 365 -key private-key.pem -config cert.cnf -out certificate.pem

Copy the the contents of ‘certificate.pem’ to the skill’s page on Amazon:


Toggle the test switch to ‘on’, otherwise Amazon will think you’re trying to publish the skill on their Skills store:


Last but not least, enable the skill on your iPhone or Android by launching the Alexa app and verifying that the skill exists in ‘Your skills’ tab.

Amazon recap

We uploaded the skill info, including:

  • The Interaction model, uploading the ‘intent schema’, ‘Custom slot types’, and ‘Sample utterances’.
  • Configured the end-point
  • Uploaded the SSL cert
  • Enabled the test flag
  • Verified that the skill is enabled by using your Alexa app on your mobile device

The moment we’ve been waiting for

Run your rails app:

rails s

Run ngrok in another terminal window:

ngrok http 3000

Say something to Alexa:

Alexa, tell Buildit to list the offices

If all goes well, you should:

  • See the request being logged in the ngrok terminal (telling you that Amazon connected and passed the request to it)
  • See that the rails controller got the request by looking at the logs
  • Hear the response from your Alexa device

If there was a problem at this stage, please contact me so I can improve the instructions.

Code walkthrough

Route to a single skills controller:

 Rails.application.routes.draw do
   # Amazon comes in with a post request
   post '/' => 'skills#root', :as => :root

Set up that controller:

class SkillsController < ApplicationController
  skip_before_action :verify_authenticity_token

  def root
    case params['request']['type']
      when 'LaunchRequest'
        response =
      when 'IntentRequest'
        response =['request']['intent'])
     render json: response

Handle the requests:

def respond intent_request
  intent_name = intent_request['name']

  Rails.logger.debug { "IntentRequest: #{intent_request.to_json}" }

  case intent_name
    when 'ListOffice'
      speech = prepare_list_office_request
    when 'OfficeWorkers'
      speech = prepare_office_workers_request(intent_request)
    when 'OfficeQuery'
      speech = prepare_office_query_request(intent_request)
    when 'Bookit'
      speech = prepare_bookit_request(intent_request)
    when 'AMAZON.StopIntent'
      speech = 'Peace, out.'
      speech = 'I am going to ignore that.'

  output =

Test walkthrough

Unit tests

Really fast, not touching any Alexa or controller code, just making sure that the methods create the correct responses:


require 'rails_helper'

RSpec.describe 'Office' do
  before :all do
    @intent_request =
  describe 'Intents' do
    it 'handles no offices' do
      expect(@intent_request.handle_list_office_request([])).to match /We don't have any offices/

    it 'handles a single office' do
      expect(@intent_request.handle_list_office_request(['NY'])).to match /NY is the only office./

    it 'handles multiple offices' do
      expect(@intent_request.handle_list_office_request(['NY', 'London'])).to match /Our offices are in NY, and last but not least is the office in London./

Integration tests

Mocking out Alexa calls, ensure that the JSON coming in and out is correct:

describe 'Intents' do
  describe 'Office IntentRequest' do
    it 'reports no offices' do
      request = JSON.parse('spec/fixtures/list_offices.json'))
      post :root, params: request, format: :json
      expect(response.body).to match /We don't have any offices/

    it 'reports a single office' do
      request = JSON.parse('spec/fixtures/list_offices.json'))
      Office.create name:'London'
      post :root, params: request, format: :json
      expect(response.body).to match /London is the only office/

    it 'reports multiple offices' do
      request = JSON.parse('spec/fixtures/list_offices.json'))
      Office.create [{name: 'London'}, {name: 'Tel Aviv'}]
      post :root, params: request, format: :json
      expect(response.body).to match /Our offices are in London, and last but not least is the office in Tel Aviv./

Audio tests

I was keen on finding a way to simulate what would otherwise be an end-to-end user-acceptance test, like a Selenium session for a web-based app.

The audio test I came up with has the following flow:

describe 'audio tests', :audio do
  it 'responds to ListOffice intent' do
    london = 'Paris'
    aviv = 'Tel Aviv'

    Office.create [{ name: london }, { name: aviv }]

    pid = play_audio 'spec/fixtures/list-office.m4a'

    client, data = start_server

    post :root, params: JSON.parse(data), format: :json
    result = (response.body =~ /(?=#{london})(?=.*#{aviv})/) > 0

    reply client, 'The list offices intent test ' + (result ? 'passed' : 'failed')
    expect(result).to be true


Line 6: Creates some offices.
Line 8: Plays an audio file that asks Alexa to list the offices
Line 10: Starts an HTTP server listening on port 80\. Make sure that rails is not running, but keep ngrok up to direct traffic to the test.
Line 12: Will direct the intent request from Alexa to the controller
Line 13: Makes sure that both office names are present in the response
Line 15: Replaces the response that would have been sent back to Alexa with a curt message about the test passing or not.
Line 16: Relays the test status back to RSpec for auditing.

This is as close as I got to an end-to-end test (audio and controller). Please let me know if you have other ways of achieving the same!


What was technically done here?

  • We registered an Alexa skill
  • We have a mechanism to direct traffic to our server
  • We have a mechanism to unit-test, integration-test and acceptance-test our skill
  • We have a mechanism that allows for a fast development cycle, running the skill locally till we’re ready to deploy it publicly.

My main learning, however, was not a technical one (despite my thinking that the audio test is nifty!). Being an advocate for TDD and BDD, I realise that now there’s a new way of thinking about intents, whether the app is a voice-enabled one or not.

We may call it CDD, being Conversation Driven Development.

The classic “As a..”, “I want to…”, “So that…” manner of describing intent seems so static compared to imagining a conversation with your product, whether it’s voice-enabled or not. In our case, try to imagine what a conversation with an office application would be like?

“Alexa, walk me through onboarding”. Through booking time, booking conference rooms, asking where office-mates are, what everyone is working on etc.

If the app happens to be a voice-enabled one, just make audio recordings of the prompts, and employ TDD using them. If it’s a classic app, use those conversations to create BDD scripts to help you implement the intents.


From Zero to Deployment: Vagrant, Ansible, Capistrano 3 to deploy your Rails Apps to DigitalOcean automatically (part 2)


Use Vagrant to create a VM using DigitalOcean and provision it using Ansible.


Parts zero and one of this blog series demonstrates some Ansible playbooks to create a VM ready for Rails deployment using Vagrant. Here we show the Vagrant file that will provision a DigitalOcean droplet for public access.

First thing to do is to install the DigitalOcean plugin:

vagrant plugin install vagrant-digitalocean


The Vagrantfile for DigitalOcean

# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| = 'digital_ocean'
config.vm.box_url = ""
config.vm.hostname = "staging"
config.vm.define "staging"
config.vm.provision 'ansible' do |ansible|
ansible.playbook = "devops/user.yml"
ansible.inventory_path = "devops/webhosts"
ansible.verbose = "vvvv"
ansible.sudo = true
config.vm.provider :digital_ocean do |provider, override|
override.ssh.private_key_path = '~/.ssh/id_rsa'
override.vm.box_url = ""
provider.client_id = ‘<YOUR ID>'
provider.api_key = ‘<YOUR KEY>'
provider.image = "Ubuntu 12.10 x64"
provider.region = "New York 2"


You can get your ID and Key from the DigitalOcean website once logged on there.


Not much to it, eh?

The Ansible files stay mostly the same, apart from the use of ‘root’ wherever ‘vagrant’ was used. And don’t forget to change your inventory file to the IP address given by DigitalOcean. I’ll be thinking about how to automate these parameters too, to have complete hands-free installations.

If you are curious to learn more about configuring VMs in DigitalOcean, please see their help page here.

From Zero to Deployment: Vagrant, Ansible, Capistrano 3 to deploy your Rails Apps to DigitalOcean automatically (part 1)

update: please refer to the prequel that sets the stage with Cucumber scenarios as a BDD exercise.


In this post, I would like to share that my anxiety about setting up a new server to host an application reminded me why I like being in IT: automation. I attempt to avoid snowflake servers and deploy a Rails application to a VM using idempotent scripts with the help of Ansible and Capistrano.

This entry is a step-by-step guide to get a VM up and running with a Rails app deployed to it. I describe the steps needed to be taken with VagrantAnsible and Capistrano to deploy to a local VM while leaving deployment to DigitalOcean for part two.

the problem

Writing code comes easy to you. As a developer, you develop and test your code with a certain ease and enjoyment . To a certain extent, you may not even think much about the production phase of your project as you may already have an environment set up. However, you might only have a certain idea of what your prod environment looks like as you may have set it up, say, a year or two ago? Maybe your development environment is out-of-sync? Maybe you have to rely on other people (sys-admins) to take care of that “stuff”? That requires A HandOff Ceremony, something we want to avoid on planet DevOps.

In summary, it would be nice to have an automated, testable, repeatable way of provisioning hosts for testing and deployment uses. Obviously, scripts and scripting systems exist for that, and after mucking around with Chef and Puppet, I opted for Ansible.

a solution

In my mind, Ansible is to shell, what CoffeeScript is to JavaScript. I can express what I want to do at a high level (given there’s a module for it) and not worry about the details. In the case of Ansible, I don’t have to worry about idempotence either. So I settled on a way to provision virtual machines (VMs) using Vagrant and Ansible.

While I do not claim to be an expert in any systems herein mentioned, I do declare that “it worked for me”. Please leave comments, tips and tricks if you see any aberrations or more elegant ways of doing things with these tools.

I’d like to credit my friend and colleague Jefferson Girao from ThoughtWorks for having introduced me to Ansible in the first place, and mention that he’s on a similar journey to optimising Rails deployment, with the goal of using Ansible only. I am taking a more conservative approach and will stick with good-old Capistrano for the Rails part.

0: punt on windows, linux.

The demo is on a Mac, but feel free to try to adapt it to other platforms.


1: Install VirtualBox, Vagrant and Anisble

Here we install stuff, not a lot. 

Get VirtualBox here, or by following the vagrant guide and then install the vagrant gem:

gem install vagrant

 Now let’s install Ansible by the command:

brew install ansible

That assumed you had brew installed. If you don’t have it, I recommend installing it as it makes Mac OS X installations easy. If you prefer not to use brew, do it the hard way


2: Prepare to build the machine

Here we create a sub-drectory that will contain our Vagrant file and later on, our Rails app. We’ll keep the Vagrant file near our source code so we can say that we’re compatible with the idea of “Infrastructure As Code” (we’ll get to that in a future chapter).

mkdir app
cd app
vagrant init

This will create an initial Vagrantfile. Replace it with this one:

In summary, when run (don’t run it yet, it will fail), this Vagrant script will spin up an Ubuntu Precise 64 instance, make its home on your private network on IP and will invoke the Ansible provisioner to run the user.yml playbook.



Before we can run the above Vagrantfile, we need to create the ‘user.yml’ file in the devops directory, or elsewhere, if you care to change the  ‘anisble.playbook’  line in Vagrantfile.

I’d like to pause and explain what that user.yml playbook will do so you don’t freak out when you see me moving rsa keys all over the place.

On one hand, I’d like to set up a machine with all needed dependencies. This will require making some apt-get and other calls that will need root rights. That’s fine. We’ll have root (later on, when talking to DigiitalOcean), but for the moment, we’ve the default privileged ‘vagrant’ account for that, which is fine. I would like, however, to run my Rails stuff under the ‘deploy’ account, which would be better off being a regular account. So now we have two accounts, ‘vagrant’ (built-in) and ‘deploy’. I care less about the vagrant user since we’ll throw it away when we provision to DigitalOcean. I do care about the deploy account though:

That ‘deploy’ account will later be used to connect to an external git host, such as bitbucket or github and it will need keys to do so. I will be using that account to log into the instance, so it would be nice if it had my key too. For the scm related issue, I generated a key pair and posted the public portion to bitbucket and github under my account, so they will allow it git operations.

So take a deep breath and step through ‘devops/user.yml’ by reading the task names.


3: Playbook: set up a user on the VM

At the app folder root, do this:

mkdir -p devops

Copy the following text into ‘user.yml’:

The names of the tasks document sufficiently what they do. Note the following however:

1. I send over a known_hosts file that includes bitbucket’s URL.
2. I send a config file that contains bitbucket’s into the deploy user’s .ssh directory so that the first git operation does not hang forever.

OK, if you’re eager to run this playbook, you’ll need the vars.yml file:

Create vars.yml in the same directory as the user.yml file and paste this into it:

Replace the text in red with your own values:

1. Running crypt on “secret” with “SaltAndPepper” will create a password token that you place in the password variable. That is the password for the deploy user created on your VM. It’s neat that we don’t have to keep clear text passwords in YAML files.
2. repo holds the git repo you’re application resides in (for a later step).

And you’ll need the templates folder with the following files in it:

Create the templates folder:

mkdir -p templates

Inside it:

1. Copy your public key into a file named ‘’
2. Copy bitbucket’s RSA signature to a file named known_hosts, thus:, ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAubiN81eDcafrgMeLzaFPsw2kNvEcqTKl/VqLat/MaB33pZy0y3rJZtnqwR2qOOvbwKZYKiEO1O6VqNEBxKvJJelCq0dTXWT5pbO2gDXC6h6QDXCaHo6pOHGPUy+YBaGQRGuSusMEASYiWunYN0vCAI8QaXnWMXNMdFP3jHAJH0eDsoiGnLPBlBp4TNm6rYI74nMzgz3B9IikW4WVK+dc8KZJZWYjAuORU3jc1c/NPskD2ASinf8v3xnfXeukU0sJ5N6m5E8VLjObPEO+mN2t/FZTMZLiFqPWc/ALSqnMnnhwrNi2rbfg/rd/IpL8Le3pSBne8+seeFVBoGqzHM9yXw==

3. Copy your deploy’s private key into a file named deploy_rsa
4. Copy your deploy’s public key into a file named
5. Copy this to a file named ssh_config:

  IdentityFile /home/deploy/.ssh/deploy_rsa
  StrictHostKeyChecking no

This will make some security people cringe – I’m bypassing checking on bitbucket. Yeah.

6. Create a copy of your sudoers file and add the following line to it:

deploy  ALL=(ALL:ALL) ALL

Then place it in the templates directory as well.

That’s all that’s needed as templates for now. 

You need an inventory file too: Create a file called webhosts and paste the following into it:


To run this playbook, enter this at the command prompt:

vagrant up web
vagrant provision web

The first line wakes up vagrant. If it’s the first time you’re trying to access Precise64, this step can take quite a bit of time – Vagrant will download the Precise64 box over your internet connection. Time to brew and drink some coffee.
The second line will be cute to watch, Ansible will light up your screen like a disco, at the end of which you’ll have a VM with Ubuntu installed as well as a login for deploy, using your own ssh key.

You can access this VM via any of the following commands:

1. vagrant ssh
2. ssh vagrant@
3. ssh deploy@

If it does not work, it’s either this blog is buggy or it’s a case of PEBKAC. Please check and let me know.

If it works, have some fun with your new free VM, something that would have otherwise cost you a few hundred dollars at your retail PC store.

By the way, adventurous developers can try to provision directly from Ansible:

vagrant up web
ansible-playbook devops/user.yml -i devops/webhosts

4: Playbook: get some linux


The playbook will give us a real Linux to allow us to move forward with our provisioning (Ruby, Rails)


Create a file called webserver.yml and paste this into it: 

Play it by issuing the following command:

ansible-playbook devops/webserver.yml -i devops/webhosts

5. Playbook: get some mySQL

The playbook will install mySQL on the provisioned VM. Create a file called dbserver.yml and paste this into it:

It will install the needed packages for mySQL and then:

  • Start the service
  • Remove the test database
  • Create a ‘deploy’ user
  • Remove anonymous users from the DB
  • Set up a my.cnf file
  • Change root password
While a great idea to change the root password, this feature renders this playbook non idempotent.

6: Playbook: get some Ruby

The playbook will install the current Ruby 2.0 version. This edition of the blog does not use RVM as it is hell to deal with non-interactive terminals, I am saving the setup of RVM with Ansible for a later post.

Create a file called virtual_ruby.yml and paste this into it:

Play it by issuing the following command:

ansible-playbook devops/virtual_ruby.yml -i devops/webhosts

7: Playbook: get the project’s ruby and install bundler

The playbook will install the project’s ruby in under the deploy user and install bundler to be used later on.

Create a file called project.yml and paste this into it:

Play it by issuing the following command:

ansible-playbook devops/project.yml -i devops/webhosts

8: Using Capistrano 3 to deploy the Rails app

This is not a playbook, of course, but a Capistrano 3 recipe.

Install Capistrano 3 following their instructions and replace the deploy.rb file with this one:

Replace the contents of config/deploy/production.rb file with this:

Deploy the app by issuing the following command:

cap production deploy 

9: Have some fun with your new scripts. See the disco colours!

You can repeat these commands to provision, re-provision or just test Ansible’s idempotence:
vagrant up web
vagrant provision web
ansible-playbook devops/user.yml -i devops/webhosts -vvvvv
ansible-playbook devops/webserver.yml -i devops/webhosts -vvvvv
ansible-playbook devops/dbserver.yml -i devops/webhosts -vvvvv
ansible-playbook devops/virtual_ruby.yml -i devops/webhosts -vvvvv
ansible-playbook devops/project.yml -i devops/webhosts -vvvvv
cap production deploy

In the next post, we’ll push the Rails project to a DigitalOcean VM instead of a local one and it run.

Please comment and send feedback about the form and content.

Happy provisioning!


How to reconnect to a database when its connection was lost

One of my projects has a long-running task that constantly needs information from the database. I needed a mechanism to assure that the task will automatically reconnect to the database if and when that connection was broken.

I came up with this scheme using a trick with rescue blocks (code abbreviated for clarity) in this gist.

def my_task

    while(true) do
      rescue Exception => ex
          sleep 10
          retry # will retry the reconnect
          retry # will retry the database_access_here call

Here’s a line-by-line explanation:

Line 4: This is where your application’s database access logic would be.

Line 5: Catch a database access exception here

Here is where it gets interesting:

Line 7: Open a new block and retry the connection.

Line 10: This retry will retry the reconnect method and will loop as long as the database connection is still down.

Line 11: The else clause will execute if _no_ exception happened in line 10, and will retry the original database call in line 4.

In my case and example, I am not counting retries because I don’t care that I’ve failed – I must continue to retry. You may want to use “retry if retries < 3” as a break mechanism.

I have also removed some mailer code that notifies me when the reconnect fails so I can (manually) see what happened to the connection. The moment the connection is re-established, life goes on as normal within the infinite while loop.

Setting up a Rails server on a GoDaddy VPS

I thought my experience with setting up a Centos 5 box from scratch with Rails 3.1 would be helpful to some readers.

1. Get a VM – this one is on GoDaddy, just for kicks.

Demo config:

Operating System: CentOS 5
Disk Space: 20 GB
And it costs $30 a month. Not too bad.

2. Get some tools

Become root for that: “$ su -”

Then issue:

# yum groupinstall ‘Development Tools’
# yum groupinstall ‘Development Libraries’
# exit

3. Install your ssh key for logins

Copy your key to ~/.ssh/authorized_keys

chmod go-w ~
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys

4. Install node.js

Become root for that: “$ su -”
Then issue:

# cd /root
# wget
# gunzip node-v0.4.11.tar.gz
# tar -xf node-v0.4.11.tar
# cd node-v0.4.11
# ./configure
# make
# make install
# exit

5. Install Git

Become root for that: “$ su -”
Then issue:

# yum install gettext-devel expat-devel curl-devel zlib-devel openssl-devel
# yum install zlib-devel
# yum install openssl-devel
# yum install perl
# yum install cpio
# yum install expat-devel
# yum install gettext-devel

# wget
# tar xzvf git-latest.tar.gz
# cd git-{date}
# autoconf
#./configure –with-curl=/usr/local
# make
# make install
# exit

6. Install RVM

$ bash < <(curl -s

then add
[[ -s “/home/your-user/.rvm/scripts/rvm” ]] && source “/home/your-user/.rvm/scripts/rvm”
to the end of .bash_profile

8. Install readine

$ rvm pkg install readline

9. Install ruby

$ rvm install 1.9.2 –with-readline-dir=$rvm_path/usr

10. Create a gemset

$ rvm gemset create rails3.1

$ rvm –default use 1.9.2@rails3.1

11. Load Rails3.1

$ export LC_CTYPE=en_US.UTF-8

$ export LANG=en_US.UTF-8

$ gem install rails 3.1

12. Create ssh key for git repo

$ ssh-keygen -t rsa

13. Upload the public key to your repo

Make sure the end of the key file has a newline

Test access by issuing
$ git clone ssh://

14. Install bundler

$ gem install bundler

Test bundler by running ‘bundle install’ in the directory created by (6)

15. Install mysql

Become root for that: “$ su -”

Then issue:

# yum install mysql
# /etc/init.d/mysqld start
# exit

16. Get a copy of your project

From git by cloning the repo and run ‘rake spec’ to see that everything is installed and running correctly
This assumes you use rSpec, else run ‘rake test’, or whatever testing framework you use.

17. Install passenger

$ gem install passenger
$ passenger start

18. Test it out…

Navigate to http://xxxx:3000 to see your app!