From 3fab9d8821bc73b28a76f27353cab34d49096ad2 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Sat, 7 Jun 2014 07:09:45 -0600 Subject: Rename `property` to `attribute` For consistency with https://github.com/rails/rails/pull/15557 --- .../attribute_methods/serialization.rb | 2 +- activerecord/lib/active_record/attributes.rb | 122 +++++++++++++++++++++ activerecord/lib/active_record/base.rb | 4 +- activerecord/lib/active_record/properties.rb | 122 --------------------- activerecord/test/cases/attributes_test.rb | 111 +++++++++++++++++++ activerecord/test/cases/base_test.rb | 6 +- activerecord/test/cases/calculations_test.rb | 6 +- activerecord/test/cases/custom_properties_test.rb | 111 ------------------- activerecord/test/cases/dirty_test.rb | 2 +- activerecord/test/cases/migration_test.rb | 6 +- 10 files changed, 246 insertions(+), 246 deletions(-) create mode 100644 activerecord/lib/active_record/attributes.rb delete mode 100644 activerecord/lib/active_record/properties.rb create mode 100644 activerecord/test/cases/attributes_test.rb delete mode 100644 activerecord/test/cases/custom_properties_test.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index 148fc9eae5..e749311268 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -62,7 +62,7 @@ module ActiveRecord if type.serialized? type = type.subtype end - property attr_name, Type::Serialized.new(type, coder) + attribute attr_name, Type::Serialized.new(type, coder) # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy # has its own hash of own serialized attributes diff --git a/activerecord/lib/active_record/attributes.rb b/activerecord/lib/active_record/attributes.rb new file mode 100644 index 0000000000..9c80121d70 --- /dev/null +++ b/activerecord/lib/active_record/attributes.rb @@ -0,0 +1,122 @@ +module ActiveRecord + module Attributes # :nodoc: + extend ActiveSupport::Concern + + Type = ActiveRecord::Type + + included do + class_attribute :user_provided_columns, instance_accessor: false # :internal: + self.user_provided_columns = {} + end + + module ClassMethods + # Defines or overrides a attribute on this model. This allows customization of + # Active Record's type casting behavior, as well as adding support for user defined + # types. + # + # +name+ The name of the methods to define attribute methods for, and the column which + # this will persist to. + # + # +cast_type+ A type object that contains information about how to type cast the value. + # See the examples section for more information. + # + # ==== Options + # The options hash accepts the following options: + # + # +default+ is the default value that the column should use on a new record. + # + # ==== Examples + # + # The type detected by Active Record can be overriden. + # + # # db/schema.rb + # create_table :store_listings, force: true do |t| + # t.decimal :price_in_cents + # end + # + # # app/models/store_listing.rb + # class StoreListing < ActiveRecord::Base + # end + # + # store_listing = StoreListing.new(price_in_cents: '10.1') + # + # # before + # store_listing.price_in_cents # => BigDecimal.new(10.1) + # + # class StoreListing < ActiveRecord::Base + # attribute :price_in_cents, Type::Integer.new + # end + # + # # after + # store_listing.price_in_cents # => 10 + # + # Users may also define their own custom types, as long as they respond to the methods + # defined on the value type. The `type_cast` method on your type object will be called + # with values both from the database, and from your controllers. See + # `ActiveRecord::Attributes::Type::Value` for the expected API. It is recommended that your + # type objects inherit from an existing type, or the base value type. + # + # class MoneyType < ActiveRecord::Type::Integer + # def type_cast(value) + # if value.include?('$') + # price_in_dollars = value.gsub(/\$/, '').to_f + # price_in_dollars * 100 + # else + # value.to_i + # end + # end + # end + # + # class StoreListing < ActiveRecord::Base + # attribute :price_in_cents, MoneyType.new + # end + # + # store_listing = StoreListing.new(price_in_cents: '$10.00') + # store_listing.price_in_cents # => 1000 + def attribute(name, cast_type, options = {}) + name = name.to_s + clear_caches_calculated_from_columns + # Assign a new hash to ensure that subclasses do not share a hash + self.user_provided_columns = user_provided_columns.merge(name => connection.new_column(name, options[:default], cast_type)) + end + + # Returns an array of column objects for the table associated with this class. + def columns + @columns ||= add_user_provided_columns(connection.schema_cache.columns(table_name)) + end + + # Returns a hash of column objects for the table associated with this class. + def columns_hash + @columns_hash ||= Hash[columns.map { |c| [c.name, c] }] + end + + def reset_column_information # :nodoc: + super + clear_caches_calculated_from_columns + end + + private + + def add_user_provided_columns(schema_columns) + existing_columns = schema_columns.map do |column| + user_provided_columns[column.name] || column + end + + existing_column_names = existing_columns.map(&:name) + new_columns = user_provided_columns.except(*existing_column_names).values + + existing_columns + new_columns + end + + def clear_caches_calculated_from_columns + @columns = nil + @columns_hash = nil + @column_types = nil + @column_defaults = nil + @raw_column_defaults = nil + @column_names = nil + @content_columns = nil + end + end + end +end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 8b0fffcf06..47de62a59b 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -19,7 +19,7 @@ require 'active_record/errors' require 'active_record/log_subscriber' require 'active_record/explain_subscriber' require 'active_record/relation/delegation' -require 'active_record/properties' +require 'active_record/attributes' module ActiveRecord #:nodoc: # = Active Record @@ -322,7 +322,7 @@ module ActiveRecord #:nodoc: include Reflection include Serialization include Store - include Properties + include Attributes end ActiveSupport.run_load_hooks(:active_record, Base) diff --git a/activerecord/lib/active_record/properties.rb b/activerecord/lib/active_record/properties.rb deleted file mode 100644 index 48ee42aaca..0000000000 --- a/activerecord/lib/active_record/properties.rb +++ /dev/null @@ -1,122 +0,0 @@ -module ActiveRecord - module Properties # :nodoc: - extend ActiveSupport::Concern - - Type = ActiveRecord::Type - - included do - class_attribute :user_provided_columns, instance_accessor: false # :internal: - self.user_provided_columns = {} - end - - module ClassMethods - # Defines or overrides a property on this model. This allows customization of - # Active Record's type casting behavior, as well as adding support for user defined - # types. - # - # +name+ The name of the methods to define attribute methods for, and the column which - # this will persist to. - # - # +cast_type+ A type object that contains information about how to type cast the value. - # See the examples section for more information. - # - # ==== Options - # The options hash accepts the following options: - # - # +default+ is the default value that the column should use on a new record. - # - # ==== Examples - # - # The type detected by Active Record can be overriden. - # - # # db/schema.rb - # create_table :store_listings, force: true do |t| - # t.decimal :price_in_cents - # end - # - # # app/models/store_listing.rb - # class StoreListing < ActiveRecord::Base - # end - # - # store_listing = StoreListing.new(price_in_cents: '10.1') - # - # # before - # store_listing.price_in_cents # => BigDecimal.new(10.1) - # - # class StoreListing < ActiveRecord::Base - # property :price_in_cents, Type::Integer.new - # end - # - # # after - # store_listing.price_in_cents # => 10 - # - # Users may also define their own custom types, as long as they respond to the methods - # defined on the value type. The `type_cast` method on your type object will be called - # with values both from the database, and from your controllers. See - # `ActiveRecord::Properties::Type::Value` for the expected API. It is recommended that your - # type objects inherit from an existing type, or the base value type. - # - # class MoneyType < ActiveRecord::Type::Integer - # def type_cast(value) - # if value.include?('$') - # price_in_dollars = value.gsub(/\$/, '').to_f - # price_in_dollars * 100 - # else - # value.to_i - # end - # end - # end - # - # class StoreListing < ActiveRecord::Base - # property :price_in_cents, MoneyType.new - # end - # - # store_listing = StoreListing.new(price_in_cents: '$10.00') - # store_listing.price_in_cents # => 1000 - def property(name, cast_type, options = {}) - name = name.to_s - clear_caches_calculated_from_columns - # Assign a new hash to ensure that subclasses do not share a hash - self.user_provided_columns = user_provided_columns.merge(name => connection.new_column(name, options[:default], cast_type)) - end - - # Returns an array of column objects for the table associated with this class. - def columns - @columns ||= add_user_provided_columns(connection.schema_cache.columns(table_name)) - end - - # Returns a hash of column objects for the table associated with this class. - def columns_hash - @columns_hash ||= Hash[columns.map { |c| [c.name, c] }] - end - - def reset_column_information # :nodoc: - super - clear_caches_calculated_from_columns - end - - private - - def add_user_provided_columns(schema_columns) - existing_columns = schema_columns.map do |column| - user_provided_columns[column.name] || column - end - - existing_column_names = existing_columns.map(&:name) - new_columns = user_provided_columns.except(*existing_column_names).values - - existing_columns + new_columns - end - - def clear_caches_calculated_from_columns - @columns = nil - @columns_hash = nil - @column_types = nil - @column_defaults = nil - @raw_column_defaults = nil - @column_names = nil - @content_columns = nil - end - end - end -end diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb new file mode 100644 index 0000000000..79ef0502cb --- /dev/null +++ b/activerecord/test/cases/attributes_test.rb @@ -0,0 +1,111 @@ +require 'cases/helper' + +class OverloadedType < ActiveRecord::Base + attribute :overloaded_float, Type::Integer.new + attribute :overloaded_string_with_limit, Type::String.new(limit: 50) + attribute :non_existent_decimal, Type::Decimal.new + attribute :string_with_default, Type::String.new, default: 'the overloaded default' +end + +class ChildOfOverloadedType < OverloadedType +end + +class GrandchildOfOverloadedType < ChildOfOverloadedType + attribute :overloaded_float, Type::Float.new +end + +class UnoverloadedType < ActiveRecord::Base + self.table_name = 'overloaded_types' +end + +module ActiveRecord + class CustomPropertiesTest < ActiveRecord::TestCase + def test_overloading_types + 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 + + def test_overloaded_properties_save + 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 + + def test_properties_assigned_in_constructor + data = OverloadedType.new(overloaded_float: '3.3') + + assert_equal 3, data.overloaded_float + end + + def test_overloaded_properties_with_limit + assert_equal 50, OverloadedType.columns_hash['overloaded_string_with_limit'].limit + assert_equal 255, UnoverloadedType.columns_hash['overloaded_string_with_limit'].limit + end + + def test_nonexistent_attribute + data = OverloadedType.new(non_existent_decimal: 1) + + assert_equal BigDecimal.new(1), data.non_existent_decimal + assert_raise ActiveRecord::UnknownAttributeError do + UnoverloadedType.new(non_existent_decimal: 1) + end + end + + def test_changing_defaults + 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 + + def test_children_inherit_custom_properties + data = ChildOfOverloadedType.new(overloaded_float: '4.4') + + assert_equal 4, data.overloaded_float + end + + def test_children_can_override_parents + data = GrandchildOfOverloadedType.new(overloaded_float: '4.4') + + assert_equal 4.4, data.overloaded_float + end + + def test_overloading_properties_does_not_change_column_order + column_names = OverloadedType.column_names + assert_equal %w(id overloaded_float unoverloaded_float overloaded_string_with_limit string_with_default non_existent_decimal), column_names + end + + def test_caches_are_cleared + klass = Class.new(OverloadedType) + + assert_equal 6, klass.columns.length + assert_not klass.columns_hash.key?('wibble') + assert_equal 6, klass.column_types.length + assert_equal 6, klass.column_defaults.length + assert_not klass.column_names.include?('wibble') + assert_equal 5, klass.content_columns.length + + klass.attribute :wibble, Type::Value.new + + assert_equal 7, klass.columns.length + assert klass.columns_hash.key?('wibble') + assert_equal 7, klass.column_types.length + assert_equal 7, klass.column_defaults.length + assert klass.column_names.include?('wibble') + assert_equal 6, klass.content_columns.length + end + end +end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index f01901514e..fad51f4924 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -985,9 +985,9 @@ class BasicsTest < ActiveRecord::TestCase class NumericData < ActiveRecord::Base self.table_name = 'numeric_data' - property :world_population, Type::Integer.new - property :my_house_population, Type::Integer.new - property :atoms_in_universe, Type::Integer.new + attribute :world_population, Type::Integer.new + attribute :my_house_population, Type::Integer.new + attribute :atoms_in_universe, Type::Integer.new end def test_big_decimal_conditions diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index b9445ee072..dfc6a7ec67 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -16,9 +16,9 @@ Company.has_many :accounts class NumericData < ActiveRecord::Base self.table_name = 'numeric_data' - property :world_population, Type::Integer.new - property :my_house_population, Type::Integer.new - property :atoms_in_universe, Type::Integer.new + attribute :world_population, Type::Integer.new + attribute :my_house_population, Type::Integer.new + attribute :atoms_in_universe, Type::Integer.new end class CalculationsTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/custom_properties_test.rb b/activerecord/test/cases/custom_properties_test.rb deleted file mode 100644 index 9ba1e83df6..0000000000 --- a/activerecord/test/cases/custom_properties_test.rb +++ /dev/null @@ -1,111 +0,0 @@ -require 'cases/helper' - -class OverloadedType < ActiveRecord::Base - property :overloaded_float, Type::Integer.new - property :overloaded_string_with_limit, Type::String.new(limit: 50) - property :non_existent_decimal, Type::Decimal.new - property :string_with_default, Type::String.new, default: 'the overloaded default' -end - -class ChildOfOverloadedType < OverloadedType -end - -class GrandchildOfOverloadedType < ChildOfOverloadedType - property :overloaded_float, Type::Float.new -end - -class UnoverloadedType < ActiveRecord::Base - self.table_name = 'overloaded_types' -end - -module ActiveRecord - class CustomPropertiesTest < ActiveRecord::TestCase - def test_overloading_types - 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 - - def test_overloaded_properties_save - 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 - - def test_properties_assigned_in_constructor - data = OverloadedType.new(overloaded_float: '3.3') - - assert_equal 3, data.overloaded_float - end - - def test_overloaded_properties_with_limit - assert_equal 50, OverloadedType.columns_hash['overloaded_string_with_limit'].limit - assert_equal 255, UnoverloadedType.columns_hash['overloaded_string_with_limit'].limit - end - - def test_nonexistent_property - data = OverloadedType.new(non_existent_decimal: 1) - - assert_equal BigDecimal.new(1), data.non_existent_decimal - assert_raise ActiveRecord::UnknownAttributeError do - UnoverloadedType.new(non_existent_decimal: 1) - end - end - - def test_changing_defaults - 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 - - def test_children_inherit_custom_properties - data = ChildOfOverloadedType.new(overloaded_float: '4.4') - - assert_equal 4, data.overloaded_float - end - - def test_children_can_override_parents - data = GrandchildOfOverloadedType.new(overloaded_float: '4.4') - - assert_equal 4.4, data.overloaded_float - end - - def test_overloading_properties_does_not_change_column_order - column_names = OverloadedType.column_names - assert_equal %w(id overloaded_float unoverloaded_float overloaded_string_with_limit string_with_default non_existent_decimal), column_names - end - - def test_caches_are_cleared - klass = Class.new(OverloadedType) - - assert_equal 6, klass.columns.length - assert_not klass.columns_hash.key?('wibble') - assert_equal 6, klass.column_types.length - assert_equal 6, klass.column_defaults.length - assert_not klass.column_names.include?('wibble') - assert_equal 5, klass.content_columns.length - - klass.property :wibble, Type::Value.new - - assert_equal 7, klass.columns.length - assert klass.columns_hash.key?('wibble') - assert_equal 7, klass.column_types.length - assert_equal 7, klass.column_defaults.length - assert klass.column_names.include?('wibble') - assert_equal 6, klass.content_columns.length - end - end -end diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb index 987c55ebc2..2beac84fb1 100644 --- a/activerecord/test/cases/dirty_test.rb +++ b/activerecord/test/cases/dirty_test.rb @@ -631,7 +631,7 @@ class DirtyTest < ActiveRecord::TestCase model_class = Class.new(ActiveRecord::Base) do self.table_name = 'numeric_data' - property :foo, type.new, default: 1 + attribute :foo, type.new, default: 1 end model = model_class.new diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 5b4531846d..9855835e27 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -13,10 +13,10 @@ require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers" class BigNumber < ActiveRecord::Base unless current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) - property :value_of_e, Type::Integer.new + attribute :value_of_e, Type::Integer.new end - property :world_population, Type::Integer.new - property :my_house_population, Type::Integer.new + attribute :world_population, Type::Integer.new + attribute :my_house_population, Type::Integer.new end class Reminder < ActiveRecord::Base; end -- cgit v1.2.3