diff options
author | Sean Griffin <sean@thoughtbot.com> | 2014-05-17 12:24:13 -0600 |
---|---|---|
committer | Sean Griffin <sean@thoughtbot.com> | 2014-05-19 08:07:21 -0700 |
commit | 0b682e4b05c8f58c77c655650af6638c483ac903 (patch) | |
tree | e22d197ed7d828dad7f249d8878a8fb8cff1ebd2 /activerecord/test/cases | |
parent | a0eec57ef0fbbbb2910a46ca65bd5b565ca0534c (diff) | |
download | rails-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/test/cases')
8 files changed, 312 insertions, 46 deletions
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 |