Raymond Cox

Boston Software Engineer

Caching Middleman & Nginx

Jun 16, 2016

I recently setup a blog with Middleman served through Nginx. I always forget the best way to cache assets, so this is a nice reminder for me and anyone interested. In general you want to cache images, javascript, and css in the client users browser for a long time so no request has to be made. In nginx this is pretty simple to do

  location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
    expires 30d; # expires after 30 days
    add_header Cache-Control "public"; # clients can cache
  }

With Middleman you need a way to bust the cache, you can do this with fingerprints.

  configure :build do
    activate :asset_hash
  end

Now to test curl https://raymondjcox.com/images/face-d04725c4.png returns

HTTP/1.1 200 OK
Server: nginx/1.11.1
Date: Thu, 16 Jun 2016 01:33:18 GMT
Content-Type: image/png
Content-Length: 40257
Last-Modified: Thu, 16 Jun 2016 00:58:02 GMT
Connection: keep-alive
ETag: "5761f99a-9d41"
Expires: Sat, 16 Jul 2016 01:33:18 GMT
Cache-Control: max-age=2592000
Cache-Control: public
Accept-Ranges: bytes

How to setup Ember Deploy

May 11, 2015

Recently I had a problem where I would have to deploy my Rails app to deploy my Ember app. I found that Ember Deploy was the solution, I’ll explain how it works and how I set it up! Ember Deploy is awesome, you should be using it if your deployment strategy is suboptimal.

Ember Deploy is the methodology that your assets should go somewhere and your index.html should go somewhere. The assets typically go on S3, and the index needs to be served by your server (due to CORS restrictions) but can go anywhere usually Redis. Ember deploy allows a lot of nifty things like preview URL’s, zero downtime and dynamically modifying index.html.

For my setup I decided to use ember-cli-deploy with a Redis and S3 adapter.

npm i ember-cli-deploy --save-dev
npm i ember-deploy-redis --save-dev
npm i ember-deploy-s3 --save-dev

ember-cli-deploy expects config/deploy.js to contain deployment settings that look like this

module.exports = {
  development: {
    buildEnv: 'development',
    store: {
      type: 'redis', // the default store is 'redis'
      host: 'localhost',
      port: 6379
    },
    assets: {
      type: 's3', // default asset-adapter is 's3'
      gzip: false,
      gzipExtensions: ['js', 'css', 'svg'],
      accessKeyId: '<your-access-key-goes-here>',
      secretAccessKey: process.env['AWS_ACCESS_KEY'],
      bucket: '<your-bucket-name>'
    }
  },

  staging: {
    buildEnv: 'staging',
    store: {
      host: 'staging-redis.example.com',
      port: 6379
    },
    assets: {
      accessKeyId: '<your-access-key-goes-here>',
      secretAccessKey: process.env['AWS_ACCESS_KEY'],
      bucket: '<your-bucket-name>'
    }
  },

   production: {
    store: {
      host: 'production-redis.example.com',
      port: 6379,
      password: '<your-redis-secret>'
    },
    assets: {
      accessKeyId: '<your-access-key-goes-here>',
      secretAccessKey: process.env['AWS_ACCESS_KEY'],
      bucket: '<your-bucket-name>'
    }
  }
};

I also specified a manifestSize which determines how many revisions Redis will keep. The only other thing I did was make some modifications to my Brocfile.js and config/environment.js to add the staging environment.

I used this gem for connecting to Redis. After that, I added a route to a controller that reads the contents of the current Redis key and sends that revision to the client. It looks something like this

class MyController
  class << self
    def redis
      @redis = Redis.new(url: 'redis://staging-redis.example.com');
    end
  end

  def index
    currentKey = redis.get('my-project:current')
    render text: redis.get(currentKey)
  end
end

Notice that my-project:current is acutally a link to the <current-revision> You can do a lot of other neat things here like take in a revision param and render that instead, or insert ruby generated content into your index.html.

After all of that is setup, the commands are easy:

  • ember deploy:list Lists all revisions

  • ember deploy Deploys assets to S3 and index to Redis

  • ember deploy:activate --revision=<revision> Moves a revision to current