diff options
Diffstat (limited to 'activerecord/lib')
14 files changed, 72 insertions, 77 deletions
diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 35ad512537..954128064d 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -116,7 +116,7 @@ module ActiveRecord end def target_reflection_has_associated_record? - !(through_reflection.macro == :belongs_to && owner[through_reflection.foreign_key].blank?) + !(through_reflection.belongs_to? && owner[through_reflection.foreign_key].blank?) end def update_through_counter?(method) diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 63773bd5e1..1b83700613 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -104,11 +104,13 @@ module ActiveRecord end def association_key_type - @klass.column_types[association_key_name.to_s].type + column = @klass.column_types[association_key_name.to_s] + column && column.type end def owner_key_type - @model.column_types[owner_key_name.to_s].type + column = @model.column_types[owner_key_name.to_s] + column && column.type end def load_slices(slices) diff --git a/activerecord/lib/active_record/associations/through_association.rb b/activerecord/lib/active_record/associations/through_association.rb index f8a85b8a6f..fcf3b219d4 100644 --- a/activerecord/lib/active_record/associations/through_association.rb +++ b/activerecord/lib/active_record/associations/through_association.rb @@ -63,14 +63,13 @@ module ActiveRecord # Note: this does not capture all cases, for example it would be crazy to try to # properly support stale-checking for nested associations. def stale_state - if through_reflection.macro == :belongs_to + if through_reflection.belongs_to? owner[through_reflection.foreign_key] && owner[through_reflection.foreign_key].to_s end end def foreign_key_present? - through_reflection.macro == :belongs_to && - !owner[through_reflection.foreign_key].nil? + through_reflection.belongs_to? && !owner[through_reflection.foreign_key].nil? end def ensure_mutable diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index b6520b9b3d..e56a4cc805 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -350,10 +350,12 @@ module ActiveRecord # # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...> # # person.column_for_attribute(:nothing) - # # => nil + # # => #<ActiveRecord::ConnectionAdapters::Column:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...> def column_for_attribute(name) - # FIXME: should this return a null object for columns that don't exist? - self.class.columns_hash[name.to_s] + name = name.to_s + self.class.columns_hash.fetch(name) do + ConnectionAdapters::Column.new(name, nil, Type::Value.new) + end end # Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example, @@ -438,16 +440,16 @@ module ActiveRecord # Filters the primary keys and readonly attributes from the attribute names. def attributes_for_update(attribute_names) - attribute_names.select do |name| - column_for_attribute(name) && !readonly_attribute?(name) + attribute_names.reject do |name| + readonly_attribute?(name) end end # Filters out the primary keys, from the attribute names, when the primary # key is to be generated (e.g. the id attribute has no value). def attributes_for_create(attribute_names) - attribute_names.select do |name| - column_for_attribute(name) && !(pk_attribute?(name) && id.nil?) + attribute_names.reject do |name| + pk_attribute?(name) && id.nil? end end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 6cd4e43ddd..4e32b78e34 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -94,8 +94,7 @@ module ActiveRecord end def _field_changed?(attr, old, value) - column = column_for_attribute(attr) || Type::Value.new - column.changed?(old, value) + column_for_attribute(attr).changed?(old, value) end end end diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index c3e601a208..5203b30462 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -72,18 +72,20 @@ module ActiveRecord @attributes.delete(attr_name) column = column_for_attribute(attr_name) + unless has_attribute?(attr_name) || self.class.columns_hash.key?(attr_name) + raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" + end + # If we're dealing with a binary column, write the data to the cache # so we don't attempt to typecast multiple times. - if column && column.binary? + if column.binary? @attributes[attr_name] = value end - if column && should_type_cast + if should_type_cast @raw_attributes[attr_name] = column.type_cast_for_write(value) - elsif !should_type_cast || @raw_attributes.has_key?(attr_name) - @raw_attributes[attr_name] = value else - raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'" + @raw_attributes[attr_name] = value end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 117c0f0969..a9b3e9cfb9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -102,8 +102,8 @@ module ActiveRecord # * <tt>:index</tt> - # Create an index for the column. Can be either <tt>true</tt> or an options hash. # - # For clarity's sake: the precision is the number of significant digits, - # while the scale is the number of digits that can be stored following + # Note: The precision is the total number of significant digits + # and the scale is the number of digits that can be stored following # the decimal point. For example, the number 123.45 has a precision of 5 # and a scale of 2. A decimal with a precision of 5 and a scale of 2 can # range from -999.99 to 999.99. diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 34054589d0..3b0dcbc6a7 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -52,7 +52,7 @@ module ActiveRecord end def extract_default(default) - type_cast_for_write(type_cast(default)) + type_cast(default) end 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 67570dad3c..0f20d52879 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -511,58 +511,16 @@ module ActiveRecord # makes this method very very slow. return default unless default - # TODO: The default extraction is related to the cast-type. - # we should probably make a type_map lookup and cast the default- - # expression accordingly - if oid.type == :enum && default =~ /\A'(.*)'::/ - return $1 - end - case default - when /\A'(.*)'::(num|date|tstz|ts|int4|int8)range\z/m - $1 + # Quoted types + when /\A[\(B]?'(.*)'::/m + $1.gsub(/''/, "'") + # Boolean types + when 'true', 'false' + default # Numeric types when /\A\(?(-?\d+(\.\d*)?\)?(::bigint)?)\z/ $1 - # Character types - when /\A\(?'(.*)'::.*\b(?:character varying|bpchar|text)\z/m - $1.gsub(/''/, "'") - # Binary data types - when /\A'(.*)'::bytea\z/m - $1 - # Date/time types - when /\A'(.+)'::(?:time(?:stamp)? with(?:out)? time zone|date)\z/ - $1 - when /\A'(.*)'::interval\z/ - $1 - # Boolean type - when 'true' - true - when 'false' - false - # Geometric types - when /\A'(.*)'::(?:point|line|lseg|box|"?path"?|polygon|circle)\z/ - $1 - # Network address types - when /\A'(.*)'::(?:cidr|inet|macaddr)\z/ - $1 - # Bit string types - when /\AB'(.*)'::"?bit(?: varying)?"?\z/ - $1 - # XML type - when /\A'(.*)'::xml\z/m - $1 - # Arrays - when /\A'(.*)'::"?\D+"?\[\]\z/ - $1 - # Hstore - when /\A'(.*)'::hstore\z/ - $1 - # JSON - when /\A'(.*)'::json\z/ - $1 - when /\A'(.*)'::money\z/ - $1 # Object identifier types when /\A-?\d+\z/ $1 diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 1d75eeda69..d6849fef2e 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -249,7 +249,7 @@ module ActiveRecord # # Instantiates a single new object # User.new(first_name: 'Jamie') def initialize(attributes = nil, options = {}) - defaults = self.class.column_defaults.dup + defaults = self.class.raw_column_defaults.dup defaults.each { |k, v| defaults[k] = v.dup if v.duplicable? } @raw_attributes = defaults @@ -432,6 +432,29 @@ module ActiveRecord "#<#{self.class} #{inspection}>" end + # Takes a PP and prettily prints this record to it, allowing you to get a nice result from `pp record` + # when pp is required. + def pretty_print(pp) + pp.object_address_group(self) do + if defined?(@attributes) && @attributes + column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? } + pp.seplist(column_names, proc { pp.text ',' }) do |column_name| + column_value = read_attribute(column_name) + pp.breakable ' ' + pp.group(1) do + pp.text column_name + pp.text ':' + pp.breakable + pp.pp column_value + end + end + else + pp.breakable ' ' + pp.text 'not initialized' + end + end + end + # Returns a hash of the given methods with their names as keys and returned values as values. def slice(*methods) Hash[methods.map! { |method| [method, public_send(method)] }].with_indifferent_access diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index f0ddf202c3..baf2b5fbf8 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -241,6 +241,14 @@ module ActiveRecord @column_defaults ||= Hash[columns.map { |c| [c.name, c.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_write(default)] + }] + end + # Returns an array of column names as strings. def column_names @column_names ||= columns.map { |column| column.name } @@ -285,6 +293,7 @@ module ActiveRecord @arel_engine = nil @column_defaults = nil + @raw_column_defauts = nil @column_names = nil @column_types = nil @content_columns = nil diff --git a/activerecord/lib/active_record/properties.rb b/activerecord/lib/active_record/properties.rb index 5fd51e09fa..48ee42aaca 100644 --- a/activerecord/lib/active_record/properties.rb +++ b/activerecord/lib/active_record/properties.rb @@ -113,6 +113,7 @@ module ActiveRecord @columns_hash = nil @column_types = nil @column_defaults = nil + @raw_column_defaults = nil @column_names = nil @content_columns = nil end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 9ee6422329..11ab1b4595 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -278,7 +278,7 @@ module ActiveRecord if group_attrs.first.respond_to?(:to_sym) association = @klass._reflect_on_association(group_attrs.first.to_sym) - associated = group_attrs.size == 1 && association && association.macro == :belongs_to # only count belongs_to associations + associated = group_attrs.size == 1 && association && association.belongs_to? # only count belongs_to associations group_fields = Array(associated ? association.foreign_key : group_attrs) else group_fields = group_attrs diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index 1a766093d0..019fe2218e 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -180,12 +180,12 @@ module ActiveRecord #:nodoc: class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc: def compute_type klass = @serializable.class - type = if klass.serialized_attributes.key?(name) + column = klass.columns_hash[name] || Type::Value.new + + type = if column.serialized? super - elsif klass.columns_hash.key?(name) - klass.columns_hash[name].type else - NilClass + column.type end { :text => :string, |