require "active_record" require "active_support/lazy_load_hooks" module ActiveRecord module PGEnum KNOWN_VERSIONS = %w[4.1 4.2 5.0 5.1 5.2 6.0 6.1 7.0].map { |v| Gem::Version.new(v) } class << self attr_reader :enabled_version def install(version) @enabled_version = approximate_version(version) # Don't immediately fail if we don't yet support the current version. # There's at least a chance it could work. if !KNOWN_VERSIONS.include?(enabled_version) && enabled_version > KNOWN_VERSIONS.last @enabled_version = KNOWN_VERSIONS.last warn "[PGEnum] Current ActiveRecord version unsupported! Falling back to: #{enabled_version}" end initialize! end def register(patch, &block) monkeypatches[patch] = block end def detected_version approximate_version Gem.loaded_specs["activerecord"].version end private def monkeypatches @patches ||= {} end def initialize! require "active_record/pg_enum/command_recorder" require "active_record/pg_enum/schema_statements" Dir[File.join(__dir__, "pg_enum", enabled_version.to_s, "*.rb")].each { |file| require file } monkeypatches.keys.each { |patch| monkeypatches.delete(patch).call } end def approximate_version(version) segments = version.respond_to?(:canonical_segments) ? version.canonical_segments.dup : version.segments segments.pop while segments.any? { |s| String === s } segments.pop while segments.size > 2 segments.push(0) while segments.size < 2 Gem::Version.new segments.join(".") end end end end ActiveSupport.on_load(:active_record) do ActiveRecord::PGEnum.install Gem.loaded_specs["activerecord"].version end # Declare an enum attribute where the values map to strings enforced by PostgreSQL's # enumerated types. # # class Conversation < ActiveRecord::Base # include PGEnum(status: %i[active archived]) # end # # This is merely a wrapper over traditional enum syntax so that you can define # string-based enums with an array of values. def PGEnum(**definitions) values = definitions.values.map do |value| if value.is_a? Array keys = value.map(&:to_sym) values = value.map(&:to_s) Hash[keys.zip(values)] else value end end Module.new do define_singleton_method(:inspect) { %{ActiveRecord::PGEnum(#{definitions.keys.join(" ")})} } define_singleton_method :included do |klass| klass.enum Hash[definitions.keys.zip(values)] end end end