Last active
November 4, 2018 02:35
-
-
Save bjeanes/5b4f699655e47cd4e42928ab8e9a7fff to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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