Engineering April 26, 2018 8 min read

Bringing Rails, webpack, react and camaleon-cms together

Bringing Rails, webpack, react and camaleon-cms together

TL;DR A Github repo is available as a template to get started with Rails, camaleon-cms, webpack(er) and react. Stick around to see some tips and tricks on how we made everything run smoothly. And please do scroll down if you want to learn more about what brought us to it.

Four months ago we started a new project, the redesign of one of our client’s website. The design team threw us a good looking, clean and fresh layout. We already knew that ruby and rails was the way to go on the backend and on the frontend we were keen on using react since speed was critical and some elements of the team had already had good experiences with it. As a bonus, it was also a good chance for the other half of the team to get their hands on it.

New vs fast

Having a dynamic (i.e. editable) website is a must 90% of the times since the end user will typically not be the developer. A choice arose between creating a content manager from scratch or using something already available in the market, meaning: a CMS. This is a crucial point, as choosing the way to go can, and will, affect how you’ll end up developing new features at any given point of the website/app’s lifecycle.

As a developer, the “new cms made by the team” approach may seem great — Make things flexible! Make features as they should be from the beginning! And so on, and so forth, you’ll start thinking up a million reasons, all of which are perfectly valid! — But hold on! You’ll need to hold your excitement down so that it doesn’t lead you on an epic journey. Sure, it can be a valuable learning experience, as it always is, but it also involves a whole lot of work. Everything you want to have, you’ll have to build.

This balance was key on past projects. We had a couple of backoffices made from scratch, sure they were flexible and we now know exactly how to extend them, the code is all known which is great. But they required a lot of effort to be put into them, long hours and plenty of difficult decisions along the way. Added to this, a requirement of this particular project was to make it fast. Sure we wanted to make it right but we wanted to make it quick. Something’s gotta give…we really needed to consider an existing CMS first.

Unavoidable comparison

When it comes to Content Management Systems, everyone with a little bit of experience has a reference on the back of their head — Wordpress. This was the case for us too. Our client had a Wordpress habit already, and they loved it. Since they had been using it for some years, we didn’t want to disrupt that and force them to adapt to a completely different user experience.

In Ruby though, unlike PHP, there’s no established reference. It was time to choose and we needed to search for a cms…fun fun fun! As the Tigger would say.

We didn’t have not had any good experiences with Ruby CMS’s, ever! Do we really want to do this?

A quick search revealed some players were still around, Refinery, Locomotive. ComfortableMexicanSofa was a name that got some laughs at the time. And some new players looked to have a bit more polish to them, like Spina, but that one seemed like it was not yet stable enough and could end up making us invest a lot of time into it.

At last we chose camaleon-cms. At the time, it still needed (and still does) some refinement. But overall the code is great and UX looked similar to Wordpress which was a huge bonus. Researching a little bit about camaleon-cms revealed that a lot of it was made by just one person. Impressive to say the least, but a little worrisome at the same time. You would hope to see a supportive community to an open product like this. Then again, there isn’t a cms in ruby with much traction, else we wouldn’t be having all this trouble just to choose a cms, now would we? Camaleon it is!

Putting things together

These are the rough steps to reproduce a setup made with Rails, camaleon-cms, react and webpacker.

If you don’t need a cms and just want to add webpack to rails, my colleague Mario Cardoso published a great guideline on the topic some time ago.

Want to use ES6 with Rails right now ? Webpack to the rescueToday I’m going to talk about setting up a Rails application so you can write complex JavaScript features using…revs.runtime-revolution.com

We won’t focus on how to make react components, I’ll assume you already know and/or are not here because of that, but instead on how to gather all these pieces together. Again, it’s good advice to follow these steps with the repo as reference.

At the time of writing, these are the versions used.

  • node stable 8.10
  • yarn 1.5.1
  • rails 5.1.5
  • ruby 2.4.2
  • webpacker 3.4.1
  • camaleon 2.4.5.1

1. Setup

Be warned, this guide contains an opinionated approach, as this next command will show.

Code
rails new camaleon-webpack-react --skip-coffee --skip-turbolinks --webpack=react

To kick-off we’ll get rid of coffee (not a fan, sorry) and turbolinks. We opted to have only webpacker compiling on the frontend and maintained sprockets on the backend as it was used by camaleon-cms. Let’s add camaleon-cms to end this bootstrapping phase.

Edit the Gemfile of our recent application and add the following lines:

Code
gem 'camaleon_cms', '~> 2.4.5.1'
gem 'draper', '~> 3'

The version on the camaleon_cms gem is more important than it may seem, as previous version can have compatibility issues with Rails 5. One more thing, for camaleon to run well on Rails 5 we should also add the drapper gem. Camaleon relies heavily on decorators.

With that, let’s actually apply these changes to our app by running the following

Code
bundle
rails generate camaleon_cms:install
rake camaleon_cms:generate_migrations
rake db:migrate

These lines required no explanation I’m sure. By now we have a running version of our app with backoffice. If you already ran into trouble when “bundling”, go ahead and remove the Gemfile.lock and bundle again. Then, run rails s in your terminal and check out what has been made.

Pretty cool right? 😎 In this step we have all main pieces brought together.

2. Themes, Assets and Views (this is a long one)

We decided to go with webpacker compiling our frontend assets and continue with sprockets for the backend. Let’s make that happen. We already see assets on too many folders.

  • app/assets
  • app/apps/themes/<each-theme-name>/assets
  • app/javascript

It’s ok to leave each theme folder around for a while. They carry examples on how to build layouts, but for the sake of organisation we’ll create a new theme and discard the rest.

Code
rails g camaleon_cms:theme runtime-revolution

I chose runtime-revolution as the theme’s name. Now we can delete all these folders. I’ll tell you why in a minute.

Code
rm -rf app/apps/themes/camaleon_first/
rm -rf app/apps/themes/default/
rm -rf app/apps/themes/new/
rm -rf app/apps/themes/runtime-revolution/assets/
rm -rf app/assets

We can now specify that all views will go only to the theme’s folder. What about assets? We’ll rename the javascript folder to assets and use that. Additionally we’ll create the structure described below for all kinds of assets for our future conventions. And also add a working example while we’re at it. Poke around the github repo to see each file in detail, although it is not important for this explanation.

Code
app
  apps
  assets
    fonts
    images
    packs
      components
        hello
          index.jsx
          style.scss
      entries-js
        homepage.js
      entries-style
        homepage.scss
      shared-js
      shared-style

We’ll have to change some configurations on webpacker so that the app compiles again.

Code
# app/config/webpacker.ymldefault: &default
  source_path: app/assets
  source_entry_path: packs/entries-js
  public_output_path: assets
  cache_path: tmp/cache/webpacker

Think of entries-js as entry points or pages. Each page should correspond to an entry point and you’ll only load what’s necessary in each page. So now in the theme’s layout index.html.erb this will be my initial code.

Code
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title><%= current_site.the_title %></title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1" /><%= raw the_head %>
    <%= content_for :head %>
  </head>
<body>
  <%= yield %>
</body>
</html>

The content_for sectionwill be used to insert each page’s entry js file. Let’s change the index.html.erb, a.k.a. our homepage.

Code
<% content_for :head do %>
  <%= javascript_pack_tag 'homepage' %>
<% end %><h1>Homepage</h1>
<%= react_component('Hello') %>

That’s great, but now our app doesn’t run! Next step will solve that.

3. webpacker-react

Once you begin building react components you may want to render them on a rails view as well as on the js side. For that we’ll use the webpacker-react gem. This is why we had <%= react_component('Hello') %> in the previous step.

Add this line to your Gemfile followed by a bundlecommand.

Code
gem ‘webpacker-react’, “~> 0.3.2”

After that, add the npm package using yarn

Code
yarn add webpacker-react

Done! So what’s next?

4. Speeding up things

Although this should already be enough to get started and build things. This is next step is a cool nice to have.

Let’s say you have a component called Hello and you have both jsx and scss files on the same folder and you want to import the styles file into the jsx. For that we need to add a little something to our environment.js in the webpack configurations.

Code
environment.loaders.prepend('sass', {
  test: /\.(css|scss|sass)$/,
  use: [{ 
    loader: 'style-loader'
  }, {
    loader: 'css-loader'
  }, {
    loader: 'sass-loader',
  }]
})

With this addition, every time we run bin/webpack-dev-server all assets are re-compiled. It would be nice to have a cache of some sort that could avoid building files not changed…meaning more speed.

Let’s try adding both of these modules.

Code
yarn add hard-source-webpack-plugin
yarn add fast-sass-loader

Leaving us with the following environment.js modifications

Code
const { environment } = require('@rails/webpacker')var HardSourceWebpackPlugin = require('hard-source-webpack-plugin');environment.plugins.append('HardSourceWebpackPlugin', new HardSourceWebpackPlugin({}))environment.loaders.prepend('sass', {
  test: /\.(css|scss|sass)$/,
  use: [{ 
    loader: 'style-loader'
  }, {
    loader: 'css-loader'
  }, {
    loader: 'sass-loader',
  }]
})const SASSLoader = environment.loaders.get('sass').use.find(el => el.loader === 'sass-loader')
SASSLoader.loader = 'fast-sass-loader'
SASSLoader.options = { sourceMap: true }module.exports = environment

Done

This is not verbatim the structure we followed on our latest project, I confess. But it could be if we were to make things all over again. This is always a WIP.

Anyway, this is it! From here you can go multiple ways to get things working with camaleon-cms. For example, if you want to override their default behaviour I would advise to create a plugin for that and use hooks as much as you can.

Contribute

It goes without saying that open source has it’s pros and cons. I’m a fan of sharing and camaleon-cms came as a great tool to avoid making big, time consuming developments.

Sometimes, changes we thought were necessary to the cms we either had to build or change on our own, but that is to be expected. If, in any case, you think those changes are likely to happen again in other projects, consider contributing by submitting a pull request to the project or opening a simple ticket.

Who knows? Maybe camaleon-cms could become the ruby “wordpress” and you contributed to that!

Let me know of any questions or comments below.

I work at Runtime Revolution, where everyone has something to learn and teach. Transform your ideas into scalable products with our help.

Runtime RevolutionWe are Rails, mobile and product development experts. We can build your product or work with you on your project.www.runtime-revolution.com