diff options
Diffstat (limited to 'activerecord')
53 files changed, 462 insertions, 190 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index e0e107e89d..ad4ee0f489 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,22 @@ +* MySQL enum type lookups, with values matching another type, no longer result + in an endless loop. + + Fixes #17402. + + *Yves Senn* + +* Raise `ArgumentError` when the body of a scope is not callable. + + *Mauro George* + +* Use type column first in multi-column indexes created with `add-reference`. + + *Derek Prior* + +* Fix `Relation.rewhere` to work with Range values. + + *Dan Olson* + * `AR::UnknownAttributeError` now includes the class name of a record. User.new(name: "Yuki Nishijima", project_attributes: {name: "kaminari"}) diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 6231851be5..bc10e96244 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -24,5 +24,5 @@ Gem::Specification.new do |s| s.add_dependency 'activesupport', version s.add_dependency 'activemodel', version - s.add_dependency 'arel', '>= 6.0.0.beta1', '< 6.1' + s.add_dependency 'arel', '>= 6.0.0.beta2', '< 6.1' end 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 455a540bdb..04a69ba446 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/filters' + module ActiveRecord # = Active Record Has Many Through Association module Associations @@ -63,11 +65,12 @@ module ActiveRecord save_through_record(record) if has_cached_counter? && !through_reflection_updates_counter_cache? - ActiveSupport::Deprecation.warn \ - "Automatic updating of counter caches on through associations has been " \ - "deprecated, and will be removed in Rails 5.0. Instead, please set the " \ - "appropriate counter_cache options on the has_many and belongs_to for " \ - "your associations to #{through_reflection.name}." + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Automatic updating of counter caches on through associations has been + deprecated, and will be removed in Rails 5. Instead, please set the + appropriate `counter_cache` options on the `has_many` and `belongs_to` + for your associations to #{through_reflection.name}. + MSG update_counter_in_database(1) end @@ -159,7 +162,7 @@ module ActiveRecord count = scope.destroy_all.length else scope.to_a.each do |record| - record.run_destroy_callbacks + record._run_destroy_callbacks end arel = scope.arel diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index f4a4e3f605..34ec397aee 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/enumerable' +require 'active_support/core_ext/string/filters' require 'mutex_m' require 'thread_safe' @@ -90,7 +91,7 @@ module ActiveRecord def undefine_attribute_methods # :nodoc: generated_attribute_methods.synchronize do - super if @attribute_methods_generated + super if defined?(@attribute_methods_generated) && @attribute_methods_generated @attribute_methods_generated = false end end @@ -205,9 +206,11 @@ module ActiveRecord def column_for_attribute(name) column = columns_hash[name.to_s] if column.nil? - ActiveSupport::Deprecation.warn \ - "`column_for_attribute` will return a null object for non-existent columns " \ - "in Rails 5.0. Use `has_attribute?` if you need to check for an attribute's existence." + ActiveSupport::Deprecation.warn(<<-MSG.squish) + `#column_for_attribute` will return a null object for non-existent + columns in Rails 5. Use `#has_attribute?` if you need to check for + an attribute's existence. + MSG end column end diff --git a/activerecord/lib/active_record/attribute_methods/serialization.rb b/activerecord/lib/active_record/attribute_methods/serialization.rb index cc82d00b20..e5ec5ddca5 100644 --- a/activerecord/lib/active_record/attribute_methods/serialization.rb +++ b/activerecord/lib/active_record/attribute_methods/serialization.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/filters' + module ActiveRecord module AttributeMethods module Serialization @@ -51,8 +53,10 @@ module ActiveRecord end def serialized_attributes - ActiveSupport::Deprecation.warn "`serialized_attributes` is deprecated " \ - "without replacement, and will be removed in Rails 5.0." + ActiveSupport::Deprecation.warn(<<-MSG.squish) + `serialized_attributes` is deprecated without replacement, and will + be removed in Rails 5.0. + MSG @serialized_attributes ||= Hash[ columns.select { |t| t.cast_type.is_a?(Type::Serialized) }.map { |c| diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 1aa760157a..523d492a48 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -289,25 +289,25 @@ module ActiveRecord end def destroy #:nodoc: - run_destroy_callbacks { super } + _run_destroy_callbacks { super } end def touch(*) #:nodoc: - run_touch_callbacks { super } + _run_touch_callbacks { super } end private def create_or_update #:nodoc: - run_save_callbacks { super } + _run_save_callbacks { super } end def _create_record #:nodoc: - run_create_callbacks { super } + _run_create_callbacks { super } end def _update_record(*) #:nodoc: - run_update_callbacks { super } + _run_update_callbacks { super } end end end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index da43e5bb10..46812b75bb 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -2,6 +2,7 @@ require 'thread' require 'thread_safe' require 'monitor' require 'set' +require 'active_support/core_ext/string/filters' module ActiveRecord # Raised when a connection could not be obtained within the connection @@ -360,7 +361,7 @@ module ActiveRecord synchronize do owner = conn.owner - conn.run_checkin_callbacks do + conn._run_checkin_callbacks do conn.expire end @@ -449,7 +450,7 @@ module ActiveRecord end def checkout_and_verify(c) - c.run_checkout_callbacks do + c._run_checkout_callbacks do c.verify! end c @@ -518,10 +519,11 @@ module ActiveRecord end def connection_pools - ActiveSupport::Deprecation.warn( - "In the next release, this will return the same as #connection_pool_list. " \ - "(An array of pools, rather than a hash mapping specs to pools.)" - ) + ActiveSupport::Deprecation.warn(<<-MSG.squish) + In the next release, this will return the same as `#connection_pool_list`. + (An array of pools, rather than a hash mapping specs to pools.) + MSG + Hash[connection_pool_list.map { |pool| [pool.spec, pool] }] end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index eb88845913..679878d860 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -108,7 +108,7 @@ module ActiveRecord when Numeric, ActiveSupport::Duration then value.to_s when Date, Time then "'#{quoted_date(value)}'" when Symbol then "'#{quote_string(value.to_s)}'" - when Class then "'#{value.to_s}'" + when Class then "'#{value}'" else "'#{quote_string(YAML.dump(value))}'" 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 adb9fcaeb8..f8b6daea5a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -60,11 +60,11 @@ module ActiveRecord def emit_warning_if_null_unspecified(options) return if options.key?(:null) - ActiveSupport::Deprecation.warn \ - "`timestamp` was called without specifying an option for `null`. In Rails " \ - "5.0, this behavior will change to `null: false`. You should manually " \ - "specify `null: true` to prevent the behavior of your existing migrations " \ - "from changing." + ActiveSupport::Deprecation.warn(<<-MSG.squish) + `#timestamp` was called without specifying an option for `null`. In Rails 5, + this behavior will change to `null: false`. You should manually specify + `null: true` to prevent the behavior of your existing migrations from changing. + MSG end end @@ -312,7 +312,7 @@ module ActiveRecord args.each do |col| column("#{col}_id", type, options) column("#{col}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic - index(polymorphic ? %w(id type).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options + index(polymorphic ? %w(type id).map { |t| "#{col}_#{t}" } : "#{col}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options end end alias :belongs_to :references diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb index b05a4f8440..6eab11b88b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_dumper.rb @@ -8,7 +8,7 @@ module ActiveRecord module ColumnDumper def column_spec(column, types) spec = prepare_column_options(column, types) - (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k.to_s}: ")} + (spec.keys - [:name, :type]).each{ |k| spec[k].insert(0, "#{k}: ")} spec end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 7105df1ee4..cbf87df356 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -629,7 +629,7 @@ module ActiveRecord type = options.delete(:type) || :integer add_column(table_name, "#{ref_name}_id", type, options) add_column(table_name, "#{ref_name}_type", :string, polymorphic.is_a?(Hash) ? polymorphic : options) if polymorphic - add_index(table_name, polymorphic ? %w[id type].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options + add_index(table_name, polymorphic ? %w[type id].map{ |t| "#{ref_name}_#{t}" } : "#{ref_name}_id", index_options.is_a?(Hash) ? index_options : {}) if index_options end alias :add_belongs_to :add_reference diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index e4cfe843a8..7f04e35042 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -91,6 +91,13 @@ module ActiveRecord collation && !collation.match(/_ci$/) end + def ==(other) + super && + collation == other.collation && + strict == other.strict && + extra == other.extra + end + private # MySQL misreports NOT NULL column default when none is given. @@ -109,6 +116,10 @@ module ActiveRecord raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}" end end + + def attributes_for_hash + super + [collation, strict, extra] + end end ## @@ -485,7 +496,7 @@ module ActiveRecord end end - def change_column_default(table_name, column_name, default) + def change_column_default(table_name, column_name, default) #:nodoc: column = column_for(table_name, column_name) change_column table_name, column_name, column.sql_type, :default => default end @@ -645,12 +656,6 @@ module ActiveRecord def initialize_type_map(m) # :nodoc: super - m.register_type(%r(enum)i) do |sql_type| - limit = sql_type[/^enum\((.+)\)/i, 1] - .split(',').map{|enum| enum.strip.length - 2}.max - Type::String.new(limit: limit) - end - m.register_type %r(tinytext)i, Type::Text.new(limit: 2**8 - 1) m.register_type %r(tinyblob)i, Type::Binary.new(limit: 2**8 - 1) m.register_type %r(text)i, Type::Text.new(limit: 2**16 - 1) @@ -671,6 +676,12 @@ module ActiveRecord m.alias_type %r(set)i, 'varchar' m.alias_type %r(year)i, 'integer' m.alias_type %r(bit)i, 'binary' + + m.register_type(%r(enum)i) do |sql_type| + limit = sql_type[/^enum\((.+)\)/i, 1] + .split(',').map{|enum| enum.strip.length - 2}.max + Type::String.new(limit: limit) + end end # MySQL is too stupid to create a temporary table for use subquery, so we have @@ -825,9 +836,9 @@ module ActiveRecord # Gather up all of the SET variables... variable_assignments = variables.map do |k, v| if v == ':default' || v == :default - "@@SESSION.#{k.to_s} = DEFAULT" # Sets the value to the global or compile default + "@@SESSION.#{k} = DEFAULT" # Sets the value to the global or compile default elsif !v.nil? - "@@SESSION.#{k.to_s} = #{quote(v)}" + "@@SESSION.#{k} = #{quote(v)}" end # or else nil; compact to clear nils out end.compact.join(', ') diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index 5f9cc6edd0..dd303c73d5 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -56,6 +56,26 @@ module ActiveRecord clone.instance_variable_set('@cast_type', type) end end + + def ==(other) + other.name == name && + other.default == default && + other.cast_type == cast_type && + other.sql_type == sql_type && + other.null == null && + other.default_function == default_function + end + alias :eql? :== + + def hash + attributes_for_hash.hash + end + + private + + def attributes_for_hash + [self.class, name, default, cast_type, sql_type, null, default_function] + end end end # :startdoc: diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index e02824b33d..609ec7dabd 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -1,4 +1,5 @@ require 'uri' +require 'active_support/core_ext/string/filters' module ActiveRecord module ConnectionAdapters @@ -221,8 +222,12 @@ module ActiveRecord # this ambiguous behaviour and in the future this function # can be removed in favor of resolve_url_connection. if configurations.key?(spec) || spec !~ /:/ - ActiveSupport::Deprecation.warn "Passing a string to ActiveRecord::Base.establish_connection " \ - "for a configuration lookup is deprecated, please pass a symbol (#{spec.to_sym.inspect}) instead" + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing a string to ActiveRecord::Base.establish_connection for a + configuration lookup is deprecated, please pass a symbol + (#{spec.to_sym.inspect}) instead. + MSG + resolve_symbol_connection(spec) else resolve_url_connection(spec) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb index a53b4ee8e2..222f10fa8f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/cidr.rb @@ -12,15 +12,15 @@ module ActiveRecord # If the subnet mask is equal to /32, don't output it if subnet_mask == (2**32 - 1) - "\"#{value.to_s}\"" + "\"#{value}\"" else - "\"#{value.to_s}/#{subnet_mask.to_s(2).count('1')}\"" + "\"#{value}/#{subnet_mask.to_s(2).count('1')}\"" end end def type_cast_for_database(value) if IPAddr === value - "#{value.to_s}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}" + "#{value}/#{value.instance_variable_get(:@mask_addr).to_s(2).count('1')}" else value end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb index 84b9490ba3..961e6224c4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/range.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/filters' + module ActiveRecord module ConnectionAdapters module PostgreSQL @@ -25,10 +27,11 @@ module ActiveRecord if !infinity?(from) && extracted[:exclude_start] if from.respond_to?(:succ) from = from.succ - ActiveSupport::Deprecation.warn \ - "Excluding the beginning of a Range is only partialy supported " \ - "through `#succ`. This is not reliable and will be removed in " \ - "the future." + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Excluding the beginning of a Range is only partialy supported + through `#succ`. This is not reliable and will be removed in + the future. + MSG else raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')" end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb index e396ff4a1e..35e699eeda 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb @@ -4,7 +4,7 @@ module ActiveRecord module OID # :nodoc: # This class uses the data from PostgreSQL pg_type table to build # the OID -> Type mapping. - # - OID is and integer representing the type. + # - OID is an integer representing the type. # - Type is an OID::Type object. # This class has side effects on the +store+ passed during initialization. class TypeMapInitializer # :nodoc: diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index cf5c8d288e..f95f45c689 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -21,7 +21,7 @@ module ActiveRecord case value when Float if value.infinite? || value.nan? - "'#{value.to_s}'" + "'#{value}'" else super end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 3294238fa0..8e209e1051 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -78,6 +78,7 @@ module ActiveRecord NATIVE_DATABASE_TYPES = { primary_key: "serial primary key", + bigserial: "bigserial", string: { name: "character varying" }, text: { name: "text" }, integer: { name: "integer" }, @@ -678,9 +679,9 @@ module ActiveRecord variables.map do |k, v| if v == ':default' || v == :default # Sets the value to the global or compile default - execute("SET SESSION #{k.to_s} TO DEFAULT", 'SCHEMA') + execute("SET SESSION #{k} TO DEFAULT", 'SCHEMA') elsif !v.nil? - execute("SET SESSION #{k.to_s} TO #{quote(v)}", 'SCHEMA') + execute("SET SESSION #{k} TO #{quote(v)}", 'SCHEMA') end end end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 5571a2d297..952aeaa703 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -1,6 +1,7 @@ +require 'thread' require 'active_support/core_ext/hash/indifferent_access' require 'active_support/core_ext/object/duplicable' -require 'thread' +require 'active_support/core_ext/string/filters' module ActiveRecord module Core @@ -88,8 +89,10 @@ module ActiveRecord mattr_accessor :maintain_test_schema, instance_accessor: false def self.disable_implicit_join_references=(value) - ActiveSupport::Deprecation.warn("Implicit join references were removed with Rails 4.1." \ - "Make sure to remove this configuration because it does nothing.") + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Implicit join references were removed with Rails 4.1. + Make sure to remove this configuration because it does nothing. + MSG end class_attribute :default_connection_handler, instance_writer: false @@ -135,8 +138,10 @@ module ActiveRecord id = ids.first if ActiveRecord::Base === id id = id.id - ActiveSupport::Deprecation.warn "You are passing an instance of ActiveRecord::Base to `find`." \ - "Please pass the id of the object by calling `.id`" + ActiveSupport::Deprecation.warn(<<-MSG.squish) + You are passing an instance of ActiveRecord::Base to `find`. + Please pass the id of the object by calling `.id` + MSG end key = primary_key @@ -150,6 +155,8 @@ module ActiveRecord raise RecordNotFound, "Couldn't find #{name} with '#{primary_key}'=#{id}" end record + rescue RangeError + raise RecordNotFound, "Couldn't find #{name} with an out of range value for '#{primary_key}'" end def find_by(*args) @@ -180,6 +187,8 @@ module ActiveRecord s.execute(hash.values, self, connection).first rescue TypeError => e raise ActiveRecord::StatementInvalid.new(e.message, e) + rescue RangeError + nil end end @@ -261,7 +270,7 @@ module ActiveRecord # # Instantiates a single new object # User.new(first_name: 'Jamie') def initialize(attributes = nil, options = {}) - @attributes = self.class.default_attributes.dup + @attributes = self.class._default_attributes.dup init_internals initialize_internals_callback @@ -272,7 +281,7 @@ module ActiveRecord init_attributes(attributes, options) if attributes yield self if block_given? - run_initialize_callbacks + _run_initialize_callbacks end # Initialize an empty model object from +coder+. +coder+ must contain @@ -294,8 +303,8 @@ module ActiveRecord self.class.define_attribute_methods - run_find_callbacks - run_initialize_callbacks + _run_find_callbacks + _run_initialize_callbacks self end @@ -331,7 +340,7 @@ module ActiveRecord @attributes = @attributes.dup @attributes.reset(self.class.primary_key) - run_initialize_callbacks + _run_initialize_callbacks @aggregation_cache = {} @association_cache = {} diff --git a/activerecord/lib/active_record/gem_version.rb b/activerecord/lib/active_record/gem_version.rb index 91747d2ccf..e820835626 100644 --- a/activerecord/lib/active_record/gem_version.rb +++ b/activerecord/lib/active_record/gem_version.rb @@ -8,7 +8,7 @@ module ActiveRecord MAJOR = 4 MINOR = 2 TINY = 0 - PRE = "beta2" + PRE = "beta4" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 251d682a02..f58145ab05 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -80,12 +80,12 @@ module ActiveRecord end def symbolized_base_class - ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_base_class is deprecated and will be removed without replacement.") + ActiveSupport::Deprecation.warn('`ActiveRecord::Base.symbolized_base_class` is deprecated and will be removed without replacement.') @symbolized_base_class ||= base_class.to_s.to_sym end def symbolized_sti_name - ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_sti_name is deprecated and will be removed without replacement.") + ActiveSupport::Deprecation.warn('`ActiveRecord::Base.symbolized_sti_name` is deprecated and will be removed without replacement.') @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class end diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index 48e82d28d9..31ff08a89d 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -247,10 +247,10 @@ module ActiveRecord # 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 - default_attributes.to_hash + _default_attributes.to_hash end - def default_attributes # :nodoc: + def _default_attributes # :nodoc: @default_attributes ||= attributes_builder.build_from_database( columns_hash.transform_values(&:default)) end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 22aa175ce2..4b58f2deb7 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -1,4 +1,5 @@ require 'thread' +require 'active_support/core_ext/string/filters' module ActiveRecord # = Active Record Reflection @@ -153,8 +154,11 @@ module ActiveRecord end def source_macro - ActiveSupport::Deprecation.warn("ActiveRecord::Base.source_macro is deprecated and " \ - "will be removed without replacement.") + ActiveSupport::Deprecation.warn(<<-MSG.squish) + ActiveRecord::Base.source_macro is deprecated and will be removed + without replacement. + MSG + macro end end @@ -339,13 +343,14 @@ module ActiveRecord return unless scope if scope.arity > 0 - ActiveSupport::Deprecation.warn \ - "The association scope '#{name}' is instance dependent (the scope " \ - "block takes an argument). Preloading happens before the individual " \ - "instances are created. This means that there is no instance being " \ - "passed to the association scope. This will most likely result in " \ - "broken or incorrect behavior. Joining, Preloading and eager loading " \ - "of these associations is deprecated and will be removed in the future." + ActiveSupport::Deprecation.warn(<<-MSG.squish) + The association scope '#{name}' is instance dependent (the scope + block takes an argument). Preloading happens before the individual + instances are created. This means that there is no instance being + passed to the association scope. This will most likely result in + broken or incorrect behavior. Joining, Preloading and eager loading + of these associations is deprecated and will be removed in the future. + MSG end end alias :check_eager_loadable! :check_preloadable! @@ -746,8 +751,11 @@ module ActiveRecord # The macro used by the source association def source_macro - ActiveSupport::Deprecation.warn("ActiveRecord::Base.source_macro is deprecated and " \ - "will be removed without replacement.") + ActiveSupport::Deprecation.warn(<<-MSG.squish) + ActiveRecord::Base.source_macro is deprecated and will be removed + without replacement. + MSG + source_reflection.source_macro end diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index eaaa409636..e20cc0e76d 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -253,7 +253,7 @@ module ActiveRecord select_value = operation_over_aggregate_column(column, operation, distinct) - column_alias = select_value.alias + column_alias = select_value.alias || select_value.to_sql relation.select_values = [select_value] query_builder = relation.arel diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index c95ec2522b..145b7378cf 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -1,4 +1,5 @@ require 'active_support/deprecation' +require 'active_support/core_ext/string/filters' module ActiveRecord module FinderMethods @@ -284,8 +285,10 @@ module ActiveRecord def exists?(conditions = :none) if Base === conditions conditions = conditions.id - ActiveSupport::Deprecation.warn "You are passing an instance of ActiveRecord::Base to `exists?`." \ - "Please pass the id of the object by calling `.id`" + ActiveSupport::Deprecation.warn(<<-MSG.squish) + You are passing an instance of ActiveRecord::Base to `exists?`. + Please pass the id of the object by calling `.id` + MSG end return false if !conditions @@ -430,13 +433,17 @@ module ActiveRecord else find_some(ids) end + rescue RangeError + raise RecordNotFound, "Couldn't find #{@klass.name} with an out of range ID" end def find_one(id) if ActiveRecord::Base === id id = id.id - ActiveSupport::Deprecation.warn "You are passing an instance of ActiveRecord::Base to `find`." \ - "Please pass the id of the object by calling `.id`" + ActiveSupport::Deprecation.warn(<<-MSG.squish) + You are passing an instance of ActiveRecord::Base to `find`. + Please pass the id of the object by calling `.id` + MSG end column = columns_hash[primary_key] diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index 545bf5debc..a8febf6a18 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -83,12 +83,12 @@ module ActiveRecord private def merge_joins - return if values[:joins].blank? + return if other.joins_values.blank? if other.klass == relation.klass - relation.joins!(*values[:joins]) + relation.joins!(*other.joins_values) else - joins_dependency, rest = values[:joins].partition do |join| + joins_dependency, rest = other.joins_values.partition do |join| case join when Hash, Symbol, Array true @@ -108,10 +108,10 @@ module ActiveRecord def merge_multi_values lhs_wheres = relation.where_values - rhs_wheres = values[:where] || [] + rhs_wheres = other.where_values lhs_binds = relation.bind_values - rhs_binds = values[:bind] || [] + rhs_binds = other.bind_values removed, kept = partition_overwrites(lhs_wheres, rhs_wheres) @@ -133,23 +133,23 @@ module ActiveRecord relation.where_values = where_values relation.bind_values = bind_values - if values[:reordering] + if other.reordering_value # override any order specified in the original relation - relation.reorder! values[:order] - elsif values[:order] + relation.reorder! other.order_values + elsif other.order_values # merge in order_values from relation - relation.order! values[:order] + relation.order! other.order_values end - relation.extend(*values[:extending]) unless values[:extending].blank? + relation.extend(*other.extending_values) unless other.extending_values.blank? end def merge_single_values - relation.from_value = values[:from] unless relation.from_value - relation.lock_value = values[:lock] unless relation.lock_value + relation.from_value = other.from_value unless relation.from_value + relation.lock_value = other.lock_value unless relation.lock_value - unless values[:create_with].blank? - relation.create_with_value = (relation.create_with_value || {}).merge(values[:create_with]) + unless other.create_with_value.blank? + relation.create_with_value = (relation.create_with_value || {}).merge(other.create_with_value) end end diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 3df0df40ee..e4b6b49087 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -109,7 +109,7 @@ module ActiveRecord # FIXME: I think we need to deprecate this behavior register_handler(Class, ->(attribute, value) { attribute.eq(value.name) }) register_handler(Base, ->(attribute, value) { attribute.eq(value.id) }) - register_handler(Range, ->(attribute, value) { attribute.in(value) }) + register_handler(Range, ->(attribute, value) { attribute.between(value) }) register_handler(Relation, RelationHandler.new) register_handler(Array, ArrayHandler.new) diff --git a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb index b8d9240bf8..b8f3285c3e 100644 --- a/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb +++ b/activerecord/lib/active_record/relation/predicate_builder/array_handler.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/filters' + module ActiveRecord class PredicateBuilder class ArrayHandler # :nodoc: @@ -6,9 +8,12 @@ module ActiveRecord nils, values = values.partition(&:nil?) if values.any? { |val| val.is_a?(Array) } - ActiveSupport::Deprecation.warn "Passing a nested array to Active Record " \ - "finder methods is deprecated and will be removed. Flatten your array " \ - "before using it for 'IN' conditions." + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Passing a nested array to Active Record finder methods is + deprecated and will be removed. Flatten your array before using + it for 'IN' conditions. + MSG + values = values.flatten end @@ -27,7 +32,7 @@ module ActiveRecord values_predicate = values_predicate.or(attribute.eq(nil)) end - array_predicates = ranges.map { |range| attribute.in(range) } + array_predicates = ranges.map { |range| attribute.between(range) } array_predicates.unshift(values_predicate) array_predicates.inject { |composite, predicate| composite.or(predicate) } end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index bbddd28ccc..2c3cfb7631 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/string/filters' require 'active_model/forbidden_attributes_protection' module ActiveRecord @@ -94,8 +95,10 @@ module ActiveRecord def check_cached_relation # :nodoc: if defined?(@arel) && @arel @arel = nil - ActiveSupport::Deprecation.warn "Modifying already cached Relation. The " \ - "cache will be reset. Use a cloned Relation to prevent this warning." + ActiveSupport::Deprecation.warn(<<-MSG.squish) + Modifying already cached Relation. The cache will be reset. Use a + cloned Relation to prevent this warning. + MSG end end @@ -913,7 +916,7 @@ module ActiveRecord where_values.reject! do |rel| case rel - when Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual + when Arel::Nodes::Between, Arel::Nodes::In, Arel::Nodes::NotIn, Arel::Nodes::Equality, Arel::Nodes::NotEqual, Arel::Nodes::LessThanOrEqual, Arel::Nodes::GreaterThanOrEqual subrelation = (rel.left.kind_of?(Arel::Attributes::Attribute) ? rel.left : rel.right) subrelation.name == target_value end diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 82c5ca291c..261fb9d992 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -118,6 +118,8 @@ HEADER if pkcol if pk != 'id' tbl.print %Q(, primary_key: "#{pk}") + elsif pkcol.sql_type == 'bigint' + tbl.print ", id: :bigserial" elsif pkcol.sql_type == 'uuid' tbl.print ", id: :uuid" tbl.print %Q(, default: "#{pkcol.default_function}") if pkcol.default_function diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 49cadb66d0..ec1edf0e01 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -139,6 +139,10 @@ module ActiveRecord # Article.published.featured.latest_article # Article.featured.titles def scope(name, body, &block) + unless body.respond_to?:call + raise ArgumentError, 'The scope body needs to be callable.' + end + if dangerous_class_method?(name) raise ArgumentError, "You tried to define a scope named \"#{name}\" " \ "on the model \"#{self.name}\", but Active Record already defined " \ diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index f9b54139d5..22a6f9af7e 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/string/filters' + module ActiveRecord module Tasks # :nodoc: class DatabaseAlreadyExists < StandardError; end # :nodoc: @@ -187,9 +189,10 @@ module ActiveRecord end def load_schema(format = ActiveRecord::Base.schema_format, file = nil) - ActiveSupport::Deprecation.warn \ - "This method will act on a specific connection in the future. " \ - "To act on the current connection, use `load_schema_current` instead." + ActiveSupport::Deprecation.warn(<<-MSG.squish) + This method will act on a specific connection in the future. + To act on the current connection, use `load_schema_current` instead. + MSG load_schema_current(format, file) end diff --git a/activerecord/lib/active_record/transactions.rb b/activerecord/lib/active_record/transactions.rb index bb06d0304b..f92e1de03b 100644 --- a/activerecord/lib/active_record/transactions.rb +++ b/activerecord/lib/active_record/transactions.rb @@ -309,7 +309,7 @@ module ActiveRecord # Ensure that it is not called if the object was never persisted (failed create), # but call it after the commit of a destroyed object. def committed!(should_run_callbacks = true) #:nodoc: - run_commit_callbacks if should_run_callbacks && destroyed? || persisted? + _run_commit_callbacks if should_run_callbacks && destroyed? || persisted? ensure force_clear_transaction_record_state end @@ -317,7 +317,7 @@ module ActiveRecord # Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record # state should be rolled back to the beginning or just to the last savepoint. def rolledback!(force_restore_state = false, should_run_callbacks = true) #:nodoc: - run_rollback_callbacks if should_run_callbacks + _run_rollback_callbacks if should_run_callbacks ensure restore_transaction_record_state(force_restore_state) clear_transaction_record_state diff --git a/activerecord/lib/active_record/type.rb b/activerecord/lib/active_record/type.rb index e3d6c5957e..e5acbbb6b3 100644 --- a/activerecord/lib/active_record/type.rb +++ b/activerecord/lib/active_record/type.rb @@ -4,6 +4,7 @@ require 'active_record/type/numeric' require 'active_record/type/time_value' require 'active_record/type/value' +require 'active_record/type/big_integer' require 'active_record/type/binary' require 'active_record/type/boolean' require 'active_record/type/date' diff --git a/activerecord/lib/active_record/type/big_integer.rb b/activerecord/lib/active_record/type/big_integer.rb new file mode 100644 index 0000000000..0c72d8914f --- /dev/null +++ b/activerecord/lib/active_record/type/big_integer.rb @@ -0,0 +1,13 @@ +require 'active_record/type/integer' + +module ActiveRecord + module Type + class BigInteger < Integer # :nodoc: + private + + def max_value + ::Float::INFINITY + end + end + end +end diff --git a/activerecord/lib/active_record/type/boolean.rb b/activerecord/lib/active_record/type/boolean.rb index 1311be3944..978d16d524 100644 --- a/activerecord/lib/active_record/type/boolean.rb +++ b/activerecord/lib/active_record/type/boolean.rb @@ -14,9 +14,13 @@ module ActiveRecord true else if !ConnectionAdapters::Column::FALSE_VALUES.include?(value) - ActiveSupport::Deprecation.warn(<<-EOM) - You attempted to assign a value which is not explicitly true or false to a boolean column. Currently this value casts to false. This will change to match Ruby's sematics, and will cast to true in Rails 5.0. If you would like to maintain the current behavior, you should explicitly handle the values you would like cast to false. - EOM + ActiveSupport::Deprecation.warn(<<-MSG.squish) + You attempted to assign a value which is not explicitly `true` or `false` + to a boolean column. Currently this value casts to `false`. This will + change to match Ruby's semantics, and will cast to `true` in Rails 5. + If you would like to maintain the current behavior, you should + explicitly handle the values you would like cast to `false`. + MSG end false end diff --git a/activerecord/lib/active_record/type/decimal_without_scale.rb b/activerecord/lib/active_record/type/decimal_without_scale.rb index cabdcecdd7..ff5559e300 100644 --- a/activerecord/lib/active_record/type/decimal_without_scale.rb +++ b/activerecord/lib/active_record/type/decimal_without_scale.rb @@ -1,8 +1,8 @@ -require 'active_record/type/integer' +require 'active_record/type/big_integer' module ActiveRecord module Type - class DecimalWithoutScale < Integer # :nodoc: + class DecimalWithoutScale < BigInteger # :nodoc: def type :decimal end diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb index 08477d1303..d69e5b3f28 100644 --- a/activerecord/lib/active_record/type/integer.rb +++ b/activerecord/lib/active_record/type/integer.rb @@ -3,21 +3,44 @@ module ActiveRecord class Integer < Value # :nodoc: include Numeric + def initialize(*) + super + @range = -max_value...max_value + end + def type :integer end alias type_cast_for_database type_cast + protected + + attr_reader :range + private def cast_value(value) case value when true then 1 when false then 0 - else value.to_i rescue nil + else + result = value.to_i rescue nil + ensure_in_range(result) if result + result end end + + def ensure_in_range(value) + unless range.cover?(value) + raise RangeError, "#{value} is too large for #{self.class} with limit #{limit || 4}" + end + end + + def max_value + limit = self.limit || 4 + 1 << (limit * 8 - 1) # 8 bits per byte with one bit for sign + end end end end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index ae5b409f99..376dc2490e 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -110,6 +110,26 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase end end +class PostgresqlLargeKeysTest < ActiveRecord::TestCase + include PostgresqlUUIDHelper + def setup + connection.create_table('big_serials', id: :bigserial) do |t| + t.string 'name' + end + end + + def test_omg + schema = StringIO.new + ActiveRecord::SchemaDumper.dump(connection, schema) + assert_match "create_table \"big_serials\", id: :bigserial, force: true", + schema.string + end + + def teardown + drop_table "big_serials" + end +end + class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase include PostgresqlUUIDHelper diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index fb535e74fc..4e1685f151 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -985,7 +985,6 @@ class BasicsTest < ActiveRecord::TestCase class NumericData < ActiveRecord::Base self.table_name = 'numeric_data' - attribute :world_population, Type::Integer.new attribute :my_house_population, Type::Integer.new attribute :atoms_in_universe, Type::Integer.new end @@ -1383,7 +1382,10 @@ class BasicsTest < ActiveRecord::TestCase c1 = Post.connection.schema_cache.columns('posts') ActiveRecord::Base.clear_cache! c2 = Post.connection.schema_cache.columns('posts') - assert_not_equal c1, c2 + c1.each_with_index do |v, i| + assert_not_same v, c2[i] + end + assert_equal c1, c2 end def test_current_scope_is_reset diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index ec6a319ab5..e886268a72 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -461,7 +461,6 @@ class CalculationsTest < ActiveRecord::TestCase assert_equal 7, Company.includes(:contracts).sum(:developer_id) end - def test_from_option_with_specified_index if Edge.connection.adapter_name == 'MySQL' or Edge.connection.adapter_name == 'Mysql2' assert_equal Edge.count(:all), Edge.from('edges USE INDEX(unique_edge_index)').count(:all) diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb index d4d67487db..80244d1439 100644 --- a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb +++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb @@ -22,6 +22,10 @@ module ActiveRecord assert_lookup_type :string, "SET('one', 'two', 'three')" end + def test_enum_type_with_value_matching_other_type + assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')" + end + def test_binary_types assert_lookup_type :binary, 'bit' assert_lookup_type :binary, 'BIT' diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index ac570ecbe0..d1ff8516e8 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -99,21 +99,10 @@ class FinderTest < ActiveRecord::TestCase end def test_exists_fails_when_parameter_has_invalid_type - if current_adapter?(:PostgreSQLAdapter, :MysqlAdapter) - assert_raises ActiveRecord::StatementInvalid do - Topic.exists?(("9"*53).to_i) # number that's bigger than int - end - else + assert_raises(RangeError) do assert_equal false, Topic.exists?(("9"*53).to_i) # number that's bigger than int end - - if current_adapter?(:PostgreSQLAdapter) - assert_raises ActiveRecord::StatementInvalid do - Topic.exists?("foo") - end - else - assert_equal false, Topic.exists?("foo") - end + assert_equal false, Topic.exists?("foo") end def test_exists_does_not_select_columns_without_alias @@ -209,6 +198,18 @@ class FinderTest < ActiveRecord::TestCase assert_equal 2, last_devs.size end + def test_find_with_large_number + assert_raises(ActiveRecord::RecordNotFound) { Topic.find('9999999999999999999999999999999') } + end + + def test_find_by_with_large_number + assert_nil Topic.find_by(id: '9999999999999999999999999999999') + end + + def test_find_by_id_with_large_number + assert_nil Topic.find_by_id('9999999999999999999999999999999') + end + def test_find_an_empty_array assert_equal [], Topic.find([]) end diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index f5be8a044b..80ac57ec7c 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -95,7 +95,7 @@ EXPECTED_TIME_ZONE_AWARE_ATTRIBUTES = false def verify_default_timezone_config if Time.zone != EXPECTED_ZONE $stderr.puts <<-MSG -\n#{self.to_s} +\n#{self} Global state `Time.zone` was leaked. Expected: #{EXPECTED_ZONE} Got: #{Time.zone} @@ -103,7 +103,7 @@ def verify_default_timezone_config end if ActiveRecord::Base.default_timezone != EXPECTED_DEFAULT_TIMEZONE $stderr.puts <<-MSG -\n#{self.to_s} +\n#{self} Global state `ActiveRecord::Base.default_timezone` was leaked. Expected: #{EXPECTED_DEFAULT_TIMEZONE} Got: #{ActiveRecord::Base.default_timezone} @@ -111,7 +111,7 @@ def verify_default_timezone_config end if ActiveRecord::Base.time_zone_aware_attributes != EXPECTED_TIME_ZONE_AWARE_ATTRIBUTES $stderr.puts <<-MSG -\n#{self.to_s} +\n#{self} Global state `ActiveRecord::Base.time_zone_aware_attributes` was leaked. Expected: #{EXPECTED_TIME_ZONE_AWARE_ATTRIBUTES} Got: #{ActiveRecord::Base.time_zone_aware_attributes} diff --git a/activerecord/test/cases/migration/references_index_test.rb b/activerecord/test/cases/migration/references_index_test.rb index 4485701a4e..ad6b828d0b 100644 --- a/activerecord/test/cases/migration/references_index_test.rb +++ b/activerecord/test/cases/migration/references_index_test.rb @@ -55,7 +55,7 @@ module ActiveRecord t.references :foo, :polymorphic => true, :index => true end - assert connection.index_exists?(table_name, [:foo_id, :foo_type], :name => :index_testings_on_foo_id_and_foo_type) + assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id) end end @@ -93,7 +93,7 @@ module ActiveRecord t.references :foo, :polymorphic => true, :index => true end - assert connection.index_exists?(table_name, [:foo_id, :foo_type], :name => :index_testings_on_foo_id_and_foo_type) + assert connection.index_exists?(table_name, [:foo_type, :foo_id], name: :index_testings_on_foo_type_and_foo_id) end end end diff --git a/activerecord/test/cases/migration/references_statements_test.rb b/activerecord/test/cases/migration/references_statements_test.rb index b8b4fa1135..988bd9c89f 100644 --- a/activerecord/test/cases/migration/references_statements_test.rb +++ b/activerecord/test/cases/migration/references_statements_test.rb @@ -42,7 +42,7 @@ module ActiveRecord def test_creates_polymorphic_index add_reference table_name, :taggable, polymorphic: true, index: true - assert index_exists?(table_name, [:taggable_id, :taggable_type]) + assert index_exists?(table_name, [:taggable_type, :taggable_id]) end def test_creates_reference_type_column_with_default diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 270e446c69..8db01fc847 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -15,7 +15,6 @@ class BigNumber < ActiveRecord::Base unless current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) attribute :value_of_e, Type::Integer.new end - attribute :world_population, Type::Integer.new attribute :my_house_population, Type::Integer.new end diff --git a/activerecord/test/cases/relation/where_chain_test.rb b/activerecord/test/cases/relation/where_chain_test.rb index b9e69bdb08..619055f1e7 100644 --- a/activerecord/test/cases/relation/where_chain_test.rb +++ b/activerecord/test/cases/relation/where_chain_test.rb @@ -149,5 +149,33 @@ module ActiveRecord assert_bound_ast value, Post.arel_table['title'], Arel::Nodes::Equality assert_equal 'alone', bind.last end + + def test_rewhere_with_range + relation = Post.where(comments_count: 1..3).rewhere(comments_count: 3..5) + + assert_equal 1, relation.where_values.size + assert_equal Post.where(comments_count: 3..5), relation + end + + def test_rewhere_with_infinite_upper_bound_range + relation = Post.where(comments_count: 1..Float::INFINITY).rewhere(comments_count: 3..5) + + assert_equal 1, relation.where_values.size + assert_equal Post.where(comments_count: 3..5), relation + end + + def test_rewhere_with_infinite_lower_bound_range + relation = Post.where(comments_count: -Float::INFINITY..1).rewhere(comments_count: 3..5) + + assert_equal 1, relation.where_values.size + assert_equal Post.where(comments_count: 3..5), relation + end + + def test_rewhere_with_infinite_range + relation = Post.where(comments_count: -Float::INFINITY..Float::INFINITY).rewhere(comments_count: 3..5) + + assert_equal 1, relation.where_values.size + assert_equal Post.where(comments_count: 3..5), relation + end end end diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index d3546bd471..c2816c3670 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -132,6 +132,13 @@ class NamedScopingTest < ActiveRecord::TestCase assert_equal Post.ranked_by_comments.limit_by(5), Post.top(5) end + def test_scopes_body_is_a_callable + e = assert_raises ArgumentError do + Class.new(Post).class_eval { scope :containing_the_letter_z, where("body LIKE '%z%'") } + end + assert_equal "The scope body needs to be callable.", e.message + end + def test_active_records_have_scope_named__all__ assert !Topic.all.empty? diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb new file mode 100644 index 0000000000..53d6a3a6aa --- /dev/null +++ b/activerecord/test/cases/type/integer_test.rb @@ -0,0 +1,106 @@ +require "cases/helper" +require "models/company" + +module ActiveRecord + module Type + class IntegerTest < ActiveRecord::TestCase + test "simple values" do + type = Type::Integer.new + assert_equal 1, type.type_cast_from_user(1) + assert_equal 1, type.type_cast_from_user('1') + assert_equal 1, type.type_cast_from_user('1ignore') + assert_equal 0, type.type_cast_from_user('bad1') + assert_equal 0, type.type_cast_from_user('bad') + assert_equal 1, type.type_cast_from_user(1.7) + assert_equal 0, type.type_cast_from_user(false) + assert_equal 1, type.type_cast_from_user(true) + assert_nil type.type_cast_from_user(nil) + end + + test "random objects cast to nil" do + type = Type::Integer.new + assert_nil type.type_cast_from_user([1,2]) + assert_nil type.type_cast_from_user({1 => 2}) + assert_nil type.type_cast_from_user((1..2)) + end + + test "casting ActiveRecord models" do + type = Type::Integer.new + firm = Firm.create(:name => 'Apple') + assert_nil type.type_cast_from_user(firm) + end + + test "casting objects without to_i" do + type = Type::Integer.new + assert_nil type.type_cast_from_user(::Object.new) + end + + test "casting nan and infinity" do + type = Type::Integer.new + assert_nil type.type_cast_from_user(::Float::NAN) + assert_nil type.type_cast_from_user(1.0/0.0) + end + + test "changed?" do + type = Type::Integer.new + + assert type.changed?(5, 5, '5wibble') + assert_not type.changed?(5, 5, '5') + assert_not type.changed?(5, 5, '5.0') + assert_not type.changed?(nil, nil, nil) + end + + test "values below int min value are out of range" do + assert_raises(::RangeError) do + Integer.new.type_cast_from_user("-2147483649") + end + end + + test "values above int max value are out of range" do + assert_raises(::RangeError) do + Integer.new.type_cast_from_user("2147483648") + end + end + + test "very small numbers are out of range" do + assert_raises(::RangeError) do + Integer.new.type_cast_from_user("-9999999999999999999999999999999") + end + end + + test "very large numbers are in range" do + assert_raises(::RangeError) do + Integer.new.type_cast_from_user("9999999999999999999999999999999") + end + end + + test "normal numbers are in range" do + type = Integer.new + assert_equal(0, type.type_cast_from_user("0")) + assert_equal(-1, type.type_cast_from_user("-1")) + assert_equal(1, type.type_cast_from_user("1")) + end + + test "int max value is in range" do + assert_equal(2147483647, Integer.new.type_cast_from_user("2147483647")) + end + + test "int min value is in range" do + assert_equal(-2147483648, Integer.new.type_cast_from_user("-2147483648")) + end + + test "columns with a larger limit have larger ranges" do + type = Integer.new(limit: 8) + + assert_equal(9223372036854775807, type.type_cast_from_user("9223372036854775807")) + assert_equal(-9223372036854775808, type.type_cast_from_user("-9223372036854775808")) + assert_raises(::RangeError) do + type.type_cast_from_user("-9999999999999999999999999999999") + end + assert_raises(::RangeError) do + type.type_cast_from_user("9999999999999999999999999999999") + end + end + end + end +end diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb index 25e6549072..b0979cbe1f 100644 --- a/activerecord/test/cases/types_test.rb +++ b/activerecord/test/cases/types_test.rb @@ -1,5 +1,4 @@ require "cases/helper" -require 'models/company' module ActiveRecord module ConnectionAdapters @@ -37,52 +36,6 @@ module ActiveRecord end end - def test_type_cast_integer - type = Type::Integer.new - assert_equal 1, type.type_cast_from_user(1) - assert_equal 1, type.type_cast_from_user('1') - assert_equal 1, type.type_cast_from_user('1ignore') - assert_equal 0, type.type_cast_from_user('bad1') - assert_equal 0, type.type_cast_from_user('bad') - assert_equal 1, type.type_cast_from_user(1.7) - assert_equal 0, type.type_cast_from_user(false) - assert_equal 1, type.type_cast_from_user(true) - assert_nil type.type_cast_from_user(nil) - end - - def test_type_cast_non_integer_to_integer - type = Type::Integer.new - assert_nil type.type_cast_from_user([1,2]) - assert_nil type.type_cast_from_user({1 => 2}) - assert_nil type.type_cast_from_user((1..2)) - end - - def test_type_cast_activerecord_to_integer - type = Type::Integer.new - firm = Firm.create(:name => 'Apple') - assert_nil type.type_cast_from_user(firm) - end - - def test_type_cast_object_without_to_i_to_integer - type = Type::Integer.new - assert_nil type.type_cast_from_user(Object.new) - end - - def test_type_cast_nan_and_infinity_to_integer - type = Type::Integer.new - assert_nil type.type_cast_from_user(Float::NAN) - assert_nil type.type_cast_from_user(1.0/0.0) - end - - def test_changing_integers - type = Type::Integer.new - - assert type.changed?(5, 5, '5wibble') - assert_not type.changed?(5, 5, '5') - assert_not type.changed?(5, 5, '5.0') - assert_not type.changed?(nil, nil, nil) - end - def test_type_cast_float type = Type::Float.new assert_equal 1.0, type.type_cast_from_user("1") diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 10de3d34cb..2b68236256 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -311,7 +311,7 @@ ActiveRecord::Schema.define do end create_table :cold_jokes, force: true do |t| - t.string :name + t.string :cold_name end create_table :friendships, force: true do |t| |