101c19f55f
The same is not true of `define_attribute`, which is meant to be the low level no-magic API that sits underneath. The differences between the two APIs are: - `attribute` - Lazy (the attribute will be defined after the schema has loaded) - Allows either a type object or a symbol - `define_attribute` - Runs immediately (might get trampled by schema loading) - Requires a type object This was the last blocker in terms of public interface requirements originally discussed for this feature back in May. All the implementation blockers have been cleared, so this feature is probably ready for release (pending one more look-over by me).
161 lines
5.1 KiB
Ruby
161 lines
5.1 KiB
Ruby
require 'cases/helper'
|
|
|
|
class OverloadedType < ActiveRecord::Base
|
|
attribute :overloaded_float, :integer
|
|
attribute :overloaded_string_with_limit, :string, limit: 50
|
|
attribute :non_existent_decimal, :decimal
|
|
attribute :string_with_default, :string, default: 'the overloaded default'
|
|
end
|
|
|
|
class ChildOfOverloadedType < OverloadedType
|
|
end
|
|
|
|
class GrandchildOfOverloadedType < ChildOfOverloadedType
|
|
attribute :overloaded_float, :float
|
|
end
|
|
|
|
class UnoverloadedType < ActiveRecord::Base
|
|
self.table_name = 'overloaded_types'
|
|
end
|
|
|
|
module ActiveRecord
|
|
class CustomPropertiesTest < ActiveRecord::TestCase
|
|
test "overloading types" do
|
|
data = OverloadedType.new
|
|
|
|
data.overloaded_float = "1.1"
|
|
data.unoverloaded_float = "1.1"
|
|
|
|
assert_equal 1, data.overloaded_float
|
|
assert_equal 1.1, data.unoverloaded_float
|
|
end
|
|
|
|
test "overloaded properties save" do
|
|
data = OverloadedType.new
|
|
|
|
data.overloaded_float = "2.2"
|
|
data.save!
|
|
data.reload
|
|
|
|
assert_equal 2, data.overloaded_float
|
|
assert_kind_of Fixnum, OverloadedType.last.overloaded_float
|
|
assert_equal 2.0, UnoverloadedType.last.overloaded_float
|
|
assert_kind_of Float, UnoverloadedType.last.overloaded_float
|
|
end
|
|
|
|
test "properties assigned in constructor" do
|
|
data = OverloadedType.new(overloaded_float: '3.3')
|
|
|
|
assert_equal 3, data.overloaded_float
|
|
end
|
|
|
|
test "overloaded properties with limit" do
|
|
assert_equal 50, OverloadedType.type_for_attribute('overloaded_string_with_limit').limit
|
|
assert_equal 255, UnoverloadedType.type_for_attribute('overloaded_string_with_limit').limit
|
|
end
|
|
|
|
test "nonexistent attribute" do
|
|
data = OverloadedType.new(non_existent_decimal: 1)
|
|
|
|
assert_equal BigDecimal.new(1), data.non_existent_decimal
|
|
assert_raise ActiveModel::AttributeAssignment::UnknownAttributeError do
|
|
UnoverloadedType.new(non_existent_decimal: 1)
|
|
end
|
|
end
|
|
|
|
test "changing defaults" do
|
|
data = OverloadedType.new
|
|
unoverloaded_data = UnoverloadedType.new
|
|
|
|
assert_equal 'the overloaded default', data.string_with_default
|
|
assert_equal 'the original default', unoverloaded_data.string_with_default
|
|
end
|
|
|
|
test "defaults are not touched on the columns" do
|
|
assert_equal 'the original default', OverloadedType.columns_hash['string_with_default'].default
|
|
end
|
|
|
|
test "children inherit custom properties" do
|
|
data = ChildOfOverloadedType.new(overloaded_float: '4.4')
|
|
|
|
assert_equal 4, data.overloaded_float
|
|
end
|
|
|
|
test "children can override parents" do
|
|
data = GrandchildOfOverloadedType.new(overloaded_float: '4.4')
|
|
|
|
assert_equal 4.4, data.overloaded_float
|
|
end
|
|
|
|
test "overloading properties does not attribute method order" do
|
|
attribute_names = OverloadedType.attribute_names
|
|
assert_equal %w(id overloaded_float unoverloaded_float overloaded_string_with_limit string_with_default non_existent_decimal), attribute_names
|
|
end
|
|
|
|
test "caches are cleared" do
|
|
klass = Class.new(OverloadedType)
|
|
|
|
assert_equal 6, klass.attribute_types.length
|
|
assert_equal 6, klass.column_defaults.length
|
|
assert_not klass.attribute_types.include?('wibble')
|
|
|
|
klass.attribute :wibble, Type::Value.new
|
|
|
|
assert_equal 7, klass.attribute_types.length
|
|
assert_equal 7, klass.column_defaults.length
|
|
assert klass.attribute_types.include?('wibble')
|
|
end
|
|
|
|
test "the given default value is cast from user" do
|
|
custom_type = Class.new(Type::Value) do
|
|
def type_cast_from_user(*)
|
|
"from user"
|
|
end
|
|
|
|
def type_cast_from_database(*)
|
|
"from database"
|
|
end
|
|
end
|
|
|
|
klass = Class.new(OverloadedType) do
|
|
attribute :wibble, custom_type.new, default: "default"
|
|
end
|
|
model = klass.new
|
|
|
|
assert_equal "from user", model.wibble
|
|
end
|
|
|
|
if current_adapter?(:PostgreSQLAdapter)
|
|
test "arrays types can be specified" do
|
|
klass = Class.new(OverloadedType) do
|
|
attribute :my_array, :string, limit: 50, array: true
|
|
attribute :my_int_array, :integer, array: true
|
|
end
|
|
|
|
string_array = ConnectionAdapters::PostgreSQL::OID::Array.new(
|
|
Type::String.new(limit: 50))
|
|
int_array = ConnectionAdapters::PostgreSQL::OID::Array.new(
|
|
Type::Integer.new)
|
|
refute_equal string_array, int_array
|
|
assert_equal string_array, klass.type_for_attribute("my_array")
|
|
assert_equal int_array, klass.type_for_attribute("my_int_array")
|
|
end
|
|
|
|
test "range types can be specified" do
|
|
klass = Class.new(OverloadedType) do
|
|
attribute :my_range, :string, limit: 50, range: true
|
|
attribute :my_int_range, :integer, range: true
|
|
end
|
|
|
|
string_range = ConnectionAdapters::PostgreSQL::OID::Range.new(
|
|
Type::String.new(limit: 50))
|
|
int_range = ConnectionAdapters::PostgreSQL::OID::Range.new(
|
|
Type::Integer.new)
|
|
refute_equal string_range, int_range
|
|
assert_equal string_range, klass.type_for_attribute("my_range")
|
|
assert_equal int_range, klass.type_for_attribute("my_int_range")
|
|
end
|
|
end
|
|
end
|
|
end
|