UUID in Rails with ActiveUUID

Episode #23 by Teacher's Avatar David Kimura

Summary

Some cases require non-auto incrementing primary keys as well as multiple master writes without id conflicts. With ActiveUUID, we can configure our table's primary key with confidence without added complexity.
rails uuid activerecord database 5:11

Resources

Summary

# Gemfile
gem 'activeuuid'

# config/initializers/activeuuid.rb
   ActiveRecord::Base.connection_pool.with_connection do
      ActiveUUID::Patches.apply!
    end

# app/models/concerns/unique_id.rb
    module UniqueId
      extend ActiveSupport::Concern

      included do
        include ActiveUUID::UUID
        before_create :populate_uuid

        private

        def populate_uuid
          begin
            self.id = UUIDTools::UUID.random_create
          end while self.class.exists?(id: id)
        end
      end
    end

# app/models/entry.rb
    class Entry < ActiveRecord::Base
      include UniqueId
      natural_key :lottery_id
      belongs_to :lottery
    end

# app/models/lottery.rb
    class Lottery < ActiveRecord::Base
      include UniqueId
      has_many :entries, dependent: :destroy
      def winning_entries
        entries.where(numbers: self.winning_number)
      end
    end

# database migrations
    create_table :entries, id: false  do |t|
      t.uuid :id, primary_key: true
      t.uuid :lottery_id
      ...
      t.timestamps null: false
    end

    create_table :lotteries, id: false  do |t|
      t.uuid :id, primary_key: true
      ...
      t.timestamps null: false
    end
 

# db/seeds.rb
   def time_rand from = 0.0, to = Time.now
      Time.at(from + rand * (to.to_f - from.to_f))
    end

    10.times do |i|
      puts "Creating Lottery ##{i}"
      lottery = Lottery.new
      lottery.name = "#{Faker::Address.state} #{Faker::Hacker.noun.titleize} Lottery"
      lottery.state = Faker::Address.state_abbr
      winning_number = []
      while winning_number.size < 4
        winning_number << Random.rand(9)
      end
      winning_number = winning_number.join('-')
      lottery.winning_number = winning_number
      random_date = time_rand
      lottery.start_date = random_date
      lottery.end_date = random_date + 1.week
      lottery.save!
      Random.rand(40_000).times do |ii|
        entry = lottery.entries.new
        entry_number = []
        while entry_number.size < 4
          entry_number << Random.rand(9)
        end
        entry_number = entry_number.join('-')
        entry.numbers = entry_number
        puts 'WINNING ENTRY' if winning_number == entry_number
        entry.save!
      end
    end