How to: Generate Rails-Compatible html.erb with Hugo

Here’s how this site is currently generated:

This works really great but, thing is, I’ve been experimenting with Rails and I discovered a feature I gotta have: split, the hacker-friendly A/B framework. Flexible content experiments? Drop in your own algorithms?

I need it.

Except running an experiment in split requires embedding code in a page’s view, you know, dynamically, and all this Hugo output is static. What to do?

Turns out, we can teach Hugo to generate Rails compatible html.erb templates. All we gotta do is define a custom output format for html.erb and Hugo will turn .md files into that, too.

The new pipeline will be like this:

Configuring Hugo to generate html.erb

Here’s what my initial data looked like–

rps@neptune ~/s/rs.io-next> tree content
content
├── email
│   ├── credit-cards.md
│   ├── finding-info.md
│   ├── health-tip.md
│   ├── lies.md
│   ├── reboot.md
│   └── welcome.md
└── posts
    ├── about.md
    ├── book.md
    ├── cuda-r-keras-nixos.md
    ├── pepe-data.md
    ├── rails-static-hugo.md
    ├── rare-pepes.md
    └── start-here.md

2 directories, 13 files

Here’s what I’ve added to my config.toml:

uglyURLs = true

[mediaTypes]
  [mediaTypes."text/x-html-erb"]
    suffixes = ["html.erb"]

[outputFormats]
  [outputFormats.HTML]
    noUgly = true
  [outputFormats.ERB]
    mediaType = "text/x-html-erb"
    isHTML = true
    path = "hugo"

[outputs]
page = ["HTML", "ERB"]

The uglyURLs code is necessary so that we don’t generate a bunch of filename/index.html.erb when what we want is filename.html.erb to copy into Rails.

The path = "hugo" line sets it so that our erb files will be saved to public/hugo and not mixed in with everything else.

Next you’ll need to create html.erb versions of some of the templates in your theme directory.

$ ls themes/hyde/layouts/_default/*html.erb

themes/hyde/layouts/_default/baseof.html.erb
themes/hyde/layouts/_default/list.html.erb
themes/hyde/layouts/_default/single.html.erb

These are what I needed; they’re exact copies of the .html versions. Hugo will use these templates to generate html.erb files. Eventually you’ll probably wish to modify them to not add Hugo’s header / footer / etc code, that way you can have Rails manage that for you.

Now it should work. If yours isn’t, I found this tutorial on building a custom output format for Gopher useful in figuring out how they work.

It lives, it lives!

rps@neptune ~/s/rs.io-next> hugo

                   | EN
+------------------+-----+
  Pages            |  27
  Paginator pages  |   0
  Non-page files   |   0
  Static files     | 580
  Processed images |   0
  Aliases          |   1
  Sitemaps         |   1
  Cleaned          |   0

Total in 99 ms

Here is what public/hugo looks like now–

rps@neptune ~/s/rs.io-next> tree public/hugo
public/hugo
├── email
│   ├── credit-cards.html.erb
│   ├── finding-info.html.erb
│   ├── health-tip.html.erb
│   ├── lies.html.erb
│   ├── reboot.html.erb
│   └── welcome.html.erb
└── posts
    ├── book.html.erb
    └── cuda-r-keras-nixos.html.erb

2 directories, 8 files

Look at all those sweet, sweet erb files!

Now, in your Rails app, create the folder app/views/pages and cp -r public/hugo/* to it, looking something like–

/home/rps/src/rs.io-rails/app/views/
├── layouts
│   ├── application.html.erb
│   ├── mailer.html.erb
│   └── mailer.text.erb
└── pages
    ├── email
    │   ├── credit-cards.html.erb
    │   ├── finding-info.html.erb
    │   ├── health-tip.html.erb
    │   ├── lies.html.erb
    │   ├── reboot.html.erb
    │   └── welcome.html.erb
    └── posts
        ├── book.html.erb
        └── cuda-r-keras-nixos.html.erb

4 directories, 11 files

Then, to your Gemfile add high_voltage. It generates static pages and will manage the copied html.erb files for us. You’ll need to create the initializer, too.

# config/initializers/high_voltage.rb
HighVoltage.configure do |config|
  config.route_drawer = HighVoltage::RouteDrawers::Root
end

Without it, all the erb files imported from Hugo will be at http://localhost:3000/pages/<filename>. The above removes /pages/ from the URL, otherwise your links will likely to be off in your Hugo-gen’d .erb.

Rails should now be serving your Hugo imports. For example app/views/pages/posts/cuda-r-keras-nixos.html.erb will be at http://localhost:3000/posts/cuda-r-keras-nixos.

Tidying up

Once you’ve got it working, you’re going to want to go back and remove public/hugo and replace it with a symbolic link to app/views/pages so that Rails’ folder is automatically updated whenever you trigger a regen with Hugo, i.e.

ln -s /home/rps/src/rs.io-rails/app/views/pages/ /home/rps/src/rs.io-next/public/hugo

You’ll also want to move and link the static assets from your $HUGO_APP/public folder into $RAILS_APP/public.

$ mv $HUGO_APP/public/css $RAILS_APP/public/
$ ln -s $RAILS_APP/public/css $HUGO_APP/public/css

Rinse & repeat for the other static folders.

You've read this far⁠—want more?

Subscribe and I'll e-mail you updates along with the ideas that I don't share anywhere else.

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.