Recently, I have rediscovered Ruby on Rails. In my opinion, it is still the best framework if you want to shell out a simple, CRUD based web application in a short time. Beyond that, is of course capable of much more but this is what it excels at. You don’t need to worry too much about basic security features, e.g. cross-site scripting, and you get fully functional web pages for your models out of the box.

On top, you get a REST API for free, which is handy when you want to replace the simple HTML front-end with some more sophisticated JavaScript-based, interactive UI. Or Elm-based, for that matter.

Witnessing meetings

The whole train of thought came up because I am not really good at routine tasks. At the same time, meetings with people to work on certain topics are a large part of my work.

One of the problems with meetings is that, in the best case, everybody comes out of the meeting with a good feeling of understanding and a clear picture of what needs to be done. But then, thirty minutes later, if you ask two people independently what was decided, they will already tell you different stories. That is just how the human brain works. We build models in our brain and they are not the same for every person. When we test the models of two different people with questions, then they will result in different answers. This effect is well researched and every police officer interrogating witnesses will confirm it.

But meetings don’t always go that well. I have experienced meetings where not everybody was sure about the purpose of the meeting, the authority of the group meeting for the purpose, or the roles of the various participants. These meetings tend to feel a bit forced as the agenda (if there is an agenda) is worked through and there is a lot of silence in between, which is usually taken as agreement with whatever is being presented.

And then there is the worst scenario, where there are tensions of various kinds create conflict that is hard to resolve. Often, the actual tensions are not explored, the reasons not understood, and so it is difficult to address them. I’m not saying that understanding the reasons will make the tensions magically go away–but not understanding them will certainly not help.

Holocracy

There is a book out there (there is always a book), that gives a formal framework for structuring an organization into circles and providing guidance like templates and rule books. It is called “Holacracy” after the method the authors are proposing. I disagree with the name because there is no “a” in Greek ὅλος, hólos “whole”. But that is a discussion for another day. Just note that I use a spelling I personally find more appropriate, but if you want to buy the book or visit the web site, mind the different spelling.

Holocracy proposes to structure an organization into a hierarchical structure of “circles”, i.e. groups of people, and a “constitution” that they follow and that provides basic information like the purpose of the group, its authority, the roles in the group, and its inner workings and interaction with other groups. It specifically addresses the problems I have mentioned in the previous section: The need for creating agendas and keeping minutes, the need to define purpose, the need to address tensions. For me as a programmer, it is quite appealing because Holocracy lends itself well to implementation in a tool. It is almost like an algorithm for organizations. (And that may well be its greatest flaw, but that is another story that will be told another time.)

The Holocracy advocates claim of course that it is not the tool that makes the method successful but really it is a change of culture and a different mindset. And yes, that is most likely true. As the saying goes: A fool with a tool is still a fool, just a more dangerous one. But I am a programmer and I am bad at routine jobs.

There are already implementations of Holocracy tools out there but in the end, it is not that hard. A simple web application can capture the mechanical aspects and leave your head free to do the mind shift. And what programmer would I be if I did not write my own implementation of it?

Finally, Rails

And this is where Ruby on Rails enters the picture. Defining a relatively simple domain model (circles, people, agendas, decisions, etc.) with some simple algorithms operating on them, that is something that can very easily done with Rails. For now, I will not at all care about styling the web pages and focus on functionality. Chances are that the front-end will be replaced by some neat browser-side code anyway. It is more important to get the basic structure in place.

Implementing the tool will be too much for a single article, so this might turn into another series of Saturday projects. To give you a flavor though, let’s start by creating an implementation for Circles, People, and the Roles. I am assuming that Rails is already installed, which I suggest you do in a *nix environment. If you are on Windows, the Windows Subsystem for Linux will do nicely, even though it can be a bit slow. I start, as in so many other frameworks, by creating a new Rails application in a convenient directory.

$ rails new holo
create
create  README.md
create  Rakefile
create  .ruby-version
create  config.ru
create  .gitignore
create  Gemfile
   run  git init from "."
...
├─ utils-merge@1.0.1
├─ wbuf@1.7.3
├─ webpack-dev-middleware@3.7.3
├─ webpack-dev-server@3.11.1
├─ websocket-driver@0.7.4
├─ websocket-extensions@0.1.4
└─ ws@6.2.1
Done in 73.22s.
Webpacker successfully installed 🎉 🍰
$ cd holo

Creating the scaffolds

After grinding away for some time, a subdirectory for the app is created and populated with quite a massive amount of stuff. (These are technical terms.) I change into the new subdirectory and create scaffolds for my three entities:

$ bin/rails generate scaffold Person name:string email:string
Running via Spring preloader in process 357
      invoke  active_record
      create    db/migrate/20210108141507_create_people.rb
      create    app/models/person.rb
      invoke    test_unit
...
create      app/assets/stylesheets/people.scss
invoke  scss
create    app/assets/stylesheets/scaffolds.scss

A scaffold is a database table, a model class in rails, a controller class, a set of views for the model, and a set of test classes for all of those things. Also, the routes (or URLs) through which these things can be accessed are automatically created, as well as a full REST API for the common operations. Neat? Let me do the other two.

$ bin/rails generate scaffold Circle name:string purpose:text
Running via Spring preloader in process 391
      invoke  active_record
      create    db/migrate/20210108141830_create_circles.rb
      create    app/models/circle.rb
...

And finally:

$ bin/rails generate scaffold Role name:string person:references circle:references
Running via Spring preloader in process 426
      invoke  active_record
      create    db/migrate/20210108141933_create_roles.rb
...

Note how I simply specified that a role will reference a person and a circle. Rails will pick that up and do the right thing based on matching the names of things.

Creating the database schema

Now the code is in place. Next, I will look at the database schema creation, which is done in code, as it should be. For this, Rails creates migration classes. These can be adjusted as necessary and then run against a development database, which is of course already set up by the framework. Why would I need to do that by hand, after all?

This is the automatically created Person schema migration:

class CreatePeople < ActiveRecord::Migration[6.0]
  def change
    create_table :people do |t|
      t.string :name
      t.string :email

      t.timestamps
    end
  end
end

It creates a table with two string columns. Ignore the timestamps for now; this is part of the optimistic transaction strategy that Rails employs. But we might want to ensure that both name and email are given for every person, and that the email is unique as it identifies the user. I will add this to the migration before I execute it.

class CreatePeople < ActiveRecord::Migration[6.0]
  def change
    create_table :people do |t|
      t.string :name, null: false
      t.string :email, null: false
      t.index :email, unique: true

      t.timestamps
    end
  end
end

Pretty self-explanatory. I will also have a look at the Role schema migration. Here is the generated code:

class CreateRoles < ActiveRecord::Migration[6.0]
  def change
    create_table :roles do |t|
      t.string :name
      t.references :person, null: false, foreign_key: true
      t.references :circle, null: false, foreign_key: true

      t.timestamps
    end
  end
end

This looks pretty much perfect already. I will just add another non-null constraint and a default value for the name.

class CreateRoles < ActiveRecord::Migration[6.0]
  def change
    create_table :roles do |t|
      t.string :name, null: false, default: "Unnamed Circle"
      t.references :person, null: false, foreign_key: true
      t.references :circle, null: false, foreign_key: true

      t.timestamps
    end
  end
end

Now, I am all set to run the migrations:

$ bin/rails db:migrate
== 20210108141507 CreatePeople: migrating =====================================
-- create_table(:people)
   -> 0.0158s
== 20210108141507 CreatePeople: migrated (0.0159s) ============================

== 20210108141830 CreateCircles: migrating ====================================
-- create_table(:circles)
   -> 0.0115s
== 20210108141830 CreateCircles: migrated (0.0117s) ===========================

== 20210108141933 CreateRoles: migrating ======================================
-- create_table(:roles)
   -> 0.0239s
== 20210108141933 CreateRoles: migrated (0.0241s) =============================

All set. For fun, let’s have a look at the actual database. For development, I am using SQLite3, which of course will be replaced by something more robust like PostgreSQL or MariaDB for production. Fortunately, Rails abstracts away all the idiosyncrasies of the underlying databases unless I specifically want to use them.

$ bin/rails dbconsole
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .tables
ar_internal_metadata  people                schema_migrations
circles               roles
sqlite> .schema people
CREATE TABLE IF NOT EXISTS "people" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar NOT NULL, "email" varchar NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
CREATE UNIQUE INDEX "index_people_on_email" ON "people" ("email");
sqlite> .schema roles
CREATE TABLE IF NOT EXISTS "roles" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar DEFAULT 'Unnamed Circle' NOT NULL, "person_id" integer NOT NULL, "circle_id" integer NOT NULL, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, CONSTRAINT "fk_rails_7a19f4fbb4"
FOREIGN KEY ("person_id")
  REFERENCES "people" ("id")
, CONSTRAINT "fk_rails_3cae5e4d90"
FOREIGN KEY ("circle_id")
  REFERENCES "circles" ("id")
);
CREATE INDEX "index_roles_on_person_id" ON "roles" ("person_id");
CREATE INDEX "index_roles_on_circle_id" ON "roles" ("circle_id");
sqlite> .schema circles
CREATE TABLE IF NOT EXISTS "circles" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "name" varchar, "purpose" text, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);

If you are SQL inclined, you can wade through this and confirm that Rails actually did the right thing. Obviously, I have left away quite a lot of things, most notably user authentication. I can add this later as there are ready-made solutions, gems in Ruby lingo, for it. Next, I will have a look at models.

Looking at models

A model is a Ruby representation of a domain concept. Rails has automatically create three of them, as instructed, and it will be able to store them in and retrieve them from the database. However, I want to do a little more. Instead of working with IDs and foreign keys, I want Rails to populate collections automatically and efficiently where entites refer to each other. To do that, I will amend the models.

The basic model is empty because it inherits all of its behavior from a framework superclass:

class Person < ApplicationRecord
end

So I will help Rails by adding some information about its place in the world:

class Person < ApplicationRecord
  has_many :roles
  has_many :circles, through: :roles

  validates :name, presence: true
  validates :email, presence: true, uniqueness: true
end

Again, I find this pretty self-explanatory. The through phrase tells the Person model that its relationship to Circles is many-to-many and Roles are actually used as the intermediary, the join table in SQL lingo. The presence phrase checks that the name or email are neither null nor a zero length string. Convenient.

Let’s have a look at the other two model classes:

class Circle < ApplicationRecord
  has_many :roles
  has_many :people, through: :roles

  validates :name, presence: true
end

The Role class is already prepopulated a little more, because when we created the scaffold, we told it already to reference the Circles and People. This is the generated code:

class Role < ApplicationRecord
  belongs_to :person
  belongs_to :circle
end

I only need to add some validation to make the user interface aware.

class Role < ApplicationRecord
  belongs_to :person
  belongs_to :circle

  validates :name, presence: true
  validates :person, presence: true
  validates :circle, presence: true
end

Done.

Trying it out

Now let’s see what we have done. Rails comes with a simple server for development purposes.

Before we try it out, let me save you a little googling. If you are on a proper *nix machine, skip to the next paragraph. But if you are in Windows and using the Windows Subsystem for Linux to follow along, please change the file holo/config/environments/development.rb as follows to circumvent some quirks of this platform:

Rails.application.configure do
...
  # Add this line anywhere:
  config.web_console.permissions = ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8']
...
  # Find this line and change it as follows:
  # config.file_watcher = ActiveSupport::EventedFileUpdateChecker
  config.file_watcher = ActiveSupport::FileUpdateChecker
...

This will allow you to use the web console, a useful debugging tool, in your browser, and it will ensure that the Rails development server can pick up changes in your source code and immediately serve them to you despite the ~broken~ special Windows file system.

Now is the big time. I start the development server like this:

$ bin/rails s -b 0.0.0.0
=> Booting Puma
=> Rails 6.0.3.4 application starting in development
=> Run `rails server --help` for more startup options
Puma starting in single mode...
* Version 4.3.7 (ruby 2.7.2-p137), codename: Mysterious Traveller
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000

And there we go. I don’t want to see the Hello World page Rails automatically created so I point my browser to the appropriate IP address and request the /people page.

The initial people page

Awesome. I like the no nonsense style. More seriously, of course I can use all kinds of CSS and whatnot to make it suit my taste but this is about function and not style. To prove it, let’s use the REST API instead.

No people in JSON

This is what no people look like in JSON. And we had to do nothing additional to get this API. Now go build your fancy Angular front-end on top of that. But fist, let’s add some people. I’ll go back to the web page and click “Add Person”. Let’s look at the web page.

New person

This is not ideal. The web page uses a standard text field for the email field. I want to harness the power of HTML5 and tell the browser that this is a text field. I will edit the form source code for this. Here is the generated code:

<%= form_with(model: person, local: true) do |form| %>
  <% if person.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(person.errors.count, "error") %> prohibited this person from being saved:</h2>

      <ul>
        <% person.errors.full_messages.each do |message| %>
          <li><%= message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :name %>
    <%= form.text_field :name %>
  </div>

  <div class="field">
    <%= form.label :email %>
    <%= form.text_field :email %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

It is the weird mix of HTML and Ruby that you will have seen in other templating engines before. Rails provided s lot of helpers to do the usual things. So indeed, I will use an email_field instead of the text_field:

<%= form_with(model: person, local: true) do |form| %>
...
  <div class="field">
    <%= form.label :email %>
    <%= form.email_field :email %>
  </div>
...
<% end %>

And indeed, now the browser shows this:

New person with email field

Let’s add someone nameless.

They are nobody

We are not happy. I will add a proper name.

They are nobody

And let’s go back to the people overview.

They are nobody

Ha! I have added you as well. Sneaky, huh?

Summary

Now this may not seem like much but really it is. Keep in mind that all of this happened in only a few minutes, and I have already create the same functionality for circles and roles.

For now, the article is already quite long, so I will continue another day.