Skip to content
This repository has been archived by the owner on Feb 5, 2019. It is now read-only.

Commit

Permalink
Fix column default for ENUM columns
Browse files Browse the repository at this point in the history
  • Loading branch information
RISCfuture committed Jun 20, 2011
1 parent 2ba2c48 commit feeb6fc
Show file tree
Hide file tree
Showing 8 changed files with 141 additions and 83 deletions.
21 changes: 11 additions & 10 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
source 'http://rubygems.org'

# DEPENDENCIES
gem 'activerecord', require: 'active_record'
gem 'activesupport', require: 'active_support/core_ext/object/try'
gem 'activerecord', '>= 3.0', require: 'active_record'
gem 'activesupport', '>= 3.0', require: 'active_support/core_ext/object/try'

# DEVELOPMENT
gem 'jeweler'
gem 'yard'
gem 'RedCloth', require: 'redcloth'
group :development do
# DEVELOPMENT
gem 'jeweler'
gem 'yard'
gem 'RedCloth', require: 'redcloth'

# TEST
gem 'rspec'
group :test do
# TEST
gem 'rspec'
gem 'factory_girl'
end
gem 'pg'
end
50 changes: 26 additions & 24 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,47 +1,49 @@
GEM
remote: http://rubygems.org/
specs:
RedCloth (4.2.3)
activemodel (3.0.3)
activesupport (= 3.0.3)
RedCloth (4.2.7)
activemodel (3.0.9)
activesupport (= 3.0.9)
builder (~> 2.1.2)
i18n (~> 0.4)
activerecord (3.0.3)
activemodel (= 3.0.3)
activesupport (= 3.0.3)
arel (~> 2.0.2)
i18n (~> 0.5.0)
activerecord (3.0.9)
activemodel (= 3.0.9)
activesupport (= 3.0.9)
arel (~> 2.0.10)
tzinfo (~> 0.3.23)
activesupport (3.0.3)
arel (2.0.7)
activesupport (3.0.9)
arel (2.0.10)
builder (2.1.2)
diff-lcs (1.1.2)
factory_girl (1.3.3)
git (1.2.5)
i18n (0.5.0)
jeweler (1.5.2)
bundler (~> 1.0.0)
jeweler (1.6.2)
bundler (~> 1.0)
git (>= 1.2.5)
rake
rake (0.8.7)
rspec (2.4.0)
rspec-core (~> 2.4.0)
rspec-expectations (~> 2.4.0)
rspec-mocks (~> 2.4.0)
rspec-core (2.4.0)
rspec-expectations (2.4.0)
pg (0.11.0)
rake (0.9.2)
rspec (2.6.0)
rspec-core (~> 2.6.0)
rspec-expectations (~> 2.6.0)
rspec-mocks (~> 2.6.0)
rspec-core (2.6.4)
rspec-expectations (2.6.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.4.0)
tzinfo (0.3.24)
yard (0.6.4)
rspec-mocks (2.6.0)
tzinfo (0.3.28)
yard (0.7.2)

PLATFORMS
ruby

DEPENDENCIES
RedCloth
activerecord
activesupport
activerecord (>= 3.0)
activesupport (>= 3.0)
factory_girl
jeweler
pg
rspec
yard
4 changes: 2 additions & 2 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ rescue LoadError
exit 1
end

Bundler.require :default
Bundler.require :default, :development

Jeweler::Tasks.new do |gem|
gem.name = "enum_type"
Expand All @@ -28,7 +28,7 @@ YARD::Rake::YardocTask.new('doc') do |doc|
doc.options << "--protected"
doc.options << "-r" << "README.textile"
doc.options << "-o" << "doc"
doc.options << "--title" << "enum_type Documentation".inspect
doc.options << "--title" << "enum_type Documentation"

doc.files = [ 'lib/**/*', 'README.textile' ]
end
Expand Down
10 changes: 6 additions & 4 deletions lib/enum_type.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
require 'enum_type/extensions'

# Adds the @enum_type@ method to a model.
#
# @example Basic usage
# class MyModel < ActiveRecord::Base
# extend EnumType
# enum_type :status, %w( open closed flagged deleted )
# enum_type :status, values: %w( open closed flagged deleted )
# end

module EnumType
Expand All @@ -16,9 +18,9 @@ module EnumType
# @param [Symbol] field An enumerated field.
# @param [Hash] options A hash of options.
# @option options [true, false] :allow_nil (false) If @true@, a nil value
# is allowed.
# is allowed.
# @option options [Array<String>] :values If given, restricts valid values
# to those in the given array.
# to those in the given array.
#
# @example Enumerated field with restricted types
# enum_type :color, values: %w( red orange yellow green blue purple )
Expand All @@ -36,7 +38,7 @@ def enum_type(*fields)
end

validates_presence_of(field) unless options[:allow_nil]
validates_inclusion_of(field, in: options[:values]) if options[:values]
validates_inclusion_of(field, in: options[:values].map(&:to_sym), allow_nil: options[:allow_nil]) if options[:values]
end
end
end
23 changes: 23 additions & 0 deletions lib/enum_type/extensions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'active_support/core_ext/module/aliasing'
require 'active_record/connection_adapters/postgresql_adapter'

# Patch the PostgreSQL adapter to recognize defaults on ENUM columns.

class ActiveRecord::ConnectionAdapters::PostgreSQLColumn
def initialize(name, default, sql_type = nil, null = true)
super(name, self.class.extract_value_from_default(default, sql_type), sql_type, null)
end

def self.extract_value_from_default_with_enum(default, type)
case default
when /\A'(.*)'::(?:#{Regexp.escape type})\z/
$1
else
extract_value_from_default_without_enum default
end
end

class << self
alias_method_chain :extract_value_from_default, :enum
end
end
97 changes: 55 additions & 42 deletions spec/enum_type_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,63 +33,76 @@ def get
end

describe EnumType do
before :each do
@field = Factory.next(:enum_field)
SpecSupport::EnumTypeTester.set_field(@field)
@model = SpecSupport::EnumTypeTester.new
end

describe "#enum_type" do
context "getter" do
context "[getter]" do
it "should return a symbol" do
@model.set 'string'
@model.send(@field).should eql(:string)
Model.connection.execute "INSERT INTO models (state) VALUES ('completed')"
Model.last.state.should eql(:completed)
end

it "should return nil if the value is nil" do
@model.set nil
@model.send(@field).should be_nil
Model.connection.execute "INSERT INTO models (state) VALUES (NULL)"
Model.last.state.should be_nil
end
end

context "setter" do
context "[setter]" do
it "should typecast the value to a string" do
@model.send :"#{@field}=", :string
@model.get.should eql('string')
m = Model.new
m.state = :failed
m.state.should eql(:failed)
m.save!
m.reload.state.should eql(:failed)
m.state_before_type_cast.should eql('failed')
end

it "should leave nil as nil" do
@model.send :"#{@field}=", nil
@model.get.should be_nil
m = Model.new
m.state = nil
m.state.should eql(nil)
m.save!
m.reload.state.should be_nil
end
end

it "should validate inclusion if :values option is given" do
SpecSupport::EnumTypeTester.enum_type @field, values: [ :a, :b ]
@model.send :"#{@field}=", :a
@model.get.should eql('a')
@model.should be_valid
@model.send :"#{@field}=", :c
@model.should_not be_valid
end

it "should not validate presence if :allow_nil is true" do
pending "Doesn't work"
SpecSupport::EnumTypeTester.enum_type @field, allow_nil: true
@model.send :"#{@field}=", nil
@model.should be_valid
end
context "[validations]" do
before :each do
@field = Factory.next(:enum_field)
SpecSupport::EnumTypeTester.set_field(@field)
@model = SpecSupport::EnumTypeTester.new
end

it "should validate inclusion if :values option is given" do
SpecSupport::EnumTypeTester.enum_type @field, values: [ :a, :b ]
@model.send :"#{@field}=", :a
@model.get.should eql('a')
@model.should be_valid
@model.send :"#{@field}=", :c
@model.should_not be_valid
end

it "should validate presence if :allow_nil is not given" do
SpecSupport::EnumTypeTester.enum_type @field
@model.send :"#{@field}=", nil
@model.should_not be_valid
it "should not validate presence if :allow_nil is true" do
pending "Doesn't work"
SpecSupport::EnumTypeTester.enum_type @field, allow_nil: true
@model.send :"#{@field}=", nil
@model.should be_valid
end

it "should validate presence if :allow_nil is not given" do
SpecSupport::EnumTypeTester.enum_type @field
@model.send :"#{@field}=", nil
@model.should_not be_valid
end

it "should validate presence if :allow_nil is false" do
SpecSupport::EnumTypeTester.enum_type @field, allow_nil: false
@model.send :"#{@field}=", nil
@model.should_not be_valid
end
end

it "should validate presence if :allow_nil is false" do
SpecSupport::EnumTypeTester.enum_type @field, allow_nil: false
@model.send :"#{@field}=", nil
@model.should_not be_valid

it "should determine the correct column default" do
Model.columns.detect { |c| c.name == 'state' }.default.should eql('pending')
end
end
end
1 change: 1 addition & 0 deletions spec/factories.rb
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
require 'factory_girl'
Factory.sequence(:enum_field) { |i| :"field#{i}" }
18 changes: 17 additions & 1 deletion spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@
require 'factories'
require 'enum_type'

ActiveRecord::Base.establish_connection(
adapter: 'postgresql',
database: 'enum_type_test',
username: 'enum_type_tester'
)

class Model < ActiveRecord::Base
extend EnumType
enum_type :state, allow_nil: true, values: %w( pending processing completed failed )
end

RSpec.configure do |config|

config.before(:each) do
Model.connection.execute "DROP TABLE IF EXISTS models"
Model.connection.execute "DROP TYPE IF EXISTS state_type"
Model.connection.execute "CREATE TYPE state_type AS ENUM ('pending', 'processing', 'completed', 'failed')"
Model.connection.execute "CREATE TABLE models (id SERIAL PRIMARY KEY, state state_type DEFAULT 'pending')"
end
end

0 comments on commit feeb6fc

Please sign in to comment.