Upgrading to Webpack 2

This is a follow up to this post where we looked at setting up Webpack with Rails.

Honestly, upgrading was super easy, though we don’t have the most complicated of setups. I used the official migration guide and went through it step-by-step.

What got upgraded?

A quick look of what webpack-related dependencies were updated:

{
  "devDependencies": {
-    "babel-core": "^6.20.0",
+    "babel-core": "^6.23.1",
    "babel-jest": "^18.0.0",
-    "babel-loader": "^6.2.9",
-    "babel-polyfill": "^6.20.0",
-    "babel-preset-es2015": "^6.18.0",
-    "babel-preset-react": "^6.16.0",
-    "babel-preset-stage-2": "^6.18.0",
+    "babel-loader": "^6.3.2",
+    "babel-polyfill": "^6.23.0",
+    "babel-preset-es2015": "^6.22.0",
+    "babel-preset-react": "^6.23.0",
+    "babel-preset-stage-2": "^6.22.0",
    "css-loader": "^0.26.1",
-    "expose-loader": "^0.7.1",
+    "expose-loader": "^0.7.3",
    "jest": "^18.1.0",
    "script-loader": "^0.7.0",
    "style-loader": "^0.13.1",
-    "webpack": "^1.14.0"
+    "webpack": "^2.2.1"
  }
}

The new ‘module.rules’

In webpack.config.js the loaders option has been renamed to rules. Additionally, the options each loader object takes are slightly different. Two biggest changes here are one, loaders now need the -loader suffix both in config and in require statements. And two, loader chaining now uses an array and the use option, as opposed to the exclamation point.

module: {
-    loaders: components.concat([
+    rules: components.concat([
    {
      test: /\.(js|jsx)$/,
      include: __dirname + "/app/assets/javascripts/webpack",
-        loader: "babel",
+        loader: "babel-loader",
      query: {
-          presets: ["es2015", "react", "stage-2"]
+          presets: [["es2015", { "modules": false }], "react", "stage-2"]
      }
    },
    {
      test: /\.css$/,
-        loader: 'style-loader!css-loader'
+        use: ['style-loader', 'css-loader']
    }
  ])
}

Webpack handles ES2015, CommonJS, and AMD modules

You’ll notice the { "modules": false } in the babel options above. We now let webpack handle any module symbols, and tell babel not to parse them. You’ll need to adjust this in your config or .babelrc or package.json–wherever you have it.

Some defaults of the UglifyJsPlugin changed

This only applies to our webpack.production.config.js, and we just went ahead and turned back on those defaults by specifying like so:

-config.plugins.push(new webpack.optimize.UglifyJsPlugin())
+config.plugins.push(new webpack.optimize.UglifyJsPlugin({
+  warnings: true,
+  sourceMap: true
+}))

Fix require statements in entry file

Just like in our config file, we need to make sure to use the -loader suffix when requiring in our application_entry.js file:

-require('expose?React!react');
-require('expose?ReactDOM!react-dom');
+require('expose-loader?React!react');
+require('expose-loader?ReactDOM!react-dom');

Conclusion

And that was it! All in all, not too bad. Again, if you want to know how / why we set things up the way we did, you can see that post here. And, if you want to check the code out yourself, you can do that here.

How to Use React with Rails

This post covers setting up with webpack 1.14.0, however the implementation doesn’t change much for webpack 2. There’s a follow up post here that covers those changes, though I recommend reading this one first.

First Things First; What Not to Do

A simple search for “react + rails” will turn up the react-rails gem immediately. It’s a first party gem you can drop into your rails app and get going quickly. It does a couple things:

  1. It vendors React and a custom React UJS library for use with the Rails asset pipeline.

  2. Adds a sprockets processor for handling JSX and ES6.

  3. Adds a couple Rails helpers for rendering react components in our controllers and views.

With virtually 0 setup, you’re able to get started with React right away, and you can make awesome components like this:

class MyComponent extends React.Component
  render() {
    return (
      <p>stuffs</p>
    )
  }
}

And that works for a while, until you want to start writing Javascript like the rest of the world. Something like:

import { DopeLibrary } from 'some_awesome_npm_thing'

class MyComponent extends React.Component
  // ...
}

export default MyComponent;

Sssssshhhhhhhiiiiiiitttttt…how do I import npm libraries…? 🤔 Answer: you have to to use npm.

react-rails isn’t all bad, though. Remember that React UJS library I mentioned? Turns out that thing is pretty sweet. It lets us write views like this:

<div>
  <p>normal server rendered html stuffs</p>
</div>

<%= react_component 'MyComponent', props, html_options %>

This works by rendering a <div> with a bunch of data attributes that represents the component to mount and the props to pass to it. Then the React UJS library listens for the appropriate turbolinks events and $(document).ready equivalents, and uses ReactDOM to render our component at that time.

The upside to this is we can easily mix our normal Rails views with our React components. The downside, though, is React UJS expects React, ReactDOM, and any components rendered with react_component to be defined globally.

So What Should We Do?

So we know we need to use npm because there isn’t a gem for a good portion of libraries we may want to use. But how do we make that work with Rails?

We don’t want to ditch sprockets, or even gem bundled assets, completely. Rails ❤️ sprockets. The asset pipeline has great support for things like bootstrap, jquery, tubolinks, asset hashing–why throw all that away? So what we can do is use webpack to create a bundle of some of our assets, and pass that bundle to our asset pipeline.

As an example for how to do this, I built a version of the TodoMVC app using Rails, React, and Redux that you can see running here and the code here. We’ll use code samples from that application in what follows.

Get Your System Ready

Since we’re going to use node modules and webpack, we need to install the tool for that. I’m using yarn as opposed to npm, and it’s what I’d recommend. Anyway, installing node and yarn is pretty simple with homebrew:

brew install node
brew install yarn

And now we need to use yarn to install webpack:

yarn global add webpack@1.14.0

And depending on your homebrew setup, you may need to adjust your .bash_profile to add packages yarn installed globally to your path. To test if you have the problem run webpack -h, if you get an error about webpack not being installed, you need to add this to your .bas_profile:

# -------- YARN --------
# i installed this with homebrew, and now i gotta add where it installs global packages to my path
#
if hash yarn 2>/dev/null; then # check if there's a yarn command to run
  export PATH="$PATH:$(yarn global bin)"
fi

Lastly, add our development and application dependencies with yarn:

yarn add --dev babel-core babel-loader babel-polyfill babel-preset-es2015 \
               babel-preset-react babel-preset-stage-2 expose-loader script-loader

yarn add react react-dom react-redux redux whatwg-fetch

That first line is basically a handful of libraries webpack needs. The second line are libraries our application actually uses.

The Directory Structure

As a reference, here’s a look at what my assets/javascripts directory looks like:

Directory Structure

The biggest difference is we’ve added a webpack subfolder to put our javascript files we want processed by webpack and a special root_components folder for use with React UJS (more on that later). We also have an application_entry.js file for webpack, which we’ll look at later. And the bundle files webpack creates.

application.js

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require cable

//= require react_ujs
//= require application_bundle
//= require fetch_bundle

Pretty normal, but hey, what’s react_ujs still doing there?! We’re still going to use it! In fact, we still install the react-rails gem. This gives us access to the render_component helper, and a bundled version of the react_ujs library we can include in our application.js. But we’re not using it to include React.

webpack.config.js

Webpack takes it’s direction from a combination of a config file, and then one or more entry files that config file specifies. Let’s take our config file in pieces (you can see the whole thing here). First, we require the packages we need and define some helper methods at the top:

const webpack = require("webpack");
const fs = require("fs");

let camelize = function(string) {
  let almost = string.replace(/(\_\w)/g, matches => matches[1].toUpperCase())

  return almost[0].toUpperCase() + almost.slice(1);
};

let filename = (path) => (path.split("/").pop().split(".").slice(0, -1).join(""));

let allFilesIn = function(dir) {
  var results = [];

  fs.readdirSync(dir).forEach(function(file) {
      let path = dir + '/' + file;
      let stat = fs.statSync(path);

      if(stat && stat.isDirectory()) {
        results = results.concat(allFilesIn(path))
      }
      else {
        results.push(path);
      }
  });

  return results;
};

Because I’m a rails guy through and through, I prefer snakecase for files and variables, and camelcase for classes. So the camelize allows us to pass a string like 'todos_list' and get back 'TodosList'. filename takes a path and gets just the filename without the extension. And allFilesIn will get all the files in a folder, recursively. We’ll use these functions in the next section:

let components = allFilesIn(__dirname + "/app/assets/javascripts/webpack/root_components/").
  map((path) => {
    return { test: require.resolve(path), loader: `expose-loader?${camelize(filename(path))}` }
  });

This gets all the components in our root_components folder, and attaches a loader to them. In this case, the expose-loader.

Lastly, we’re ready to generate the config object webpack will use:

let config = {
  entry: {
    fetch: 'whatwg-fetch',
    application: __dirname + "/app/assets/javascripts/webpack/application_entry.js"
  },

  output: {
    path: __dirname + "/app/assets/javascripts",
    filename: "[name]_bundle.js"
  },

  module: {
    loaders: components.concat([
      {
        test: /\.(js|jsx)$/,
        include: __dirname + "/app/assets/javascripts/webpack",
        loader: "babel",
        query: {
          presets: ["es2015", "react", "stage-2"]
        }
      }
    ])
  }
};

module.exports = config;

We add two entry files: one for the fetch polyfill, copied directly from their documentation, and one for our application. We specify where we want our bundled files outputted with the output object. And then we define our loaders. This is just an array of objects that represent a loader. At a minimum they have a test property which we use to match files to process, and a loader property, which says which loader to use. So we take our components list and concatenate it with the babel loader. This will run against any of our .js files or .jsx files under the include path, or or assets/javsacripts/webpack folder. This allows us to write ES6 and JSX code and have it transpiled to Javascript the browser can understand.

application_entry.js

Last thing we need to do is define our entry file:

require('expose?React!react');
require('expose?ReactDOM!react-dom');

(
  (context) => { context.keys().map(context) }
)(require.context("./root_components", true, /\.(js|jsx)$/));

The first two lines require and globally expose React and ReactDOM. The last bit uses require.context to require all of our .js and .jsx files in /root_componets, which are exposed globally when they’re required because of the loader we setup in our config file.

Running it All

We can run webpack in development and watch modes with

webpack -d --watch --config webpack.config.js

This will re-generate our bundle files if we make changes to our Javascript files. We needn’t mess with any of the hot reloading features of webpack because Rails and sprockets will grab those changes in development when we refresh our page. In fact, aside from running the above webpack command, we needn’t change our normal Rails development workflow at all.

Conclusion

We’re now able to create React components like the rest of the Javascript world, for example:

import React from 'react'
import { connect } from 'react-redux'

import { setFilter, visibleTodos, destroyCompleted } from '../stores/todos_store.js'

let TodosFooter = ({ totalTodos, incompleteTodos, completedTodos, isSelected }) => (
  // jsx stuff here
)

TodosFooter = connect(
  (state, props) => {
    // do some map state to props stuff
  }
)(TodosFooter);

export default TodosFooter;

But perhaps the best part, we’re able to do all that without giving up the things we love about Rails. Or turning our Rails app into strictly an API. We get to use Rails in the ways we love to; mostly serving server-renderd HTML and punting to React if and when it makes sense to. We still get all the gem-ified assets we’ve grown accostomed to using.

And we get to use React as a first-class citizen, with access to the modern Javascript libraries are using, whether that be Redux or React extensions or testing libraries.

I know webpack is anything but simple, but hopefully that wasn’t too terrible. Again, you can see this setup running a TodoMVC implementation here, and the code here.

Reason #244 to Hate Javascript

Generate an RFC 4122 compliant UUID: Ruby

require 'securerandom' # at worst, if you're using Rails you won't have to write this
SecureRandom.uuid

Generate an RFC 4122 compliant UUID: Javascript. It’s not actually apart of the language itself or a standard lib equivalent. Instead, you need to decide on which of about a half dozen npm packages you want to use. Or you can read through this stupidly long Stack Overflow post.

Let’s say, you’re like me and you settle on broofa’s despite quickly reading through a few different comments explaining what some of the problems with that approach might be. His was by far the most readable. Anyway, you get:

const uuid() = function() {
  // stolen from: http://stackoverflow.com/a/2117523/1947079
  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
    var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r&0x3 | 0x8);
    return v.toString(16);
  });
}

This sucks, I don’t want to be responsible for generating UUIDs. Or making sure they adhere to the RFC. (Or even understanding it.) Or making sure they’re performant. Or make my team read that code. I want my language to handle stuff like that 😡

Reason #137 to Hate Javascript

Load all files in folder and sub-folders: Ruby

Dir.glob(File.join(Rails.root, 'folder/of_interest/', '*.rb')).each do |helper_file|
  require helper_file
end

Load all files in folder and sub-folders: Javascript

const webpack = require("webpack");
const fs = require("fs");

let camelize = function(string) {
  let almost = string.replace(/(\_\w)/g, matches => matches[1].toUpperCase())

  return almost[0].toUpperCase() + almost.slice(1);
};

let filename = (path) => (path.split("/").pop().split(".").slice(0, -1).join(""));

let allFilesIn = function(dir) {
  var results = [];

  fs.readdirSync(dir).forEach(function(file) {
      let path = dir + '/' + file;
      let stat = fs.statSync(path);

      if(stat && stat.isDirectory()) {
        results = results.concat(allFilesIn(path))
      }
      else {
        results.push(path);
      }
  });

  return results;
};

let components = allFilesIn(__dirname + "/folder/of_interest/").
  map((path) => {
    return { test: require.resolve(path), loader: `expose-loader?${camelize(filename(path))}` }
  });

let config = {
  entry: {
    application: __dirname + "/app/assets/javascripts/application_entry.js"
  },

  output: {
    path: __dirname + "/app/assets/javascripts"
  },

  module: {
    loaders: components.concat([
      {
        test: /\.(js|jsx)$/,
        include: __dirname + "/folder/of_interest/",
        loader: "babel",
        query: {
          presets: ["es2015", "react", "stage-2"]
        }
      }
    ])
  }
};

module.exports = config;

But wait, theres more–we still need to require in the entry file:

(
  (context) => { context.keys().map(context) }
)(require.context("./folder/of_interest", true, /\.(js|jsx)$/));

You might notice, we’re exposing modules in /folder/of_interest/ globally and that’s part of the complexity here. True. We’re doing that to work with the react_ujs library that’s a part of the react-rails gem.

What?! 😳 In my code? Yes! 😁

Emoji’s might be one of the coolest inventions to hit cell phones. Just behind the Internet. And texting itself. They’re capable of adding so much meaning, but probably most importantly, they’re fun to use.

And clearly, people tend to agree. Most people litter their texts with them. In fact, I can tell when my wife’s displeased with me just by the decreased frequency of smiley faces in her texts. Facebook recently added them. Hundreds of people took the time to write an entire book using nothing else. GitHub even took the time to make them a part of the software development process.

But you know where I haven’t seen them? Comments. And why not? I’d be willing to guess most people (certainly millennials and younger) use them in their text message conversations all. the. time. To convey that extra level of meaning. Because “😂😂😂” shows your amusement better than “lol”. And “👌” is quicker cooler than “ok”.

What are comments if not quick text messages to your future reader? By adding emojis to our comments we can convey not only the technical details of why we wrote the code we did, but also how we felt about the code when we wrote it. And I’d argue that can be valuable to your future reader, be that yourself or someone else.

Not to mention, it can make the drudgery of documenting code more bearable. You know…slightly…

Let’s look at some examples from codebases I’m juggling at work right now:

Utterly Exasperated

class Compass::History::Loan < Compass::Connection
  self.table_name = "KWHLOAN"

  # i'm not _entirely_ sure of the structure here, but it looks to me like a
  # KWHLOAN can be associated with exactly one KWHBASE row when both the HSEQ
  # and HDATE columns are equal. in other words:
  #
  # SELECT * FROM KWHBASE INNER JOIN KWHLOAN ON KWHBASE.HDATE = KWHLOAN.HDATW AND
  #                                             KWHBASE.HSEQ = KWHLOAN.HSEQ;
  #
  # we're replicating that as a has_one association by making the HDATE column
  # the primary key and adding conditions for the HSEQ portion. that's a
  # little misleading because technically there is no primary/foreign key
  # associating these two tables 😪
  has_one :past_payment, ->(loan) {
    where(hseq: loan.hseq)
  }, class_name: "Compass::History::Base", foreign_key: :hdate,
                                           primary_key: :hdate
end

I think adding this “exasperated sigh” emoji helps draw attention to our comment. It adds my emotions at the time of writing. I was exasperated with this code. I felt like my hands were tied. I needed an association the legacy database didn’t really support, and so we’re stuck doing something kinda gross. The words convey the technical details of why. The emoji? That cues my future reader into how I felt about this code. All with a simple keystroke.

An Inside Joke, Just for Fun

def setup_organization # for use in testing when we need groups
  groups = {
    'married' => ['zoey@serenity.com', 'wash@serenity.com'],
    'tough' => ['zoey@serenity.com', 'mal@serenity.com', 'jayne@serenity.com'],
    'large_and_semi_muscular' => ['wash@serenity.com'] # 😂
  }

  # ...do some FactoryGirl shenanigans...
end

Perhaps, not the most necessary usage. But sometimes it’s important to have fun with your code and your future reader. Especially, in test code, which isn’t always the most fun to read or write. At least now the reader knows there’s an inside joke here, and, if curious enough, could look it up.

Who Designed This?!? 😡

(See what I did there? 😂 And again.)

def should_alert?
  # luckily, the type and user.alerts_type arguments don't match 😑
  !vulnerabilities.empty? &&
    (user.alerts_type == "all" || case type
                                    when "new"
                                      user.alerts_type == "new"
                                    when "updated"
                                      user.alerts_type == "updates"
                                  end)
end

This defines a quick helper method that lets us know if we should alert a user based on his alert settings and the type of alert we’re processing. But, of course, type and user.alerts_type don’t use the same verbiage so we have this complicated case checking. Me being me, I added some sarcastic text explaining this, and my personal “wtf?!” face emoji.

Normal prose has both voice and style. I think style comes pretty naturally when writing code. Whether good or bad, most developers have style in their code. How they indent things, when they comment, what constructs they use to do X vs Y, those are all part of a coder’s style. But I don’t think voice comes through as evidently. Emoji’s add the voice. They make your code more personal, and ultimately more enjoyable to read.

If you’re sold, here is the emoji plugin I use with Sublime Text 3 which makes it super easy to search for and insert emojis. Here’s hoping we see more 😁’s and 💯’s than 😑’s in our comments!