aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/lib
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/lib
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/lib')
-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
21 files changed, 258 insertions, 71 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