Today Icelab Learned

Creating and storing SSL certificates

As part of the SSL Certificate generation process you’ll need to create a Certificate Signing Request. You can do this with the command:

openssl req -nodes -newkey rsa:2048 -sha256 -keyout server.key -out server.csr

This will generate two files: a public .csr file, and a private .key file

You can view the files with the commands cat server.csr and cat server.key

Here at Icelab we recommend saving this information in a new 1Password secure note named with the following format:

[domain-name.com] SSL CSR

Save the data from both files into the secure note and continue to be happy and confident in the face of adversity :)

Hash.new with a block works well as a keyed cache

We’re all familiar with the @foo ||= expensive_computation technique to memoize (i.e. cache) the output of slow computations or to avoid unnecessary object creation.

If you want to do the same but with results that will vary by a single parameter, you can use Ruby’s hash with its block-based initializer to handle the caching for you:

def my_cache
  @my_cache ||= Hash.new do |hash, key|
    hash[key] = some_slow_computation(key)
  end
end

# Computed once
my_cache[:foo]

# Then cached
my_cache[:foo]

You can see this work in practice inside dry-component’s Injector, where it caches objects to allow arbitrarily long chaining of injector strategies without creating duplicate injector objects.

Selecting colours in HTML

Somehow in the excitement around HTML5’s new input types I missed the addition of an input type specifically for selecting colours:

<input type="color">

Include this and you’ll get a platform-native colour selector that takes and returns hex colour values, like my debugging colour of choice #ff0099:

Selecting colours in HTML vGogBW

Note that the value needs to a fully hex string, not a shortcut like #f09. And, alas, if you’re viewing this in Safari you’ll notice that it isn’t supported at all.

Creating an IAM user for access to an S3 bucket

When you create an S3 bucket for a web app to use, you should create a specific IAM user to grant access to that bucket.

To do this, first create a user:

  1. Visit the IAM section of the AWS console
  2. Go to the “Users” section and click the “Create New Users” button
  3. Enter the user name(s) you want to create (e.g. my-bucket-s3 for a bucket named my-bucket)
  4. When you’re shown the user credentials, save them somewhere safe, like our team 1Password vault
  5. When you return to the IAM users list, go to the newly created user and click on their “Permissions” tab. Click on the “Inline Policies” section and create a new policy
  6. Choose to create a “Custom Policy”
  7. Name the policy “my-bucket-s3-access” (or something similarly descriptive) and paste the following into the “Policy Document” area (replacing my-bucket with your bucket’s name):

    {
      "Statement": [
        {
          "Effect": "Allow",
          "Action": "s3:ListAllMyBuckets",
          "Resource": "arn:aws:s3:::*"
        },
        {
          "Effect": "Allow",
          "Action": "s3:*",
          "Resource": [
            "arn:aws:s3:::my-bucket",
            "arn:aws:s3:::my-bucket/*"
          ]
        }
      ]
    }
    
  8. Click “Apply Policy”

The access policy should now be active and your app should be able to access the bucket with the new IAM user’s credentials.

ChromeVox

ChromeVox is a screen reader extension for Chrome.

I found it a helpful extension when addressing accessibility issues on a recent project. I was able to replicate the voice prompts noted in the accessibility audit. Thus, fixing them was much easier!

One thing to note is that you’ll want to change the default voice. The default just seems to cackle at you. You can do this via Settings > Extensions > ChromeVox > Options.

Using CloudFront as ‘origin pull’ (with bonus gzip)

CloudFront can act as a CDN that ‘pulls’ content from an origin server. It’s really easy to set up, you simply set the ‘Origin Domain Name’ to wherever you’re hosting your app: foobar.herokuapp.com for example and then … nothing. That’s all you need to do. Requests that come through CloudFront will pass through to your origin server and then be cached thereafter.

For bonus points it’ll also gzip your content for you without you having to do much work at all. Set ‘Compress Objects Automatically’ to ‘Yes’ and you’re done. If CloudFront can serve a gzipped version (and the client can receive one) it’ll serve up a compressed version of your file instead.

Symbolize keys

If you’re writing plain old Ruby you can symbolize the keys of a hash with the following:

hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}

This does the following:

hash = {"hello" => "jojo"}
hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
=> {:hello=>"jojo"}

However if you’re doing this in a web application you may want to make a module to make hash and array data transformations easily available to you. In most of our apps we would use transproc to help us with this — if you’re working in one of Icelab’s Rodakase apps this will be available to you.

require "transproc/all"

module Functions
  extend Transproc::Registry

  import Transproc::HashTransformations
  import Transproc::ArrayTransformations

  def self.t(*args)
    self[*args]
  end
end

Functions.t(:symbolize_keys)[{"foo" => "bar"}]
# => {foo: "bar"}

If you are using Rails then you can use the method hash.symbolize_keys or the destructive version hash.symbolize_keys!
(both made available via ActiveSupport).

In React setState is not "guaranteed" to be synchronous.

I discovered recently that if you do something link this in react:

this.setState({reallyUseful: true})

if (this.state.reallyUseful) {
  this.doSomethingAmazing()
}

… it’s not very useful and more often than not nothing amazing happens, just confusion and sadness.

This is because setState it turns out, is asynchronous. Of course most of the time you are doing your amazing things in your render function so everything is sweet but on the odd occasion that you need to pass that value elsewhere, eg: publishing an event, beware!

From the docs:

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value.

There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

Read about setState here.

Editor’s note — setState will also take a callback as its second argument that’ll be executed after this.state has been updated:

this.setState(
  { reallyUseful: true },
  function() {
    if (this.state.reallyUseful) {
      this.doSomethingAmazing()
    }
  }
)

Pry is amazing

Pry is amazing! It has a whole bunch of helpful shortcuts you can use while working in a session.

I found the exception handling shortcuts particularly helpful. _ex_ will give you the last raised exception, and wtf? will show you a stacktrace from that exception (which is helpful, since stacktraces aren’t normally shown in interactive terminal sessions like this). Hilariously, you can add more question marks or exclamation marks on the end to see more detail.

If you work with Ruby app consoles regularly, you’d do yourself a favour to give the Pry Wiki a good read!

Authenticating with AWS Elasticsearch

The AWS Elasticseach service offers authentication via an IAM user, or by whitelisting IPs.

Here’s how to use IAM credentials to sign requests to the service when using Faraday and how to hook that into the Ruby elasticsearch gem.

To sign requests using Faraday, you can use a gem called faraday_middleware-aws-signers-v4, which provides a middleware that will sign your requests.

require 'faraday_middleware'
require 'faraday_middleware/aws_signers_v4'

conn = Faraday.new(url: 'address-of-your-AWS-es-service') do |faraday|
  faraday.request :aws_signers_v4, {
    credentials: Aws::Credentials.new(ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY']),
    service_name: 'es',
    region: 'ap-southeast-2'
  }

  faraday.adapter :typhoeus
end

To get the client provided by the elasticsearch gem to use your Faraday configuation, you can pass that configuration to it like so:

faraday_config = lambda do |faraday|
  faraday.request :aws_signers_v4, {
    credentials: Aws::Credentials.new(
      ENV["ELASTICSEARCH_AWS_ACCESS_KEY_ID"],
      ENV["ELASTICSEARCH_AWS_SECRET_ACCESS_KEY"]
      ),
      service_name: "es",
      region: ENV["ELASTICSEARCH_AWS_REGION"]
    }
    faraday.adapter :typhoeus
  end

elasticsearch_host_config = {
  host:   ENV["ELASTICSEARCH_HOST"],
  port:   ENV["ELASTICSEARCH_PORT"],
  scheme: ENV["ELASTICSEARCH_SCHEME"]
}

transport = Elasticsearch::Transport::Transport::HTTP::Faraday.new(hosts: [elasticsearch_host_config], &faraday_config)

client = Elasticsearch::Client.new(transport: transport)

You can then use the client object as usual, and you’ll get automatically signed requests.