ULID::Rails

This gem makes it possible to use ULID for DB in a Ruby on Rails app.

Installation

gem 'ulid-rails'

And then execute:

$ bundle

Or install it yourself as:

$ gem install ulid-rails

Usage

First, load up the gem with require 'ulid/rails'.

Migrations

Specify id: false to create_table and add id column as 16-byte binary type.

  def change
    create_table :users, id: false do |t|
      t.binary :id, limit: 16, primary_key: true
      # ...
    end
  end

MySQL note: You can also declare the id column as t.column :id, 'binary(16)' when using MySQL, given that the syntax in the example will generate a SQL that makes the id as VARBINARY(16) instead of BINARY(16).

Model Changes

Just add the below lines to your models.

class MyModel < ApplicationRecord
  include ULID::Rails
  ulid :id, auto_generate: true # The first argument is the ULID column name
end

Extract timestamp

Since ULID includes milli seconds precision timestamp, you don't need to store created_at. ulid-rails provides a helper method that defines timestamp method which extract timestamp from ULID column.

class MyModel < ApplicationRecord
  include ULID::Rails
  ulid :id, auto_generate: true # The first argument is the ULID column name

  # defines `created_at` method which extract timestamp value from id column.
  # This way you don't need physical `created_at` column.
  ulid_extract_timestamp :id, :created_at
end

created_at virtual column

MySQL 5.7 and higher Only (for now)

You can define a "virtual column" in MySQL DB that acts same as a physical column. Defining the virtual created_at is kind of comlicated so this gem provides a helper method for it.

A virtual column is useful if you want to add index on the timestamp column or want to execute raw SQL with created_at.

create_table :users, id: false do |t|
  t.binary :id, limit: 16, primary_key: true
  t.datetime :updated_at
  t.virtual_ulid_timestamp :created_at, :id
end

virtual_ulid_timestamp takes two arguments, the first one is the name of the column name (typically, created_at) and the second one is the ULID column that creation timestamp is extracted from.

Auto-generate ULID

If auto_generate is true, ULID is auto-generated before create by default. If not specified, the default is false.

class Model < ApplicationRecord
  ulid :id, auto_generate: true #  auto-generate enabled
  ulid :foreign_key # auto-generate disabled
end

Foreign Keys

You need to specicfy type option

    # MySQL
    create_table :admin_usees do |t|
      t.references :admin_user, foreign_key: true, type: "BINARY(16)"
    end

Many to many associations

Please note that this library doesn't work properly with has_and_belongs_to_many associations.

Our recommendation is to be explicit and instead use the has_many, through: join_class association. Notice that for it to work properly you must specify the has_many to the join class in the main classes of the association, and your join class must have belongs_to main classes defined as shown in the example below:

  class User < ActiveRecord::Base
    include ULID::Rails
    ulid :id, auto_generate: true

    has_many :user_articles
    has_many :articles, through: :user_articles
  end

  class UserArticle < ActiveRecord::Base
    include ULID::Rails
    ulid :id, auto_generate: true
    ulid :user_id
    ulid :article_id

    belongs_to :user
    belongs_to :article
  end

  class Article < ActiveRecord::Base
    include ULID::Rails
    ulid :id, auto_generate: true

    has_many :user_articles
  end

Development

Run tests

Just run the below command to test with all supported DB engines.

$ docker-compose run test

Or run with a specific ActiveRecord version

$ docker-compose run -e AR_VERSION=6.1 test

Or run tests locally, without docker-compose

$ AR_VERSION=6.1 bundle update && AR_VERSION=6.1 bundle exec rake test

License

The gem is available as open source under the terms of the MIT License.