Saying stuff about stuff.

Dynamically setting Rails default_url_options in Heroku review apps

I love Heroku review apps but not when URLs in emails point to the parent app – and if you have a hypermedia API its _links can end up totally broken. Why does this happen and how can it be fixed?

My usual approach used to be to configure Rails.application.routes.default_url_options[:host] from a DEFAULT_URL_HOST environment variable, but if the review app inherits this variable then all of its URLs generated outside the controller/view request/response cycle – including URLs in emails and serializers such as ActiveModel::Serializers – incorrectly point to the parent app. In the past I’ve been forced to manually update a review app’s config but that isn’t a solution.

It isn’t possible to dynamically set an environment variable but you can detect a review app at run-time by enabling the HEROKU_APP_NAME environment variable and then use it to generate the correct host. To do this in Rails create an initializer config/initializers/default_url_options.rb with the following contents:

# If a default host is specifically defined then it's used otherwise the app is
# assumed to be a Heroku review app. Note that `Hash#fetch` is used defensively
# so the app will blow up at boot-time if both `DEFAULT_URL_HOST` and
# `HEROKU_APP_NAME` aren't defined.
host = ENV['DEFAULT_URL_HOST'] ||
  "#{ENV.fetch('HEROKU_APP_NAME')}.herokuapp.com"

# Set the correct protocol as SSL isn't configured in development or test.
protocol = Rails.application.config.force_ssl ? 'https' : 'http'

Rails.application.routes.default_url_options.merge!(
  host: host,
  protocol: protocol,
)

In staging and production apps you should set the DEFAULT_URL_HOST using heroku config:set, but in review apps tell Heroku to pick up the HEROKU_APP_NAME variable by adding it to the app’s app.json (and remove references to DEFAULT_URL_HOST):

{
  "env": {
    "HEROKU_APP_NAME": {
      "required": true
    }
  }
}

Note that this also means in development/test/CI you’ll need to define the DEFAULT_URL_HOST environment variable or the app won’t boot – on my development machine I use direnv, dotenv is also popular.

And that’s it, now all URLs generated in emails and serializers will correctly point to the review app’s host.

RSpec --next-failure

I recently discovered RSpec’s --only-failures feature after seeing Increase Your Quality of Life: An RSpec Primer. I thought I was being clever to combine it with --fail-fast – allowing me to methodically step through failures one at a time – only to discover that the RSpec team already has this covered with the --next-failure option and that they’ve both been available since 2015.

To enable these features example status persistence must be configured in spec/spec_helper.rb (remember to add the path to your .gitignore):

RSpec.configure do |config|
  config.example_status_persistence_file_path = "spec/examples.txt"
end

Now rspec --only-failures and rspec --next-failure will work as advertised. They can also be combined with an additional path filter and so will work with an entire directory, a single file, or any other RSpec path filter:

rspec --next-failure
rspec --next-failure spec/system
rspec --next-failure spec/models/foo_spec.rb
rspec --next-failure spec/models/foo_spec.rb[1:2]

These RSpec nuggets have made me much more efficient when encountering a large swathes of failing tests, it makes me wonder of what else I’m unaware.

v now automatically reads from stdin

I previously wrote v to help with a couple of my common wants when starting Vim. Something I often forget is to pass the - when piping stdin to Vim, expecting it to behave in the same way as other commands like less. So I’ve updated v to automatically pass the - argument to Vim if it detects that it’s receiving content from stdin.

Here’s what now happens:

$ echo testy | v
Vim: Reading from stdin...

As ever the source is on GitHub. For the curious here are the tests for this newly added functionality.

v, a Vim command line wrapper

I wrote v to help with a couple of my common wants when starting Vim. It’s a script written in Ruby and the source is on GitHub, here are the two things it does.

Open path/to/file:line-number

If v is passed an argument matching path/to/file:line-number it will load the file and place the cursor on the specified line, this allows you to easily copy/paste output from tools such as RSpec and start editing in the correct place.

Example:

v ./spec/path/to/failure_spec.rb:123

Automatically load ./Session.vim

When v is run with no arguments it looks in the current directory for a file called Session.vim and loads it as a Vim session. Note that this only occurs when no arguments are passed, so you can load other specified files without spoiling your session.

--dry-run

To see what v will execute for the given arguments add --dry-run.

Example:

$ v --dry-run ./spec/path/to/failure_spec.rb:123
vim ./spec/path/to/failure_spec.rb +123

--help

What command line script would be complete without corresponding help? Run v --help for usage.

Trying my hand at (code) golf

I’ve never attempted code golf before but encountered the following tweet that looked achievable.

JS Golf #4: play(“hello=goodbye&answer=42&x=y”) === {hello:”goodbye”,answer:”42”,x:”y”} (parse url encoded parameter strings into objects)

JS Golf #4

I thought I’d describe the journey to my shortest solution (lower score is better) which ended up touching on a few of JavaScript’s newer language features.

An unoptimised solution

Here’s my starting “normal” code that does the job.

function play(querystring) {
  return querystring.split('&').reduce(function(object, param) {
    const parts = param.split('=')
    object[parts[0]] = parts[1]
    return object
  }, {})
}

Score: 104

Apply some manual minification.

function play(q){return q.split('&').reduce(function(o,p){let k=p.split('=');o[k[0]]=k[1];return o},{})}

Score: 81

Arrow functions are shorter, don’t require parentheses when there’s a single argument, and implicitly return when used without braces.

play=q=>q.split('&').reduce((o,p)=>{let a=p.split('=');o[a[0]]=a[1];return o},{})

Score: 79

Use destructuring assignment.

play=q=>q.split('&').reduce((o,p)=>{let [k,v]=p.split('=');o[k]=v;return o},{})

Score: 75

Don’t worry about setting global variables 😱

play=q=>q.split('&').reduce((o,p)=>{[k,v]=p.split('=');o[k]=v;return o},{})

Score: 71

Template literals also call a function so there’s no need for parentheses.

play=q=>q.split`&`.reduce((o,p)=>{[k,v]=p.split`=`;o[k]=v;return o},{})

Score: 65

Arrow function implicit returns again. I couldn’t use a semicolon to turn the function body into a single line but a comma works.

play=q=>q.split`&`.reduce((o,p)=>([k,v]=p.split`=`,o[k]=v)&&o,{})

Score: 64

Using another comma instead of && is possible as the comma operator returns the last expression.

play=q=>q.split`&`.reduce((o,p)=>([k,v]=p.split`=`,o[k]=v,o),{})

Score: 60

I started looking at whether I could use something other than reduce as its multiple arguments mean parenthesis can’t be removed. In the good old days before ES5 introduced reduce and friends I would have created the data object manually by looping through the array adding properties to it like so:

function play(querystring) {
  var object = {}
  var params = querystring.split('&')

  for (var i = 0; i < params.length; i++) {
    var keyvalue = params[i].split('=')
    object[keyvalue[0]] = keyvalue[1]
  }

  return object
}

It turns out that this technique actually shortens the code by a further 4 characters, particularly as nowadays we don’t need to use a verbose for loop. So play now loops over the querystring’s parameters setting key/value pairs on an accumulator object which it then returns — using map instead of forEach for its shorter name.

o={},play=q=>q.split`&`.map(p=>([k,v]=p.split`=`,o[k]=v))&&o

The winner? Yes!

This solution currently shares the top score of 60 characters — someone got there first with an essentially identical version.

It’s been an interesting exercise and taking a deeper look into some of JavaScript’s features has been useful — despite being focussed on hacky ways to shorten code. Re-adding whitespace and proper variable names gives us the shape of the code which looks remarkably Lisp-ish and very un-JavaScript.

object = {},
  play = querystring =>
    querystring.split `&`
      .map(
        param => (
          [key, value] = param.split `=`,
            object[key] = value
        )
      ) && object

Bonus round: a different approach. Score: 63

I wondered if something could be done about the two uses of split so tried a different approach, this time creating an array of key/values with a single split. Combined with the lessons learned so far it’s a good effort but falls just short at 63 characters.

o={},play=q=>q.split(/[&=]/).map((p,i,a)=>i%2?o[a[i-1]]=p:p)&&o

Update: Thanks to @Benjie for removing another valuable character:

o={},play=q=>q.split(/&|=/).map((p,i,a)=>i%2?o[a[i-1]]=p:p)&&o

For good measure here it is deminified:

object = {},
  play = querystring =>
    querystring.split(/&|=/)
      .map(
        (param, index, array) =>
          index % 2
            ? object[array[index - 1]] = param
            : 1
      ) && object

My open source contributions in 2016

I find it immensely satisfying to be able to contribute to open source, here’s what I managed in 2016.

Screamshot

I created Screamshot for a personal project and especially wanted to make it open source once I had an idea for a logo.

Dragonfly S3 Server

Another open source creation that sprung from a personal project — and something I have yet to write about separately. The motivation is to remove Dragonfly requests from being served by the main Rails app.

Allow Dragonfly to fetch a URL with basic auth

Whilst creating Screamshot I discovered that Dragonfly didn’t understand URLs containing authentication credentials. A quick pull request and it was taken care of.

Keep rspec-rails’ gem description up-to-date

A tiny, seemingly pointless, but nevertheless helpful, pull request.

A Ruby API client for Medium

I made the decision to cross-post my blog posts to Medium but didn’t like the look of the existing Ruby libraries so I wrote my own. It was also featured in Ruby Weekly Issue 312.

Open source libraries are great and so is open source documentation; I found Medium’s API documentation to be good and readable which makes being able to contribute more satisfying.

The funny thing about some larger companies’ open source projects is that you are required to sign a licence agreement to contribute. I think I understand why this might potentially be required from a just-in-case cover-all-bases legal perspective but it seems slightly at odds with the whole point of an open source licence.

Quotesplit

Another little ditty that I haven’t yet written about separately, Quotesplit is a Ruby library for quote-aware whitespace string splitting — like Shellwords.split but without raising ArgumentError: Unmatched double quote.

Rails: don’t introduce Uglifier to an app when JavaScipt has specifically been turned off

I wrote about my ~/.railsrc and found a small something that I could contribute to Rails.

A tiny amendment to Paperclip’s regular expressions

This is one of those annoying (for the receiver) pull requests on the one hand making the project a tiny bit “better” — which is what open source enables — but on the other hand creating work and introducing risk for little to no gain.

Peity

Peity is my most “successful” open source project to date but, having been created in 2009, it’s stable and mature and saw only a scattering of minor commits and a patch release during 2016.

My ~/.railsrc

I create a new Rails app remarkably often and over the years have found it difficult to remember my preferred options for rails new. The out-of-the-box experience of Rails has always been one of its strong points but I end up taking a lot out of a freshly created Rails project. I can’t believe it took me so long to discover ~/.railsrc, a file that stores your default preferences for rails new — it must have passed me by on its arrival years ago with Rails 3.2. From what I can gather this feature is also underutilised by other experienced Rails devs so it’s time to evangelise.

Some of my choices are purely personal preference but some are slightly more quantifiable and follow the principle that gems, configuration, and other files should enter a project when they serve a purpose instead of being added by default (on a related note I recently had a pull request merged to ensure that Uglifier isn’t added unnecessarily) — this seems to be the opposite approach to something like Suspenders which configures everything up-front.

My preferences

Here are the current contents of my ~/.railsrc:

--database=postgresql
--skip-action-cable
--skip-action-mailer
--skip-bundle
--skip-javascript
--skip-keeps
--skip-spring
--skip-test

--database=postgresql

Heroku uses PostgreSQL. I use Heroku. I use PostgreSQL.

--skip-action-cable

I haven’t tried Action Cable yet and am not sure where it fits into the way I typically apply “realtime” so until I find out I’ll continue using Pusher.

--skip-action-mailer

Sending email isn’t usually the initial functionality of an app, there’ll also be a bit of configuration to integrate with an email-sending service, so I choose to leave it all until then.

--skip-bundle

If I’m somewhere with no (or even worse, slow) internet I’d rather get on with writing code as soon as possible instead of waiting for my machine to notice it’s offline so I run bundle install --local which usually does the job.

--skip-javascript

If and when I want jQuery — or React, or something completely different like browserify — I’ll add it myself. (This also skips Turbolinks.)

--skip-keeps

I know where everything goes so the scattering of .keep files are just noise for me.

--skip-spring

I’ve never actually used Spring.

--skip-test

After many years of actively disliking RSpec (!) it’s now my testing framework of choice. My first few commits of a project tend to be about validating an idea so I’ll add and configure rspec-rails later.

What am I missing?

Tweet me your ~/.railsrc preferences.

#FirstSevenLanguages

Lingo (Macromedia Director)
ActionScript
JavaScript
VBScript
SQL
CFScript
Ruby

Ruby Medium API Client

Medium have a few official API clients (Go / Node / Python) but there isn’t one for Ruby and although there already appear to be (at least) a couple of “unofficial” Ruby Medium API clients I wasn’t entirely convinced by them (lack of tests / lack of source code).

After looking over Medium’s API docs and their existing clients — each of which is barely more than a single file — I decided to write my own Ruby Medium API client. It does little more than communicate with the correct endpoints and convert to and from JSON; I’d even argue that it’s thinner than the official clients as, for example, it doesn’t attempt to know the list of valid publishing licences or statuses or the valid attributes for a post — which seems like a losing game at the best of times but particularly for an “unofficial” client.

Medium’s API docs are particularly useful as they provide example HTTP responses for each endpoint making it easy to test by mocking responses with Webmock — in fact my first work on the library was test-first on a train with no internet*. I’ve been using it for a while as I now cross-post on Medium, it’s working well and helping to smooth my publishing flow (as opposed to doing it manually which is time-consuming and error-prone).

Here’s how to use it to create a post that points back to a canonical URL (an access token is required):

client = Medium::Client.new(access_token)
client.create_post(
  canonicalUrl: 'http://example.com/hello-world',
  content: '<p>Some interesting words.</p>',
  contentFormat: 'html',
  publishStatus: 'draft',
  title: 'Hello World',
)

* Pro-tip: prepare for an internet-less train journey by queueing up docs that might be required. Also, always use the most excellent DevDocs which feels like a web app from the future.

Peity and Interactive Data Visualization for the Web

Did I ever tell you that Peity — “a jQuery plugin for generating progressive <svg> pie, donut, bar and line charts” and my most successful open source project to-date (it currently has more than 3500 stars on GitHub) — was mentioned in a real book made of paper and printed by O’Reilly?

The book is called Interactive Data Visualization for the Web and is available from:

It’s a short and sweet mention of Peity, my favourite remark is:

+10 cuteness points

This news is a couple of years old now (the book was published in 2013!) but it looks like a second edition is coming in 2017.


It’s also nice to see that a friend and fellow New Bamboo alumni is mentioned in the book for his project Dashku.