提交 4d3e88fc 编写于 作者: S Sean Griffin

Don't type cast the default on the column

If we want to have type decorators mess with the attribute, but not the
column, we need to stop type casting on the column. Where possible, we
changed the tests to test the value of `column_defaults`, which is
public API. `Column#default` is not.
上级 9f867802
......@@ -23,7 +23,8 @@ def prepare_column_options(column, types)
spec[:precision] = column.precision.inspect if column.precision
spec[:scale] = column.scale.inspect if column.scale
spec[:null] = 'false' unless column.null
spec[:default] = column.type_cast_for_schema(column.default) if column.has_default?
spec[:default] = schema_default(column) if column.has_default?
spec.delete(:default) if spec[:default].nil?
spec
end
......@@ -31,6 +32,15 @@ def prepare_column_options(column, types)
def migration_keys
[:name, :limit, :precision, :scale, :default, :null]
end
private
def schema_default(column)
default = column.type_cast_from_database(column.default)
unless default.nil?
column.type_cast_for_schema(default)
end
end
end
end
end
......@@ -62,15 +62,14 @@ def initialize(name, default, cast_type, sql_type = nil, null = true, collation
@extra = extra
super(name, default, cast_type, sql_type, null)
assert_valid_default(default)
extract_default
end
def default
@default ||= if blob_or_text_column?
null || strict ? nil : ''
elsif missing_default_forged_as_empty_string?(@original_default)
nil
else
super
def extract_default
if blob_or_text_column?
@default = null || strict ? nil : ''
elsif missing_default_forged_as_empty_string?(@default)
@default = nil
end
end
......
......@@ -13,7 +13,7 @@ module Format
ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/
end
attr_reader :name, :cast_type, :null, :sql_type, :default_function
attr_reader :name, :cast_type, :null, :sql_type, :default, :default_function
delegate :type, :precision, :scale, :limit, :klass, :accessor,
:text?, :number?, :binary?, :changed?,
......@@ -35,7 +35,7 @@ def initialize(name, default, cast_type, sql_type = nil, null = true)
@cast_type = cast_type
@sql_type = sql_type
@null = null
@original_default = default
@default = default
@default_function = nil
end
......@@ -51,13 +51,8 @@ def human_name
Base.human_attribute_name(@name)
end
def default
@default ||= type_cast_from_database(@original_default)
end
def with_type(type)
dup.tap do |clone|
clone.instance_variable_set('@default', nil)
clone.instance_variable_set('@cast_type', type)
end
end
......
......@@ -230,14 +230,16 @@ def type_for_attribute(attr_name) # :nodoc:
# Returns a hash where the keys are column names and the values are
# default values when instantiating the AR object for this table.
def column_defaults
@column_defaults ||= Hash[columns.map { |c| [c.name, c.default] }]
@column_defaults ||= Hash[columns_hash.map { |name, column|
[name, column.type_cast_from_database(column.default)]
}]
end
# Returns a hash where the keys are the column names and the values
# are the default values suitable for use in `@raw_attriubtes`
def raw_column_defaults # :nodoc:
@raw_column_defauts ||= Hash[column_defaults.map { |name, default|
[name, columns_hash[name].type_cast_for_database(default)]
@raw_column_defaults ||= Hash[columns_hash.map { |name, column|
[name, column.default]
}]
end
......@@ -285,7 +287,7 @@ def reset_column_information
@arel_engine = nil
@column_defaults = nil
@raw_column_defauts = nil
@raw_column_defaults = nil
@column_names = nil
@column_types = nil
@content_columns = nil
......
......@@ -41,9 +41,8 @@ def test_column
def test_default
@connection.add_column 'pg_arrays', 'score', :integer, array: true, default: [4, 4, 2]
PgArray.reset_column_information
column = PgArray.columns_hash["score"]
assert_equal([4, 4, 2], column.default)
assert_equal([4, 4, 2], PgArray.column_defaults['score'])
assert_equal([4, 4, 2], PgArray.new.score)
ensure
PgArray.reset_column_information
......@@ -52,9 +51,8 @@ def test_default
def test_default_strings
@connection.add_column 'pg_arrays', 'names', :string, array: true, default: ["foo", "bar"]
PgArray.reset_column_information
column = PgArray.columns_hash["names"]
assert_equal(["foo", "bar"], column.default)
assert_equal(["foo", "bar"], PgArray.column_defaults['names'])
assert_equal(["foo", "bar"], PgArray.new.names)
ensure
PgArray.reset_column_information
......@@ -68,7 +66,7 @@ def test_change_column_with_array
column = PgArray.columns_hash['snippets']
assert_equal :text, column.type
assert_equal [], column.default
assert_equal [], PgArray.column_defaults['snippets']
assert column.array
end
......@@ -85,8 +83,7 @@ def test_change_column_default_with_array
@connection.change_column_default :pg_arrays, :tags, []
PgArray.reset_column_information
column = PgArray.columns_hash['tags']
assert_equal [], column.default
assert_equal [], PgArray.column_defaults['tags']
end
def test_type_cast_array
......
......@@ -43,12 +43,10 @@ def test_bit_string_varying_column
end
def test_default
column = PostgresqlBitString.columns_hash["a_bit"]
assert_equal "00000011", column.default
assert_equal "00000011", PostgresqlBitString.column_defaults['a_bit']
assert_equal "00000011", PostgresqlBitString.new.a_bit
column = PostgresqlBitString.columns_hash["a_bit_varying"]
assert_equal "0011", column.default
assert_equal "0011", PostgresqlBitString.column_defaults['a_bit_varying']
assert_equal "0011", PostgresqlBitString.new.a_bit_varying
end
......
......@@ -42,9 +42,8 @@ def test_column
def test_enum_defaults
@connection.add_column 'postgresql_enums', 'good_mood', :mood, default: 'happy'
PostgresqlEnum.reset_column_information
column = PostgresqlEnum.columns_hash["good_mood"]
assert_equal "happy", column.default
assert_equal "happy", PostgresqlEnum.column_defaults['good_mood']
assert_equal "happy", PostgresqlEnum.new.good_mood
ensure
PostgresqlEnum.reset_column_information
......
......@@ -35,12 +35,10 @@ def test_column
end
def test_default
column = PostgresqlPoint.columns_hash["y"]
assert_equal [12.2, 13.3], column.default
assert_equal [12.2, 13.3], PostgresqlPoint.column_defaults['y']
assert_equal [12.2, 13.3], PostgresqlPoint.new.y
column = PostgresqlPoint.columns_hash["z"]
assert_equal [14.4, 15.5], column.default
assert_equal [14.4, 15.5], PostgresqlPoint.column_defaults['z']
assert_equal [14.4, 15.5], PostgresqlPoint.new.z
end
......
......@@ -64,9 +64,8 @@ def test_column
def test_default
@connection.add_column 'hstores', 'permissions', :hstore, default: '"users"=>"read", "articles"=>"write"'
Hstore.reset_column_information
column = Hstore.columns_hash["permissions"]
assert_equal({"users"=>"read", "articles"=>"write"}, column.default)
assert_equal({"users"=>"read", "articles"=>"write"}, Hstore.column_defaults['permissions'])
assert_equal({"users"=>"read", "articles"=>"write"}, Hstore.new.permissions)
ensure
Hstore.reset_column_information
......
......@@ -43,9 +43,8 @@ def test_column
def test_default
@connection.add_column 'json_data_type', 'permissions', :json, default: '{"users": "read", "posts": ["read", "write"]}'
JsonDataType.reset_column_information
column = JsonDataType.columns_hash["permissions"]
assert_equal({"users"=>"read", "posts"=>["read", "write"]}, column.default)
assert_equal({"users"=>"read", "posts"=>["read", "write"]}, JsonDataType.column_defaults['permissions'])
assert_equal({"users"=>"read", "posts"=>["read", "write"]}, JsonDataType.new.permissions)
ensure
JsonDataType.reset_column_information
......
......@@ -32,8 +32,7 @@ def test_column
end
def test_default
column = PostgresqlMoney.columns_hash["depth"]
assert_equal BigDecimal.new("150.55"), column.default
assert_equal BigDecimal.new("150.55"), PostgresqlMoney.column_defaults['depth']
assert_equal BigDecimal.new("150.55"), PostgresqlMoney.new.depth
end
......
......@@ -339,7 +339,7 @@ def test_columns_with_default
column = @conn.columns('ex').find { |x|
x.name == 'number'
}
assert_equal 10, column.default
assert_equal '10', column.default
end
end
......
......@@ -98,17 +98,7 @@ def type_cast_from_user(value)
assert_equal 'Hello! decorated!', model.a_string
assert_equal 'whatever', model.another_string
assert_equal 'Hello! decorated! decorated!', child.a_string
# We are round tripping the default, and we don't undo our decoration
assert_equal 'whatever decorated! decorated!', child.another_string
end
test "defaults are decorated on the column" do
Model.attribute :a_string, Type::String.new, default: 'whatever'
Model.decorate_attribute_type(:a_string, :test) { |t| StringDecorator.new(t) }
column = Model.columns_hash['a_string']
assert_equal 'whatever decorated!', column.default
assert_equal 'whatever decorated!', child.another_string
end
class Multiplier < SimpleDelegator
......
......@@ -154,7 +154,7 @@ def test_mysql_integer_not_null_defaults
t.column :omit, :integer, :null => false
end
assert_equal 0, klass.columns_hash['zero'].default
assert_equal '0', klass.columns_hash['zero'].default
assert !klass.columns_hash['zero'].null
# 0 in MySQL 4, nil in 5.
assert [0, nil].include?(klass.columns_hash['omit'].default)
......
......@@ -68,9 +68,9 @@ def test_create_table_with_defaults
five = columns.detect { |c| c.name == "five" } unless mysql
assert_equal "hello", one.default
assert_equal true, two.default
assert_equal false, three.default
assert_equal 1, four.default
assert_equal true, two.type_cast_from_database(two.default)
assert_equal false, three.type_cast_from_database(three.default)
assert_equal '1', four.default
assert_equal "hello", five.default unless mysql
end
......@@ -275,7 +275,7 @@ def test_keeping_default_and_notnull_constraints_on_change
person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
person_klass.reset_column_information
assert_equal 99, person_klass.columns_hash["wealth"].default
assert_equal 99, person_klass.column_defaults["wealth"]
assert_equal false, person_klass.columns_hash["wealth"].null
# Oracle needs primary key value from sequence
if current_adapter?(:OracleAdapter)
......@@ -287,20 +287,20 @@ def test_keeping_default_and_notnull_constraints_on_change
# change column default to see that column doesn't lose its not null definition
person_klass.connection.change_column_default "testings", "wealth", 100
person_klass.reset_column_information
assert_equal 100, person_klass.columns_hash["wealth"].default
assert_equal 100, person_klass.column_defaults["wealth"]
assert_equal false, person_klass.columns_hash["wealth"].null
# rename column to see that column doesn't lose its not null and/or default definition
person_klass.connection.rename_column "testings", "wealth", "money"
person_klass.reset_column_information
assert_nil person_klass.columns_hash["wealth"]
assert_equal 100, person_klass.columns_hash["money"].default
assert_equal 100, person_klass.column_defaults["money"]
assert_equal false, person_klass.columns_hash["money"].null
# change column
person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
person_klass.reset_column_information
assert_equal 1000, person_klass.columns_hash["money"].default
assert_equal 1000, person_klass.column_defaults["money"]
assert_equal false, person_klass.columns_hash["money"].null
# change column, make it nullable and clear default
......
......@@ -53,13 +53,13 @@ def test_rename_column_preserves_default_value_not_null
add_column 'test_models', 'salary', :integer, :default => 70000
default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
assert_equal 70000, default_before
assert_equal '70000', default_before
rename_column "test_models", "salary", "annual_salary"
assert TestModel.column_names.include?("annual_salary")
default_after = connection.columns("test_models").find { |c| c.name == "annual_salary" }.default
assert_equal 70000, default_after
assert_equal '70000', default_after
end
if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
......@@ -193,14 +193,21 @@ def test_change_column
old_columns = connection.columns(TestModel.table_name)
assert old_columns.find { |c|
c.name == 'approved' && c.type == :boolean && c.default == true
default = c.type_cast_from_database(c.default)
c.name == 'approved' && c.type == :boolean && default == true
}
change_column :test_models, :approved, :boolean, :default => false
new_columns = connection.columns(TestModel.table_name)
assert_not new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
assert_not new_columns.find { |c|
default = c.type_cast_from_database(c.default)
c.name == 'approved' and c.type == :boolean and default == true
}
assert new_columns.find { |c|
default = c.type_cast_from_database(c.default)
c.name == 'approved' and c.type == :boolean and default == false
}
change_column :test_models, :approved, :boolean, :default => true
end
......
......@@ -567,7 +567,7 @@ def test_adding_multiple_columns
assert_equal 8, columns.size
[:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
assert_equal 0, column(:age).default
assert_equal '0', column(:age).default
end
def test_removing_columns
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册