Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@bjeanes
Last active November 4, 2018 02:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bjeanes/5b4f699655e47cd4e42928ab8e9a7fff to your computer and use it in GitHub Desktop.
Save bjeanes/5b4f699655e47cd4e42928ab8e9a7fff to your computer and use it in GitHub Desktop.
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem "rails"
gem "pg"
gem 'ulid', github: 'bjeanes/ulid-ruby'
gem 'pry-byebug'
gem 'uuidtools'
end
require "active_record"
require "action_controller/railtie"
`createdb uuid-as-ulid`
ActiveRecord::Base.establish_connection(adapter: "postgresql", database: "uuid-as-ulid")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
enable_extension 'uuid-ossp'
enable_extension 'pgcrypto'
create_table :books, id: :uuid, force: true do |t|
t.string :name
t.timestamps
end
create_table :categories, id: :uuid, force: true do |t|
t.string :name
t.timestamps
end
create_table :categorizations, id: :uuid, force: true do |t|
t.references :book, foreign_key: true, type: :uuid, null: false
t.references :category, foreign_key: true, type: :uuid, null: false
t.boolean :primary, default: false, null: false
t.timestamps
end
end
class UlidType < ActiveRecord::Type::Value
def cast(value)
Ulid.parse(value)
end
def serialize(value)
Ulid.parse(value).uuid
end
class Ulid
attr_reader :bytes
def initialize(bytes)
@bytes = bytes
end
def uuid
UUIDTools::UUID.parse_raw(bytes).to_s
end
def ulid
ULID.decode(bytes)
end
def self.parse(value)
case value
when nil
nil
when /#{UUIDTools::UUID_REGEXP}/i
new UUIDTools::UUID.parse(value).raw
when /#{ULID::Generator::REGEXP}/i
new ULID.decode(value)
when UlidType::Ulid
value
when String
if value.length == 16 # bytes / 128-bits
new UUIDTools::UUID.parse_raw(value).raw
else
raise "Unknown value #{value}"
end
else
raise "Unknown value #{value}"
end
end
def to_s
ULID.encode(bytes)
end
def inspect
to_s.inspect
end
def ==(other)
case other
when self.class
bytes == other.bytes
when String
to_s == other
else
false
end
end
end
end
class Book < ActiveRecord::Base
attribute :id, UlidType.new
has_many :categorizations
has_many :categories, through: :categorizations
end
class Category < ActiveRecord::Base
attribute :id, UlidType.new
has_many :categorizations
has_many :books, through: :categorizations
def self.primaries
Category.joins(:categorizations).merge(Categorization.primaries)
end
end
class Categorization < ActiveRecord::Base
attribute :id, UlidType.new
belongs_to :book
belongs_to :category
def self.primaries
where(primary: true)
end
end
class TestApp < Rails::Application
secrets.secret_token = "secret_token"
secrets.secret_key_base = "secret_key_base"
config.logger = Logger.new($stdout)
Rails.logger = config.logger
end
require "minitest/autorun"
class UlidTest < Minitest::Test
def test_ids
category = Category.create(name: 'Fiction')
assert_equal category, Category.find(category.to_param)
# lazy; not checking strictly but enough to show that it is Crockford Base-32
assert category.to_param =~ /\A[A-Z0-9]{26}\z/
end
def test_providing_ulid
category = Category.create(id: ULID.generate, name: 'Fiction')
assert_equal category, Category.find(category.to_param)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment