A Heroku-like name generator in Scala

I use a lot temporary directories at work; unfortunately I end up with headaches when I’ve spent my whole day browsing these directories which looks like SHA-1 hash keys. I love developing for the Heroku platform in my spare time, and I think one of the coolest thing is their application random naming feature. If you don’t name your application at creation time, then Heroku will randomly pick a name for you. It looks like an haiku; an evoking image of the natural world. That name is unique, human-friendly, and definitely cool!

A few examples:

  • delicate-spectrum-1819
  • late-equinox-7634
  • snowy-sea-8407
  • strong-drake-4157
  • silent-cloud-2920
  • divine-dream-2340
  • fluffy-mountain-2939
  • green-cedar-3646
  • winter-night-9260
  • powerful-equinox-8694

The following Scala lines return a Heroku-like name like the ones above; feel free to use this anywhere you need random and human-friendly tokens.

import scala.util.Random.nextInt

// full dictionaries at https://github.com/bmarcot/haiku
val adjs = List("autumn", "hidden", "bitter", "misty", "silent",
  "reckless", "daunting", "short", "rising", "strong", "timber", "tumbling",
  "silver", "dusty", "celestial", "cosmic", "crescent", "double", "far",
  "terrestrial", "huge", "deep", "epic", "titanic", "mighty", "powerful")

val nouns = List("waterfall", "river", "breeze", "moon", "rain",
  "wind", "sea", "morning", "snow", "lake", "sunset", "pine", "shadow", "leaf",
  "sequoia", "cedar", "wrath", "blessing", "spirit", "nova", "storm", "burst",
  "giant", "elemental", "throne", "game", "weed", "stone", "apogee", "bang")

def getRandElt[A](xs: List[A]): A = xs.apply(nextInt(xs.size))

def getRandNumber(ra: Range): String = {
  (ra.head + nextInt(ra.end - ra.head)).toString
}

def haiku: String = {
  val xs = getRandNumber(1000 to 9999) :: List(nouns, adjs).map(getRandElt)
  xs.reverse.mkString("-")
}

print(haiku)

You may want to check out the GitHub project instead. It comes with a richer dictionary with more adjectives and words. There are also useful functions to check the uniqueness of words into the dictionary, should you want to add your own ones.

Advertisements

Dynamic select boxes with Rails 4

Introduction

I recently started doing some Rails development for a personal project (a wine cellar manager). I wanted to add a new wine to the database such that choosing the new wine’s country should update the list of available appellations to pick out for that country. The appellations are stored into the database and each appellation belongs to a country.

It turns out I did not find much info about that kind of feature; and most tutorials were either over-complicated stuff with a lot of JavaScript, or just outdated material targeting Rails 2 or 3.

Finally I thought that was a good opportunity to understand more deeply how a Rails application is constructed. At the time of writing, I used Rails 4 and Ruby 2.0. I tried to stick as much as possible to the Rails precept: convention over configuration. I don’t use any extra gems, and the code comes as Rails 4’s staple languages: CoffeeScript, Ruby and Embedded Ruby.

Ressources

The full app’s code has been released as a Rails project on GitHub; if at some point something fails in your local environment I would suggest you check first that your file contents match those on the reference. There is also a live demo running on Heroku of what we are about to build. Be sure to take a look at these links!

Code

The stub

Let’s start by generating the application stub and a basic controller.

rails new dynboxes

The models

We add the Country model; a country is a pair of a primary key and a name. Cities will reference their country through the primary key, or PK. Don’t worry about creating the PK, Rails handles this when inserting a new element into the database.

rails g model Country name:string

We also add the City model; a city is a triplet of a primary key, a name and a foreign key (FK) pointing to a country.

rails g model City name:string country:references

Add the following lines in app/models/city.rb, it details the association between Country and City. Depending on the order of the commands, these lines might have been automatically added during the generation.

# app/models/city.rb
 
class City < ActiveRecord::Base
  belongs_to :country
end

We can create the database’s schema with a couple of rake commands:

rake db:create
rake db:migrate

Let’s seed our database with a few country/city pairs — feel free to add some of your own pick :p Add yours in db/seeds.rb:

# db/seeds.rb
 
Country.create(name: "france")
Country.create(name: "italy")
 
City.create(name: "paris", country_id: Country.find_by(name: "france").id)
City.create(name: "nice", country_id: Country.find_by(name: "france").id)
City.create(name: "roma", country_id: Country.find_by(name: "italy").id)
City.create(name: "venezia", country_id: Country.find_by(name: "italy").id)

And run that rake command to populate the database with the new pairs:

rake db:seed

The controller

The controller is the corner stone of the application; we generate the main controller (this also generates an index view — this is our front-page):

rails g controller welcome index

We define 3 actions here — indexupdate_cities and show. The index responds to a front-page request, show responds to the form submitting, and update_cities responds to a select box update request when a new country is selected from the #countries_select select box. The app/controllers/welcome_controller.rb looks like:

# app/controllers/welcome/controller.rb

class WelcomeController < ApplicationController

  def index
    @countries = Country.all
    @cities = City.where("country_id = ?", Country.first.id)
  end
  
  def show
    @city = City.find_by("id = ?", params[:trip][:city_id])
  end
 
  def update_cities
    @cities = City.where("country_id = ?", params[:country_id])
    respond_to do |format|
      format.js
    end
  end

end

Don’t forget to declare the route for update_cities and show actions, add this line to your config/routes.rb:

# config/routes.rb
 
get 'welcome/update_cities', as: 'update_cities'
get 'welcome/show'

The views

Last thing to do is to create a couple of views to render our app. I propose to use dynamic select boxes in a form; this idea can be extended to build many other features however. Add the following lines into app/views/welcome/index.html.erb. Point out how we set HTML id to the select boxes here. The cities_select id is important: we will update _this_ HTML component with AJAX.

<!-- app/views/welcome/index.html.erb -->
 
<%= form_for :trip, url: {action: "show"}, html: {method: "get"} do |f| %>
  <%= f.select :country_id, options_for_select(@countries.collect { |country|
    [country.name.titleize, country.id] }, 1), {}, { id: 'countries_select' } %>
  <%= f.select :city_id, options_for_select(@cities.collect { |city|
    [city.name.titleize, city.id] }, 0), {}, { id: 'cities_select' } %>
  <%= f.submit "Go!" %>
<% end %>

The show view in app/views/welcome/show.html.erb prints out the city we just chose, this responds to the form action submit of the index view:

<!-- app/views/welcome/show.html.erb -->
 
<p><%= @city.name.titleize %></p>
<p><%= link_to 'Go Back', welcome_index_path %></p>

Last view to add is a partial rendering of the city in the #cities_select select box, which is called from the index view actually. We add an option entry with the city name and city primary key. We use a partial to keep things well separated and clean. In case your not acquainted with partials, a partial is a small piece of code to render one element in a collection; and Rails calls this code from a loop to render a full collection. The partial rendering in app/views/cities/_city.html.erb looks like:

<!-- app/views/cities/_city.html.erb -->
 
<option value="<%= city.id %>"><%= city.name.titleize %></option>

The AJAX magic

Now let’s write the core part, please have a cup of Coffee(Script)! Add the following lines in app/assets/javascripts/welcome.js.coffee. Basically this code sends an AJAX GET request to that URL /update_cities with country_id in parameter. Then we use this parameter to return a list of cities belonging to the relevant country.

# app/assets/javascripts/welcome.js.coffee
 
$ ->
  $(document).on 'change', '#countries_select', (evt) ->
    $.ajax 'update_cities',
      type: 'GET'
      dataType: 'script'
      data: {
        country_id: $("#countries_select option:selected").val()
      }
      error: (jqXHR, textStatus, errorThrown) ->
        console.log("AJAX Error: #{textStatus}")
      success: (data, textStatus, jqXHR) ->
        console.log("Dynamic country select OK!")

Exactly, the update_cities action responds as a JavaScript, which is in app/views/welcome/update_cities.js.coffee:

# app/views/welcome/update_cities.js.coffee
 
$("#cities_select").empty()
  .append("<%= escape_javascript(render(:partial => @cities)) %>")

This CoffeScript is straightforward: empty the cities_select HTML component, then append to the response some cities from the City collection. It’s important to point out that the select box is “HTML-repopulated” by calling the partial we have declared previously in the views.

Last words

Please feel free to fork the reference on GitHub and do push-requests with your improvements. Alternatively you can post your feedback in the comment section, thanks!

Heroku + Github deployment

This is the straightforward approach I recently used to push some work on both Github and Heroku platforms. I think it might be improved with a Git post-receive hook; but let’s begin with a simple workflow for now.

# 1. Create an empty Git repository, or 'rails init my_app'
mkdir my_app
cd my_app && git init

# 2. Add the Github remote
git add remote github https://github.com/<user>/<repo>.git

# 3. Pull the existing history (if any) from the Github repository
git pull github master

# 4. Create an Heroku app (this step adds the Heroku remote)
heroku create

# 5. Commit some work
touch foo.c && git add -A && git commit -m "initial commit"

# 6. Push your commits to Github
git push github master

# 7. And roll out the commits to Heroku
git push heroku master

No magic here; I just use basic git commands.