Automatic layout for VX Network with D3.js

I’ve lately used vx visualization components to implement different data visualizations. It provides small pieces you combine and customize to build your own custom visualizations. I’ve found it quite comprehensible and deeply customizable.

vx includes a network graph component for showing graph data which I needed for a project. Unfortunately it doesn’t automatically calculate the positions for the graph nodes, so I decided to use the D3.js force simulation to calculate the X and Y coordinates for the nodes.

Screenshot of vx network graph

React component’s componentDidMount callback sets up d3 forceSimulation which takes care of setting the x and y properties for the passed nodes.

The full code for a simple network graph with automatic layout is below. You can also view how it looks and play around with it in CodeSandbox.

import React from "react";
import ReactDOM from "react-dom";

import { Graph } from "@vx/network";
import {
  forceCenter,
  forceLink,
  forceManyBody,
  forceSimulation
} from "d3-force";

// The node rendered by the graph
class NetworkNode extends React.Component {
  render() {
    return <circle r={10} fill={"#9280FF"} />;
  }
}

class Network extends React.Component {
  constructor(props) {
    super(props);

    const links = props.network.links;
    const nodes = props.network.nodes;

    this.state = {
      data: {
        nodes,
        links
      }
    };
  }

  // Update force if the width or height of the graph changes
  componentDidUpdate(newProps) {
    if (
      newProps.width !== this.props.width ||
      newProps.height !== this.props.height
    ) {
      this.force = this.force
        .force("center", forceCenter(
          newProps.width / 2,
          newProps.height / 2
        ))
        .restart();
    }
  }

  // Setup D3 force
  componentDidMount() {
    this.force = forceSimulation(this.state.data.nodes)
      .force(
        "link",
        forceLink()
          .id(function(d) {
            return d.id;
          })
          .links(this.state.data.links)
      )
      .force("charge", forceManyBody().strength(-500))
      .force(
        "center",
        forceCenter(this.props.width / 2, this.props.height / 2)
      );

    // Force-update the component on each force tick
    this.force.on("tick", () => this.forceUpdate());
  }

  render() {
    if (!this.force) {
      return null;
    }

    return (
      <div style={{ width: "100%", height: "100%" }}>
        <svg width={this.props.width} height={this.props.height}>
          <rect
            width={this.props.width}
            height={this.props.height}
            fill="#f9fcff"
          />
          <Graph graph={this.state.data} nodeComponent={NetworkNode} />
        </svg>
      </div>
    );
  }
}

function App() {
  const nodes = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 5 }];
  const links = [
    { source: 1, target: 2 },
    { source: 1, target: 3 },
    { source: 1, target: 4 },
    { source: 2, target: 4 },
    { source: 3, target: 4 },
    { source: 4, target: 5 }
  ];
  return (
    <div className="App">
      <Network
        width={400}
        height={400}
        network={{
          nodes: nodes,
          links: links
        }}
      />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Comments

How to set Bundler to fetch Github gems using HTTPS

Fetching Github gems using HTTPS instead of git protocol is going to be the new default when Bundler 2.0 is released. Here are the instructions on how to do it locally and on Heroku and Travis CI before Bundler 2.0 is released.

Configure Bundler to use HTTPS for GitHub sources:

bundle config --global github.https true

And on Heroku (note the double underscore):

heroku config:set BUNDLE_GITHUB__HTTPS=true

And on Travis (put it to your .travis.yml file):

before_install:
  - "bundle config --global github.https true"

Comments

Year in books 2015 & 2016

Last year I missed the posting of the yearly reading update so this post covers both 2015 and 2016.

I managed to hit my reading goals and they were also good reading years in quality.

In 2015 I read 82 books (24 of them comics) and in 2016 read 65 books (20 of them comics). In 2016 I also started doing lot more re-reading of previously read books or parts of them.

Books with 5 stars

I’m not a literary critic and don’t know which books are good or bad so I give my stars according to my own feeling on how much I got out of the book and how big of an impact I estimate it having on my life.

Here’s the list:


You can view all the books I read in my Goodreads profile for 2015 and for 2016.

Check out also previous Year in books posts for 2013 and 2014.

You can follow the books I read on Goodreads. It gets updated when I get reading done. I’m also interested in following people with similar taste.

Comments

Year in books 2014

I enjoyed a bunch of books in 2014 (46 in total). My selection process for picking books to read is rather random but this year I could lift a few themes. I feature my favorite books of the year in this post.

Bird by Bird: Some Instructions on Writing and Life by Anne Lamott

Bird by Bird

This is a book about writing and writer’s life. Even though I don’t write that much I could find plenty of good advice. Encouraging and inspiring (even though some people might find the honesty off-putting) read that also reminded me of the importance of the shitty first draft.

The Obstacle Is the Way: The Timeless Art of Turning Trials into Triumph by Ryan Holiday

The Obstacle Is the Way

Meditations by Marcus Aurelius

Meditations

I’ve been interested about Stoicism and read two books related to it. Great advice for the rough times and one-liners to spice up your day. I already bought couple other Stoic books so will read more during 2015.

Foundation and Empire & Second Foundation by Isaac Asimov

Foundation and Empire

Second Foundation

On the fiction front I read the second and third installment of the science fiction series Foundation. I enjoy the dry writing style of Asimov. The books are low in action and very dialogue heavy so not for everyone. I love the plots and uncovering the mysteries of psychohistory. I will continue reading the series in 2015.

World War Z by Max Brooks

World War Z

Another fiction pick is World War Z. It is a fictional collection of stories about lives of people after the Zombie war. The stories encompass various nationalities in different life situations told as dialogues with the interviewer of the book. Stories blend together social, economic, religious, political, and environmental themes and there is a clear critique and commentary on the current state of the world.

After reading the book I returned back to the stories in my head and marvelled on all the view points the book included. Event if you don’t like zombies, I would recommend it. It’s sometimes categorized as horror but I wouldn’t label it as such.

Functional JavaScript: Introducing Functional Programming with Underscore.js by Michal Fogus

Functional JavaScript

I didn’t read that many technical books this year but from the ones I did, this was my favorite. I’ve being dabbling with Clojure and this book linked many of those functional programming ideas to JavaScript.

Waking Up: A Guide to Spirituality Without Religion by Sam Harris

Waking Up

Still the Mind by Alan Watts

Still the Mind

On the spirituality front these books were my favorites.

The Innovators: How a Group of Hackers, Geniuses, and Geeks Created the Digital Revolution by Walter Isaacson

The Innovators

This is the book I have talked about the most this year, so I guess that makes it my favorite. It tells the stories of people and teams that have invented the ideas and built the technologies we rely on in today’s digital world: programming, microprocessor, personal computer, software, internet and so on. It also observes what are the commonalities of the innovators and tries to lift themes from their stories. While reading the book I also read some of the papers written by the early pioneers. Reading papers written in the 60’s and 70’s you see how so many parts of the technology puzzle took decades to go from idea to wide usage.

I also found it interesting that when the first interactive computers were invented, the first user interface designers were psychologists. The teams building these interactive machines thought of it as a man-machine symbiosis so it only made sense to put engineers, the experts of the machine, and psychologists, the experts of the human, to the same team to work on the machines.

Here is a trailer of the book:


You can view all the books I reading in 2014 on my GoodReads profile. I wrote similar post also a year ago so you can see what I enjoyed in 2013.

My plan of writing notes or mini-reviews on the books I read failed miserably so I’m doing it properly this in 2015. See you in a year!

Comments

ActiveRecord migrations best practices

There are certain guidelines I follow when writing migrations using the ActiveRecord migrations library. The guidelines have formed during the years I’ve been working with Ruby on Rails.

Here is an example migration class that follows all the guidelines reviewed in this post:

class AddAreaToPlots < ActiveRecord::Migration
  class Plot < ActiveRecord::Base; end

  def up
    add_column :plots, :area, :float

    Plot.find_each do |plot|
      plot.area = plot.width * plot.height
      plot.save!
    end
  end

  def down
    remove_column :plots, :area
  end
end

Redefine ActiveRecord classes

If possible, ActiveRecord models should not be used in migrations. Migrations should be simple and failproof transformations of database schema and data. Using complex model objects that complicate the migration should be avoided.

If you have strong SQL skills, you can use it for data transformations. Well written SQL is often faster than using ActiveRecord. For schema changes I would use the ActiveRecord methods which are readable and give extra features provided by the framework (most notably the migration/rollback support talked later in this post).

If you want to use the familiar ActiveRecord API to do the data transformations, the ActiveRecord classes should be redefined. As shown in the example, the Plot class has been redefined inside the migration class. The redefinition overrides the model class from the application code and makes it a bare ActiveRecord class. This means it has no validations, associations or callbacks. If some of the callbacks should be called in the migration, those can be reimplemented in the redefined class. Same goes for methods in the model class.

Why go through this trouble? When the application changes, the models change with it. It’s possible that the models change in a way that break the migrations. Or the models can be renamed or removed which will also break the migrations.

It might feel weird but it just works, makes your migrations more robust, and protects you from trouble in the future.

Use find_each

When modifying records, Model.find_each should be used to fetch the records in batches. If Model.all is used on a big database table, ActiveRecord will instantiate objects for all rows in the table which will consume all the available memory and the process starts swapping memory like crazy slowing the migration.

By default find_each loads the records in batches of 1000 but that can be configured. Only drawback of find_each is that you can’t use order, offset or limit. This is because find_each orders the records by id and then uses offset and limit to control the loading of the batches. Other than these caveats, there are no drawbacks on using find_each. Filtering the records using where works just fine.

Don’t change the data using the change method

The change method for migration classes was introduced in Rails 3.1 to make the writing of migrations simpler. Using change and methods that support inversing themselves, you can write migrations like this:

class AddAreaToPlots < ActiveRecord::Migration def change add_column :plots, :area, :float end end

When the migration are run by ActiveRecord, it executes the “up” version of the command adding the area column. On rollback it runs the code and executes the “down” version of the command for all the methods that have it. In the case of adding a column, rollback would remove it.

If you modify your data within the change method, ActiveRecord will run that code on both migration and rollback which is probably not what is wanted. Here is an example:

class AddAreaToPlots < ActiveRecord::Migration
  class Plot < ActiveRecord::Base; end

  def change
    add_column :plots, :area, :float

    Plot.find_each do |plot|
      plot.area = plot.width * plot.height
      plot.save!
    end
  end
end

On rollback the migration would first remove the column and then try to populate the column which would of course fail due to the missing column breaking the rollback.


In addition to the following these guideline it doesn’t hurt to understand how the ActiveRecord migrations work. Read the official Active Record Migrations guide to get more information.

Do you have any best practices you follow to make your migrations better? I’d be interested hear about those in the comments.

Addition: Migrations should not contain seed data

Seed data refers to the initial data the database must have for the application to function correctly at all. Rails provides a [mechanism for populating the database with seed data] which should be used. Migrations are meant only to provide the structure of the database, not the contents.

If migrations are used for seed data, there is a chance that a database is created without the seed data. When the incorrect migrations get older and a new database is bootstrapped using rake db:schema:load it uses the db/schema.rb file to get the right structure, but the data from the migrations won’t be created in the database.

Thanks for Christian Hellsten for the addition!

Comments

More in the archive...