aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord
diff options
context:
space:
mode:
authorSean Griffin <sean@thoughtbot.com>2014-05-17 12:24:13 -0600
committerSean Griffin <sean@thoughtbot.com>2014-05-19 08:07:21 -0700
commit0b682e4b05c8f58c77c655650af6638c483ac903 (patch)
treee22d197ed7d828dad7f249d8878a8fb8cff1ebd2 /activerecord
parenta0eec57ef0fbbbb2910a46ca65bd5b565ca0534c (diff)
downloadrails-0b682e4b05c8f58c77c655650af6638c483ac903.tar.gz
rails-0b682e4b05c8f58c77c655650af6638c483ac903.tar.bz2
rails-0b682e4b05c8f58c77c655650af6638c483ac903.zip
Delegate `Column#type` to the injected type object
The decision to wrap type registrations in a proc was made for two reasons. 1. Some cases need to make an additional decision based on the type (e.g. a `Decimal` with a 0 scale) 2. Aliased types are automatically updated if they type they point to is updated later. If a user or another adapter decides to change the object used for `decimal` columns, `numeric`, and `number` will automatically point to the new type, without having to track what types are aliased explicitly. Everything else here should be pretty straightforward. PostgreSQL ranges had to change slightly, since the `simplified_type` method is gone.
Diffstat (limited to 'activerecord')
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_adapter.rb38
-rw-r--r--activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb26
-rw-r--r--activerecord/lib/active_record/connection_adapters/column.rb41
-rw-r--r--activerecord/lib/active_record/connection_adapters/mysql_adapter.rb1
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/column.rb5
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql/oid.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb9
-rw-r--r--activerecord/lib/active_record/connection_adapters/type.rb20
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/binary.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/boolean.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/date.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/date_time.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/decimal.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/float.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/integer.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/string.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/text.rb13
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/time.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/timestamp.rb11
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/type_map.rb50
-rw-r--r--activerecord/lib/active_record/connection_adapters/type/value.rb1
-rw-r--r--activerecord/test/cases/adapters/mysql/quoting_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/composite_test.rb1
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb12
-rw-r--r--activerecord/test/cases/column_definition_test.rb44
-rw-r--r--activerecord/test/cases/column_test.rb34
-rw-r--r--activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb61
-rw-r--r--activerecord/test/cases/connection_adapters/type/type_map_test.rb102
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb100
29 files changed, 570 insertions, 117 deletions
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
index d9c939689f..8a7a869eec 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
@@ -361,10 +361,46 @@ module ActiveRecord
pool.checkin self
end
+ def type_map # :nodoc:
+ @type_map ||= Type::TypeMap.new.tap do |mapping|
+ initialize_type_map(mapping)
+ end
+ end
+
protected
def lookup_cast_type(sql_type) # :nodoc:
- Type::Value.new
+ type_map.lookup(sql_type)
+ end
+
+ def initialize_type_map(m) # :nodoc:
+ m.register_type %r(boolean)i, Type::Boolean.new
+ m.register_type %r(char)i, Type::String.new
+ m.register_type %r(binary)i, Type::Binary.new
+ m.alias_type %r(blob)i, 'binary'
+ m.register_type %r(text)i, Type::Text.new
+ m.alias_type %r(clob)i, 'text'
+ m.register_type %r(date)i, Type::Date.new
+ m.register_type %r(time)i, Type::Time.new
+ m.register_type %r(timestamp)i, Type::Timestamp.new
+ m.register_type %r(datetime)i, Type::DateTime.new
+ m.alias_type %r(numeric)i, 'decimal'
+ m.alias_type %r(number)i, 'decimal'
+ m.register_type %r(float)i, Type::Float.new
+ m.alias_type %r(double)i, 'float'
+ m.register_type %r(int)i, Type::Integer.new
+ m.register_type(%r(decimal)i) do |sql_type|
+ if Type.extract_scale(sql_type) == 0
+ Type::Integer.new
+ else
+ Type::Decimal.new
+ end
+ end
+ end
+
+ def reload_type_map # :nodoc:
+ type_map.clear
+ initialize_type_map(type_map)
end
def translate_exception_class(e, sql)
diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
index 7074f69583..5eb2e86d48 100644
--- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb
@@ -97,18 +97,6 @@ module ActiveRecord
private
- def simplified_type(field_type)
- return :boolean if adapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
-
- case field_type
- when /enum/i, /set/i then :string
- when /year/i then :integer
- when /bit/i then :binary
- else
- super
- end
- end
-
def extract_limit(sql_type)
case sql_type
when /^enum\((.+)\)/i
@@ -318,6 +306,11 @@ module ActiveRecord
# DATABASE STATEMENTS ======================================
+ def clear_cache!
+ super
+ reload_type_map
+ end
+
# Executes the SQL statement in the context of this connection.
def execute(sql, name = nil)
log(sql, name) { @connection.query(sql) }
@@ -645,6 +638,15 @@ module ActiveRecord
protected
+ def initialize_type_map(m)
+ super
+ m.alias_type %r(tinyint\(1\))i, 'boolean' if emulate_booleans
+ m.alias_type %r(enum)i, 'varchar'
+ m.alias_type %r(set)i, 'varchar'
+ m.alias_type %r(year)i, 'integer'
+ m.alias_type %r(bit)i, 'binary'
+ end
+
# MySQL is too stupid to create a temporary table for use subquery, so we have
# to give it some prompting in the form of a subsubquery. Ugh!
def subquery_for(key, select)
diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb
index 3bab325e42..0087c20b88 100644
--- a/activerecord/lib/active_record/connection_adapters/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/column.rb
@@ -13,11 +13,13 @@ module ActiveRecord
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
end
- attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale, :default_function
+ attr_reader :name, :default, :cast_type, :limit, :null, :sql_type, :precision, :scale, :default_function
attr_accessor :primary, :coder
alias :encoded? :coder
+ delegate :type, to: :cast_type
+
# Instantiates a new column in the table.
#
# +name+ is the column's name, such as <tt>supplier_id</tt> in <tt>supplier_id int(11)</tt>.
@@ -35,7 +37,6 @@ module ActiveRecord
@limit = extract_limit(sql_type)
@precision = extract_precision(sql_type)
@scale = extract_scale(sql_type)
- @type = simplified_type(sql_type)
@default = extract_default(default)
@default_function = nil
@primary = nil
@@ -256,6 +257,8 @@ module ActiveRecord
end
private
+ delegate :extract_scale, to: Type
+
def extract_limit(sql_type)
$1.to_i if sql_type =~ /\((.*)\)/
end
@@ -263,40 +266,6 @@ module ActiveRecord
def extract_precision(sql_type)
$2.to_i if sql_type =~ /^(numeric|decimal|number)\((\d+)(,\d+)?\)/i
end
-
- def extract_scale(sql_type)
- case sql_type
- when /^(numeric|decimal|number)\((\d+)\)/i then 0
- when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
- end
- end
-
- def simplified_type(field_type)
- case field_type
- when /int/i
- :integer
- when /float|double/i
- :float
- when /decimal|numeric|number/i
- extract_scale(field_type) == 0 ? :integer : :decimal
- when /datetime/i
- :datetime
- when /timestamp/i
- :timestamp
- when /time/i
- :time
- when /date/i
- :date
- when /clob/i, /text/i
- :text
- when /blob/i, /binary/i
- :binary
- when /char/i
- :string
- when /boolean/i
- :boolean
- end
- end
end
end
# :startdoc:
diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
index 69e2b0ab2b..bf09bfe217 100644
--- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb
@@ -223,6 +223,7 @@ module ActiveRecord
# Clears the prepared statements cache.
def clear_cache!
+ super
@statements.clear
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
index 77fdebbbc9..1dd8acc257 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/column.rb
@@ -165,11 +165,6 @@ module ActiveRecord
super
end
end
-
- # Maps PostgreSQL-specific data types to logical Rails types.
- def simplified_type(field_type)
- @oid_type.simplified_type(field_type) || super
- end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
index a97c33ae6f..90bf6c6d1a 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb
@@ -2,10 +2,7 @@ module ActiveRecord
module ConnectionAdapters
module PostgreSQL
module OID # :nodoc:
- class Type
- def type; end
- def simplified_type(sql_type); type end
-
+ class Type < Type::Value
def infinity(options = {})
::Float::INFINITY * (options[:negative] ? -1 : 1)
end
@@ -136,11 +133,11 @@ module ActiveRecord
end
class Range < Type
- attr_reader :subtype
- def simplified_type(sql_type); sql_type.to_sym end
+ attr_reader :subtype, :type
- def initialize(subtype)
+ def initialize(subtype, type)
@subtype = subtype
+ @type = type
end
def extract_bounds(value)
@@ -412,7 +409,7 @@ This is not reliable and will be removed in the future.
def register_range_type(row)
if subtype = @store[row['rngsubtype'].to_i]
- register row['oid'], OID::Range.new(subtype)
+ register row['oid'], OID::Range.new(subtype, row['typname'].to_sym)
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
index 183d0c4ec6..59e157744f 100644
--- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
+++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb
@@ -539,10 +539,6 @@ module ActiveRecord
private
- def type_map
- @type_map
- end
-
def get_oid_type(oid, fmod, column_name)
if !type_map.key?(oid)
initialize_type_map(type_map, [oid])
@@ -554,11 +550,6 @@ module ActiveRecord
}
end
- def reload_type_map
- type_map.clear
- initialize_type_map(type_map)
- end
-
def initialize_type_map(type_map, oids = nil)
if supports_ranges?
query = <<-SQL
diff --git a/activerecord/lib/active_record/connection_adapters/type.rb b/activerecord/lib/active_record/connection_adapters/type.rb
index 1b27377cde..34b1e9e39e 100644
--- a/activerecord/lib/active_record/connection_adapters/type.rb
+++ b/activerecord/lib/active_record/connection_adapters/type.rb
@@ -1,8 +1,28 @@
require 'active_record/connection_adapters/type/value'
+require 'active_record/connection_adapters/type/binary'
+require 'active_record/connection_adapters/type/boolean'
+require 'active_record/connection_adapters/type/date'
+require 'active_record/connection_adapters/type/date_time'
+require 'active_record/connection_adapters/type/decimal'
+require 'active_record/connection_adapters/type/float'
+require 'active_record/connection_adapters/type/integer'
+require 'active_record/connection_adapters/type/string'
+require 'active_record/connection_adapters/type/text'
+require 'active_record/connection_adapters/type/time'
+require 'active_record/connection_adapters/type/timestamp'
+require 'active_record/connection_adapters/type/type_map'
module ActiveRecord
module ConnectionAdapters
module Type # :nodoc:
+ class << self
+ def extract_scale(sql_type)
+ case sql_type
+ when /^(numeric|decimal|number)\((\d+)\)/i then 0
+ when /^(numeric|decimal|number)\((\d+)(,(\d+))\)/i then $4.to_i
+ end
+ end
+ end
end
end
end
diff --git a/activerecord/lib/active_record/connection_adapters/type/binary.rb b/activerecord/lib/active_record/connection_adapters/type/binary.rb
new file mode 100644
index 0000000000..168d824d3d
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/binary.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class Binary < Value # :nodoc:
+ def type
+ :binary
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/boolean.rb b/activerecord/lib/active_record/connection_adapters/type/boolean.rb
new file mode 100644
index 0000000000..938d227632
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/boolean.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class Boolean < Value # :nodoc:
+ def type
+ :boolean
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/date.rb b/activerecord/lib/active_record/connection_adapters/type/date.rb
new file mode 100644
index 0000000000..1632f3c8f4
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/date.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class Date < Value # :nodoc:
+ def type
+ :date
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/date_time.rb b/activerecord/lib/active_record/connection_adapters/type/date_time.rb
new file mode 100644
index 0000000000..1d7d3cfbbf
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/date_time.rb
@@ -0,0 +1,13 @@
+require 'active_record/connection_adapters/type/timestamp'
+
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class DateTime < Timestamp # :nodoc:
+ def type
+ :datetime
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/decimal.rb b/activerecord/lib/active_record/connection_adapters/type/decimal.rb
new file mode 100644
index 0000000000..5b39ea9e2f
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/decimal.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class Decimal < Value # :nodoc:
+ def type
+ :decimal
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/float.rb b/activerecord/lib/active_record/connection_adapters/type/float.rb
new file mode 100644
index 0000000000..089169e7c9
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/float.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class Float < Value # :nodoc:
+ def type
+ :float
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/integer.rb b/activerecord/lib/active_record/connection_adapters/type/integer.rb
new file mode 100644
index 0000000000..5510a11bd4
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/integer.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class Integer < Value # :nodoc:
+ def type
+ :integer
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/string.rb b/activerecord/lib/active_record/connection_adapters/type/string.rb
new file mode 100644
index 0000000000..0feb4299f5
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/string.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class String < Value # :nodoc:
+ def type
+ :string
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/text.rb b/activerecord/lib/active_record/connection_adapters/type/text.rb
new file mode 100644
index 0000000000..ee5842a3fc
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/text.rb
@@ -0,0 +1,13 @@
+require 'active_record/connection_adapters/type/string'
+
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class Text < String # :nodoc:
+ def type
+ :text
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/time.rb b/activerecord/lib/active_record/connection_adapters/type/time.rb
new file mode 100644
index 0000000000..a3a687a8ad
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/time.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class Time < Value # :nodoc:
+ def type
+ :time
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/timestamp.rb b/activerecord/lib/active_record/connection_adapters/type/timestamp.rb
new file mode 100644
index 0000000000..92bf0a1954
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/timestamp.rb
@@ -0,0 +1,11 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class Timestamp < Value # :nodoc:
+ def type
+ :timestamp
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/type_map.rb b/activerecord/lib/active_record/connection_adapters/type/type_map.rb
new file mode 100644
index 0000000000..d89171a820
--- /dev/null
+++ b/activerecord/lib/active_record/connection_adapters/type/type_map.rb
@@ -0,0 +1,50 @@
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class TypeMap # :nodoc:
+ def initialize
+ @mapping = {}
+ end
+
+ def lookup(lookup_key)
+ matching_pair = @mapping.reverse_each.detect do |key, _|
+ key === lookup_key
+ end
+
+ if matching_pair
+ matching_pair.last.call(lookup_key)
+ else
+ default_value
+ end
+ end
+
+ def register_type(key, value = nil, &block)
+ raise ::ArgumentError unless value || block
+
+ if block
+ @mapping[key] = block
+ else
+ @mapping[key] = proc { value }
+ end
+ end
+
+ def alias_type(key, target_key)
+ register_type(key) do |sql_type|
+ metadata = sql_type[/\(.*\)/, 0]
+ lookup("#{target_key}#{metadata}")
+ end
+ end
+
+ def clear
+ @mapping.clear
+ end
+
+ private
+
+ def default_value
+ @default_value ||= Value.new
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/lib/active_record/connection_adapters/type/value.rb b/activerecord/lib/active_record/connection_adapters/type/value.rb
index 36f680050f..f7d7b9351b 100644
--- a/activerecord/lib/active_record/connection_adapters/type/value.rb
+++ b/activerecord/lib/active_record/connection_adapters/type/value.rb
@@ -2,6 +2,7 @@ module ActiveRecord
module ConnectionAdapters
module Type
class Value # :nodoc:
+ def type; end
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql/quoting_test.rb b/activerecord/test/cases/adapters/mysql/quoting_test.rb
index f650e3fde8..d8a954efa8 100644
--- a/activerecord/test/cases/adapters/mysql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/mysql/quoting_test.rb
@@ -9,13 +9,13 @@ module ActiveRecord
end
def test_type_cast_true
- c = Column.new(nil, 1, Type::Value.new, 'boolean')
+ c = Column.new(nil, 1, Type::Boolean.new)
assert_equal 1, @conn.type_cast(true, nil)
assert_equal 1, @conn.type_cast(true, c)
end
def test_type_cast_false
- c = Column.new(nil, 1, Type::Value.new, 'boolean')
+ c = Column.new(nil, 1, Type::Boolean.new)
assert_equal 0, @conn.type_cast(false, nil)
assert_equal 0, @conn.type_cast(false, c)
end
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb
index 68b9e6daf7..1e7071c136 100644
--- a/activerecord/test/cases/adapters/postgresql/composite_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb
@@ -85,7 +85,6 @@ class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase
class FullAddressType
def type; :full_address end
- def simplified_type(sql_type); type end
def type_cast(value)
if value =~ /\("?([^",]*)"?,"?([^",]*)"?\)/
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 63170e710e..0c4f06d6a9 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -47,13 +47,13 @@ module ActiveRecord
end
def test_type_cast_true
- c = Column.new(nil, 1, Type::Value.new, 'int')
+ c = Column.new(nil, 1, Type::Integer.new)
assert_equal 't', @conn.type_cast(true, nil)
assert_equal 1, @conn.type_cast(true, c)
end
def test_type_cast_false
- c = Column.new(nil, 1, Type::Value.new, 'int')
+ c = Column.new(nil, 1, Type::Integer.new)
assert_equal 'f', @conn.type_cast(false, nil)
assert_equal 0, @conn.type_cast(false, c)
end
@@ -61,16 +61,16 @@ module ActiveRecord
def test_type_cast_string
assert_equal '10', @conn.type_cast('10', nil)
- c = Column.new(nil, 1, Type::Value.new, 'int')
+ c = Column.new(nil, 1, Type::Integer.new)
assert_equal 10, @conn.type_cast('10', c)
- c = Column.new(nil, 1, Type::Value.new, 'float')
+ c = Column.new(nil, 1, Type::Float.new)
assert_equal 10.1, @conn.type_cast('10.1', c)
- c = Column.new(nil, 1, Type::Value.new, 'binary')
+ c = Column.new(nil, 1, Type::Binary.new)
assert_equal '10.1', @conn.type_cast('10.1', c)
- c = Column.new(nil, 1, Type::Value.new, 'date')
+ c = Column.new(nil, 1, Type::Date.new)
assert_equal '10.1', @conn.type_cast('10.1', c)
end
diff --git a/activerecord/test/cases/column_definition_test.rb b/activerecord/test/cases/column_definition_test.rb
index 33aba6421e..23e6254577 100644
--- a/activerecord/test/cases/column_definition_test.rb
+++ b/activerecord/test/cases/column_definition_test.rb
@@ -12,13 +12,13 @@ module ActiveRecord
end
def test_can_set_coder
- column = Column.new("title", nil, Type::Value.new, "varchar(20)")
+ column = Column.new("title", nil, Type::String.new, "varchar(20)")
column.coder = YAML
assert_equal YAML, column.coder
end
def test_encoded?
- column = Column.new("title", nil, Type::Value.new, "varchar(20)")
+ column = Column.new("title", nil, Type::String.new, "varchar(20)")
assert !column.encoded?
column.coder = YAML
@@ -26,7 +26,7 @@ module ActiveRecord
end
def test_type_case_coded_column
- column = Column.new("title", nil, Type::Value.new, "varchar(20)")
+ column = Column.new("title", nil, Type::String.new, "varchar(20)")
column.coder = YAML
assert_equal "hello", column.type_cast("--- hello")
end
@@ -34,7 +34,7 @@ module ActiveRecord
# Avoid column definitions in create table statements like:
# `title` varchar(255) DEFAULT NULL
def test_should_not_include_default_clause_when_default_is_null
- column = Column.new("title", nil, Type::Value.new, "varchar(20)")
+ column = Column.new("title", nil, Type::String.new, "varchar(20)")
column_def = ColumnDefinition.new(
column.name, "string",
column.limit, column.precision, column.scale, column.default, column.null)
@@ -42,7 +42,7 @@ module ActiveRecord
end
def test_should_include_default_clause_when_default_is_present
- column = Column.new("title", "Hello", Type::Value.new, "varchar(20)")
+ column = Column.new("title", "Hello", Type::String.new, "varchar(20)")
column_def = ColumnDefinition.new(
column.name, "string",
column.limit, column.precision, column.scale, column.default, column.null)
@@ -50,7 +50,7 @@ module ActiveRecord
end
def test_should_specify_not_null_if_null_option_is_false
- column = Column.new("title", "Hello", Type::Value.new, "varchar(20)", false)
+ column = Column.new("title", "Hello", Type::String.new, "varchar(20)", false)
column_def = ColumnDefinition.new(
column.name, "string",
column.limit, column.precision, column.scale, column.default, column.null)
@@ -59,68 +59,68 @@ module ActiveRecord
if current_adapter?(:MysqlAdapter)
def test_should_set_default_for_mysql_binary_data_types
- binary_column = MysqlAdapter::Column.new("title", "a", Type::Value.new, "binary(1)")
+ binary_column = MysqlAdapter::Column.new("title", "a", Type::Binary.new, "binary(1)")
assert_equal "a", binary_column.default
- varbinary_column = MysqlAdapter::Column.new("title", "a", Type::Value.new, "varbinary(1)")
+ varbinary_column = MysqlAdapter::Column.new("title", "a", Type::Binary.new, "varbinary(1)")
assert_equal "a", varbinary_column.default
end
def test_should_not_set_default_for_blob_and_text_data_types
assert_raise ArgumentError do
- MysqlAdapter::Column.new("title", "a", Type::Value.new, "blob")
+ MysqlAdapter::Column.new("title", "a", Type::Binary.new, "blob")
end
assert_raise ArgumentError do
- MysqlAdapter::Column.new("title", "Hello", Type::Value.new, "text")
+ MysqlAdapter::Column.new("title", "Hello", Type::Text.new)
end
- text_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "text")
+ text_column = MysqlAdapter::Column.new("title", nil, Type::Text.new)
assert_equal nil, text_column.default
- not_null_text_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "text", false)
+ not_null_text_column = MysqlAdapter::Column.new("title", nil, Type::Text.new, "text", false)
assert_equal "", not_null_text_column.default
end
def test_has_default_should_return_false_for_blob_and_text_data_types
- blob_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "blob")
+ blob_column = MysqlAdapter::Column.new("title", nil, Type::Binary.new, "blob")
assert !blob_column.has_default?
- text_column = MysqlAdapter::Column.new("title", nil, Type::Value.new, "text")
+ text_column = MysqlAdapter::Column.new("title", nil, Type::Text.new)
assert !text_column.has_default?
end
end
if current_adapter?(:Mysql2Adapter)
def test_should_set_default_for_mysql_binary_data_types
- binary_column = Mysql2Adapter::Column.new("title", "a", Type::Value.new, "binary(1)")
+ binary_column = Mysql2Adapter::Column.new("title", "a", Type::Binary.new, "binary(1)")
assert_equal "a", binary_column.default
- varbinary_column = Mysql2Adapter::Column.new("title", "a", Type::Value.new, "varbinary(1)")
+ varbinary_column = Mysql2Adapter::Column.new("title", "a", Type::Binary.new, "varbinary(1)")
assert_equal "a", varbinary_column.default
end
def test_should_not_set_default_for_blob_and_text_data_types
assert_raise ArgumentError do
- Mysql2Adapter::Column.new("title", "a", Type::Value.new, "blob")
+ Mysql2Adapter::Column.new("title", "a", Type::Binary.new, "blob")
end
assert_raise ArgumentError do
- Mysql2Adapter::Column.new("title", "Hello", Type::Value.new, "text")
+ Mysql2Adapter::Column.new("title", "Hello", Type::Text.new)
end
- text_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "text")
+ text_column = Mysql2Adapter::Column.new("title", nil, Type::Text.new)
assert_equal nil, text_column.default
- not_null_text_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "text", false)
+ not_null_text_column = Mysql2Adapter::Column.new("title", nil, Type::Text.new, "text", false)
assert_equal "", not_null_text_column.default
end
def test_has_default_should_return_false_for_blob_and_text_data_types
- blob_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "blob")
+ blob_column = Mysql2Adapter::Column.new("title", nil, Type::Binary.new, "blob")
assert !blob_column.has_default?
- text_column = Mysql2Adapter::Column.new("title", nil, Type::Value.new, "text")
+ text_column = Mysql2Adapter::Column.new("title", nil, Type::Text.new)
assert !text_column.has_default?
end
end
diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb
index c2135d75da..91605a52f9 100644
--- a/activerecord/test/cases/column_test.rb
+++ b/activerecord/test/cases/column_test.rb
@@ -5,7 +5,7 @@ module ActiveRecord
module ConnectionAdapters
class ColumnTest < ActiveRecord::TestCase
def test_type_cast_boolean
- column = Column.new("field", nil, Type::Value.new, "boolean")
+ column = Column.new("field", nil, Type::Boolean.new)
assert column.type_cast('').nil?
assert column.type_cast(nil).nil?
@@ -36,14 +36,14 @@ module ActiveRecord
end
def test_type_cast_string
- column = Column.new("field", nil, Type::Value.new, "varchar")
+ column = Column.new("field", nil, Type::String.new)
assert_equal "1", column.type_cast(true)
assert_equal "0", column.type_cast(false)
assert_equal "123", column.type_cast(123)
end
def test_type_cast_integer
- column = Column.new("field", nil, Type::Value.new, "integer")
+ column = Column.new("field", nil, Type::Integer.new)
assert_equal 1, column.type_cast(1)
assert_equal 1, column.type_cast('1')
assert_equal 1, column.type_cast('1ignore')
@@ -56,50 +56,50 @@ module ActiveRecord
end
def test_type_cast_non_integer_to_integer
- column = Column.new("field", nil, Type::Value.new, "integer")
+ column = Column.new("field", nil, Type::Integer.new)
assert_nil column.type_cast([1,2])
assert_nil column.type_cast({1 => 2})
assert_nil column.type_cast((1..2))
end
def test_type_cast_activerecord_to_integer
- column = Column.new("field", nil, Type::Value.new, "integer")
+ column = Column.new("field", nil, Type::Integer.new)
firm = Firm.create(:name => 'Apple')
assert_nil column.type_cast(firm)
end
def test_type_cast_object_without_to_i_to_integer
- column = Column.new("field", nil, Type::Value.new, "integer")
+ column = Column.new("field", nil, Type::Integer.new)
assert_nil column.type_cast(Object.new)
end
def test_type_cast_nan_and_infinity_to_integer
- column = Column.new("field", nil, Type::Value.new, "integer")
+ column = Column.new("field", nil, Type::Integer.new)
assert_nil column.type_cast(Float::NAN)
assert_nil column.type_cast(1.0/0.0)
end
def test_type_cast_float
- column = Column.new("field", nil, Type::Value.new, "float")
+ column = Column.new("field", nil, Type::Float.new)
assert_equal 1.0, column.type_cast("1")
end
def test_type_cast_decimal
- column = Column.new("field", nil, Type::Value.new, "decimal")
+ column = Column.new("field", nil, Type::Decimal.new)
assert_equal BigDecimal.new("0"), column.type_cast(BigDecimal.new("0"))
assert_equal BigDecimal.new("123"), column.type_cast(123.0)
assert_equal BigDecimal.new("1"), column.type_cast(:"1")
end
def test_type_cast_binary
- column = Column.new("field", nil, Type::Value.new, "binary")
+ column = Column.new("field", nil, Type::Binary.new)
assert_equal nil, column.type_cast(nil)
assert_equal "1", column.type_cast("1")
assert_equal 1, column.type_cast(1)
end
def test_type_cast_time
- column = Column.new("field", nil, Type::Value.new, "time")
+ column = Column.new("field", nil, Type::Time.new)
assert_equal nil, column.type_cast(nil)
assert_equal nil, column.type_cast('')
assert_equal nil, column.type_cast('ABC')
@@ -109,7 +109,11 @@ module ActiveRecord
end
def test_type_cast_datetime_and_timestamp
- [Column.new("field", nil, Type::Value.new, "datetime"), Column.new("field", nil, Type::Value.new, "timestamp")].each do |column|
+ columns = [
+ Column.new("field", nil, Type::DateTime.new),
+ Column.new("field", nil, Type::Timestamp.new),
+ ]
+ columns.each do |column|
assert_equal nil, column.type_cast(nil)
assert_equal nil, column.type_cast('')
assert_equal nil, column.type_cast(' ')
@@ -121,7 +125,7 @@ module ActiveRecord
end
def test_type_cast_date
- column = Column.new("field", nil, Type::Value.new, "date")
+ column = Column.new("field", nil, Type::Date.new)
assert_equal nil, column.type_cast(nil)
assert_equal nil, column.type_cast('')
assert_equal nil, column.type_cast(' ')
@@ -132,7 +136,7 @@ module ActiveRecord
end
def test_type_cast_duration_to_integer
- column = Column.new("field", nil, Type::Value.new, "integer")
+ column = Column.new("field", nil, Type::Integer.new)
assert_equal 1800, column.type_cast(30.minutes)
assert_equal 7200, column.type_cast(2.hours)
end
@@ -147,7 +151,7 @@ module ActiveRecord
if current_adapter?(:SQLite3Adapter)
def test_binary_encoding
- column = SQLite3Column.new("field", nil, Type::Value.new, "binary")
+ column = SQLite3Column.new("field", nil, Type::Binary.new)
utf8_string = "a string".encode(Encoding::UTF_8)
type_cast = column.type_cast(utf8_string)
diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
new file mode 100644
index 0000000000..d4d67487db
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
@@ -0,0 +1,61 @@
+require "cases/helper"
+
+if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
+module ActiveRecord
+ module ConnectionAdapters
+ class MysqlTypeLookupTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_boolean_types
+ emulate_booleans(true) do
+ assert_lookup_type :boolean, 'tinyint(1)'
+ assert_lookup_type :boolean, 'TINYINT(1)'
+ end
+ end
+
+ def test_string_types
+ assert_lookup_type :string, "enum('one', 'two', 'three')"
+ assert_lookup_type :string, "ENUM('one', 'two', 'three')"
+ assert_lookup_type :string, "set('one', 'two', 'three')"
+ assert_lookup_type :string, "SET('one', 'two', 'three')"
+ end
+
+ def test_binary_types
+ assert_lookup_type :binary, 'bit'
+ assert_lookup_type :binary, 'BIT'
+ end
+
+ def test_integer_types
+ emulate_booleans(false) do
+ assert_lookup_type :integer, 'tinyint(1)'
+ assert_lookup_type :integer, 'TINYINT(1)'
+ assert_lookup_type :integer, 'year'
+ assert_lookup_type :integer, 'YEAR'
+ end
+ end
+
+ private
+
+ def assert_lookup_type(type, lookup)
+ cast_type = @connection.type_map.lookup(lookup)
+ assert_equal type, cast_type.type
+ end
+
+ def emulate_booleans(value)
+ old_emulate_booleans = @connection.emulate_booleans
+ change_emulate_booleans(value)
+ yield
+ ensure
+ change_emulate_booleans(old_emulate_booleans)
+ end
+
+ def change_emulate_booleans(value)
+ @connection.emulate_booleans = value
+ @connection.clear_cache!
+ end
+ end
+ end
+end
+end
diff --git a/activerecord/test/cases/connection_adapters/type/type_map_test.rb b/activerecord/test/cases/connection_adapters/type/type_map_test.rb
new file mode 100644
index 0000000000..565f44eef8
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/type/type_map_test.rb
@@ -0,0 +1,102 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ module Type
+ class TypeMapTest < ActiveRecord::TestCase
+ def test_default_type
+ mapping = TypeMap.new
+
+ assert_kind_of Value, mapping.lookup(:undefined)
+ end
+
+ def test_registering_types
+ boolean = Boolean.new
+ mapping = TypeMap.new
+
+ mapping.register_type(/boolean/i, boolean)
+
+ assert_equal mapping.lookup('boolean'), boolean
+ end
+
+ def test_overriding_registered_types
+ time = Time.new
+ timestamp = Timestamp.new
+ mapping = TypeMap.new
+
+ mapping.register_type(/time/i, time)
+ mapping.register_type(/time/i, timestamp)
+
+ assert_equal mapping.lookup('time'), timestamp
+ end
+
+ def test_fuzzy_lookup
+ string = String.new
+ mapping = TypeMap.new
+
+ mapping.register_type(/varchar/i, string)
+
+ assert_equal mapping.lookup('varchar(20)'), string
+ end
+
+ def test_aliasing_types
+ string = String.new
+ mapping = TypeMap.new
+
+ mapping.register_type(/string/i, string)
+ mapping.alias_type(/varchar/i, 'string')
+
+ assert_equal mapping.lookup('varchar'), string
+ end
+
+ def test_changing_type_changes_aliases
+ time = Time.new
+ timestamp = Timestamp.new
+ mapping = TypeMap.new
+
+ mapping.register_type(/timestamp/i, time)
+ mapping.alias_type(/datetime/i, 'timestamp')
+ mapping.register_type(/timestamp/i, timestamp)
+
+ assert_equal mapping.lookup('datetime'), timestamp
+ end
+
+ def test_aliases_keep_metadata
+ mapping = TypeMap.new
+
+ mapping.register_type(/decimal/i) { |sql_type| sql_type }
+ mapping.alias_type(/number/i, 'decimal')
+
+ assert_equal mapping.lookup('number(20)'), 'decimal(20)'
+ assert_equal mapping.lookup('number'), 'decimal'
+ end
+
+ def test_register_proc
+ string = String.new
+ binary = Binary.new
+ mapping = TypeMap.new
+
+ mapping.register_type(/varchar/i) do |type|
+ if type.include?('(')
+ string
+ else
+ binary
+ end
+ end
+
+ assert_equal mapping.lookup('varchar(20)'), string
+ assert_equal mapping.lookup('varchar'), binary
+ end
+
+ def test_requires_value_or_block
+ mapping = TypeMap.new
+
+ assert_raises(ArgumentError) do
+ mapping.register_type(/only key/i)
+ end
+ end
+ end
+ end
+ end
+end
+
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
new file mode 100644
index 0000000000..a5b01260d3
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -0,0 +1,100 @@
+require "cases/helper"
+
+unless current_adapter?(:PostgreSQLAdapter) # PostgreSQL does not use type strigns for lookup
+module ActiveRecord
+ module ConnectionAdapters
+ class TypeLookupTest < ActiveRecord::TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_boolean_types
+ assert_lookup_type :boolean, 'boolean'
+ assert_lookup_type :boolean, 'BOOLEAN'
+ end
+
+ def test_string_types
+ assert_lookup_type :string, 'char'
+ assert_lookup_type :string, 'varchar'
+ assert_lookup_type :string, 'VARCHAR'
+ assert_lookup_type :string, 'varchar(255)'
+ assert_lookup_type :string, 'character varying'
+ end
+
+ def test_binary_types
+ assert_lookup_type :binary, 'binary'
+ assert_lookup_type :binary, 'BINARY'
+ assert_lookup_type :binary, 'blob'
+ assert_lookup_type :binary, 'BLOB'
+ end
+
+ def test_text_types
+ assert_lookup_type :text, 'text'
+ assert_lookup_type :text, 'TEXT'
+ assert_lookup_type :text, 'clob'
+ assert_lookup_type :text, 'CLOB'
+ end
+
+ def test_date_types
+ assert_lookup_type :date, 'date'
+ assert_lookup_type :date, 'DATE'
+ end
+
+ def test_time_types
+ assert_lookup_type :time, 'time'
+ assert_lookup_type :time, 'TIME'
+ end
+
+ def test_timestamp_types
+ assert_lookup_type :timestamp, 'timestamp'
+ assert_lookup_type :timestamp, 'TIMESTAMP'
+ end
+
+ def test_datetime_types
+ assert_lookup_type :datetime, 'datetime'
+ assert_lookup_type :datetime, 'DATETIME'
+ end
+
+ def test_decimal_types
+ assert_lookup_type :decimal, 'decimal'
+ assert_lookup_type :decimal, 'decimal(2,8)'
+ assert_lookup_type :decimal, 'DECIMAL'
+ assert_lookup_type :decimal, 'numeric'
+ assert_lookup_type :decimal, 'numeric(2,8)'
+ assert_lookup_type :decimal, 'NUMERIC'
+ assert_lookup_type :decimal, 'number'
+ assert_lookup_type :decimal, 'number(2,8)'
+ assert_lookup_type :decimal, 'NUMBER'
+ end
+
+ def test_float_types
+ assert_lookup_type :float, 'float'
+ assert_lookup_type :float, 'FLOAT'
+ assert_lookup_type :float, 'double'
+ assert_lookup_type :float, 'DOUBLE'
+ end
+
+ def test_integer_types
+ assert_lookup_type :integer, 'integer'
+ assert_lookup_type :integer, 'INTEGER'
+ assert_lookup_type :integer, 'tinyint'
+ assert_lookup_type :integer, 'smallint'
+ assert_lookup_type :integer, 'bigint'
+ assert_lookup_type :integer, 'decimal(2)'
+ assert_lookup_type :integer, 'decimal(2,0)'
+ assert_lookup_type :integer, 'numeric(2)'
+ assert_lookup_type :integer, 'numeric(2,0)'
+ assert_lookup_type :integer, 'number(2)'
+ assert_lookup_type :integer, 'number(2,0)'
+ end
+
+ private
+
+ def assert_lookup_type(type, lookup)
+ cast_type = @connection.type_map.lookup(lookup)
+ assert_equal type, cast_type.type
+ end
+ end
+ end
+end
+end