Joined November 2013
·

Latchezar Tzvetkoff

Sofia, Bulgaria
·
·

Posted to Postgres Enum Type in Rails 4 over 1 year ago

Worth noting that if you're going to use ENUMs you'll need a migration for them:

class CreateEnumerations < ActiveRecord::Migration
  ENUM_TYPES = {
    :common_sex => [
      'Male', 'Female',
    ],
  }

  def self.up
    ENUM_TYPES.each do |type, values|
      values = "'" + values.join("', '") + "'"
      sql = "CREATE TYPE #{type} AS ENUM(#{values})"
      execute sql
    end
  end

  def self.down
    ENUM_TYPES.each_key do |type|
      sql = "DROP TYPE #{type}"
      execute sql
    end
  end
end

Also, a nicer core_ext/initializer for patching ActiveRecord::ConnectionAdapters::Column and adding type aliases could be put in place:

module AppName
  module ActiveRecord
    module ConnectionAdapters
      module Column
        ENUM_TYPES = [
          :common_sex,
          :demographic_projections_race,
          :demographic_projections_education,
        ]

        def self.included(base)
          ENUM_TYPES.each do |type|
            ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID.alias_type type.to_s, 'text'
          end

          base.class_eval do
            def simplified_type_with_custom_enum_types(type)
              sym = type.to_sym

              if sym.in?(ENUM_TYPES)
                sym
              else
                simplified_type_without_custom_enum_types(type)
              end
            end

            alias_method_chain :simplified_type, :custom_enum_types
          end
        end
      end
    end
  end
end

unless ::ActiveRecord::ConnectionAdapters::Column.include?(::AppName::ActiveRecord::ConnectionAdapters::Column)
  ::ActiveRecord::ConnectionAdapters::Column.send(:include, ::AppName::ActiveRecord::ConnectionAdapters::Column)
end

This way everything sums up to just 2 files (or more, if you need to add more ENUMs as the project goes)
And last, but not least - typecasting the value is optional:

User.where(:sex => 'Male')

I prefer just setting it in the global config without modifying $PATH. It's already a mess.
And it's all CLI.

git config --global pager.log `brew --prefix`/share/git-core/contrib/diff-highlight/diff-highlight
git config --global pager.show `brew --prefix`/share/git-core/contrib/diff-highlight/diff-highlight
git config --global pager.diff `brew --prefix`/share/git-core/contrib/diff-highlight/diff-highlight

Here's a full rails test-like gist: https://gist.github.com/tzvetkoff/7287456

OK, everything is cool but... why don't you use Rails's STI & default_scopes for that?
Besides the really nice query interface, you'll also get the write association methods working for free, as ActiveRecord can populate default values from the current scope.

# The person, a real object
class Person < ActiveRecord::Base
  # Every computer that person has access to
  has_many :accounts
  has_many :computers, :through => :accounts

  # Only comps that this account can administrate
  has_many :administrator_accounts, :class_name => 'Account::Administrator'
  has_many :administrated_computers, :through => :administrator_accounts, :source => :computer
end

# The account - a relationship (<3) between a person and a computer
class Account < ActiveRecord::Base
  belongs_to :person
  belongs_to :computer

  # The administrator subclass - here we're using a `default_scope` rather than just normal STI with a `type` field (which I prefer)
  class Administrator < Account
    default_scope lambda { where(:role => 'administrator') }
  end
end

# The computer being used
class Computer < ActiveRecord::Base
  # [ALL] Users
  has_many :accounts
  has_many :people, :through => :accounts

  # Administrators through abnormal accounts
  has_many :administrator_accounts, :class_name => 'Account::Administrator'
  has_many :administrators, :through => :administrator_accounts, :source => :person
end

person1 = Person.create
person2 = Person.create

comp1 = Computer.create
comp2 = Computer.create

person1.computers << comp1
person1.administrated_computers << comp2

person2.computers << comp2
person2.administrated_computers << comp1

ap person1.computers.to_a
ap person1. administrated_computers.to_a
ap person2.computers.to_a
ap person2. administrated_computers.to_a
Achievements
121 Karma
0 Total ProTip Views