Making the Static Dynamic
The right stack for the job
Here at Mapillary we believe in dividing our services into small maintainable parts. We use the right stack at the right place. We force ourselves to write clear API:s between our services, no cheating. Our iPhone app uses the same API as our website. A feature developed in one of our server APIs can instantly be used by both our javascript front-end guy as well as our iPhone and Android developers.
The static problem
Our website http://mapillary.com uses the middleman framework and is hosted on heroku, this prevents us from cheating with the APIs. To communicate with our servers we use angularjs, the whole web page is static which is awesome.
However we just recently bumped into a problem. We want some of our links to be socially shareable. If a user finds a beautiful image in our archives (such as this one http://www.mapillary.com/map/im/pXA7tF-BIYMM2gStoOBMrQ) we want them to be able to share that link with their friends. This requires that we fill in our meta tags on the site accordingly. This is where rack is the best of friends.
Rack
Rack lets you write middlewares. A middleware is a piece of code that has access to everything in the HTML request by the user and the HTML response from the server. This is what we came up with. Add the following to your config.ru file.
# Datamapper is awesome for accesing your database in ruby
require 'data_mapper'
# Create an Image object
class Image
include DataMapper::Resource
property :k, String
property :title, String
end
# Setup Database
DataMapper.setup(:default, ENV['DATABASE_URL'])
class DYNAMIC_META
def initialize(app)
@app = app
end
def call(env)
status, headers, body = @app.call env
# Check for request path
if env['REQUEST_PATH']
# Match it to our specific path
match = env['REQUEST_PATH'].match(/^\/map\/im\/([^\/]*)$/)
if match
key = match[1]
# If there is a match lookup up the image in our database
image = Image.all(k: key).first
if body
body.each do |line|
# Replace static content with dynamic content
if line =~ /.*twitter:.*/
line.gsub!(/<meta content='' name='twitter:image:src'>/, "<meta content='https://example.com/#{key}/image.jpg' name='twitter:image:src'>")
line.gsub!(/<meta content='' name='twitter:title'>/, "<meta content='#{image.title}' name='twitter:title'>")
body = [line]
end
end
end
end
end
[status, headers, body]
end
end
use DYNAMIC_META
Why is this piece of code so magical. Well its the easiest smallest piece of server side include I can think of. It enables us to use middleman and all of the features we love yet having a tiny part of our website to be dynamic.