mjml-remote-rails

Based on mjml-rails, this gem offloads the actual MJML processing to a microservice (e.g a lambda function). If the microservice returns an error, Mjml::Remote::Template::ParseError will be raised.

See mjml-lambda for an example lambda function that will do it.

Installation

Add this line to your application's Gemfile:

gem 'mjml-remote-rails'

And then execute:

$ bundle

Or install it yourself as:

$ gem install mjml-remote-rails

Configuring

Mjml::Remote.setup do |setup|
  # Override template language - default is erb
  config.template_language = :haml
  # config.endpoint is required!
  config.endpoint = "https://<some-server/<some-endpoint>"
  # headers for the requests can be set - default is no headers
  config.headers = { "x-api-key" => "some header based authentication" }
end

...why?!

Calling MJML from ruby is pretty heavy memory-wise and threw a major spanner in the works of our heavily multi-threaded background job workers when lots of emails were being processed. Installing the mjml binary also added a decent amount of weight to our docker containers once you factored in the other dependencies you need.

Moving the actual MJML processing to a lambda function means we don't need to worry about this any more, and as all our email processing is backgrounded anyway, it won't really make much of a difference to our users.

Falling back to regular MJML in development

Gemfile

group :production do
  gem "mjml-remote-rails"
end
group :development do
  gem "mjml-rails", require: false
  gem "mjml-remote-rails", require: false
end

config/application.rb at the top after requiring rails:

if ENV["MJML_ENDPOINT"]
  require "mjml-remote-rails"
else
  require "mjml-rails"
end

config/initializers/mjml.rb

if Rails.env.production? || ENV["MJML_ENDPOINT"]
  Mjml::Remote.setup do |config|
    ...
  end
else
  Mjml.setup do |config|
    ...
  end
end