From cc405496ce85ee0073268baefdb2be5d4b062f91 Mon Sep 17 00:00:00 2001 From: Ben Woosley Date: Tue, 18 Jun 2013 15:10:48 -0700 Subject: =?UTF-8?q?Fix=20that=20a=20collection=20proxy=20could=20be=20cach?= =?UTF-8?q?ed=20before=20the=20save=20of=20the=20owner,=20resulting=20in?= =?UTF-8?q?=20an=20invalid=20proxy=20lacking=20the=20owner=E2=80=99s=20id.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Absent this fix calls like: owner.association.update_all to behave unexpectedly because they try to act on association objects where owner_id is null. more evidence here: https://gist.github.com/Empact/5865555 ``` Active Record 3.2.13 -- create_table(:firms, {:force=>true}) -> 0.1371s -- create_table(:clients, {:force=>true}) -> 0.0005s 1 clients. 1 expected. 1 clients updated. 1 expected. ``` ``` Active Record 4.0.0 -- create_table(:firms, {:force=>true}) -> 0.1606s -- create_table(:clients, {:force=>true}) -> 0.0004s 1 clients. 1 expected. 0 clients updated. 1 expected. ``` --- activerecord/CHANGELOG.md | 6 ++++++ .../lib/active_record/associations/collection_association.rb | 8 +++++++- .../test/cases/associations/has_many_associations_test.rb | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 210151a2ad..92e1575484 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Cache CollectionAssociation#reader proxies separately before and after + the owner has been saved so that the proxy is not cached without the + owner's id. + + *Ben Woosley* + * Honor overridden `rack.test` in Rack environment for the connection management middleware. diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 1836ff0910..c5dff7ee1a 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -33,7 +33,13 @@ module ActiveRecord reload end - @proxy ||= CollectionProxy.create(klass, self) + if owner.new_record? + # Cache the proxy separately before the owner has an id + # or else a post-save proxy will still lack the id + @new_record_proxy ||= CollectionProxy.create(klass, self) + else + @proxy ||= CollectionProxy.create(klass, self) + end end # Implements the writer method, e.g. foo.items= for Foo.has_many :items diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index e34b993029..c72d9342a9 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -387,6 +387,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal "Summit", Firm.all.merge!(:order => "id").first.clients_using_primary_key.first.name end + def test_update_all_on_association_accessed_before_save + firm = Firm.new(name: 'Firm') + firm.clients << Client.first + firm.save! + assert_equal firm.clients.count, firm.clients.update_all(description: 'Great!') + end + def test_belongs_to_sanity c = Client.new assert_nil c.firm, "belongs_to failed sanity check on new object" -- cgit v1.2.3 From 0f3cefa445c6b43711b29f38257adcedede74baa Mon Sep 17 00:00:00 2001 From: Byron Bischoff Date: Wed, 22 Oct 2014 11:39:47 -0700 Subject: =?UTF-8?q?copy=20reflection=5Fscopes=E2=80=99s=20unscoped=20value?= =?UTF-8?q?=20when=20building=20scope=20for=20preloading,=20fixes=20#11036?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/active_record/associations/preloader/association.rb | 4 ++++ .../test/cases/associations/has_many_associations_test.rb | 8 ++++++++ 2 files changed, 12 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index c0639742be..2300435f91 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -136,6 +136,10 @@ module ActiveRecord preload_values = preload_scope.values preload_binds = preload_scope.bind_values + if values[:unscope] + scope.unscope! values[:unscope] + end + scope.where_values = Array(values[:where]) + Array(preload_values[:where]) scope.references_values = Array(values[:references]) + Array(preload_values[:references]) scope.bind_values = (reflection_binds + preload_binds) diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index e34b993029..fdbe411f86 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1895,6 +1895,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal [bulb1, bulb2], car.all_bulbs.sort_by(&:id) end + test 'unscopes the default scope of associated model when used with include' do + car = Car.create! + bulb = Bulb.create! name: "other", car: car + + assert_equal bulb, Car.find(car.id).all_bulbs.first + assert_equal bulb, Car.includes(:all_bulbs).find(car.id).all_bulbs.first + end + test "raises RecordNotDestroyed when replaced child can't be destroyed" do car = Car.create! original_child = FailedBulb.create!(car: car) -- cgit v1.2.3 From 28eb085cdf989395957cbc50c7e1bb981e70ff9b Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Sat, 1 Nov 2014 16:45:31 +0530 Subject: - Remove unused duplicated method `add_column_position` from AbstractMysqlAdapter --- .../active_record/connection_adapters/abstract_mysql_adapter.rb | 8 -------- 1 file changed, 8 deletions(-) (limited to 'activerecord') 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 7f04e35042..5f512758e9 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -585,14 +585,6 @@ module ActiveRecord end end - def add_column_position!(sql, options) - if options[:first] - sql << " FIRST" - elsif options[:after] - sql << " AFTER #{quote_column_name(options[:after])}" - end - end - # SHOW VARIABLES LIKE 'name' def show_variable(name) variables = select_all("SHOW VARIABLES LIKE '#{name}'", 'SCHEMA') -- cgit v1.2.3 From 8355b48c37157dabf31baeb5f0af9ad403a6141b Mon Sep 17 00:00:00 2001 From: Jason Perrin Date: Mon, 3 Nov 2014 19:14:14 -0700 Subject: Correct test description for large integer test --- activerecord/test/cases/type/integer_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb index 53d6a3a6aa..5942f77e18 100644 --- a/activerecord/test/cases/type/integer_test.rb +++ b/activerecord/test/cases/type/integer_test.rb @@ -68,7 +68,7 @@ module ActiveRecord end end - test "very large numbers are in range" do + test "very large numbers are out of range" do assert_raises(::RangeError) do Integer.new.type_cast_from_user("9999999999999999999999999999999") end -- cgit v1.2.3 From 9007b789e197ad0445a03b591f982a44b92f715d Mon Sep 17 00:00:00 2001 From: Ted O'Meara Date: Thu, 30 Oct 2014 14:01:38 -0400 Subject: Added SchemaDumper support for tables with jsonb columns. --- activerecord/CHANGELOG.md | 4 ++++ .../connection_adapters/postgresql_adapter.rb | 1 + activerecord/test/cases/adapters/postgresql/json_test.rb | 8 ++++++++ activerecord/test/cases/schema_dumper_test.rb | 7 ------- activerecord/test/schema/postgresql_specific_schema.rb | 15 ++++----------- 5 files changed, 17 insertions(+), 18 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index f7ca41908d..1961ba93c6 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Added SchemaDumper support for tables with jsonb columns. + + *Ted O'Meara* + * Deprecate `sanitize_sql_hash_for_conditions` without replacement. Using a `Relation` for performing queries and updates is the prefered API. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index e472741660..9941f74766 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -104,6 +104,7 @@ module ActiveRecord macaddr: { name: "macaddr" }, uuid: { name: "uuid" }, json: { name: "json" }, + jsonb: { name: "jsonb" }, ltree: { name: "ltree" }, citext: { name: "citext" }, point: { name: "point" }, diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index 86ba849445..7a9fdd45e8 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -3,8 +3,11 @@ require "cases/helper" require 'active_record/base' require 'active_record/connection_adapters/postgresql_adapter' +require 'support/schema_dumping_helper' module PostgresqlJSONSharedTestCases + include SchemaDumpingHelper + class JsonDataType < ActiveRecord::Base self.table_name = 'json_data_type' @@ -64,6 +67,11 @@ module PostgresqlJSONSharedTestCases JsonDataType.reset_column_information end + def test_schema_dumping + output = dump_table_schema("json_data_type") + assert_match(/t.#{column_type.to_s}\s+"payload",\s+default: {}/, output) + end + def test_cast_value_on_write x = JsonDataType.new payload: {"string" => "foo", :symbol => :bar} assert_equal({"string" => "foo", :symbol => :bar}, x.payload_before_type_cast) diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 2241c41f36..6303393c22 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -271,13 +271,6 @@ class SchemaDumperTest < ActiveRecord::TestCase end end - def test_schema_dump_includes_json_shorthand_definition - output = standard_dump - if %r{create_table "postgresql_json_data_type"} =~ output - assert_match %r|t.json "json_data", default: {}|, output - end - end - def test_schema_dump_includes_inet_shorthand_definition output = standard_dump if %r{create_table "postgresql_network_addresses"} =~ output diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb index e9294a11b9..7c3b170c08 100644 --- a/activerecord/test/schema/postgresql_specific_schema.rb +++ b/activerecord/test/schema/postgresql_specific_schema.rb @@ -1,7 +1,9 @@ ActiveRecord::Schema.define do - %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times postgresql_network_addresses postgresql_uuids postgresql_ltrees - postgresql_oids postgresql_xml_data_type defaults geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent postgresql_json_data_type postgresql_citext).each do |table_name| + %w(postgresql_tsvectors postgresql_hstores postgresql_arrays postgresql_moneys postgresql_numbers postgresql_times + postgresql_network_addresses postgresql_uuids postgresql_ltrees postgresql_oids postgresql_xml_data_type defaults + geometrics postgresql_timestamp_with_zones postgresql_partitioned_table postgresql_partitioned_table_parent + postgresql_citext).each do |table_name| execute "DROP TABLE IF EXISTS #{quote_table_name table_name}" end @@ -108,15 +110,6 @@ _SQL _SQL end - if 't' == select_value("select 'json'=ANY(select typname from pg_type)") - execute <<_SQL - CREATE TABLE postgresql_json_data_type ( - id SERIAL PRIMARY KEY, - json_data json default '{}'::json - ); -_SQL - end - execute <<_SQL CREATE TABLE postgresql_numbers ( id SERIAL PRIMARY KEY, -- cgit v1.2.3 From 4bcb0942241fe10d734b9777a61e0e0026cf0d5d Mon Sep 17 00:00:00 2001 From: Paul Annesley Date: Tue, 4 Nov 2014 13:58:09 -0800 Subject: activerecord: rake db:create shows underlying error message. --- activerecord/lib/active_record/tasks/mysql_database_tasks.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb index d890196f47..eafbb2c249 100644 --- a/activerecord/lib/active_record/tasks/mysql_database_tasks.rb +++ b/activerecord/lib/active_record/tasks/mysql_database_tasks.rb @@ -31,6 +31,7 @@ module ActiveRecord end establish_connection configuration else + $stderr.puts error.inspect $stderr.puts "Couldn't create database for #{configuration.inspect}, #{creation_options.inspect}" $stderr.puts "(If you set the charset manually, make sure you have a matching collation)" if configuration['encoding'] end -- cgit v1.2.3 From 8fee923888192a658d8823b31e77ed0683dfd665 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 5 Nov 2014 10:59:50 -0700 Subject: Improve performance of AR object instantiation We introduced a performance hit by adding an additional iteration through a model's attributes on creation. We don't actually need the values from `Result` to be a hash, we can separate the columns and values and zip them up ourself during the iteration that we have to do. --- .../lib/active_record/attribute_set/builder.rb | 21 +++++++++++++--- activerecord/lib/active_record/inheritance.rb | 12 ++++++---- activerecord/lib/active_record/persistence.rb | 28 +++++++++++++++++++--- activerecord/lib/active_record/querying.rb | 2 +- activerecord/lib/active_record/result.rb | 9 +++++++ 5 files changed, 61 insertions(+), 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index d4a787f2fe..92640f1579 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -8,18 +8,33 @@ module ActiveRecord end def build_from_database(values = {}, additional_types = {}) - attributes = build_attributes_from_values(values, additional_types) + build_from_database_pairs(values.keys, values.values, additional_types) + end + + def build_from_database_pairs(columns = [], values = [], additional_types = {}) + attributes = build_attributes_from_values(columns, values, additional_types) add_uninitialized_attributes(attributes) AttributeSet.new(attributes) end private - def build_attributes_from_values(values, additional_types) - values.each_with_object({}) do |(name, value), hash| + def build_attributes_from_values(columns, values, additional_types) + # We are performing manual iteration here as this method is a performance + # hotspot + hash = {} + index = 0 + length = columns.length + + while index < length + name = columns[index] + value = values[index] type = additional_types.fetch(name, types[name]) hash[name] = Attribute.from_database(name, value, type) + index += 1 end + + hash end def add_uninitialized_attributes(attributes) diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index f58145ab05..4aad3217cb 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -165,15 +165,19 @@ module ActiveRecord # record instance. For single-table inheritance, we check the record # for a +type+ column and return the corresponding class. def discriminate_class_for_record(record) - if using_single_table_inheritance?(record) - find_sti_class(record[inheritance_column]) + discriminate_class_for_value(record[inheritance_column]) + end + + def discriminate_class_for_value(value) + if using_single_table_inheritance?(value) + find_sti_class(value) else super end end - def using_single_table_inheritance?(record) - record[inheritance_column].present? && columns_hash.include?(inheritance_column) + def using_single_table_inheritance?(value) + value.present? && columns_hash.include?(inheritance_column) end def find_sti_class(type_name) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 755ff2b2f1..9d2c9d3b9c 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -65,19 +65,41 @@ module ActiveRecord # how this "single-table" inheritance mapping is implemented. def instantiate(attributes, column_types = {}) klass = discriminate_class_for_record(attributes) - attributes = klass.attributes_builder.build_from_database(attributes, column_types) - klass.allocate.init_with('attributes' => attributes, 'new_record' => false) + klass.instantiate_pairs(attributes.keys, attributes.values, column_types) + end + + def instantiate_pairs(columns, values, column_types = {}) # :nodoc: + attributes = attributes_builder.build_from_database_pairs(columns, values, column_types) + allocate.init_with('attributes' => attributes, 'new_record' => false) + end + + def instantiate_result_set(result_set, column_types = {}) # :nodoc: + inheritance_column_index = inheritance_column && result_set.columns.find_index(inheritance_column) + + result_set.each_pair.map do |columns, values| + inheritance_value = inheritance_column_index && values[inheritance_column_index] + klass = discriminate_class_for_value(inheritance_value) + klass.instantiate_pairs(columns, values, column_types) + end end private # Called by +instantiate+ to decide which class to use for a new # record instance. # - # See +ActiveRecord::Inheritance#discriminate_class_for_record+ for + # See +ActiveRecord::Inheritance#discriminate_class_for_value+ for # the single-table inheritance discriminator. + def discriminate_class_for_value(*) + self + end + def discriminate_class_for_record(record) self end + + def inheritance_column + nil + end end # Returns true if this object hasn't been saved yet -- that is, a record diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb index e8de4db3a7..9d4df81b07 100644 --- a/activerecord/lib/active_record/querying.rb +++ b/activerecord/lib/active_record/querying.rb @@ -47,7 +47,7 @@ module ActiveRecord } message_bus.instrument('instantiation.active_record', payload) do - result_set.map { |record| instantiate(record, column_types) } + instantiate_result_set(result_set, column_types) end end diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index 3a3e65ef32..c84ad586e2 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -54,6 +54,15 @@ module ActiveRecord end end + def each_pair + return to_enum(__method__) unless block_given? + + columns = @columns.map { |c| c.dup.freeze } + @rows.each do |row| + yield columns, row + end + end + def to_hash hash_rows end -- cgit v1.2.3 From 75099a933d0bf5e037037f78fbb305520955b064 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 5 Nov 2014 13:34:44 -0700 Subject: Remove unneccesary default parameters --- activerecord/lib/active_record/attribute_set/builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 92640f1579..0a62c68bfb 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -11,7 +11,7 @@ module ActiveRecord build_from_database_pairs(values.keys, values.values, additional_types) end - def build_from_database_pairs(columns = [], values = [], additional_types = {}) + def build_from_database_pairs(columns, values, additional_types) attributes = build_attributes_from_values(columns, values, additional_types) add_uninitialized_attributes(attributes) AttributeSet.new(attributes) -- cgit v1.2.3 From de83f28dc1385eb037c70b6c631fc3aeade9fceb Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Thu, 6 Nov 2014 15:31:18 +0100 Subject: tests, use SchemaDumpingHelper to dump a specific table. This makes debugging the generated schema output much easier. As a side effect it also shaves off 2.5 seconds of test runtime. --- .../test/cases/adapters/postgresql/uuid_test.rb | 24 +++++++++++----------- 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 376dc2490e..c3705e7dac 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -3,6 +3,7 @@ require "cases/helper" require 'active_record/base' require 'active_record/connection_adapters/postgresql_adapter' +require 'support/schema_dumping_helper' module PostgresqlUUIDHelper def connection @@ -112,6 +113,8 @@ end class PostgresqlLargeKeysTest < ActiveRecord::TestCase include PostgresqlUUIDHelper + include SchemaDumpingHelper + def setup connection.create_table('big_serials', id: :bigserial) do |t| t.string 'name' @@ -119,10 +122,8 @@ class PostgresqlLargeKeysTest < ActiveRecord::TestCase end def test_omg - schema = StringIO.new - ActiveRecord::SchemaDumper.dump(connection, schema) - assert_match "create_table \"big_serials\", id: :bigserial, force: true", - schema.string + schema = dump_table_schema "big_serials" + assert_match "create_table \"big_serials\", id: :bigserial, force: true", schema end def teardown @@ -132,6 +133,7 @@ end class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase include PostgresqlUUIDHelper + include SchemaDumpingHelper class UUID < ActiveRecord::Base self.table_name = 'pg_uuids' @@ -191,17 +193,15 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase end def test_schema_dumper_for_uuid_primary_key - schema = StringIO.new - ActiveRecord::SchemaDumper.dump(connection, schema) - assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: "uuid_generate_v1\(\)"/, schema.string) - assert_match(/t\.uuid "other_uuid", default: "uuid_generate_v4\(\)"/, schema.string) + schema = dump_table_schema "pg_uuids" + assert_match(/\bcreate_table "pg_uuids", id: :uuid, default: "uuid_generate_v1\(\)"/, schema) + assert_match(/t\.uuid "other_uuid", default: "uuid_generate_v4\(\)"/, schema) end def test_schema_dumper_for_uuid_primary_key_with_custom_default - schema = StringIO.new - ActiveRecord::SchemaDumper.dump(connection, schema) - assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: "my_uuid_generator\(\)"/, schema.string) - assert_match(/t\.uuid "other_uuid_2", default: "my_uuid_generator\(\)"/, schema.string) + schema = dump_table_schema "pg_uuids_2" + assert_match(/\bcreate_table "pg_uuids_2", id: :uuid, default: "my_uuid_generator\(\)"/, schema) + assert_match(/t\.uuid "other_uuid_2", default: "my_uuid_generator\(\)"/, schema) end end end -- cgit v1.2.3 From 300c96ce158a6ebd3a83b057a8cdf2d29de0fe9d Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Thu, 6 Nov 2014 15:38:03 +0100 Subject: tests, remove unneeded requires. --- activerecord/test/cases/adapters/postgresql/enum_test.rb | 2 -- activerecord/test/cases/adapters/postgresql/hstore_test.rb | 3 --- activerecord/test/cases/adapters/postgresql/json_test.rb | 3 --- activerecord/test/cases/adapters/postgresql/uuid_test.rb | 3 --- 4 files changed, 11 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb index d99c4a292e..83cedc5a7b 100644 --- a/activerecord/test/cases/adapters/postgresql/enum_test.rb +++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb @@ -1,8 +1,6 @@ # -*- coding: utf-8 -*- require "cases/helper" require 'support/connection_helper' -require 'active_record/base' -require 'active_record/connection_adapters/postgresql_adapter' class PostgresqlEnumTest < ActiveRecord::TestCase include ConnectionHelper diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index 1296eb72c0..f14ffe5ddc 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -1,8 +1,5 @@ # encoding: utf-8 - require "cases/helper" -require 'active_record/base' -require 'active_record/connection_adapters/postgresql_adapter' class PostgresqlHstoreTest < ActiveRecord::TestCase class Hstore < ActiveRecord::Base diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb index 7a9fdd45e8..340ca29c0e 100644 --- a/activerecord/test/cases/adapters/postgresql/json_test.rb +++ b/activerecord/test/cases/adapters/postgresql/json_test.rb @@ -1,8 +1,5 @@ # encoding: utf-8 - require "cases/helper" -require 'active_record/base' -require 'active_record/connection_adapters/postgresql_adapter' require 'support/schema_dumping_helper' module PostgresqlJSONSharedTestCases diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index c3705e7dac..5753da1173 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -1,8 +1,5 @@ # encoding: utf-8 - require "cases/helper" -require 'active_record/base' -require 'active_record/connection_adapters/postgresql_adapter' require 'support/schema_dumping_helper' module PostgresqlUUIDHelper -- cgit v1.2.3 From d424ded6fd4b923802e9dab62b463a6f3439417f Mon Sep 17 00:00:00 2001 From: Franky W Date: Thu, 6 Nov 2014 09:07:29 -0800 Subject: Print out a meaningful error when ActiveRecord::ReadOnlyRecord is raised Currently, there is no messages which get printed out. Convoluted system may have hooks that create other objects in which case we only fail with no messages. This commit changes this information allowing you to know which object is the one that actually raised the error. --- activerecord/lib/active_record/persistence.rb | 4 ++-- activerecord/test/cases/readonly_test.rb | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 9d2c9d3b9c..06c8bceb30 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -188,7 +188,7 @@ module ActiveRecord # and destroy returns +false+. See # ActiveRecord::Callbacks for further details. def destroy - raise ReadOnlyRecord if readonly? + raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly? destroy_associations destroy_row if persisted? @destroyed = true @@ -519,7 +519,7 @@ module ActiveRecord end def create_or_update - raise ReadOnlyRecord if readonly? + raise ReadOnlyRecord, "#{self.class} is marked as readonly" if readonly? result = new_record? ? _create_record : _update_record result != false end diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb index 2afd25c989..37d277eaa1 100644 --- a/activerecord/test/cases/readonly_test.rb +++ b/activerecord/test/cases/readonly_test.rb @@ -22,9 +22,12 @@ class ReadOnlyTest < ActiveRecord::TestCase assert !dev.save dev.name = 'Forbidden.' end - assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save } - assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save! } - assert_raise(ActiveRecord::ReadOnlyRecord) { dev.destroy } + e = assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save } + assert_equal "Developer is marked as readonly", e.message + e = assert_raise(ActiveRecord::ReadOnlyRecord) { dev.save! } + assert_equal "Developer is marked as readonly", e.message + e = assert_raise(ActiveRecord::ReadOnlyRecord) { dev.destroy } + assert_equal "Developer is marked as readonly", e.message end -- cgit v1.2.3 From 58f3cf9bcb64ad2ee4997e89f4d7646f4c7bdbe7 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 7 Nov 2014 09:58:15 +0100 Subject: docs, the abstract data type `:timestamp` was removed. See #15184 [ci skip] --- .../active_record/connection_adapters/abstract/schema_definitions.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') 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 f8b6daea5a..dd4c4ae9fc 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -124,8 +124,8 @@ module ActiveRecord # which is one of the following: # :primary_key, :string, :text, # :integer, :float, :decimal, - # :datetime, :timestamp, :time, - # :date, :binary, :boolean. + # :datetime, :time, :date, + # :binary, :boolean. # # You may use a type not in this list as long as it is supported by your # database (for example, "polygon" in MySQL), but this will not be database -- cgit v1.2.3 From 35f502e9b6edf857b2388e86f429e9a88dd35a3d Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 7 Nov 2014 10:12:01 +0100 Subject: remove the last traces of OpenBase from our codebase. [ci skip] --- activerecord/test/config.example.yml | 6 ------ 1 file changed, 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml index ce30cff9e7..e3b55d640e 100644 --- a/activerecord/test/config.example.yml +++ b/activerecord/test/config.example.yml @@ -69,12 +69,6 @@ connections: username: rails encoding: utf8 - openbase: - arunit: - username: admin - arunit2: - username: admin - oracle: arunit: adapter: oracle_enhanced -- cgit v1.2.3 From 48e3edf9ad724426a3694ca6b589da0d5e0de1ee Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 7 Nov 2014 10:45:34 +0100 Subject: pg tests, only execute what's necessary. --- .../test/cases/adapters/postgresql/bytea_test.rb | 2 +- .../test/cases/adapters/postgresql/hstore_test.rb | 51 +++++++++++----------- 2 files changed, 26 insertions(+), 27 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb index 7872f91943..ec4aebf03a 100644 --- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb +++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb @@ -17,7 +17,6 @@ class PostgresqlByteaTest < ActiveRecord::TestCase end end @column = ByteaDataType.columns_hash['payload'] - assert(@column.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLColumn)) end teardown do @@ -25,6 +24,7 @@ class PostgresqlByteaTest < ActiveRecord::TestCase end def test_column + assert @column.is_a?(ActiveRecord::ConnectionAdapters::PostgreSQLColumn) assert_equal :binary, @column.type end diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb index f14ffe5ddc..6a9c6483fe 100644 --- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb +++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb @@ -1,38 +1,38 @@ # encoding: utf-8 require "cases/helper" -class PostgresqlHstoreTest < ActiveRecord::TestCase - class Hstore < ActiveRecord::Base - self.table_name = 'hstores' +if ActiveRecord::Base.connection.supports_extensions? + class PostgresqlHstoreTest < ActiveRecord::TestCase + class Hstore < ActiveRecord::Base + self.table_name = 'hstores' - store_accessor :settings, :language, :timezone - end + store_accessor :settings, :language, :timezone + end - def setup - @connection = ActiveRecord::Base.connection + def setup + @connection = ActiveRecord::Base.connection - unless @connection.extension_enabled?('hstore') - @connection.enable_extension 'hstore' - @connection.commit_db_transaction - end + unless @connection.extension_enabled?('hstore') + @connection.enable_extension 'hstore' + @connection.commit_db_transaction + end - @connection.reconnect! + @connection.reconnect! - @connection.transaction do - @connection.create_table('hstores') do |t| - t.hstore 'tags', :default => '' - t.hstore 'payload', array: true - t.hstore 'settings' + @connection.transaction do + @connection.create_table('hstores') do |t| + t.hstore 'tags', :default => '' + t.hstore 'payload', array: true + t.hstore 'settings' + end end + @column = Hstore.columns_hash['tags'] end - @column = Hstore.columns_hash['tags'] - end - teardown do - @connection.execute 'drop table if exists hstores' - end + teardown do + @connection.execute 'drop table if exists hstores' + end - if ActiveRecord::Base.connection.supports_extensions? def test_hstore_included_in_extensions assert @connection.respond_to?(:extensions), "connection should have a list of extensions" assert @connection.extensions.include?('hstore'), "extension list should include hstore" @@ -312,10 +312,8 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase dupe = record.dup assert_equal({"one" => "two"}, dupe.tags.to_hash) end - end - - private + private def assert_array_cycle(array) # test creation x = Hstore.create!(payload: array) @@ -343,4 +341,5 @@ class PostgresqlHstoreTest < ActiveRecord::TestCase x.reload assert_equal(hash, x.tags) end + end end -- cgit v1.2.3 From 92534179096b9b4838bbaee343bba1127095f5e6 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 7 Nov 2014 10:55:45 +0100 Subject: pg tests, get rid of `sql_types_test.rb`. --- .../test/cases/adapters/postgresql/bytea_test.rb | 7 +++++++ .../test/cases/adapters/postgresql/datatype_test.rb | 7 +++++++ .../test/cases/adapters/postgresql/sql_types_test.rb | 18 ------------------ 3 files changed, 14 insertions(+), 18 deletions(-) delete mode 100644 activerecord/test/cases/adapters/postgresql/sql_types_test.rb (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb index ec4aebf03a..aeebec034d 100644 --- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb +++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb @@ -28,6 +28,13 @@ class PostgresqlByteaTest < ActiveRecord::TestCase assert_equal :binary, @column.type end + def test_binary_columns_are_limitless_the_upper_limit_is_one_GB + assert_equal 'bytea', @connection.type_to_sql(:binary, 100_000) + assert_raise ActiveRecord::ActiveRecordError do + @connection.type_to_sql :binary, 4294967295 + end + end + def test_type_cast_binary_converts_the_encoding assert @column diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index a0a34e4b87..2c4839338c 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -94,6 +94,13 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase assert @first_oid.reload assert_equal new_value, @first_oid.obj_id end + + def test_text_columns_are_limitless_the_upper_limit_is_one_GB + assert_equal 'text', @connection.type_to_sql(:text, 100_000) + assert_raise ActiveRecord::ActiveRecordError do + @connection.type_to_sql :text, 4294967295 + end + end end class PostgresqlInternalDataTypeTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/adapters/postgresql/sql_types_test.rb b/activerecord/test/cases/adapters/postgresql/sql_types_test.rb deleted file mode 100644 index d7d40f6385..0000000000 --- a/activerecord/test/cases/adapters/postgresql/sql_types_test.rb +++ /dev/null @@ -1,18 +0,0 @@ -require "cases/helper" - -class SqlTypesTest < ActiveRecord::TestCase - def test_binary_types - assert_equal 'bytea', type_to_sql(:binary, 100_000) - assert_raise ActiveRecord::ActiveRecordError do - type_to_sql :binary, 4294967295 - end - assert_equal 'text', type_to_sql(:text, 100_000) - assert_raise ActiveRecord::ActiveRecordError do - type_to_sql :text, 4294967295 - end - end - - def type_to_sql(*args) - ActiveRecord::Base.connection.type_to_sql(*args) - end -end -- cgit v1.2.3 From c2fc9848b1495768a4c550e4cc532882146902c9 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 7 Nov 2014 16:23:15 -0800 Subject: default scopes should break the cache on has_many. if you specify a default scope on a model, it will break caching. We cannot predict what will happen inside the scope, so play it safe for now. fixes #17495 --- .../associations/collection_association.rb | 3 ++- .../associations/has_many_associations_test.rb | 26 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index bdfd569be2..4411e5ae62 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -409,7 +409,8 @@ module ActiveRecord def get_records if reflection.scope_chain.any?(&:any?) || scope.eager_loading? || - klass.current_scope + klass.current_scope || + klass.default_scopes.any? return scope.to_a end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index fdbe411f86..1617e883ba 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -76,6 +76,32 @@ class HasManyAssociationsTest < ActiveRecord::TestCase dev.developer_projects.map(&:project_id).sort end + def test_default_scope_on_relations_is_not_cached + counter = 0 + posts = Class.new(ActiveRecord::Base) { + self.table_name = 'posts' + self.inheritance_column = 'not_there' + post = self + + comments = Class.new(ActiveRecord::Base) { + self.table_name = 'comments' + self.inheritance_column = 'not_there' + belongs_to :post, :class => post + default_scope -> { + counter += 1 + where("id = :inc", :inc => counter) + } + } + has_many :comments, :class => comments, :foreign_key => 'post_id' + } + assert_equal 0, counter + post = posts.first + assert_equal 0, counter + sql = capture_sql { post.comments.to_a } + post.comments.reset + assert_not_equal sql, capture_sql { post.comments.to_a } + end + def test_has_many_build_with_options college = College.create(name: 'UFMT') Student.create(active: true, college_id: college.id, name: 'Sarah') -- cgit v1.2.3 From 9bd43868502f5e8a67649ffb5e766853a05714c5 Mon Sep 17 00:00:00 2001 From: alfa-jpn Date: Sat, 8 Nov 2014 17:03:54 +0900 Subject: default scopes should break the cache on singulur_association. fixes #17495 --- .../associations/singular_association.rb | 3 ++- .../associations/belongs_to_associations_test.rb | 29 ++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb index c360ef1b2c..c44242a0f0 100644 --- a/activerecord/lib/active_record/associations/singular_association.rb +++ b/activerecord/lib/active_record/associations/singular_association.rb @@ -41,7 +41,8 @@ module ActiveRecord def get_records if reflection.scope_chain.any?(&:any?) || scope.eager_loading? || - klass.current_scope + klass.current_scope || + klass.default_scopes.any? return scope.limit(1).to_a end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 25555bd75c..bcd0cf82d5 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -57,6 +57,35 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase end end + def test_default_scope_on_relations_is_not_cached + counter = 0 + + comments = Class.new(ActiveRecord::Base) { + self.table_name = 'comments' + self.inheritance_column = 'not_there' + + posts = Class.new(ActiveRecord::Base) { + self.table_name = 'posts' + self.inheritance_column = 'not_there' + + default_scope -> { + counter += 1 + where("id = :inc", :inc => counter) + } + + has_many :comments, :class => comments + } + belongs_to :post, :class => posts, :inverse_of => false + } + + assert_equal 0, counter + comment = comments.first + assert_equal 0, counter + sql = capture_sql { comment.post } + comment.reload + assert_not_equal sql, capture_sql { comment.post } + end + def test_proxy_assignment account = Account.find(1) assert_nothing_raised { account.firm = account.firm } -- cgit v1.2.3 From f43f56e16e99dae083cf1400a48c236d1af265d4 Mon Sep 17 00:00:00 2001 From: Sammy Larbi Date: Wed, 8 Oct 2014 15:58:11 -0500 Subject: Ensure HABTM relationships produce valid class names (Fixes #17119) --- activerecord/lib/active_record/associations.rb | 2 +- .../associations/builder/has_and_belongs_to_many.rb | 2 +- .../associations/has_and_belongs_to_many_associations_test.rb | 11 ++++++++++- activerecord/test/fixtures/computers.yml | 5 +++++ activerecord/test/fixtures/developers.yml | 1 + activerecord/test/models/developer.rb | 2 ++ activerecord/test/schema/schema.rb | 5 +++++ 7 files changed, 25 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 8911506694..d2bcdc55bf 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1700,7 +1700,7 @@ module ActiveRecord hm_options[:through] = middle_reflection.name hm_options[:source] = join_model.right_reflection.name - [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table].each do |k| + [:before_add, :after_add, :before_remove, :after_remove, :autosave, :validate, :join_table, :class_name].each do |k| hm_options[k] = options[k] if options.key? k end diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index 815e8eb97f..357b28ac94 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -98,7 +98,7 @@ module ActiveRecord::Associations::Builder def middle_options(join_model) middle_options = {} - middle_options[:class] = join_model + middle_options[:class_name] = "#{lhs_model.name}::#{join_model.name}" middle_options[:source] = join_model.left_reflection.name if options.key? :foreign_key middle_options[:foreign_key] = options[:foreign_key] diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 859310575e..9a57683ee3 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -1,5 +1,6 @@ require "cases/helper" require 'models/developer' +require 'models/computer' require 'models/project' require 'models/company' require 'models/customer' @@ -80,7 +81,7 @@ end class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, - :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings + :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings, :computers def setup_data_for_habtm_case ActiveRecord::Base.connection.execute('delete from countries_treaties') @@ -883,4 +884,12 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase child.special_projects << SpecialProject.new("name" => "Special Project") assert child.save, 'child object should be saved' end + + def test_habtm_with_reflection_using_class_name_and_fixtures + assert_not_nil Developer._reflections['shared_computers'] + # Checking the fixture for named association is important here, because it's the only way + # we've been able to reproduce this bug + assert_not_nil File.read(File.expand_path("../../../fixtures/developers.yml", __FILE__)).index("shared_computers") + assert_equal developers(:david).shared_computers.first, computers(:laptop) + end end diff --git a/activerecord/test/fixtures/computers.yml b/activerecord/test/fixtures/computers.yml index 7281a4d768..ad5ae2ec71 100644 --- a/activerecord/test/fixtures/computers.yml +++ b/activerecord/test/fixtures/computers.yml @@ -3,3 +3,8 @@ workstation: system: 'Linux' developer: 1 extendedWarranty: 1 + +laptop: + system: 'MacOS 1' + developer: 1 + extendedWarranty: 1 diff --git a/activerecord/test/fixtures/developers.yml b/activerecord/test/fixtures/developers.yml index 1a74563dc6..54b74e7a74 100644 --- a/activerecord/test/fixtures/developers.yml +++ b/activerecord/test/fixtures/developers.yml @@ -2,6 +2,7 @@ david: id: 1 name: David salary: 80000 + shared_computers: laptop jamis: id: 2 diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 3627cfdd09..d2a5a7fc49 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -15,6 +15,8 @@ class Developer < ActiveRecord::Base accepts_nested_attributes_for :projects + has_and_belongs_to_many :shared_computers, class_name: "Computer" + has_and_belongs_to_many :projects_extended_by_name, -> { extending(DeveloperProjectsAssociationExtension) }, :class_name => "Project", diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 539a0d2a49..720a127585 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -228,6 +228,11 @@ ActiveRecord::Schema.define do t.integer :extendedWarranty, null: false end + create_table :computers_developers, id: false, force: true do |t| + t.references :computer + t.references :developer + end + create_table :contracts, force: true do |t| t.integer :developer_id t.integer :company_id -- cgit v1.2.3 From 52c3a16fa07cde643af3c2200e4b87bcb470eb12 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Sun, 9 Nov 2014 20:38:40 -0700 Subject: Revert the behavior of booleans in string columns to that of 4.1 Why are people assigning booleans to string columns? >_> We unintentionally changed the behavior on Sqlite3 and PostgreSQL. Boolean values should cast to the database's representation of true and false. This is 't' and 'f' by default, and "1" and "0" on Mysql. The implementation to make the connection adapter specific behavior is hacky at best, and should be re-visted once we decide how we actually want to separate the concerns related to things that should change based on the database adapter. That said, this isn't something I'd expect to change based on my database adapter. We're storing a string, so the way the database represents a boolean should be irrelevant. It also seems strange for us to give booleans special behavior at all in string columns. Why is `to_s` not sufficient? It's inconsistent and confusing. Perhaps we should consider deprecating in the future. Fixes #17571 --- .../connection_adapters/abstract_mysql_adapter.rb | 24 +++++++++++++++++++++- activerecord/lib/active_record/type/string.rb | 8 ++++---- activerecord/test/cases/type/string_test.rb | 4 ++-- 3 files changed, 29 insertions(+), 7 deletions(-) (limited to 'activerecord') 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 5f512758e9..26015528e5 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -648,6 +648,8 @@ module ActiveRecord def initialize_type_map(m) # :nodoc: super + register_class_with_limit m, %r(char)i, MysqlString + 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) @@ -672,7 +674,7 @@ module ActiveRecord 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) + MysqlString.new(limit: limit) end end @@ -847,6 +849,26 @@ module ActiveRecord end end end + + class MysqlString < Type::String # :nodoc: + def type_cast_for_database(value) + case value + when true then "1" + when false then "0" + else super + end + end + + private + + def cast_value(value) + case value + when true then "1" + when false then "0" + else super + end + end + end end end end diff --git a/activerecord/lib/active_record/type/string.rb b/activerecord/lib/active_record/type/string.rb index 150defb106..fbc0af2c5a 100644 --- a/activerecord/lib/active_record/type/string.rb +++ b/activerecord/lib/active_record/type/string.rb @@ -15,8 +15,8 @@ module ActiveRecord case value when ::Numeric, ActiveSupport::Duration then value.to_s when ::String then ::String.new(value) - when true then "1" - when false then "0" + when true then "t" + when false then "f" else super end end @@ -25,8 +25,8 @@ module ActiveRecord def cast_value(value) case value - when true then "1" - when false then "0" + when true then "t" + when false then "f" # String.new is slightly faster than dup else ::String.new(value.to_s) end diff --git a/activerecord/test/cases/type/string_test.rb b/activerecord/test/cases/type/string_test.rb index 420177ed49..4d78f287f1 100644 --- a/activerecord/test/cases/type/string_test.rb +++ b/activerecord/test/cases/type/string_test.rb @@ -4,8 +4,8 @@ module ActiveRecord class StringTypeTest < ActiveRecord::TestCase test "type casting" do type = Type::String.new - assert_equal "1", type.type_cast_from_user(true) - assert_equal "0", type.type_cast_from_user(false) + assert_equal "t", type.type_cast_from_user(true) + assert_equal "f", type.type_cast_from_user(false) assert_equal "123", type.type_cast_from_user(123) end -- cgit v1.2.3 From c7deaa860131c66e231478e9d3193ab9fc4895cf Mon Sep 17 00:00:00 2001 From: Sammy Larbi Date: Mon, 10 Nov 2014 08:33:44 -0600 Subject: Fixes a bug found when running individual tests against #17217 after merging --- activerecord/test/cases/validations/uniqueness_validation_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb index 18221cc73d..c6b58d469d 100644 --- a/activerecord/test/cases/validations/uniqueness_validation_test.rb +++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb @@ -41,7 +41,7 @@ class TopicWithUniqEvent < Topic end class UniquenessValidationTest < ActiveRecord::TestCase - fixtures :topics, 'warehouse-things', :developers + fixtures :topics, 'warehouse-things' repair_validations(Topic, Reply) -- cgit v1.2.3 From 5e4a99821dd9a710fe3ab434881cfdae15bd0838 Mon Sep 17 00:00:00 2001 From: Andrey Deryabin Date: Mon, 10 Nov 2014 22:55:40 +0300 Subject: tiny code improvement in sqlite3 adapter: - remove unused method `supports_add_column?` - change additional restriction method to `valid_alter_table_type?` - fix code style --- .../lib/active_record/connection_adapters/sqlite3_adapter.rb | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 1b5e3bdbac..b18cb353f1 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -179,10 +179,6 @@ module ActiveRecord true end - def supports_add_column? - true - end - def supports_views? true end @@ -451,12 +447,12 @@ module ActiveRecord # See: http://www.sqlite.org/lang_altertable.html # SQLite has an additional restriction on the ALTER TABLE statement - def valid_alter_table_options( type, options) + def valid_alter_table_type?(type) type.to_sym != :primary_key end def add_column(table_name, column_name, type, options = {}) #:nodoc: - if supports_add_column? && valid_alter_table_options( type, options ) + if valid_alter_table_type?(type) super(table_name, column_name, type, options) else alter_table(table_name) do |definition| -- cgit v1.2.3 From 76e7305ea16b41a3674f4213ab7faa8c65196d75 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 10 Nov 2014 12:06:58 -0700 Subject: add a Table#name accessor like TableDefinition#name --- activerecord/CHANGELOG.md | 4 +++ .../abstract/schema_definitions.rb | 36 ++++++++++++---------- .../test/cases/migration/change_table_test.rb | 6 ++++ 3 files changed, 29 insertions(+), 17 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 2c66ddb983..1307427e6c 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Add `Table#name` to match `TableDefinition#name` + + *Cody Cutrer* + * Cache `CollectionAssociation#reader` proxies separately before and after the owner has been saved so that the proxy is not cached without the owner's id. 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 dd4c4ae9fc..5c4c214385 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -414,8 +414,10 @@ module ActiveRecord class Table include TimestampDefaultDeprecation + attr_reader :name + def initialize(table_name, base) - @table_name = table_name + @name = table_name @base = base end @@ -425,12 +427,12 @@ module ActiveRecord # ====== Creating a simple column # t.column(:name, :string) def column(column_name, type, options = {}) - @base.add_column(@table_name, column_name, type, options) + @base.add_column(name, column_name, type, options) end # Checks to see if a column exists. See SchemaStatements#column_exists? def column_exists?(column_name, type = nil, options = {}) - @base.column_exists?(@table_name, column_name, type, options) + @base.column_exists?(name, column_name, type, options) end # Adds a new index to the table. +column_name+ can be a single Symbol, or @@ -443,19 +445,19 @@ module ActiveRecord # ====== Creating a named index # t.index([:branch_id, :party_id], unique: true, name: 'by_branch_party') def index(column_name, options = {}) - @base.add_index(@table_name, column_name, options) + @base.add_index(name, column_name, options) end # Checks to see if an index exists. See SchemaStatements#index_exists? def index_exists?(column_name, options = {}) - @base.index_exists?(@table_name, column_name, options) + @base.index_exists?(name, column_name, options) end # Renames the given index on the table. # # t.rename_index(:user_id, :account_id) def rename_index(index_name, new_index_name) - @base.rename_index(@table_name, index_name, new_index_name) + @base.rename_index(name, index_name, new_index_name) end # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps @@ -463,7 +465,7 @@ module ActiveRecord # t.timestamps def timestamps(options = {}) emit_warning_if_null_unspecified(options) - @base.add_timestamps(@table_name, options) + @base.add_timestamps(name, options) end # Changes the column's definition according to the new options. @@ -472,7 +474,7 @@ module ActiveRecord # t.change(:name, :string, limit: 80) # t.change(:description, :text) def change(column_name, type, options = {}) - @base.change_column(@table_name, column_name, type, options) + @base.change_column(name, column_name, type, options) end # Sets a new default value for a column. See SchemaStatements#change_column_default @@ -480,7 +482,7 @@ module ActiveRecord # t.change_default(:qualification, 'new') # t.change_default(:authorized, 1) def change_default(column_name, default) - @base.change_column_default(@table_name, column_name, default) + @base.change_column_default(name, column_name, default) end # Removes the column(s) from the table definition. @@ -488,7 +490,7 @@ module ActiveRecord # t.remove(:qualification) # t.remove(:qualification, :experience) def remove(*column_names) - @base.remove_columns(@table_name, *column_names) + @base.remove_columns(name, *column_names) end # Removes the given index from the table. @@ -502,21 +504,21 @@ module ActiveRecord # ====== Remove the index named by_branch_party in the table_name table # t.remove_index name: :by_branch_party def remove_index(options = {}) - @base.remove_index(@table_name, options) + @base.remove_index(name, options) end # Removes the timestamp columns (+created_at+ and +updated_at+) from the table. # # t.remove_timestamps def remove_timestamps - @base.remove_timestamps(@table_name) + @base.remove_timestamps(name) end # Renames a column. # # t.rename(:description, :name) def rename(column_name, new_column_name) - @base.rename_column(@table_name, column_name, new_column_name) + @base.rename_column(name, column_name, new_column_name) end # Adds a reference. Optionally adds a +type+ column, if :polymorphic option is provided. @@ -531,7 +533,7 @@ module ActiveRecord def references(*args) options = args.extract_options! args.each do |ref_name| - @base.add_reference(@table_name, ref_name, options) + @base.add_reference(name, ref_name, options) end end alias :belongs_to :references @@ -546,7 +548,7 @@ module ActiveRecord def remove_references(*args) options = args.extract_options! args.each do |ref_name| - @base.remove_reference(@table_name, ref_name, options) + @base.remove_reference(name, ref_name, options) end end alias :remove_belongs_to :remove_references @@ -558,8 +560,8 @@ module ActiveRecord [:string, :text, :integer, :float, :decimal, :datetime, :timestamp, :time, :date, :binary, :boolean].each do |column_type| define_method column_type do |*args| options = args.extract_options! - args.each do |name| - @base.add_column(@table_name, name, column_type, options) + args.each do |column_name| + @base.add_column(name, column_name, column_type, options) end end end diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb index 777a48ad14..6613783fba 100644 --- a/activerecord/test/cases/migration/change_table_test.rb +++ b/activerecord/test/cases/migration/change_table_test.rb @@ -213,6 +213,12 @@ module ActiveRecord t.rename :bar, :baz end end + + def test_table_name_set + with_change_table do |t| + assert_equal :delete_me, t.name + end + end end end end -- cgit v1.2.3 From fd1364e021155d04318571ec08d50e2c740199db Mon Sep 17 00:00:00 2001 From: Andrey Deryabin Date: Tue, 11 Nov 2014 01:33:29 +0300 Subject: remove never called method `limited_update_conditions` --- .../active_record/connection_adapters/abstract/database_statements.rb | 4 ---- .../lib/active_record/connection_adapters/abstract_mysql_adapter.rb | 4 ---- 2 files changed, 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 1a3ed28d66..12b16b2473 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -287,10 +287,6 @@ module ActiveRecord "DEFAULT VALUES" end - def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) - "WHERE #{quoted_primary_key} IN (SELECT #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})" - end - # Sanitizes the given LIMIT parameter in order to prevent SQL injection. # # The +limit+ may be anything that can evaluate to a string via #to_s. It 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 26015528e5..167453657d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -631,10 +631,6 @@ module ActiveRecord end end - def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) - where_sql - end - def strict_mode? self.class.type_cast_config_to_boolean(@config.fetch(:strict, true)) end -- cgit v1.2.3 From b1879124a82b34168412ac699cf6f654e005c4d6 Mon Sep 17 00:00:00 2001 From: Andrey Deryabin Date: Wed, 12 Nov 2014 15:11:36 +0300 Subject: Follow the coding conventions --- activerecord/lib/active_record/scoping/named.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index ec1edf0e01..35420e6551 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -139,7 +139,7 @@ module ActiveRecord # Article.published.featured.latest_article # Article.featured.titles def scope(name, body, &block) - unless body.respond_to?:call + unless body.respond_to?(:call) raise ArgumentError, 'The scope body needs to be callable.' end -- cgit v1.2.3 From 07e0d8cc0c108b2814d5c8d29f5f3f5b1db4f696 Mon Sep 17 00:00:00 2001 From: shiksha Date: Thu, 13 Nov 2014 11:13:44 +0530 Subject: Allow habtm class_name option to be consistent with other associations --- .../associations/builder/has_and_belongs_to_many.rb | 2 +- .../associations/has_and_belongs_to_many_associations_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index 357b28ac94..092b4ebd2f 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -110,7 +110,7 @@ module ActiveRecord::Associations::Builder rhs_options = {} if options.key? :class_name - rhs_options[:foreign_key] = options[:class_name].foreign_key + rhs_options[:foreign_key] = options[:class_name].to_s.foreign_key rhs_options[:class_name] = options[:class_name] end diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 9a57683ee3..092e85949c 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -79,6 +79,10 @@ class SubDeveloper < Developer :association_foreign_key => "developer_id" end +class DeveloperWithSymbolClassName < Developer + has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys +end + class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects, :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings, :computers @@ -892,4 +896,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase assert_not_nil File.read(File.expand_path("../../../fixtures/developers.yml", __FILE__)).index("shared_computers") assert_equal developers(:david).shared_computers.first, computers(:laptop) end + + def test_with_symbol_class_name + assert_nothing_raised NoMethodError do + DeveloperWithSymbolClassName.new + end + end end -- cgit v1.2.3 From aeb917d475dfccb8b5f3b5a57f62df5358ad3ef0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 13 Nov 2014 15:31:54 -0800 Subject: exec_prepared is GVL friendly, so lets use it. also increase the version of pg required so that people will get the GVL friendly version --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 9941f74766..6310d70192 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -13,7 +13,7 @@ require 'active_record/connection_adapters/postgresql/database_statements' require 'arel/visitors/bind_visitor' # Make sure we're using pg high enough for PGResult#values -gem 'pg', '~> 0.11' +gem 'pg', '~> 0.15' require 'pg' require 'ipaddr' @@ -596,9 +596,7 @@ module ActiveRecord } log(sql, name, type_casted_binds, stmt_key) do - @connection.send_query_prepared(stmt_key, type_casted_binds.map { |_, val| val }) - @connection.block - @connection.get_last_result + @connection.exec_prepared(stmt_key, type_casted_binds.map { |_, val| val }) end rescue ActiveRecord::StatementInvalid => e pgerror = e.original_exception -- cgit v1.2.3 From 9ae210ba091bc9f54de08da4be1540f0a9782771 Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Fri, 14 Nov 2014 09:55:32 +0100 Subject: Build fix when running in isolation `Computer` class needs to be require See #17217 for more details --- activerecord/test/cases/adapters/mysql2/explain_test.rb | 1 + activerecord/test/cases/adapters/postgresql/explain_test.rb | 1 + activerecord/test/cases/adapters/sqlite3/explain_test.rb | 1 + activerecord/test/cases/associations/belongs_to_associations_test.rb | 1 + activerecord/test/cases/associations/callbacks_test.rb | 1 + activerecord/test/cases/associations/eager_test.rb | 1 + activerecord/test/cases/associations/extension_test.rb | 1 + activerecord/test/cases/associations/has_many_associations_test.rb | 1 + .../test/cases/associations/has_many_through_associations_test.rb | 1 + activerecord/test/cases/associations/has_one_associations_test.rb | 1 + activerecord/test/cases/associations_test.rb | 1 + activerecord/test/cases/attribute_methods_test.rb | 1 + activerecord/test/cases/autosave_association_test.rb | 1 + activerecord/test/cases/base_test.rb | 1 + activerecord/test/cases/callbacks_test.rb | 2 ++ activerecord/test/cases/finder_test.rb | 1 + activerecord/test/cases/fixtures_test.rb | 1 + activerecord/test/cases/integration_test.rb | 1 + activerecord/test/cases/migration_test.rb | 1 + activerecord/test/cases/modules_test.rb | 1 + activerecord/test/cases/persistence_test.rb | 1 + activerecord/test/cases/readonly_test.rb | 1 + activerecord/test/cases/relation/merging_test.rb | 1 + activerecord/test/cases/relations_test.rb | 1 + activerecord/test/cases/scoping/default_scoping_test.rb | 1 + activerecord/test/cases/scoping/named_scoping_test.rb | 1 + activerecord/test/cases/scoping/relation_scoping_test.rb | 1 + activerecord/test/cases/timestamp_test.rb | 1 + activerecord/test/cases/transactions_test.rb | 1 + activerecord/test/cases/validations_test.rb | 1 + 30 files changed, 31 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb index 675703caa1..f67f21fab1 100644 --- a/activerecord/test/cases/adapters/mysql2/explain_test.rb +++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb @@ -1,5 +1,6 @@ require "cases/helper" require 'models/developer' +require 'models/computer' module ActiveRecord module ConnectionAdapters diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb index 19053b6732..d2dd04b84b 100644 --- a/activerecord/test/cases/adapters/postgresql/explain_test.rb +++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb @@ -1,5 +1,6 @@ require "cases/helper" require 'models/developer' +require 'models/computer' module ActiveRecord module ConnectionAdapters diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb index f1d6119d2e..de6e35ef57 100644 --- a/activerecord/test/cases/adapters/sqlite3/explain_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb @@ -1,5 +1,6 @@ require "cases/helper" require 'models/developer' +require 'models/computer' module ActiveRecord module ConnectionAdapters diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index bcd0cf82d5..17394cb6f7 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -1,5 +1,6 @@ require 'cases/helper' require 'models/developer' +require 'models/computer' require 'models/project' require 'models/company' require 'models/topic' diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb index 5b7e462f64..a531e0e02c 100644 --- a/activerecord/test/cases/associations/callbacks_test.rb +++ b/activerecord/test/cases/associations/callbacks_test.rb @@ -3,6 +3,7 @@ require 'models/post' require 'models/author' require 'models/project' require 'models/developer' +require 'models/computer' require 'models/company' class AssociationCallbacksTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 8234ee95be..4539b99504 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -17,6 +17,7 @@ require 'models/subscriber' require 'models/subscription' require 'models/book' require 'models/developer' +require 'models/computer' require 'models/project' require 'models/member' require 'models/membership' diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb index 4c1fdfdd9a..9d373cd73b 100644 --- a/activerecord/test/cases/associations/extension_test.rb +++ b/activerecord/test/cases/associations/extension_test.rb @@ -3,6 +3,7 @@ require 'models/post' require 'models/comment' require 'models/project' require 'models/developer' +require 'models/computer' require 'models/company_in_module' class AssociationsExtensionsTest < ActiveRecord::TestCase diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 69f7bde4bc..5a963b6458 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1,5 +1,6 @@ require "cases/helper" require 'models/developer' +require 'models/computer' require 'models/project' require 'models/company' require 'models/contract' diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index cddf1a1f72..df4a30ae9b 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -15,6 +15,7 @@ require 'models/toy' require 'models/contract' require 'models/company' require 'models/developer' +require 'models/computer' require 'models/subscriber' require 'models/book' require 'models/subscription' diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index d412b3168e..2ecfcb521d 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -1,5 +1,6 @@ require "cases/helper" require 'models/developer' +require 'models/computer' require 'models/project' require 'models/company' require 'models/ship' diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index 9b0cf4c18f..04ef50e58a 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -1,6 +1,7 @@ require "cases/helper" require 'models/computer' require 'models/developer' +require 'models/computer' require 'models/project' require 'models/company' require 'models/categorization' diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index c3e8ceb0da..731d433706 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -1,6 +1,7 @@ require "cases/helper" require 'models/minimalistic' require 'models/developer' +require 'models/computer' require 'models/auto_id' require 'models/boolean' require 'models/computer' diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 734fd5fe18..71c34d816a 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -4,6 +4,7 @@ require 'models/comment' require 'models/company' require 'models/customer' require 'models/developer' +require 'models/computer' require 'models/invoice' require 'models/line_item' require 'models/order' diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 3675401555..098d5b8451 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -10,6 +10,7 @@ require 'models/category' require 'models/company' require 'models/customer' require 'models/developer' +require 'models/computer' require 'models/project' require 'models/default' require 'models/auto_id' diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb index c8f56e3c73..5e07f8a03c 100644 --- a/activerecord/test/cases/callbacks_test.rb +++ b/activerecord/test/cases/callbacks_test.rb @@ -1,4 +1,6 @@ require "cases/helper" +require 'models/developer' +require 'models/computer' class CallbackDeveloper < ActiveRecord::Base self.table_name = 'developers' diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index dc73faa5be..33a59d4678 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -10,6 +10,7 @@ require 'models/reply' require 'models/entrant' require 'models/project' require 'models/developer' +require 'models/computer' require 'models/customer' require 'models/toy' require 'models/matey' diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 7141d3ee7f..4b0ebe13d1 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -10,6 +10,7 @@ require 'models/company' require 'models/computer' require 'models/course' require 'models/developer' +require 'models/computer' require 'models/joke' require 'models/matey' require 'models/parrot' diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb index dfb8a608cb..0021988083 100644 --- a/activerecord/test/cases/integration_test.rb +++ b/activerecord/test/cases/integration_test.rb @@ -3,6 +3,7 @@ require 'cases/helper' require 'models/company' require 'models/developer' +require 'models/computer' require 'models/owner' require 'models/pet' diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 6b7a0a9000..3192b797b4 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -5,6 +5,7 @@ require 'bigdecimal/util' require 'models/person' require 'models/topic' require 'models/developer' +require 'models/computer' require MIGRATIONS_ROOT + "/valid/2_we_need_reminders" require MIGRATIONS_ROOT + "/rename/1_we_need_things" diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb index e87773df94..6f65bf80eb 100644 --- a/activerecord/test/cases/modules_test.rb +++ b/activerecord/test/cases/modules_test.rb @@ -2,6 +2,7 @@ require "cases/helper" require 'models/company_in_module' require 'models/shop' require 'models/developer' +require 'models/computer' class ModulesTest < ActiveRecord::TestCase fixtures :accounts, :companies, :projects, :developers, :collections, :products, :variants diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 2170fe6118..2bcd496415 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -8,6 +8,7 @@ require 'models/reply' require 'models/category' require 'models/company' require 'models/developer' +require 'models/computer' require 'models/project' require 'models/minimalistic' require 'models/warehouse_thing' diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb index 5b903e4f2c..1c919f0b57 100644 --- a/activerecord/test/cases/readonly_test.rb +++ b/activerecord/test/cases/readonly_test.rb @@ -3,6 +3,7 @@ require 'models/author' require 'models/post' require 'models/comment' require 'models/developer' +require 'models/computer' require 'models/project' require 'models/reader' require 'models/person' diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb index 8f40a1890d..eb76ef6328 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -2,6 +2,7 @@ require 'cases/helper' require 'models/author' require 'models/comment' require 'models/developer' +require 'models/computer' require 'models/post' require 'models/project' require 'models/rating' diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 410b5ba5a3..8ac4a4ca36 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -7,6 +7,7 @@ require 'models/comment' require 'models/author' require 'models/entrant' require 'models/developer' +require 'models/computer' require 'models/reply' require 'models/company' require 'models/bird' diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index a5c4404175..880d0e8293 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -2,6 +2,7 @@ require 'cases/helper' require 'models/post' require 'models/comment' require 'models/developer' +require 'models/computer' class DefaultScopingTest < ActiveRecord::TestCase fixtures :developers, :posts, :comments diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index c2816c3670..41f3449828 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -5,6 +5,7 @@ require 'models/comment' require 'models/reply' require 'models/author' require 'models/developer' +require 'models/computer' class NamedScopingTest < ActiveRecord::TestCase fixtures :posts, :authors, :topics, :comments, :author_addresses diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index 73835c85a8..d7bcbf6203 100644 --- a/activerecord/test/cases/scoping/relation_scoping_test.rb +++ b/activerecord/test/cases/scoping/relation_scoping_test.rb @@ -2,6 +2,7 @@ require "cases/helper" require 'models/post' require 'models/author' require 'models/developer' +require 'models/computer' require 'models/project' require 'models/comment' require 'models/category' diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb index abf6becc17..db474c63a4 100644 --- a/activerecord/test/cases/timestamp_test.rb +++ b/activerecord/test/cases/timestamp_test.rb @@ -1,6 +1,7 @@ require 'cases/helper' require 'support/ddl_helper' require 'models/developer' +require 'models/computer' require 'models/owner' require 'models/pet' require 'models/toy' diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 5cccf2dda5..7160e8324d 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -2,6 +2,7 @@ require "cases/helper" require 'models/topic' require 'models/reply' require 'models/developer' +require 'models/computer' require 'models/book' require 'models/author' require 'models/post' diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index 55804f9576..db8159eff8 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -4,6 +4,7 @@ require 'models/topic' require 'models/reply' require 'models/person' require 'models/developer' +require 'models/computer' require 'models/parrot' require 'models/company' -- cgit v1.2.3 From 85b40edd949678abe2a50a4291029be123cb03c5 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 14 Nov 2014 07:39:32 -0700 Subject: Use `DelegateClass` instead of `SimpleDelegator` for type decorators There is a significant performance difference between the two. Closes --- .../lib/active_record/attribute_methods/time_zone_conversion.rb | 2 +- activerecord/lib/active_record/type/serialized.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb index b7fe079ef5..33d9d2002c 100644 --- a/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb +++ b/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb @@ -1,7 +1,7 @@ module ActiveRecord module AttributeMethods module TimeZoneConversion - class TimeZoneConverter < SimpleDelegator # :nodoc: + class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc: include Type::Decorator def type_cast_from_database(value) diff --git a/activerecord/lib/active_record/type/serialized.rb b/activerecord/lib/active_record/type/serialized.rb index 17004b3593..3191a868ef 100644 --- a/activerecord/lib/active_record/type/serialized.rb +++ b/activerecord/lib/active_record/type/serialized.rb @@ -1,6 +1,6 @@ module ActiveRecord module Type - class Serialized < SimpleDelegator # :nodoc: + class Serialized < DelegateClass(Type::Value) # :nodoc: include Mutable include Decorator -- cgit v1.2.3 From 70d1b5a7f8e25b077168deaf592e0e58c3f2bdd1 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 14 Nov 2014 10:45:53 -0700 Subject: Revert "Improve performance of AR object instantiation" This reverts commit 8fee923888192a658d8823b31e77ed0683dfd665. Conflicts: activerecord/lib/active_record/attribute_set/builder.rb This solution sucks, and is hard to actually apply across the board. Going to try other solutions --- .../lib/active_record/attribute_set/builder.rb | 21 +++------------- activerecord/lib/active_record/inheritance.rb | 12 ++++------ activerecord/lib/active_record/persistence.rb | 28 +++------------------- activerecord/lib/active_record/querying.rb | 2 +- activerecord/lib/active_record/result.rb | 9 ------- 5 files changed, 11 insertions(+), 61 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 0a62c68bfb..d4a787f2fe 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -8,33 +8,18 @@ module ActiveRecord end def build_from_database(values = {}, additional_types = {}) - build_from_database_pairs(values.keys, values.values, additional_types) - end - - def build_from_database_pairs(columns, values, additional_types) - attributes = build_attributes_from_values(columns, values, additional_types) + attributes = build_attributes_from_values(values, additional_types) add_uninitialized_attributes(attributes) AttributeSet.new(attributes) end private - def build_attributes_from_values(columns, values, additional_types) - # We are performing manual iteration here as this method is a performance - # hotspot - hash = {} - index = 0 - length = columns.length - - while index < length - name = columns[index] - value = values[index] + def build_attributes_from_values(values, additional_types) + values.each_with_object({}) do |(name, value), hash| type = additional_types.fetch(name, types[name]) hash[name] = Attribute.from_database(name, value, type) - index += 1 end - - hash end def add_uninitialized_attributes(attributes) diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 4aad3217cb..f58145ab05 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -165,19 +165,15 @@ module ActiveRecord # record instance. For single-table inheritance, we check the record # for a +type+ column and return the corresponding class. def discriminate_class_for_record(record) - discriminate_class_for_value(record[inheritance_column]) - end - - def discriminate_class_for_value(value) - if using_single_table_inheritance?(value) - find_sti_class(value) + if using_single_table_inheritance?(record) + find_sti_class(record[inheritance_column]) else super end end - def using_single_table_inheritance?(value) - value.present? && columns_hash.include?(inheritance_column) + def using_single_table_inheritance?(record) + record[inheritance_column].present? && columns_hash.include?(inheritance_column) end def find_sti_class(type_name) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 06c8bceb30..507faf2879 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -65,41 +65,19 @@ module ActiveRecord # how this "single-table" inheritance mapping is implemented. def instantiate(attributes, column_types = {}) klass = discriminate_class_for_record(attributes) - klass.instantiate_pairs(attributes.keys, attributes.values, column_types) - end - - def instantiate_pairs(columns, values, column_types = {}) # :nodoc: - attributes = attributes_builder.build_from_database_pairs(columns, values, column_types) - allocate.init_with('attributes' => attributes, 'new_record' => false) - end - - def instantiate_result_set(result_set, column_types = {}) # :nodoc: - inheritance_column_index = inheritance_column && result_set.columns.find_index(inheritance_column) - - result_set.each_pair.map do |columns, values| - inheritance_value = inheritance_column_index && values[inheritance_column_index] - klass = discriminate_class_for_value(inheritance_value) - klass.instantiate_pairs(columns, values, column_types) - end + attributes = klass.attributes_builder.build_from_database(attributes, column_types) + klass.allocate.init_with('attributes' => attributes, 'new_record' => false) end private # Called by +instantiate+ to decide which class to use for a new # record instance. # - # See +ActiveRecord::Inheritance#discriminate_class_for_value+ for + # See +ActiveRecord::Inheritance#discriminate_class_for_record+ for # the single-table inheritance discriminator. - def discriminate_class_for_value(*) - self - end - def discriminate_class_for_record(record) self end - - def inheritance_column - nil - end end # Returns true if this object hasn't been saved yet -- that is, a record diff --git a/activerecord/lib/active_record/querying.rb b/activerecord/lib/active_record/querying.rb index 9d4df81b07..e8de4db3a7 100644 --- a/activerecord/lib/active_record/querying.rb +++ b/activerecord/lib/active_record/querying.rb @@ -47,7 +47,7 @@ module ActiveRecord } message_bus.instrument('instantiation.active_record', payload) do - instantiate_result_set(result_set, column_types) + result_set.map { |record| instantiate(record, column_types) } end end diff --git a/activerecord/lib/active_record/result.rb b/activerecord/lib/active_record/result.rb index c84ad586e2..3a3e65ef32 100644 --- a/activerecord/lib/active_record/result.rb +++ b/activerecord/lib/active_record/result.rb @@ -54,15 +54,6 @@ module ActiveRecord end end - def each_pair - return to_enum(__method__) unless block_given? - - columns = @columns.map { |c| c.dup.freeze } - @rows.each do |row| - yield columns, row - end - end - def to_hash hash_rows end -- cgit v1.2.3 From 0f29c216074c5da6644feddb5184c4881c078b0d Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 14 Nov 2014 11:20:28 -0700 Subject: Reduce the amount of work performed when instantiating AR models We don't know which attributes will or won't be used, and we don't want to create massive bottlenecks at instantiation. Rather than doing *any* iteration over types and values, we can lazily instantiate the object. The lazy attribute hash should not fully implement hash, or subclass hash at any point in the future. It is not meant to be a replacement, but instead implement its own interface which happens to overlap. --- .../active_record/attribute_methods/primary_key.rb | 1 + activerecord/lib/active_record/attribute_set.rb | 14 ++-- .../lib/active_record/attribute_set/builder.rb | 78 +++++++++++++++++----- activerecord/lib/active_record/core.rb | 2 - activerecord/lib/active_record/model_schema.rb | 2 +- activerecord/test/cases/attribute_set_test.rb | 9 +++ 6 files changed, 79 insertions(+), 27 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 9bd333bbac..104d84a1f8 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -120,6 +120,7 @@ module ActiveRecord def primary_key=(value) @primary_key = value && value.to_s @quoted_primary_key = nil + @attributes_builder = nil end end end diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb index 98ac63c7e1..21c58cbf1d 100644 --- a/activerecord/lib/active_record/attribute_set.rb +++ b/activerecord/lib/active_record/attribute_set.rb @@ -2,8 +2,6 @@ require 'active_record/attribute_set/builder' module ActiveRecord class AttributeSet # :nodoc: - delegate :keys, to: :initialized_attributes - def initialize(attributes) @attributes = attributes end @@ -25,6 +23,10 @@ module ActiveRecord attributes.key?(name) && self[name].initialized? end + def keys + attributes.initialized_keys + end + def fetch_value(name, &block) self[name].value(&block) end @@ -43,7 +45,7 @@ module ActiveRecord end def initialize_dup(_) - @attributes = attributes.transform_values(&:dup) + @attributes = attributes.dup super end @@ -58,12 +60,6 @@ module ActiveRecord end end - def ensure_initialized(key) - unless self[key].initialized? - write_from_database(key, nil) - end - end - protected attr_reader :attributes diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index d4a787f2fe..3946e02d10 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -1,35 +1,83 @@ module ActiveRecord class AttributeSet # :nodoc: class Builder # :nodoc: - attr_reader :types + attr_reader :types, :always_initialized - def initialize(types) + def initialize(types, always_initialized = nil) @types = types + @always_initialized = always_initialized end def build_from_database(values = {}, additional_types = {}) - attributes = build_attributes_from_values(values, additional_types) - add_uninitialized_attributes(attributes) + if always_initialized && !values.key?(always_initialized) + values[always_initialized] = nil + end + + attributes = LazyAttributeHash.new(types, values, additional_types) AttributeSet.new(attributes) end private + end + end + + class LazyAttributeHash + delegate :select, :transform_values, to: :materialize + delegate :[], :[]=, :freeze, to: :delegate_hash + + def initialize(types, values, additional_types) + @types = types + @values = values + @additional_types = additional_types + @materialized = false + @delegate_hash = {} + assign_default_proc + end + + def key?(key) + delegate_hash.key?(key) || values.key?(key) || types.key?(key) + end + + def initialized_keys + delegate_hash.keys | values.keys + end - def build_attributes_from_values(values, additional_types) - values.each_with_object({}) do |(name, value), hash| - type = additional_types.fetch(name, types[name]) - hash[name] = Attribute.from_database(name, value, type) + def initialize_dup(_) + @delegate_hash = delegate_hash.transform_values(&:dup) + assign_default_proc + super + end + + def initialize_clone(_) + @delegate_hash = delegate_hash.clone + super + end + + protected + + attr_reader :types, :values, :additional_types, :delegate_hash + + private + + def assign_default_proc + delegate_hash.default_proc = proc do |hash, name| + type = additional_types.fetch(name, types[name]) + + if values.key?(name) + hash[name] = Attribute.from_database(name, values[name], type) + elsif type + hash[name] = Attribute.uninitialized(name, type) end end + end - def add_uninitialized_attributes(attributes) - types.each_key do |name| - next if attributes.key? name - type = types[name] - attributes[name] = - Attribute.uninitialized(name, type) - end + def materialize + unless @materialized + values.each_key { |key| delegate_hash[key] } + types.each_key { |key| delegate_hash[key] } + @materialized = true end + delegate_hash end end end diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 952aeaa703..89d8932e9e 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -536,8 +536,6 @@ module ActiveRecord end def init_internals - @attributes.ensure_initialized(self.class.primary_key) - @aggregation_cache = {} @association_cache = {} @readonly = false diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index a444aac23c..adad7774b9 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -231,7 +231,7 @@ module ActiveRecord end def attributes_builder # :nodoc: - @attributes_builder ||= AttributeSet::Builder.new(column_types) + @attributes_builder ||= AttributeSet::Builder.new(column_types, primary_key) end def column_types # :nodoc: diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb index dc20c3c676..a531be482e 100644 --- a/activerecord/test/cases/attribute_set_test.rb +++ b/activerecord/test/cases/attribute_set_test.rb @@ -123,6 +123,15 @@ module ActiveRecord assert_nil attributes.fetch_value(:bar) end + test "the primary_key is always initialized" do + builder = AttributeSet::Builder.new({ foo: Type::Integer.new }, :foo) + attributes = builder.build_from_database + + assert attributes.key?(:foo) + assert_equal [:foo], attributes.keys + assert attributes[:foo].initialized? + end + class MyType def type_cast_from_user(value) return if value.nil? -- cgit v1.2.3 From 3f63ac4e4dd22a452a37ad7a654cf5e4f4e66349 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 14 Nov 2014 11:58:35 -0700 Subject: Correctly determine if an attribute is uninitialized In real usage, we give the builder a types hash with a default value of `Type::Value.new`. This means we need to explicitly check for the key, rather than the truthiness of the type to determine if it's a known but uninitialized attribute --- activerecord/lib/active_record/attribute_set/builder.rb | 2 +- activerecord/test/cases/attribute_set_test.rb | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 3946e02d10..54e9cbb779 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -65,7 +65,7 @@ module ActiveRecord if values.key?(name) hash[name] = Attribute.from_database(name, values[name], type) - elsif type + elsif types.key?(name) hash[name] = Attribute.uninitialized(name, type) end end diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb index a531be482e..de63672963 100644 --- a/activerecord/test/cases/attribute_set_test.rb +++ b/activerecord/test/cases/attribute_set_test.rb @@ -109,7 +109,15 @@ module ActiveRecord test "fetch_value returns nil for unknown attributes" do attributes = attributes_with_uninitialized_key - assert_nil attributes.fetch_value(:wibble) + assert_nil attributes.fetch_value(:wibble) { "hello" } + end + + test "fetch_value returns nil for unknown attributes when types has a default" do + types = Hash.new(Type::Value.new) + builder = AttributeSet::Builder.new(types) + attributes = builder.build_from_database + + assert_nil attributes.fetch_value(:wibble) { "hello" } end test "fetch_value uses the given block for uninitialized attributes" do -- cgit v1.2.3 From 895a53e7161e9bdc6285fe03f16b49ec74972416 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 14 Nov 2014 12:02:05 -0700 Subject: Allow `LazyAttributeHash` to be marshalled `default_proc` makes a hash unmarshallable, and adds unneccessary overhead. Since we control all access to the hash, let's just handle it in that method. This has the side effect of improving performance on initialization (but not neccessarily on access). We'll need to profile further once the tests are passing. --- .../lib/active_record/attribute_set/builder.rb | 30 ++++++++++++---------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 54e9cbb779..4134032659 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -23,7 +23,7 @@ module ActiveRecord class LazyAttributeHash delegate :select, :transform_values, to: :materialize - delegate :[], :[]=, :freeze, to: :delegate_hash + delegate :[]=, :freeze, to: :delegate_hash def initialize(types, values, additional_types) @types = types @@ -31,20 +31,26 @@ module ActiveRecord @additional_types = additional_types @materialized = false @delegate_hash = {} - assign_default_proc end def key?(key) delegate_hash.key?(key) || values.key?(key) || types.key?(key) end + def [](key) + if delegate_hash.key?(key) + delegate_hash[key] + else + assign_default_value(key) + end + end + def initialized_keys delegate_hash.keys | values.keys end def initialize_dup(_) @delegate_hash = delegate_hash.transform_values(&:dup) - assign_default_proc super end @@ -59,22 +65,20 @@ module ActiveRecord private - def assign_default_proc - delegate_hash.default_proc = proc do |hash, name| - type = additional_types.fetch(name, types[name]) + def assign_default_value(name) + type = additional_types.fetch(name, types[name]) - if values.key?(name) - hash[name] = Attribute.from_database(name, values[name], type) - elsif types.key?(name) - hash[name] = Attribute.uninitialized(name, type) - end + if values.key?(name) + delegate_hash[name] = Attribute.from_database(name, values[name], type) + elsif types.key?(name) + delegate_hash[name] = Attribute.uninitialized(name, type) end end def materialize unless @materialized - values.each_key { |key| delegate_hash[key] } - types.each_key { |key| delegate_hash[key] } + values.each_key { |key| self[key] } + types.each_key { |key| self[key] } @materialized = true end delegate_hash -- cgit v1.2.3 From 48f2620df98e6926d38b8951087ab1bbc512580c Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 14 Nov 2014 12:37:25 -0700 Subject: Don't freeze the same hash we use for memoization --- activerecord/lib/active_record/attribute_set/builder.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 4134032659..73b77d2eda 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -23,7 +23,6 @@ module ActiveRecord class LazyAttributeHash delegate :select, :transform_values, to: :materialize - delegate :[]=, :freeze, to: :delegate_hash def initialize(types, values, additional_types) @types = types @@ -45,6 +44,13 @@ module ActiveRecord end end + def []=(key, value) + if frozen? + raise RuntimeError, "Can't modify frozen hash" + end + delegate_hash[key] = value + end + def initialized_keys delegate_hash.keys | values.keys end @@ -54,11 +60,6 @@ module ActiveRecord super end - def initialize_clone(_) - @delegate_hash = delegate_hash.clone - super - end - protected attr_reader :types, :values, :additional_types, :delegate_hash -- cgit v1.2.3 From da99a2a2982d35f670ad9647463e09bfe9032b70 Mon Sep 17 00:00:00 2001 From: Sam Date: Mon, 17 Nov 2014 14:55:17 +1100 Subject: PERF: optimise type lookup to avoid invoking procs --- .../connection_adapters/postgresql_adapter.rb | 20 +++++++++++++------- .../lib/active_record/type/hash_lookup_type_map.rb | 20 +++++++++++++++++++- 2 files changed, 32 insertions(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 6310d70192..d3e5b0a4ad 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -431,16 +431,22 @@ module ActiveRecord private def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc: - if !type_map.key?(oid) + + result = type_map.fetch(oid, fmod, sql_type) { + nil + } + + unless result load_additional_types(type_map, [oid]) + result = type_map.fetch(oid, fmod, sql_type) { + warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String." + Type::Value.new.tap do |cast_type| + type_map.register_type(oid, cast_type) + end + } end - type_map.fetch(oid, fmod, sql_type) { - warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String." - Type::Value.new.tap do |cast_type| - type_map.register_type(oid, cast_type) - end - } + result end def initialize_type_map(m) # :nodoc: diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb index bf92680268..2a146d38a9 100644 --- a/activerecord/lib/active_record/type/hash_lookup_type_map.rb +++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb @@ -1,6 +1,12 @@ module ActiveRecord module Type class HashLookupTypeMap < TypeMap # :nodoc: + + def initialize + @cache = {} + super + end + delegate :key?, to: :@mapping def lookup(type, *args) @@ -8,12 +14,24 @@ module ActiveRecord end def fetch(type, *args, &block) - @mapping.fetch(type, block).call(type, *args) + cache = (@cache[type] ||= {}) + resolved = cache[args] + + unless resolved + resolved = cache[args] = @mapping.fetch(type, block).call(type, *args) + end + + resolved end def alias_type(type, alias_type) register_type(type) { |_, *args| lookup(alias_type, *args) } end + + def register_type(key, value=nil, &block) + @cache = {} + super(key, value, &block) + end end end end -- cgit v1.2.3 From 409e7e4fc5332bf6199ac927d816c51f1d6a3307 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Mon, 17 Nov 2014 05:59:06 -0800 Subject: Remove pointless `private` This class no longer has any private methods --- activerecord/lib/active_record/attribute_set/builder.rb | 2 -- 1 file changed, 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 73b77d2eda..60154a0647 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -16,8 +16,6 @@ module ActiveRecord attributes = LazyAttributeHash.new(types, values, additional_types) AttributeSet.new(attributes) end - - private end end -- cgit v1.2.3 From 29b3e548f32c42cb99d9dd2d03c2cd34fa53d867 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Mon, 17 Nov 2014 07:11:13 -0800 Subject: Improve the performance of quoting table names on PG This caused a pretty major performance regression for 4.2, as this is a hotspot for query construction. We're still slightly slower than 4.1, but it's much less significant. --- .../connection_adapters/postgresql/utils.rb | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb index 0290bcb48c..9a0b80d7d3 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/utils.rb @@ -18,7 +18,11 @@ module ActiveRecord end def quoted - parts.map { |p| PGconn.quote_ident(p) }.join SEPARATOR + if schema + PGconn.quote_ident(schema) << SEPARATOR << PGconn.quote_ident(identifier) + else + PGconn.quote_ident(identifier) + end end def ==(o) @@ -32,8 +36,11 @@ module ActiveRecord protected def unquote(part) - return unless part - part.gsub(/(^"|"$)/,'') + if part && part.start_with?('"') + part[1..-2] + else + part + end end def parts @@ -57,7 +64,11 @@ module ActiveRecord # * "schema_name".table_name # * "schema.name"."table name" def extract_schema_qualified_name(string) - table, schema = string.scan(/[^".\s]+|"[^"]*"/)[0..1].reverse + schema, table = string.scan(/[^".\s]+|"[^"]*"/) + if table.nil? + table = schema + schema = nil + end PostgreSQL::Name.new(schema, table) end end -- cgit v1.2.3 From bf149679f426c1f6d3e5d280ed19b42f62c9b33f Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Mon, 17 Nov 2014 14:36:00 -0800 Subject: Force table creation in tests If something causes the teardown block to not get run (errors, interrupting test runs), we have to manually delete them, which is a pain. --- activerecord/test/cases/migration/foreign_key_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb index 406dd70c37..52e963a14f 100644 --- a/activerecord/test/cases/migration/foreign_key_test.rb +++ b/activerecord/test/cases/migration/foreign_key_test.rb @@ -17,11 +17,11 @@ module ActiveRecord setup do @connection = ActiveRecord::Base.connection - @connection.create_table "rockets" do |t| + @connection.create_table "rockets", force: true do |t| t.string :name end - @connection.create_table "astronauts" do |t| + @connection.create_table "astronauts", force: true do |t| t.string :name t.references :rocket end -- cgit v1.2.3 From c01b20b658c9fe4b7d54f4a227a09cb090b5763d Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Mon, 17 Nov 2014 14:56:06 -0800 Subject: rm `reorder_bind_params` Arel handles this for us automatically. Updated tests, as BindParam is no longer a subclass of SqlLiteral. We should remove the second argument to substitute_at entirely, as it's no longer used --- .../lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- .../connection_adapters/postgresql/database_statements.rb | 4 ---- activerecord/lib/active_record/relation.rb | 1 - activerecord/lib/active_record/relation/query_methods.rb | 10 ---------- activerecord/test/cases/adapters/mysql/connection_test.rb | 2 +- .../test/cases/adapters/postgresql/postgresql_adapter_test.rb | 5 +---- .../test/cases/adapters/sqlite3/sqlite3_adapter_test.rb | 2 +- activerecord/test/cases/bind_parameter_test.rb | 4 ++-- activerecord/test/cases/relations_test.rb | 2 +- 9 files changed, 7 insertions(+), 25 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 582dd360f0..a2df76259b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -265,7 +265,7 @@ module ActiveRecord # Returns a bind substitution value given a bind +index+ and +column+ # NOTE: The column param is currently being used by the sqlserver-adapter def substitute_at(column, index = 0) - Arel::Nodes::BindParam.new '?' + Arel::Nodes::BindParam.new end # REFERENTIAL INTEGRITY ==================================== diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index cf379ab210..d09468329a 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -156,10 +156,6 @@ module ActiveRecord end end - def substitute_at(column, index = 0) - Arel::Nodes::BindParam.new "$#{index + 1}" - end - def exec_query(sql, name = 'SQL', binds = []) execute_and_clear(sql, name, binds) do |result| types = {} diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 03bce4f5b7..3aaf037d31 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -84,7 +84,6 @@ module ActiveRecord um = relation .arel .compile_update(substitutes, @klass.primary_key) - reorder_bind_params(um.ast, bvs) @klass.connection.update( um, diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index a686e3263b..cb4e33f1b1 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -881,19 +881,9 @@ module ActiveRecord arel.from(build_from) if from_value arel.lock(lock_value) if lock_value - # Reorder bind indexes if joins produced bind values - bvs = arel.bind_values + bind_values - reorder_bind_params(arel.ast, bvs) arel end - def reorder_bind_params(ast, bvs) - ast.grep(Arel::Nodes::BindParam).each_with_index do |bp, i| - column = bvs[i].first - bp.replace connection.substitute_at(column, i) - end - end - def symbol_unscoping(scope) if !VALID_UNSCOPING_VALUES.include?(scope) raise ArgumentError, "Called unscope() with invalid unscoping argument ':#{scope}'. Valid arguments are :#{VALID_UNSCOPING_VALUES.to_a.join(", :")}." diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index 3dabb1104a..9353607f5d 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -70,7 +70,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase def test_bind_value_substitute bind_param = @connection.substitute_at('foo', 0) - assert_equal Arel.sql('?'), bind_param + assert_equal Arel.sql('?'), bind_param.to_sql end def test_exec_no_binds diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index a71c0dfb26..7d6c15c826 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -305,10 +305,7 @@ module ActiveRecord def test_substitute_at bind = @connection.substitute_at(nil, 0) - assert_equal Arel.sql('$1'), bind - - bind = @connection.substitute_at(nil, 1) - assert_equal Arel.sql('$2'), bind + assert_equal Arel.sql('$1'), bind.to_sql end def test_partial_index diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 8c1c22d3bf..54e0309167 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -134,7 +134,7 @@ module ActiveRecord def test_bind_value_substitute bind_param = @conn.substitute_at('foo', 0) - assert_equal Arel.sql('?'), bind_param + assert_equal Arel.sql('?'), bind_param.to_sql end def test_exec_no_binds diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb index 0bc7ee6d64..d1fdfb2458 100644 --- a/activerecord/test/cases/bind_parameter_test.rb +++ b/activerecord/test/cases/bind_parameter_test.rb @@ -33,7 +33,7 @@ module ActiveRecord def test_binds_are_logged sub = @connection.substitute_at(@pk, 0) binds = [[@pk, 1]] - sql = "select * from topics where id = #{sub}" + sql = "select * from topics where id = #{sub.to_sql}" @connection.exec_query(sql, 'SQL', binds) @@ -44,7 +44,7 @@ module ActiveRecord def test_binds_are_logged_after_type_cast sub = @connection.substitute_at(@pk, 0) binds = [[@pk, "3"]] - sql = "select * from topics where id = #{sub}" + sql = "select * from topics where id = #{sub.to_sql}" @connection.exec_query(sql, 'SQL', binds) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 8ac4a4ca36..ca86d58b35 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1703,7 +1703,7 @@ class RelationTest < ActiveRecord::TestCase end def test_unscope_removes_binds - left = Post.where(id: Arel::Nodes::BindParam.new('?')) + left = Post.where(id: Arel::Nodes::BindParam.new) column = Post.columns_hash['id'] left.bind_values += [[column, 20]] -- cgit v1.2.3 From eb26f24bde62cbbcd8ef0e7ee9c64060b098baff Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Mon, 17 Nov 2014 15:04:40 -0800 Subject: Remove the unused second argument to `substitute_at` Oh hey, we got to remove some code because of that! --- .../lib/active_record/associations/association_scope.rb | 3 +-- .../associations/join_dependency/join_association.rb | 2 +- .../active_record/connection_adapters/abstract_adapter.rb | 4 ++-- activerecord/lib/active_record/locking/optimistic.rb | 2 +- activerecord/lib/active_record/persistence.rb | 2 +- activerecord/lib/active_record/relation.rb | 2 +- activerecord/lib/active_record/relation/merger.rb | 12 ------------ activerecord/test/cases/adapters/mysql/connection_test.rb | 2 +- .../cases/adapters/postgresql/postgresql_adapter_test.rb | 2 +- .../test/cases/adapters/sqlite3/sqlite3_adapter_test.rb | 2 +- activerecord/test/cases/bind_parameter_test.rb | 4 ++-- 11 files changed, 12 insertions(+), 25 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index b965230e60..dcbd57e61d 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -11,8 +11,7 @@ module ActiveRecord end def bind_value(scope, column, value, alias_tracker) - substitute = alias_tracker.connection.substitute_at( - column, scope.bind_values.length) + substitute = alias_tracker.connection.substitute_at(column) scope.bind_values += [[column, @block.call(value)]] substitute end diff --git a/activerecord/lib/active_record/associations/join_dependency/join_association.rb b/activerecord/lib/active_record/associations/join_dependency/join_association.rb index e7d3c9ba40..5dede5527d 100644 --- a/activerecord/lib/active_record/associations/join_dependency/join_association.rb +++ b/activerecord/lib/active_record/associations/join_dependency/join_association.rb @@ -67,7 +67,7 @@ module ActiveRecord value = foreign_klass.base_class.name column = klass.columns_hash[reflection.type.to_s] - substitute = klass.connection.substitute_at(column, bind_values.length) + substitute = klass.connection.substitute_at(column) bind_values.push [column, value] constraint = constraint.and table[reflection.type].eq substitute end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index a2df76259b..57aa2f9c58 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -262,9 +262,9 @@ module ActiveRecord # QUOTING ================================================== - # Returns a bind substitution value given a bind +index+ and +column+ + # Returns a bind substitution value given a bind +column+ # NOTE: The column param is currently being used by the sqlserver-adapter - def substitute_at(column, index = 0) + def substitute_at(column) Arel::Nodes::BindParam.new end diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index 52eeb8ae1f..ced694ba9a 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -120,7 +120,7 @@ module ActiveRecord if locking_enabled? column_name = self.class.locking_column column = self.class.columns_hash[column_name] - substitute = self.class.connection.substitute_at(column, relation.bind_values.length) + substitute = self.class.connection.substitute_at(column) relation = relation.where(self.class.arel_table[column_name].eq(substitute)) relation.bind_values << [column, self[column_name].to_i] diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 507faf2879..ee3b7b6163 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -487,7 +487,7 @@ module ActiveRecord def relation_for_destroy pk = self.class.primary_key column = self.class.columns_hash[pk] - substitute = self.class.connection.substitute_at(column, 0) + substitute = self.class.connection.substitute_at(column) relation = self.class.unscoped.where( self.class.arel_table[pk].eq(substitute)) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 3aaf037d31..8575e5a7be 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -98,7 +98,7 @@ module ActiveRecord end substitutes = values.each_with_index.map do |(arel_attr, _), i| - [arel_attr, @klass.connection.substitute_at(binds[i][0], i)] + [arel_attr, @klass.connection.substitute_at(binds[i][0])] end [substitutes, binds] diff --git a/activerecord/lib/active_record/relation/merger.rb b/activerecord/lib/active_record/relation/merger.rb index a8febf6a18..a27f990f74 100644 --- a/activerecord/lib/active_record/relation/merger.rb +++ b/activerecord/lib/active_record/relation/merger.rb @@ -118,18 +118,6 @@ module ActiveRecord where_values = kept + rhs_wheres bind_values = filter_binds(lhs_binds, removed) + rhs_binds - conn = relation.klass.connection - bv_index = 0 - where_values.map! do |node| - if Arel::Nodes::Equality === node && Arel::Nodes::BindParam === node.right - substitute = conn.substitute_at(bind_values[bv_index].first, bv_index) - bv_index += 1 - Arel::Nodes::Equality.new(node.left, substitute) - else - node - end - end - relation.where_values = where_values relation.bind_values = bind_values diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index 9353607f5d..52eebe1886 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -69,7 +69,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase end def test_bind_value_substitute - bind_param = @connection.substitute_at('foo', 0) + bind_param = @connection.substitute_at('foo') assert_equal Arel.sql('?'), bind_param.to_sql end diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb index 7d6c15c826..c3c696b871 100644 --- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb +++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb @@ -304,7 +304,7 @@ module ActiveRecord end def test_substitute_at - bind = @connection.substitute_at(nil, 0) + bind = @connection.substitute_at(nil) assert_equal Arel.sql('$1'), bind.to_sql end diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index 54e0309167..d83c65cf0e 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -133,7 +133,7 @@ module ActiveRecord end def test_bind_value_substitute - bind_param = @conn.substitute_at('foo', 0) + bind_param = @conn.substitute_at('foo') assert_equal Arel.sql('?'), bind_param.to_sql end diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb index d1fdfb2458..c4634d11e2 100644 --- a/activerecord/test/cases/bind_parameter_test.rb +++ b/activerecord/test/cases/bind_parameter_test.rb @@ -31,7 +31,7 @@ module ActiveRecord if ActiveRecord::Base.connection.supports_statement_cache? def test_binds_are_logged - sub = @connection.substitute_at(@pk, 0) + sub = @connection.substitute_at(@pk) binds = [[@pk, 1]] sql = "select * from topics where id = #{sub.to_sql}" @@ -42,7 +42,7 @@ module ActiveRecord end def test_binds_are_logged_after_type_cast - sub = @connection.substitute_at(@pk, 0) + sub = @connection.substitute_at(@pk) binds = [[@pk, "3"]] sql = "select * from topics where id = #{sub.to_sql}" -- cgit v1.2.3 From 6f7910aed5ceeb8833de760f396858bee917c7bc Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Mon, 17 Nov 2014 17:58:49 -0800 Subject: [PERF] Speed up integer type casting from DB MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't have the check the range when the value is coming from the DB, so override type_cast_from_database to short-circuit the extra work. type_cast_from_database (small) 3437507.5 (±29.2%) i/s - 14223135 in 4.996973s type_cast_from_database (large) 3158588.7 (±28.3%) i/s - 13265628 in 4.992121s type_cast (small) 481984.8 (±14.2%) i/s - 2352012 in 5.005694s type_cast (large) 477331.8 (±14.2%) i/s - 2332824 in 5.012365s Comparison: type_cast_from_database (small): 3437507.5 i/s type_cast_from_database (large): 3158588.7 i/s - 1.09x slower type_cast (small): 481984.8 i/s - 7.13x slower type_cast (large): 477331.8 i/s - 7.20x slower The difference is huge but the absolute gain is quite small. That being said this is a hotspot and it showed up on the radar when benchmarking discourse. --- activerecord/lib/active_record/type/integer.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb index d69e5b3f28..fb6d9df58f 100644 --- a/activerecord/lib/active_record/type/integer.rb +++ b/activerecord/lib/active_record/type/integer.rb @@ -12,7 +12,9 @@ module ActiveRecord :integer end - alias type_cast_for_database type_cast + def type_cast_from_database(value) + value.to_i unless value.nil? + end protected -- cgit v1.2.3 From 52c70d496f34ed4d6c9c8cc1719bc7f86011634b Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Mon, 17 Nov 2014 18:09:40 -0800 Subject: :nail_care: Put escape clause first, keeps @sgrif happy :grin: See comment on 6f7910a --- activerecord/lib/active_record/type/integer.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb index fb6d9df58f..0149d60b6e 100644 --- a/activerecord/lib/active_record/type/integer.rb +++ b/activerecord/lib/active_record/type/integer.rb @@ -13,7 +13,8 @@ module ActiveRecord end def type_cast_from_database(value) - value.to_i unless value.nil? + return if value.nil? + value.to_i end protected -- cgit v1.2.3 From 3098579f276d5cecbca0b57e35526b99fadfbe5f Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Mon, 17 Nov 2014 18:33:07 -0800 Subject: Revert "[PERF] Speed up integer type casting from DB" This reverts commit 6f7910a and 52c70d4. Query params are type cased through the same method, so this approach doesn't work. --- activerecord/lib/active_record/type/integer.rb | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb index 0149d60b6e..d69e5b3f28 100644 --- a/activerecord/lib/active_record/type/integer.rb +++ b/activerecord/lib/active_record/type/integer.rb @@ -12,10 +12,7 @@ module ActiveRecord :integer end - def type_cast_from_database(value) - return if value.nil? - value.to_i - end + alias type_cast_for_database type_cast protected -- cgit v1.2.3 From 9aa2b2d7bf52c0f3974090cd033341be2edeae6d Mon Sep 17 00:00:00 2001 From: Sam Date: Tue, 18 Nov 2014 21:38:27 +1100 Subject: PERF: stop allocating the string "id" over and over --- activerecord/lib/active_record/attribute_methods/read.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index bf2a084a00..55beb309ca 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -76,12 +76,14 @@ module ActiveRecord end end + ID = 'id'.freeze + # Returns the value of the attribute identified by attr_name after # it has been typecast (for example, "2004-12-12" in a date column is cast # to a date object, like Date.new(2004, 12, 12)). def read_attribute(attr_name, &block) name = attr_name.to_s - name = self.class.primary_key if name == 'id' + name = self.class.primary_key if name == ID @attributes.fetch_value(name, &block) end -- cgit v1.2.3 From 8b2c98f6c7c0b7371265905a43fc7d3ad966643a Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 18 Nov 2014 11:26:16 -0800 Subject: Remove call to `key?` in `LazyAttributeHash#[]` Performance improvement, as well as improved code clarity --- activerecord/lib/active_record/attribute_set/builder.rb | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index 60154a0647..b433205af3 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -35,11 +35,7 @@ module ActiveRecord end def [](key) - if delegate_hash.key?(key) - delegate_hash[key] - else - assign_default_value(key) - end + delegate_hash[key] || assign_default_value(key) end def []=(key, value) -- cgit v1.2.3 From 3ecc5d30eb0901f4e3c4d3bca1957f633a6a695b Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 18 Nov 2014 11:46:17 -0800 Subject: LazyAttributeHash is private --- activerecord/lib/active_record/attribute_set/builder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index b433205af3..fd134a304b 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -19,7 +19,7 @@ module ActiveRecord end end - class LazyAttributeHash + class LazyAttributeHash # :nodoc: delegate :select, :transform_values, to: :materialize def initialize(types, values, additional_types) -- cgit v1.2.3 From edc39ff756d09b746bdc1acb8d814c3148289846 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 18 Nov 2014 12:31:23 -0800 Subject: Remove needless call to `key?` when building attributes This appears to be a performance hotspot, see #17655. --- activerecord/lib/active_record/attribute_set/builder.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_set/builder.rb b/activerecord/lib/active_record/attribute_set/builder.rb index fd134a304b..05138ae36d 100644 --- a/activerecord/lib/active_record/attribute_set/builder.rb +++ b/activerecord/lib/active_record/attribute_set/builder.rb @@ -62,9 +62,11 @@ module ActiveRecord def assign_default_value(name) type = additional_types.fetch(name, types[name]) + value_present = true + value = values.fetch(name) { value_present = false } - if values.key?(name) - delegate_hash[name] = Attribute.from_database(name, values[name], type) + if value_present + delegate_hash[name] = Attribute.from_database(name, value, type) elsif types.key?(name) delegate_hash[name] = Attribute.uninitialized(name, type) end -- cgit v1.2.3 From 52fddcc653458456f98b3683dffd781cf00b35fe Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 18 Nov 2014 13:40:03 -0800 Subject: Speed up integer casting from DB We don't have the check the range when the value is coming from the DB, so override type_cast_from_database to short-circuit the extra work. The difference is huge but the absolute gain is quite small. That being said this is a hotspot and it showed up on the radar when benchmarking discourse. --- activerecord/lib/active_record/type/integer.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb index d69e5b3f28..36bbd9cd5e 100644 --- a/activerecord/lib/active_record/type/integer.rb +++ b/activerecord/lib/active_record/type/integer.rb @@ -14,6 +14,11 @@ module ActiveRecord alias type_cast_for_database type_cast + def type_cast_from_database(value) + return if value.nil? + value.to_i + end + protected attr_reader :range -- cgit v1.2.3 From 78e7a0d3b7eaafe1ad0a45e3e355e1123cae3f8b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 18 Nov 2014 14:22:33 -0800 Subject: pull the preloader allocation in to a factory method --- activerecord/lib/active_record/relation.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 8575e5a7be..460daf99bc 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -639,7 +639,7 @@ module ActiveRecord preload = preload_values preload += includes_values unless eager_loading? - preloader = ActiveRecord::Associations::Preloader.new + preloader = build_preloader preload.each do |associations| preloader.preload @records, associations end @@ -650,6 +650,10 @@ module ActiveRecord @records end + def build_preloader + ActiveRecord::Associations::Preloader.new + end + def references_eager_loaded_tables? joined_tables = arel.join_sources.map do |join| if join.is_a?(Arel::Nodes::StringJoin) -- cgit v1.2.3 From 08576b94ad4f19dfc368619d7751e211d23dcad8 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 18 Nov 2014 15:19:15 -0800 Subject: Improve the performance of reading attributes We added a comparison to "id", and call to `self.class.primary_key` a *lot*. We also have performance hits from `&block` all over the place. We skip the check in a new method, in order to avoid breaking the behavior of `read_attribute` --- activerecord/lib/active_record/aggregations.rb | 4 ++-- .../lib/active_record/associations/has_many_association.rb | 2 +- .../associations/has_many_through_association.rb | 2 +- activerecord/lib/active_record/attribute_methods.rb | 4 ++-- activerecord/lib/active_record/attribute_methods/dirty.rb | 2 +- .../lib/active_record/attribute_methods/primary_key.rb | 2 +- activerecord/lib/active_record/attribute_methods/read.rb | 14 ++++++++++---- activerecord/lib/active_record/attribute_set.rb | 4 ++-- activerecord/lib/active_record/enum.rb | 2 +- activerecord/lib/active_record/reflection.rb | 2 +- activerecord/lib/active_record/validations/uniqueness.rb | 2 +- activerecord/test/cases/inheritance_test.rb | 2 +- 12 files changed, 24 insertions(+), 18 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/aggregations.rb b/activerecord/lib/active_record/aggregations.rb index e576ec4d40..1040e6e3bb 100644 --- a/activerecord/lib/active_record/aggregations.rb +++ b/activerecord/lib/active_record/aggregations.rb @@ -230,8 +230,8 @@ module ActiveRecord private def reader_method(name, class_name, mapping, allow_nil, constructor) define_method(name) do - if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !read_attribute(key).nil? }) - attrs = mapping.collect {|key, _| read_attribute(key)} + if @aggregation_cache[name].nil? && (!allow_nil || mapping.any? {|key, _| !_read_attribute(key).nil? }) + attrs = mapping.collect {|key, _| _read_attribute(key)} object = constructor.respond_to?(:call) ? constructor.call(*attrs) : class_name.constantize.send(constructor, *attrs) diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index 1413efaf7f..93084e0dcf 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -66,7 +66,7 @@ module ActiveRecord # the loaded flag is set to true as well. def count_records count = if has_cached_counter? - owner.read_attribute cached_counter_attribute_name + owner._read_attribute cached_counter_attribute_name else scope.count 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 bde23fc116..6329fdfe95 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -20,7 +20,7 @@ module ActiveRecord # SELECT query will be generated by using #length instead. def size if has_cached_counter? - owner.read_attribute cached_counter_attribute_name(reflection) + owner._read_attribute cached_counter_attribute_name(reflection) elsif loaded? target.size else diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 34ec397aee..d766996d37 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -332,7 +332,7 @@ module ActiveRecord # task.attribute_present?(:title) # => true # task.attribute_present?(:is_done) # => true def attribute_present?(attribute) - value = read_attribute(attribute) + value = _read_attribute(attribute) !value.nil? && !(value.respond_to?(:empty?) && value.empty?) end @@ -433,7 +433,7 @@ module ActiveRecord end def typecasted_attribute_value(name) - read_attribute(name) + _read_attribute(name) end end end diff --git a/activerecord/lib/active_record/attribute_methods/dirty.rb b/activerecord/lib/active_record/attribute_methods/dirty.rb index 2f02738f6d..9ba46ec4c7 100644 --- a/activerecord/lib/active_record/attribute_methods/dirty.rb +++ b/activerecord/lib/active_record/attribute_methods/dirty.rb @@ -110,7 +110,7 @@ module ActiveRecord if attribute_changed?(attr) changed_attributes[attr] else - clone_attribute_value(:read_attribute, attr) + clone_attribute_value(:_read_attribute, attr) end end diff --git a/activerecord/lib/active_record/attribute_methods/primary_key.rb b/activerecord/lib/active_record/attribute_methods/primary_key.rb index 104d84a1f8..c28374e4ab 100644 --- a/activerecord/lib/active_record/attribute_methods/primary_key.rb +++ b/activerecord/lib/active_record/attribute_methods/primary_key.rb @@ -17,7 +17,7 @@ module ActiveRecord def id if pk = self.class.primary_key sync_with_transaction_state - read_attribute(pk) + _read_attribute(pk) end end diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 55beb309ca..20f0936e52 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -27,7 +27,7 @@ module ActiveRecord <<-EOMETHOD def #{method_name} name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name} - read_attribute(name) { |n| missing_attribute(n, caller) } + _read_attribute(name) { |n| missing_attribute(n, caller) } end EOMETHOD end @@ -64,7 +64,7 @@ module ActiveRecord generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 def #{temp_method} name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name} - read_attribute(name) { |n| missing_attribute(n, caller) } + _read_attribute(name) { |n| missing_attribute(n, caller) } end STR @@ -84,13 +84,19 @@ module ActiveRecord def read_attribute(attr_name, &block) name = attr_name.to_s name = self.class.primary_key if name == ID - @attributes.fetch_value(name, &block) + _read_attribute(name, &block) + end + + # This method exists to avoid the expensive primary_key check internally, without + # breaking compatibility with the read_attribute API + def _read_attribute(attr_name) # :nodoc: + @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? } end private def attribute(attribute_name) - read_attribute(attribute_name) + _read_attribute(attribute_name) end end end diff --git a/activerecord/lib/active_record/attribute_set.rb b/activerecord/lib/active_record/attribute_set.rb index 21c58cbf1d..6b1d7ea79e 100644 --- a/activerecord/lib/active_record/attribute_set.rb +++ b/activerecord/lib/active_record/attribute_set.rb @@ -27,8 +27,8 @@ module ActiveRecord attributes.initialized_keys end - def fetch_value(name, &block) - self[name].value(&block) + def fetch_value(name) + self[name].value { |n| yield n if block_given? } end def write_from_database(name, value) diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 5958373e88..f053372cfb 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -142,7 +142,7 @@ module ActiveRecord private def save_changed_attribute(attr_name, old) if (mapping = self.class.defined_enums[attr_name.to_s]) - value = read_attribute(attr_name) + value = _read_attribute(attr_name) if attribute_changed?(attr_name) if mapping[old] == value clear_attribute_changes([attr_name]) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 4b58f2deb7..f4a351b092 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -287,7 +287,7 @@ module ActiveRecord def association_scope_cache(conn, owner) key = conn.prepared_statements if polymorphic? - key = [key, owner.read_attribute(@foreign_type)] + key = [key, owner._read_attribute(@foreign_type)] end @association_scope_cache[key] ||= @scope_lock.synchronize { @association_scope_cache[key] ||= yield diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 2dba4c7b94..3e8afe37a8 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -79,7 +79,7 @@ module ActiveRecord scope_value = record.send(reflection.foreign_key) scope_item = reflection.foreign_key else - scope_value = record.read_attribute(scope_item) + scope_value = record._read_attribute(scope_item) end relation = relation.and(table[scope_item].eq(scope_value)) end diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb index 792950d24d..0338669016 100644 --- a/activerecord/test/cases/inheritance_test.rb +++ b/activerecord/test/cases/inheritance_test.rb @@ -22,7 +22,7 @@ class InheritanceTest < ActiveRecord::TestCase company = Company.first company = company.dup company.extend(Module.new { - def read_attribute(name) + def _read_attribute(name) return ' ' if name == 'type' super end -- cgit v1.2.3 From 995c697511a4e77fbe1e00793486ae07b18c5017 Mon Sep 17 00:00:00 2001 From: dtaniwaki Date: Wed, 19 Nov 2014 10:17:44 +0900 Subject: Support symbol foreign key to delete --- .../connection_adapters/abstract/schema_statements.rb | 2 +- activerecord/test/cases/migration/foreign_key_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'activerecord') 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 cbf87df356..cc86c3776e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -740,7 +740,7 @@ module ActiveRecord end fk_name_to_delete = options.fetch(:name) do - fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column] } + fk_to_delete = foreign_keys(from_table).detect {|fk| fk.column == options[:column].to_s } if fk_to_delete fk_to_delete.name diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb index 52e963a14f..51e21528c2 100644 --- a/activerecord/test/cases/migration/foreign_key_test.rb +++ b/activerecord/test/cases/migration/foreign_key_test.rb @@ -162,6 +162,14 @@ module ActiveRecord assert_equal [], @connection.foreign_keys("astronauts") end + def test_remove_foreign_key_by_symbol_column + @connection.add_foreign_key :astronauts, :rockets, column: :rocket_id + + assert_equal 1, @connection.foreign_keys("astronauts").size + @connection.remove_foreign_key :astronauts, column: :rocket_id + assert_equal [], @connection.foreign_keys("astronauts") + end + def test_remove_foreign_key_by_name @connection.add_foreign_key :astronauts, :rockets, column: "rocket_id", name: "fancy_named_fk" -- cgit v1.2.3 From 74c35b46e362a128fe06f909744da8bb1aeed0e0 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 19 Nov 2014 11:24:18 -0800 Subject: Introduce test to demonstrate regression caused by da99a2a2 --- activerecord/test/cases/type/type_map_test.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/type/type_map_test.rb b/activerecord/test/cases/type/type_map_test.rb index 4e32f92dd0..81ac7394a8 100644 --- a/activerecord/test/cases/type/type_map_test.rb +++ b/activerecord/test/cases/type/type_map_test.rb @@ -124,6 +124,16 @@ module ActiveRecord assert_equal mapping.lookup(3), 'string' assert_kind_of Type::Value, mapping.lookup(4) end + + def test_aliases_are_not_improperly_cached + mapping = HashLookupTypeMap.new + + mapping.register_type("foo") { |*args| args.join("-") } + mapping.alias_type("bar", "foo") + + assert_equal "bar-1-2-3", mapping.lookup("bar", 1, 2, 3) + assert_equal "foo-1-2-3", mapping.lookup("foo", 1, 2, 3) + end end end end -- cgit v1.2.3 From b03f9ef86a44b190948be0737bf9ab68e30fc112 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 19 Nov 2014 11:24:49 -0800 Subject: Revert "PERF: optimise type lookup to avoid invoking procs" This reverts commit da99a2a2982d35f670ad9647463e09bfe9032b70. --- .../connection_adapters/postgresql_adapter.rb | 20 +++++++------------- .../lib/active_record/type/hash_lookup_type_map.rb | 20 +------------------- 2 files changed, 8 insertions(+), 32 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index d3e5b0a4ad..6310d70192 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -431,22 +431,16 @@ module ActiveRecord private def get_oid_type(oid, fmod, column_name, sql_type = '') # :nodoc: - - result = type_map.fetch(oid, fmod, sql_type) { - nil - } - - unless result + if !type_map.key?(oid) load_additional_types(type_map, [oid]) - result = type_map.fetch(oid, fmod, sql_type) { - warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String." - Type::Value.new.tap do |cast_type| - type_map.register_type(oid, cast_type) - end - } end - result + type_map.fetch(oid, fmod, sql_type) { + warn "unknown OID #{oid}: failed to recognize type of '#{column_name}'. It will be treated as String." + Type::Value.new.tap do |cast_type| + type_map.register_type(oid, cast_type) + end + } end def initialize_type_map(m) # :nodoc: diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb index 2a146d38a9..bf92680268 100644 --- a/activerecord/lib/active_record/type/hash_lookup_type_map.rb +++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb @@ -1,12 +1,6 @@ module ActiveRecord module Type class HashLookupTypeMap < TypeMap # :nodoc: - - def initialize - @cache = {} - super - end - delegate :key?, to: :@mapping def lookup(type, *args) @@ -14,24 +8,12 @@ module ActiveRecord end def fetch(type, *args, &block) - cache = (@cache[type] ||= {}) - resolved = cache[args] - - unless resolved - resolved = cache[args] = @mapping.fetch(type, block).call(type, *args) - end - - resolved + @mapping.fetch(type, block).call(type, *args) end def alias_type(type, alias_type) register_type(type) { |_, *args| lookup(alias_type, *args) } end - - def register_type(key, value=nil, &block) - @cache = {} - super(key, value, &block) - end end end end -- cgit v1.2.3 From 7d54cccb8474e92454f33c9df975d20a80119fb9 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 19 Nov 2014 11:29:05 -0800 Subject: =?UTF-8?q?That=20last=20test=20was=20incorrect...=20=E2=98=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- activerecord/test/cases/type/type_map_test.rb | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/type/type_map_test.rb b/activerecord/test/cases/type/type_map_test.rb index 81ac7394a8..4e32f92dd0 100644 --- a/activerecord/test/cases/type/type_map_test.rb +++ b/activerecord/test/cases/type/type_map_test.rb @@ -124,16 +124,6 @@ module ActiveRecord assert_equal mapping.lookup(3), 'string' assert_kind_of Type::Value, mapping.lookup(4) end - - def test_aliases_are_not_improperly_cached - mapping = HashLookupTypeMap.new - - mapping.register_type("foo") { |*args| args.join("-") } - mapping.alias_type("bar", "foo") - - assert_equal "bar-1-2-3", mapping.lookup("bar", 1, 2, 3) - assert_equal "foo-1-2-3", mapping.lookup("foo", 1, 2, 3) - end end end end -- cgit v1.2.3 From cafed35b610d830049b7dffd924cbcdeedab72bb Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 19 Nov 2014 11:54:19 -0800 Subject: Add tests for `TypeMap#fetch` and push up to `TypeMap` It doesn't make sense for the subclass to implement this method, and not have it on the parent. We can also DRY up the implementation of `#lookup` to be defined in terms of fetch, which will give us a single point of entry --- .../lib/active_record/type/hash_lookup_type_map.rb | 4 ---- activerecord/lib/active_record/type/type_map.rb | 6 +++++- activerecord/test/cases/type/type_map_test.rb | 15 +++++++++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb index bf92680268..90a8c674cb 100644 --- a/activerecord/lib/active_record/type/hash_lookup_type_map.rb +++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb @@ -3,10 +3,6 @@ module ActiveRecord class HashLookupTypeMap < TypeMap # :nodoc: delegate :key?, to: :@mapping - def lookup(type, *args) - @mapping.fetch(type, proc { default_value }).call(type, *args) - end - def fetch(type, *args, &block) @mapping.fetch(type, block).call(type, *args) end diff --git a/activerecord/lib/active_record/type/type_map.rb b/activerecord/lib/active_record/type/type_map.rb index 88c5f9c497..13d4943861 100644 --- a/activerecord/lib/active_record/type/type_map.rb +++ b/activerecord/lib/active_record/type/type_map.rb @@ -6,6 +6,10 @@ module ActiveRecord end def lookup(lookup_key, *args) + fetch(lookup_key, *args) { default_value } + end + + def fetch(lookup_key, *args) matching_pair = @mapping.reverse_each.detect do |key, _| key === lookup_key end @@ -13,7 +17,7 @@ module ActiveRecord if matching_pair matching_pair.last.call(lookup_key, *args) else - default_value + yield lookup_key, *args end end diff --git a/activerecord/test/cases/type/type_map_test.rb b/activerecord/test/cases/type/type_map_test.rb index 4e32f92dd0..1e4eba1961 100644 --- a/activerecord/test/cases/type/type_map_test.rb +++ b/activerecord/test/cases/type/type_map_test.rb @@ -124,6 +124,21 @@ module ActiveRecord assert_equal mapping.lookup(3), 'string' assert_kind_of Type::Value, mapping.lookup(4) end + + def test_fetch + mapping = TypeMap.new + mapping.register_type(1, "string") + + assert_equal "string", mapping.fetch(1) { "int" } + assert_equal "int", mapping.fetch(2) { "int" } + end + + def test_fetch_yields_args + mapping = TypeMap.new + + assert_equal "foo-1-2-3", mapping.fetch("foo", 1, 2, 3) { |*args| args.join("-") } + assert_equal "bar-1-2-3", mapping.fetch("bar", 1, 2, 3) { |*args| args.join("-") } + end end end end -- cgit v1.2.3 From f767ac22fa213df754e160c59189d28ed4f95568 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 19 Nov 2014 12:04:51 -0800 Subject: Reintroduce cache with tests --- .../lib/active_record/type/hash_lookup_type_map.rb | 10 ++++--- activerecord/lib/active_record/type/type_map.rb | 28 ++++++++++++------- activerecord/test/cases/type/type_map_test.rb | 32 ++++++++++++++++++++++ 3 files changed, 56 insertions(+), 14 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/type/hash_lookup_type_map.rb b/activerecord/lib/active_record/type/hash_lookup_type_map.rb index 90a8c674cb..82d9327fc0 100644 --- a/activerecord/lib/active_record/type/hash_lookup_type_map.rb +++ b/activerecord/lib/active_record/type/hash_lookup_type_map.rb @@ -3,13 +3,15 @@ module ActiveRecord class HashLookupTypeMap < TypeMap # :nodoc: delegate :key?, to: :@mapping - def fetch(type, *args, &block) - @mapping.fetch(type, block).call(type, *args) - end - def alias_type(type, alias_type) register_type(type) { |_, *args| lookup(alias_type, *args) } end + + private + + def perform_fetch(type, *args, &block) + @mapping.fetch(type, block).call(type, *args) + end end end end diff --git a/activerecord/lib/active_record/type/type_map.rb b/activerecord/lib/active_record/type/type_map.rb index 13d4943861..7c194c0cdf 100644 --- a/activerecord/lib/active_record/type/type_map.rb +++ b/activerecord/lib/active_record/type/type_map.rb @@ -3,26 +3,22 @@ module ActiveRecord class TypeMap # :nodoc: def initialize @mapping = {} + @cache = Hash.new do |h, key| + h[key] = {} + end end def lookup(lookup_key, *args) fetch(lookup_key, *args) { default_value } end - def fetch(lookup_key, *args) - matching_pair = @mapping.reverse_each.detect do |key, _| - key === lookup_key - end - - if matching_pair - matching_pair.last.call(lookup_key, *args) - else - yield lookup_key, *args - end + def fetch(lookup_key, *args, &block) + @cache[lookup_key][args] ||= perform_fetch(lookup_key, *args, &block) end def register_type(key, value = nil, &block) raise ::ArgumentError unless value || block + @cache.clear if block @mapping[key] = block @@ -44,6 +40,18 @@ module ActiveRecord private + def perform_fetch(lookup_key, *args) + matching_pair = @mapping.reverse_each.detect do |key, _| + key === lookup_key + end + + if matching_pair + matching_pair.last.call(lookup_key, *args) + else + yield lookup_key, *args + end + end + def default_value @default_value ||= Value.new end diff --git a/activerecord/test/cases/type/type_map_test.rb b/activerecord/test/cases/type/type_map_test.rb index 1e4eba1961..172c6dfc4c 100644 --- a/activerecord/test/cases/type/type_map_test.rb +++ b/activerecord/test/cases/type/type_map_test.rb @@ -139,6 +139,38 @@ module ActiveRecord assert_equal "foo-1-2-3", mapping.fetch("foo", 1, 2, 3) { |*args| args.join("-") } assert_equal "bar-1-2-3", mapping.fetch("bar", 1, 2, 3) { |*args| args.join("-") } end + + def test_fetch_memoizes + mapping = TypeMap.new + + looked_up = false + mapping.register_type(1) do + fail if looked_up + looked_up = true + "string" + end + + assert_equal "string", mapping.fetch(1) + assert_equal "string", mapping.fetch(1) + end + + def test_fetch_memoizes_on_args + mapping = TypeMap.new + mapping.register_type("foo") { |*args| args.join("-") } + + assert_equal "foo-1-2-3", mapping.fetch("foo", 1, 2, 3) { |*args| args.join("-") } + assert_equal "foo-2-3-4", mapping.fetch("foo", 2, 3, 4) { |*args| args.join("-") } + end + + def test_register_clears_cache + mapping = TypeMap.new + + mapping.register_type(1, "string") + mapping.lookup(1) + mapping.register_type(1, "int") + + assert_equal "int", mapping.lookup(1) + end end end end -- cgit v1.2.3 From a668b09ee387bf241a3481a764a18a5c8a4df8cd Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 19 Nov 2014 08:44:45 +1100 Subject: PERF: avoid string allocations --- activerecord/lib/active_record/relation/query_methods.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index cb4e33f1b1..eb69943551 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1059,8 +1059,13 @@ module ActiveRecord def build_select(arel, selects) if !selects.empty? expanded_select = selects.map do |field| - columns_hash.key?(field.to_s) ? arel_table[field] : field + if (Symbol === field || String === field) && columns_hash.key?(field.to_s) + arel_table[field] + else + field + end end + arel.project(*expanded_select) else arel.project(@klass.arel_table[Arel.star]) -- cgit v1.2.3 From 9ce105734d0b28de324c79a9e89906219f1555b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20den=20Berg?= Date: Thu, 20 Nov 2014 09:58:45 +0100 Subject: Explain that default_scope also influences update_all This was not explicitly stated before and I needed to try it out to be certain. A little explicit statement in the API docs might help here. --- activerecord/lib/active_record/relation.rb | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 460daf99bc..fc3306ee81 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -304,11 +304,12 @@ module ActiveRecord klass.current_scope = previous end - # Updates all records with details given if they match a set of conditions supplied, limits and order can - # also be supplied. This method constructs a single SQL UPDATE statement and sends it straight to the - # database. It does not instantiate the involved models and it does not trigger Active Record callbacks - # or validations. Values passed to `update_all` will not go through ActiveRecord's type-casting behavior. - # It should receive only values that can be passed as-is to the SQL database. + # Updates all records in the current scope (respecting the default_scope, where, + # limit and order specified) with details given. This method constructs a single SQL update_all + # statement and sends it straight to the database. It does not instantiate the involved models and it does not + # trigger Active Record callbacks or validations. Values passed to `update_all` will not go through + # ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL + # database. # # ==== Parameters # @@ -319,6 +320,12 @@ module ActiveRecord # # Update all customers with the given attributes # Customer.update_all wants_email: true # + # # Update all active accounts with the given attributes + # class Account < ActiveRecord::Base + # default_scope -> { where active: true } + # end + # Account.update_all(failed_logins: 0) + # # # Update all books with 'Rails' in their title # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David') # -- cgit v1.2.3 From d56be864f61cc00eeff27ed42e7cec4194c5347f Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Thu, 20 Nov 2014 15:30:46 +0100 Subject: synchronize code and docs for `timestamps` and `add_timestamps`. This makes the following changes: * warn if `:null` is not passed to `add_timestamps` * `timestamps` method docs link to `add_timestamps` docs * explain where additional options go * adjust examples to include `null: false` (to prevent deprecation warnings) --- .../connection_adapters/abstract/schema_definitions.rb | 11 +++++------ .../connection_adapters/abstract/schema_statements.rb | 7 +++++-- .../lib/active_record/connection_adapters/abstract_adapter.rb | 1 + activerecord/test/cases/adapters/mysql/active_schema_test.rb | 2 +- activerecord/test/cases/adapters/mysql2/active_schema_test.rb | 2 +- activerecord/test/cases/ar_schema_test.rb | 9 +++++++++ 6 files changed, 22 insertions(+), 10 deletions(-) (limited to 'activerecord') 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 5c4c214385..d2236d046b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -226,7 +226,7 @@ module ActiveRecord # t.integer :shop_id, :creator_id # t.string :item_number, index: true # t.string :name, :value, default: "Untitled" - # t.timestamps + # t.timestamps null: false # end # # There's a short-hand method for each of the type values declared at the top. And then there's @@ -287,7 +287,9 @@ module ActiveRecord end # Appends :datetime columns :created_at and - # :updated_at to the table. + # :updated_at to the table. See SchemaStatements#add_timestamps + # + # t.timestamps null: false def timestamps(*args) options = args.extract_options! emit_warning_if_null_unspecified(options) @@ -412,8 +414,6 @@ module ActiveRecord # end # class Table - include TimestampDefaultDeprecation - attr_reader :name def initialize(table_name, base) @@ -462,9 +462,8 @@ module ActiveRecord # Adds timestamps (+created_at+ and +updated_at+) columns to the table. See SchemaStatements#add_timestamps # - # t.timestamps + # t.timestamps null: false def timestamps(options = {}) - emit_warning_if_null_unspecified(options) @base.add_timestamps(name, options) 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 cc86c3776e..7cc5e666af 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -835,11 +835,14 @@ module ActiveRecord columns end - # Adds timestamps (+created_at+ and +updated_at+) columns to the named table. + include TimestampDefaultDeprecation + # Adds timestamps (+created_at+ and +updated_at+) columns to +table_name+. + # Additional options (like null: false) are forwarded to #add_column. # - # add_timestamps(:suppliers) + # add_timestamps(:suppliers, null: false) # def add_timestamps(table_name, options = {}) + emit_warning_if_null_unspecified(options) add_column table_name, :created_at, :datetime, options add_column table_name, :updated_at, :datetime, options end diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 57aa2f9c58..fa3483a258 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -24,6 +24,7 @@ module ActiveRecord autoload :TableDefinition autoload :Table autoload :AlterTable + autoload :TimestampDefaultDeprecation end autoload_at 'active_record/connection_adapters/abstract/connection_pool' do diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb index a84673e452..413f67da26 100644 --- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb @@ -92,7 +92,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase with_real_execute do begin ActiveRecord::Base.connection.create_table :delete_me - ActiveRecord::Base.connection.add_timestamps :delete_me + ActiveRecord::Base.connection.add_timestamps :delete_me, null: true assert column_present?('delete_me', 'updated_at', 'datetime') assert column_present?('delete_me', 'created_at', 'datetime') ensure diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb index 49cfafd7a5..3d97182356 100644 --- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb @@ -92,7 +92,7 @@ class ActiveSchemaTest < ActiveRecord::TestCase with_real_execute do begin ActiveRecord::Base.connection.create_table :delete_me - ActiveRecord::Base.connection.add_timestamps :delete_me + ActiveRecord::Base.connection.add_timestamps :delete_me, null: true assert column_present?('delete_me', 'updated_at', 'datetime') assert column_present?('delete_me', 'created_at', 'datetime') ensure diff --git a/activerecord/test/cases/ar_schema_test.rb b/activerecord/test/cases/ar_schema_test.rb index b6333240a8..025c9fe6f9 100644 --- a/activerecord/test/cases/ar_schema_test.rb +++ b/activerecord/test/cases/ar_schema_test.rb @@ -115,6 +115,15 @@ if ActiveRecord::Base.connection.supports_migrations? end end + def test_timestamps_without_null_is_deprecated_on_add_timestamps + assert_deprecated do + ActiveRecord::Schema.define do + create_table :has_timestamps + add_timestamps :has_timestamps + end + end + end + def test_no_deprecation_warning_from_timestamps_on_create_table assert_not_deprecated do ActiveRecord::Schema.define do -- cgit v1.2.3 From 5aedabe82d3a9d7df19007aa0fd7719d4a55ef7e Mon Sep 17 00:00:00 2001 From: claudiob Date: Thu, 20 Nov 2014 14:54:22 -0800 Subject: Wrap code snippets in +, not backticks, in sdoc I grepped the source code for code snippets wrapped in backticks in the comments and replaced the backticks with plus signs so they are correctly displayed in the Rails documentation. [ci skip] --- activerecord/lib/active_record/associations/collection_association.rb | 4 ++-- .../lib/active_record/connection_adapters/connection_specification.rb | 2 +- activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 2 +- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 93f611dd8d..d59cebb08b 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -175,8 +175,8 @@ module ActiveRecord end # Removes all records from the association without calling callbacks - # on the associated records. It honors the `:dependent` option. However - # if the `:dependent` value is `:destroy` then in that case the `:delete_all` + # on the associated records. It honors the +:dependent+ option. However + # if the +:dependent+ value is +:destroy+ then in that case the +:delete_all+ # deletion strategy for the association is applied. # # You can force a particular deletion strategy by passing a parameter. diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 609ec7dabd..e54e3199ff 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -234,7 +234,7 @@ module ActiveRecord end end - # Takes the environment such as `:production` or `:development`. + # Takes the environment such as +:production+ or +:development+. # This requires that the @configurations was initialized with a key that # matches. # diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 3357ed52e5..8b7459ef27 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -58,7 +58,7 @@ module ActiveRecord # * :encoding - (Optional) Sets the client encoding by executing "SET NAMES " after connection. # * :reconnect - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html). # * :strict - Defaults to true. Enable STRICT_ALL_TABLES. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html) - # * :variables - (Optional) A hash session variables to send as `SET @@SESSION.key = value` on each database connection. Use the value `:default` to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html). + # * :variables - (Optional) A hash session variables to send as SET @@SESSION.key = value on each database connection. Use the value +:default+ to set a variable to its DEFAULT value. (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/set-statement.html). # * :sslca - Necessary to use MySQL with an SSL connection. # * :sslkey - Necessary to use MySQL with an SSL connection. # * :sslcert - Necessary to use MySQL with an SSL connection. diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 6310d70192..3698957d8d 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -125,7 +125,7 @@ module ActiveRecord PostgreSQL::SchemaCreation.new self end - # Adds `:array` option to the default set provided by the + # Adds +:array+ option to the default set provided by the # AbstractAdapter def prepare_column_options(column, types) # :nodoc: spec = super @@ -134,7 +134,7 @@ module ActiveRecord spec end - # Adds `:array` as a valid migration key + # Adds +:array+ as a valid migration key def migration_keys super + [:array] end -- cgit v1.2.3 From 3e92806357543cdbabad813884a591fca69193c2 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 20 Nov 2014 17:37:54 -0800 Subject: raise a better exception for renaming long indexes --- .../connection_adapters/abstract/schema_statements.rb | 3 +++ .../connection_adapters/postgresql/schema_statements.rb | 3 +++ activerecord/test/cases/migration/index_test.rb | 14 ++++++++++++++ 3 files changed, 20 insertions(+) (limited to 'activerecord') 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 7cc5e666af..99fa9b7d29 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -571,6 +571,9 @@ module ActiveRecord # rename_index :people, 'index_people_on_last_name', 'index_users_on_last_name' # def rename_index(table_name, old_name, new_name) + if new_name.length > allowed_index_name_length + raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters" + end # this is a naive implementation; some DBs may support this more efficiently (Postgres, for instance) old_index_def = indexes(table_name).detect { |i| i.name == old_name } return unless old_index_def diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 51853c6812..b4b066c06e 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -478,6 +478,9 @@ module ActiveRecord end def rename_index(table_name, old_name, new_name) + if new_name.length > allowed_index_name_length + raise ArgumentError, "Index name '#{new_name}' on table '#{table_name}' is too long; the limit is #{allowed_index_name_length} characters" + end execute "ALTER INDEX #{quote_column_name(old_name)} RENAME TO #{quote_table_name(new_name)}" end diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb index ac932378fd..b23b9a679f 100644 --- a/activerecord/test/cases/migration/index_test.rb +++ b/activerecord/test/cases/migration/index_test.rb @@ -36,6 +36,20 @@ module ActiveRecord assert connection.index_name_exists?(table_name, 'new_idx', true) end + def test_rename_index_too_long + too_long_index_name = good_index_name + 'x' + # keep the names short to make Oracle and similar behave + connection.add_index(table_name, [:foo], :name => 'old_idx') + e = assert_raises(ArgumentError) { + connection.rename_index(table_name, 'old_idx', too_long_index_name) + } + assert_match(/too long; the limit is #{connection.allowed_index_name_length} characters/, e.message) + + # if the adapter doesn't support the indexes call, pick defaults that let the test pass + assert connection.index_name_exists?(table_name, 'old_idx', false) + end + + def test_double_add_index connection.add_index(table_name, [:foo], :name => 'some_idx') assert_raises(ArgumentError) { -- cgit v1.2.3 From 17efb3b919e8eff1ca5da49b12e3c1f607eb93e3 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Thu, 20 Nov 2014 19:17:10 -0700 Subject: Remove is_a? check when ignoring tables Technically changes the API, as it will allow any object which responds to `===`. Personally, I think this is more flexible. --- activerecord/lib/active_record/schema_dumper.rb | 7 +------ activerecord/test/cases/schema_dumper_test.rb | 10 ---------- 2 files changed, 1 insertion(+), 16 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/schema_dumper.rb b/activerecord/lib/active_record/schema_dumper.rb index 261fb9d992..1dd2cadc01 100644 --- a/activerecord/lib/active_record/schema_dumper.rb +++ b/activerecord/lib/active_record/schema_dumper.rb @@ -244,12 +244,7 @@ HEADER def ignored?(table_name) ['schema_migrations', ignore_tables].flatten.any? do |ignored| - case ignored - when String; remove_prefix_and_suffix(table_name) == ignored - when Regexp; remove_prefix_and_suffix(table_name) =~ ignored - else - raise StandardError, 'ActiveRecord::SchemaDumper.ignore_tables accepts an array of String and / or Regexp values.' - end + ignored === remove_prefix_and_suffix(table_name) end end end diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 6303393c22..8731531f54 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -162,16 +162,6 @@ class SchemaDumperTest < ActiveRecord::TestCase assert_no_match %r{create_table "schema_migrations"}, output end - def test_schema_dump_illegal_ignored_table_value - stream = StringIO.new - old_ignore_tables, ActiveRecord::SchemaDumper.ignore_tables = ActiveRecord::SchemaDumper.ignore_tables, [5] - assert_raise(StandardError) do - ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) - end - ensure - ActiveRecord::SchemaDumper.ignore_tables = old_ignore_tables - end - def test_schema_dumps_index_columns_in_right_order index_definition = standard_dump.split(/\n/).grep(/add_index.*companies/).first.strip if current_adapter?(:MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter) -- cgit v1.2.3 From f766abd4cf3eb75469d3646cfb6d85e668c619f3 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 21 Nov 2014 14:57:25 +0100 Subject: make it possible to access fixtures excluded by a `default_scope`. Prior to this patch you'd end up with an error like: ``` ActiveRecord::RecordNotFound: Couldn't find with 'id'= [WHERE ()] ``` --- activerecord/CHANGELOG.md | 4 ++++ activerecord/lib/active_record/fixtures.rb | 2 +- activerecord/test/cases/fixtures_test.rb | 14 ++++++++++++++ activerecord/test/fixtures/bulbs.yml | 5 +++++ 4 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 activerecord/test/fixtures/bulbs.yml (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index e4c59673cd..fcc6523d6e 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Make it possible to access fixtures excluded by a `default_scope`. + + *Yves Senn* + * Add `Table#name` to match `TableDefinition#name`. *Cody Cutrer* diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index db6421dacb..660ba18a98 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -806,7 +806,7 @@ module ActiveRecord def find if model_class - model_class.find(fixture[model_class.primary_key]) + model_class.unscoped.find(fixture[model_class.primary_key]) else raise FixtureClassNotFound, "No class attached to find." end diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index 4b0ebe13d1..9edeb8b47f 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -5,6 +5,7 @@ require 'models/admin/randomly_named_c1' require 'models/admin/user' require 'models/binary' require 'models/book' +require 'models/bulb' require 'models/category' require 'models/company' require 'models/computer' @@ -853,3 +854,16 @@ class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase assert_equal 'randomly_named_table', Admin::ClassNameThatDoesNotFollowCONVENTIONS.table_name end end + +class FixturesWithDefaultScopeTest < ActiveRecord::TestCase + fixtures :bulbs + + test "inserts fixtures excluded by a default scope" do + assert_equal 1, Bulb.count + assert_equal 2, Bulb.unscoped.count + end + + test "allows access to fixtures excluded by a default scope" do + assert_equal "special", bulbs(:special).name + end +end diff --git a/activerecord/test/fixtures/bulbs.yml b/activerecord/test/fixtures/bulbs.yml new file mode 100644 index 0000000000..e5ce2b796c --- /dev/null +++ b/activerecord/test/fixtures/bulbs.yml @@ -0,0 +1,5 @@ +defaulty: + name: defaulty + +special: + name: special -- cgit v1.2.3 From 91e3dab804fffe0b1daebb438091418faa1fa256 Mon Sep 17 00:00:00 2001 From: "siddharth@vinsol.com" Date: Thu, 20 Nov 2014 01:35:04 +0530 Subject: Fix includes on association with a scope containing joins along with conditions on the joined assoiciation --- activerecord/CHANGELOG.md | 6 ++++++ .../lib/active_record/associations/preloader/association.rb | 10 ++-------- activerecord/lib/active_record/relation/query_methods.rb | 6 ++---- activerecord/test/cases/associations/eager_test.rb | 6 ++++++ activerecord/test/models/post.rb | 1 + 5 files changed, 17 insertions(+), 12 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index fcc6523d6e..a92571a04a 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -2,6 +2,12 @@ *Yves Senn* +* Fix includes on association with a scope containing joins along with conditions + on the joined assoiciation. + + *Siddharth Sharma* + + * Add `Table#name` to match `TableDefinition#name`. *Cody Cutrer* diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb index 496c426986..7d6523dbc4 100644 --- a/activerecord/lib/active_record/associations/preloader/association.rb +++ b/activerecord/lib/active_record/associations/preloader/association.rb @@ -142,14 +142,8 @@ module ActiveRecord scope._select! preload_values[:select] || values[:select] || table[Arel.star] scope.includes! preload_values[:includes] || values[:includes] - - if preload_values.key? :order - scope.order! preload_values[:order] - else - if values.key? :order - scope.order! values[:order] - end - end + scope.joins! preload_values[:joins] || values[:joins] + scope.order! preload_values[:order] || values[:order] if preload_values[:readonly] || values[:readonly] scope.readonly! diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index eb69943551..6e384facce 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -427,14 +427,12 @@ module ActiveRecord # => SELECT "users".* FROM "users" LEFT JOIN bookmarks ON bookmarks.bookmarkable_type = 'Post' AND bookmarks.user_id = users.id def joins(*args) check_if_method_has_arguments!(:joins, args) - - args.compact! - args.flatten! - spawn.joins!(*args) end def joins!(*args) # :nodoc: + args.compact! + args.flatten! self.joins_values += args self end diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 4539b99504..dd4f530791 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -904,6 +904,12 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_no_queries {assert_equal posts(:sti_comments), comment.post} end + def test_eager_association_with_scope_with_joins + assert_nothing_raised do + Post.includes(:very_special_comment_with_post_with_joins).to_a + end + end + def test_preconfigured_includes_with_has_many posts = authors(:david).posts_with_comments one = posts.detect { |p| p.id == 1 } diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index 36cf221d45..a9996e5236 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -78,6 +78,7 @@ class Post < ActiveRecord::Base has_one :very_special_comment has_one :very_special_comment_with_post, -> { includes(:post) }, :class_name => "VerySpecialComment" + has_one :very_special_comment_with_post_with_joins, -> { joins(:post).order('posts.id') }, class_name: "VerySpecialComment" has_many :special_comments has_many :nonexistant_comments, -> { where 'comments.id < 0' }, :class_name => 'Comment' -- cgit v1.2.3 From 71bb8c34f539f8ffbb7e7968f50f797e08812133 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Fri, 21 Nov 2014 15:33:46 -0700 Subject: ConnectionAdapter#substitute_at is technically public API... We can't change the signature without a deprecation cycle. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index fa3483a258..e0a4af5359 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -265,7 +265,7 @@ module ActiveRecord # Returns a bind substitution value given a bind +column+ # NOTE: The column param is currently being used by the sqlserver-adapter - def substitute_at(column) + def substitute_at(column, _unused = 0) Arel::Nodes::BindParam.new end -- cgit v1.2.3 From dcc143cd702bfbfef9cb92e1fde75171d088a5ac Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Sat, 22 Nov 2014 13:16:14 -0700 Subject: Rename the primary key index when renaming a table in pg Also checked to make sure this does not affect foreign key constraints. (It doesn't). Fixes #12856 Closes #14088 --- activerecord/CHANGELOG.md | 6 ++++ .../postgresql/schema_statements.rb | 3 ++ .../cases/adapters/postgresql/rename_table_test.rb | 34 ++++++++++++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 activerecord/test/cases/adapters/postgresql/rename_table_test.rb (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index fcc6523d6e..16f776df58 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Renaming a table in pg also renames the primary key index. + + Fixes #12856 + + *Sean Griffin* + * Make it possible to access fixtures excluded by a `default_scope`. *Yves Senn* diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index b4b066c06e..515ff2dd44 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -411,7 +411,10 @@ module ActiveRecord pk, seq = pk_and_sequence_for(new_name) if seq && seq.identifier == "#{table_name}_#{pk}_seq" new_seq = "#{new_name}_#{pk}_seq" + idx = "#{table_name}_pkey" + new_idx = "#{new_name}_pkey" execute "ALTER TABLE #{quote_table_name(seq)} RENAME TO #{quote_table_name(new_seq)}" + execute "ALTER INDEX #{quote_table_name(idx)} RENAME TO #{quote_table_name(new_idx)}" end rename_table_indexes(table_name, new_name) diff --git a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb new file mode 100644 index 0000000000..056a035622 --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb @@ -0,0 +1,34 @@ +require "cases/helper" + +class PostgresqlRenameTableTest < ActiveRecord::TestCase + def setup + @connection = ActiveRecord::Base.connection + @connection.create_table :before_rename, force: true + end + + def teardown + @connection.execute 'DROP TABLE IF EXISTS "before_rename"' + @connection.execute 'DROP TABLE IF EXISTS "after_rename"' + end + + test "renaming a table also renames the primary key index" do + # sanity check + assert_equal 1, num_indices_named("before_rename_pkey") + assert_equal 0, num_indices_named("after_rename_pkey") + + @connection.rename_table :before_rename, :after_rename + + assert_equal 0, num_indices_named("before_rename_pkey") + assert_equal 1, num_indices_named("after_rename_pkey") + end + + private + + def num_indices_named(name) + @connection.execute(<<-SQL).values.length + SELECT 1 FROM "pg_index" + JOIN "pg_class" ON "pg_index"."indexrelid" = "pg_class"."oid" + WHERE "pg_class"."relname" = '#{name}' + SQL + end +end -- cgit v1.2.3 From 6cd9e2bd29b26e1ea781fafce18614217b7adf7b Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Sat, 22 Nov 2014 15:36:50 -0700 Subject: Fix build failures For some reason changing `.find` to `.unscoped.find` in https://github.com/rails/rails/commit/f766abd4cf3eb75469d3646cfb6d85e668c619f3 caused `scoping` to leak in some tests when run in isolation (looks like a concurrency issue?). `relation_scoping_test.rb` is a case that failed. From what I can tell it should not be possible, but changing to the block form fixes it. There is a deeper issue that I can't seem to find. /cc @senny --- activerecord/lib/active_record/fixtures.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 660ba18a98..7c53ee0a9a 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -806,7 +806,9 @@ module ActiveRecord def find if model_class - model_class.unscoped.find(fixture[model_class.primary_key]) + model_class.unscoped do + model_class.find(fixture[model_class.primary_key]) + end else raise FixtureClassNotFound, "No class attached to find." end -- cgit v1.2.3 From 5dc598814efa45a1019c262ee01e5cc2db864d26 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Sat, 22 Nov 2014 20:29:29 -0500 Subject: Add changelog entry for .reflections API change `.reflections` public API changed to return a String instead of a Symbol as keys. see commit 1f31488499111fdfce79d8dc1cc8fb008f7cdb25 and 6259e4e2dcca9a79f22f96658c33efe81936bc0d [fixes #16928] [fixes #17610] --- activerecord/CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 16f776df58..a4de332d4c 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Change `reflections` public api to return the keys as String objects. + + Fixes #16928. + + *arthurnn* + * Renaming a table in pg also renames the primary key index. Fixes #12856 -- cgit v1.2.3 From 31b3069615529eabc42cc5bee1cd76e52bd1560b Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Sat, 22 Nov 2014 20:33:00 -0500 Subject: Update reflections public API doc --- activerecord/lib/active_record/reflection.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index f4a351b092..4219632596 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -63,7 +63,7 @@ module ActiveRecord # Returns a Hash of name of the reflection as the key and a AssociationReflection as the value. # - # Account.reflections # => {balance: AggregateReflection} + # Account.reflections # => {"balance" => AggregateReflection} # # @api public def reflections -- cgit v1.2.3 From be8d1a9b57175732f9dafe23f790da82d4b06135 Mon Sep 17 00:00:00 2001 From: Zachary Scott Date: Sun, 23 Nov 2014 12:06:57 -0800 Subject: Add a test for reflection keys as Strings, fixes #16928 See also PR: #17610 --- activerecord/test/cases/reflection_test.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 84abaf0291..094fcccc89 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -209,6 +209,10 @@ class ReflectionTest < ActiveRecord::TestCase assert_not_equal Object.new, Firm._reflections['clients'] end + def test_reflections_should_return_keys_as_strings + assert Category.reflections.keys.all? { |key| key.is_a? String }, "Model.reflections is expected to return string for keys" + end + def test_has_and_belongs_to_many_reflection assert_equal :has_and_belongs_to_many, Category.reflections['posts'].macro assert_equal :posts, Category.reflect_on_all_associations(:has_and_belongs_to_many).first.name -- cgit v1.2.3 From 77fbc5358616c131884df4e0b622241ebf2a129b Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Mon, 24 Nov 2014 12:14:47 +0100 Subject: cleanup, remove trailing whitespace [ci skip] --- activerecord/lib/active_record/migration.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 2024b225e4..4e00eb3d99 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -184,8 +184,8 @@ module ActiveRecord # you wish to downgrade. Alternatively, you can also use the STEP option if you # wish to rollback last few migrations. rake db:migrate STEP=2 will rollback # the latest two migrations. - # - # If any of the migrations throw an ActiveRecord::IrreversibleMigration exception, + # + # If any of the migrations throw an ActiveRecord::IrreversibleMigration exception, # that step will fail and you'll have some manual work to do. # # == Database support -- cgit v1.2.3 From 91c0c277698ab6ca4132a580f4212aa913492a8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20van=20den=20Berg?= Date: Mon, 24 Nov 2014 16:03:07 +0100 Subject: Reword documentation for update_all It now contains a carefully formulated reference to the "current relation" which might help clarify that the receiving will generate its own scope, escaping the need for explicitly referencing `default_scope` which is, after all, just another way of specifying a scope and nothing special. --- activerecord/lib/active_record/relation.rb | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index fc3306ee81..561ed222d1 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -304,8 +304,7 @@ module ActiveRecord klass.current_scope = previous end - # Updates all records in the current scope (respecting the default_scope, where, - # limit and order specified) with details given. This method constructs a single SQL update_all + # Updates all records in the current relation with details given. This method constructs a single SQL UPDATE # statement and sends it straight to the database. It does not instantiate the involved models and it does not # trigger Active Record callbacks or validations. Values passed to `update_all` will not go through # ActiveRecord's type-casting behavior. It should receive only values that can be passed as-is to the SQL @@ -320,12 +319,6 @@ module ActiveRecord # # Update all customers with the given attributes # Customer.update_all wants_email: true # - # # Update all active accounts with the given attributes - # class Account < ActiveRecord::Base - # default_scope -> { where active: true } - # end - # Account.update_all(failed_logins: 0) - # # # Update all books with 'Rails' in their title # Book.where('title LIKE ?', '%Rails%').update_all(author: 'David') # -- cgit v1.2.3 From 72e8442797b90d865811e39418ef9d49238b193a Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Mon, 24 Nov 2014 11:24:31 -0700 Subject: Ensure the type map's cache is thread safe Thanks to @thedarkone for pointing out that an instance of this object is used in a shared context. --- activerecord/lib/active_record/type/type_map.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/type/type_map.rb b/activerecord/lib/active_record/type/type_map.rb index 7c194c0cdf..09f5ba6b74 100644 --- a/activerecord/lib/active_record/type/type_map.rb +++ b/activerecord/lib/active_record/type/type_map.rb @@ -1,10 +1,12 @@ +require 'thread_safe' + module ActiveRecord module Type class TypeMap # :nodoc: def initialize @mapping = {} - @cache = Hash.new do |h, key| - h[key] = {} + @cache = ThreadSafe::Cache.new do |h, key| + h.fetch_or_store(key, ThreadSafe::Cache.new) end end @@ -13,7 +15,9 @@ module ActiveRecord end def fetch(lookup_key, *args, &block) - @cache[lookup_key][args] ||= perform_fetch(lookup_key, *args, &block) + @cache[lookup_key].fetch_or_store(args) do + perform_fetch(lookup_key, *args, &block) + end end def register_type(key, value = nil, &block) -- cgit v1.2.3 From 83821e2c4a32b17b153fcbcb247dec6e32f47284 Mon Sep 17 00:00:00 2001 From: claudiob Date: Mon, 24 Nov 2014 10:27:58 -0800 Subject: Mark comments that should not be in the docs Some comments that are meant to separate blocks of code in a file show up on http://api.rubyonrails.org as though they were part of the documentation. This commit hides those comments from the documentation. Stems from the discussion with @zzak at https://github.com/voloko/sdoc/issues/79#issuecomment-64158738 [ci skip] --- .../lib/active_record/connection_adapters/abstract_mysql_adapter.rb | 2 ++ .../lib/active_record/connection_adapters/mysql2_adapter.rb | 6 ++++++ activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 4 ++++ .../lib/active_record/connection_adapters/sqlite3_adapter.rb | 2 ++ 4 files changed, 14 insertions(+) (limited to 'activerecord') 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 167453657d..c824a7b11b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -285,7 +285,9 @@ module ActiveRecord end end + #-- # DATABASE STATEMENTS ====================================== + #++ def clear_cache! super diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index 5b83131f0e..d19bd80576 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -66,7 +66,9 @@ module ActiveRecord exception.error_number if exception.respond_to?(:error_number) end + #-- # QUOTING ================================================== + #++ def quote_string(string) @connection.escape(string) @@ -80,7 +82,9 @@ module ActiveRecord end end + #-- # CONNECTION MANAGEMENT ==================================== + #++ def active? return false unless @connection @@ -104,7 +108,9 @@ module ActiveRecord end end + #-- # DATABASE STATEMENTS ====================================== + #++ def explain(arel, binds = []) sql = "EXPLAIN #{to_sql(arel, binds.dup)}" diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 8b7459ef27..53ad71b445 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -137,7 +137,9 @@ module ActiveRecord @connection.quote(string) end + #-- # CONNECTION MANAGEMENT ==================================== + #++ def active? if @connection.respond_to?(:stat) @@ -178,7 +180,9 @@ module ActiveRecord end end + #-- # DATABASE STATEMENTS ====================================== + #++ def select_rows(sql, name = nil, binds = []) @connection.query_with_result = true diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index b18cb353f1..0b8b6a2bf8 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -266,7 +266,9 @@ module ActiveRecord end end + #-- # DATABASE STATEMENTS ====================================== + #++ def explain(arel, binds = []) sql = "EXPLAIN QUERY PLAN #{to_sql(arel, binds)}" -- cgit v1.2.3 From fbef981fdc7ba64678f7ae1fc82b2cd790469280 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 24 Nov 2014 14:15:45 -0800 Subject: allow the "USING" statement to be specified on change column calls --- .../postgresql/schema_statements.rb | 4 +++- .../adapters/postgresql/change_schema_test.rb | 25 ++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 activerecord/test/cases/adapters/postgresql/change_schema_test.rb (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 515ff2dd44..208302b365 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -433,7 +433,9 @@ module ActiveRecord quoted_table_name = quote_table_name(table_name) sql_type = type_to_sql(type, options[:limit], options[:precision], options[:scale]) sql_type << "[]" if options[:array] - execute "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}" + sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}" + sql << " USING #{options[:using]}" if options[:using] + execute sql change_column_default(table_name, column_name, options[:default]) if options_include_default?(options) change_column_null(table_name, column_name, options[:null], options[:default]) if options.key?(:null) diff --git a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb new file mode 100644 index 0000000000..90cd28c2ec --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb @@ -0,0 +1,25 @@ +require 'cases/helper' + +module ActiveRecord + class Migration + class PGChangeSchemaTest < ActiveRecord::TestCase + attr_reader :connection + + def setup + super + @connection = ActiveRecord::Base.connection + connection.create_table(:strings) do |t| + t.string :somedate + end + end + + def teardown + connection.drop_table :strings + end + + def test_change_string_to_date + connection.change_column :strings, :somedate, :timestamp, using: 'CAST("somedate" AS timestamp)' + end + end + end +end -- cgit v1.2.3 From 63963801c01279d99930de1cb252a8238909bc0c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 24 Nov 2014 14:18:33 -0800 Subject: oops, forgot to add the real assertion! --- activerecord/test/cases/adapters/postgresql/change_schema_test.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb index 90cd28c2ec..ec1b446dab 100644 --- a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb @@ -19,6 +19,7 @@ module ActiveRecord def test_change_string_to_date connection.change_column :strings, :somedate, :timestamp, using: 'CAST("somedate" AS timestamp)' + assert_equal :datetime, connection.columns(:strings).find { |c| c.name == 'somedate' }.type end end end -- cgit v1.2.3 From cdd90f39d796986dabf1678b3277b230dbe18961 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Mon, 24 Nov 2014 14:29:04 -0800 Subject: allow types to be passed in for USING casts This allows us so abstract the migration from the type that is actually used by Rails. For example, ":string" may be a varchar or something, but the framework does that translation, and the app shouldn't need to know. --- .../active_record/connection_adapters/postgresql/schema_statements.rb | 3 +++ 1 file changed, 3 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 208302b365..193c950261 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -435,6 +435,9 @@ module ActiveRecord sql_type << "[]" if options[:array] sql = "ALTER TABLE #{quoted_table_name} ALTER COLUMN #{quote_column_name(column_name)} TYPE #{sql_type}" sql << " USING #{options[:using]}" if options[:using] + if options[:cast_as] + sql << " USING CAST(#{quote_column_name(column_name)} AS #{type_to_sql(options[:cast_as], options[:limit], options[:precision], options[:scale])})" + end execute sql change_column_default(table_name, column_name, options[:default]) if options_include_default?(options) -- cgit v1.2.3 From 9e7037f19865021030893880b16be7b1ecbd3470 Mon Sep 17 00:00:00 2001 From: eileencodes Date: Mon, 24 Nov 2014 14:56:27 -0500 Subject: Combine aliased_table_for and aliased_name_for This refactoring reduces the number of conditionals needed to build `aliased_table_for` and removes `aliased_name_for` because it's no longer necessary. `aliased_name_for` was also used in `JoinDependency#initialize` so that was replaced with `aliased_table_for` as well. --- .../lib/active_record/associations/alias_tracker.rb | 15 +++------------ .../lib/active_record/associations/join_dependency.rb | 2 +- 2 files changed, 4 insertions(+), 13 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/alias_tracker.rb b/activerecord/lib/active_record/associations/alias_tracker.rb index a6a1947148..0c3234ed24 100644 --- a/activerecord/lib/active_record/associations/alias_tracker.rb +++ b/activerecord/lib/active_record/associations/alias_tracker.rb @@ -57,20 +57,10 @@ module ActiveRecord end def aliased_table_for(table_name, aliased_name) - table_alias = aliased_name_for(table_name, aliased_name) - - if table_alias == table_name - Arel::Table.new(table_name) - else - Arel::Table.new(table_name).alias(table_alias) - end - end - - def aliased_name_for(table_name, aliased_name) if aliases[table_name].zero? # If it's zero, we can have our table_name aliases[table_name] = 1 - table_name + Arel::Table.new(table_name) else # Otherwise, we need to use an alias aliased_name = connection.table_alias_for(aliased_name) @@ -78,11 +68,12 @@ module ActiveRecord # Update the count aliases[aliased_name] += 1 - if aliases[aliased_name] > 1 + table_alias = if aliases[aliased_name] > 1 "#{truncate(aliased_name)}_#{aliases[aliased_name]}" else aliased_name end + Arel::Table.new(table_name).alias(table_alias) end end diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index c5c4edd090..285d0ec9af 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -94,7 +94,7 @@ module ActiveRecord # def initialize(base, associations, joins) @alias_tracker = AliasTracker.create(base.connection, joins) - @alias_tracker.aliased_name_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1 + @alias_tracker.aliased_table_for(base.table_name, base.table_name) # Updates the count for base.table_name to 1 tree = self.class.make_tree associations @join_root = JoinBase.new base, build(tree, base) @join_root.children.each { |child| construct_tables! @join_root, child } -- cgit v1.2.3 From 03c1055b74a8689a7253bbcb1ec87ae0c44a9d92 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Tue, 25 Nov 2014 19:48:09 +0900 Subject: Fix out of range error message --- activerecord/lib/active_record/type/integer.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/type/integer.rb b/activerecord/lib/active_record/type/integer.rb index 36bbd9cd5e..750f353472 100644 --- a/activerecord/lib/active_record/type/integer.rb +++ b/activerecord/lib/active_record/type/integer.rb @@ -38,7 +38,7 @@ module ActiveRecord def ensure_in_range(value) unless range.cover?(value) - raise RangeError, "#{value} is too large for #{self.class} with limit #{limit || 4}" + raise RangeError, "#{value} is out of range for #{self.class} with limit #{limit || 4}" end end -- cgit v1.2.3 From 9e9793b440c044b765f2d1f702feeb92aef2b139 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Tue, 25 Nov 2014 18:11:38 +0100 Subject: do not trigger AR lazy load hook before initializers ran. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [Rafael Mendonça França & Yves Senn] This require caused the `active_record.set_configs` initializer to run immediately, before `config/initializers`. This means that setting any configuration on `Rails.application.config.active_record` inside of an initializer had no effects when rails was loaded through `rake`. Introduced by #6518 /cc @rafaelfranca --- activerecord/lib/active_record/railtie.rb | 2 -- 1 file changed, 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index a4ceacbf44..f1bdbc845c 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -36,8 +36,6 @@ module ActiveRecord config.eager_load_namespaces << ActiveRecord rake_tasks do - require "active_record/base" - namespace :db do task :load_config do ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration -- cgit v1.2.3 From 5c4495538bb4e8808ce077f08ea6b809952daa44 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Mon, 24 Nov 2014 12:04:01 +0100 Subject: bring back `db:test:prepare`. This reverts deprecations added in #13528. The task is brought back for two reasons: 1. Give plugins a way to hook into the test database initialization process 2. Give the user a way to force a test database synchronization While `test:prepare` is still a dependency of every test task, `db:test:prepare` no longer hooks into it. This means that `test:prepare` runs before the schema is synchronized. Plugins, which insert data can now hook into `db:test:prepare`. The automatic schema maintenance can't detect when a migration is rolled-back, modified and reapplied. In this case the user has to fall back to `db:test:prepare` to force the synchronization to happen. --- activerecord/CHANGELOG.md | 15 +++++++++++++++ activerecord/lib/active_record/migration.rb | 9 ++++++++- activerecord/lib/active_record/railties/databases.rake | 10 +++++----- 3 files changed, 28 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 9130cfbe9e..2140b1ea83 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,18 @@ +* Bring back `db:test:prepare` to synchronize the test database schema. + + Manual synchronization using `bin/rake db:test:prepare` is required + when a migration is rolled-back, edited and reapplied. + + `ActiveRecord::Base.maintain_test_schema` now uses `db:test:prepare` + to synchronize the schema. Plugins can use this task as a hook to + provide custom behavior after the schema has been loaded. + + NOTE: `test:prepare` runs before the schema was synchronized. + + Fixes #17171, #15787. + + *Yves Senn* + * Change `reflections` public api to return the keys as String objects. Fixes #16928. diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 4e00eb3d99..92f2951f2d 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -395,7 +395,14 @@ module ActiveRecord def load_schema_if_pending! if ActiveRecord::Migrator.needs_migration? || !ActiveRecord::Migrator.any_migrations? - ActiveRecord::Tasks::DatabaseTasks.load_schema_current_if_exists + # Roundrip to Rake to allow plugins to hook into database initialization. + FileUtils.cd Rails.root do + current_config = Base.connection_config + Base.clear_all_connections! + system("bin/rake db:test:prepare") + # Establish a new connection, the old database may be gone (db:test:prepare uses purge) + Base.establish_connection(current_config) + end check_pending! end end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 3ec25f9f17..21b1c3f721 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -305,7 +305,7 @@ db_namespace = namespace :db do end # desc "Recreate the test database from the current schema" - task :load => %w(db:test:deprecated db:test:purge) do + task :load => %w(db:test:purge) do case ActiveRecord::Base.schema_format when :ruby db_namespace["test:load_schema"].invoke @@ -315,7 +315,7 @@ db_namespace = namespace :db do end # desc "Recreate the test database from an existent schema.rb file" - task :load_schema => %w(db:test:deprecated db:test:purge) do + task :load_schema => %w(db:test:purge) do begin should_reconnect = ActiveRecord::Base.connection_pool.active_connection? ActiveRecord::Schema.verbose = false @@ -328,7 +328,7 @@ db_namespace = namespace :db do end # desc "Recreate the test database from an existent structure.sql file" - task :load_structure => %w(db:test:deprecated db:test:purge) do + task :load_structure => %w(db:test:purge) do ActiveRecord::Tasks::DatabaseTasks.load_schema_for ActiveRecord::Base.configurations['test'], :sql, ENV['SCHEMA'] end @@ -349,12 +349,12 @@ db_namespace = namespace :db do task :clone_structure => %w(db:test:deprecated db:structure:dump db:test:load_structure) # desc "Empty the test database" - task :purge => %w(db:test:deprecated environment load_config) do + task :purge => %w(environment load_config) do ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations['test'] end # desc 'Check for pending migrations and load the test schema' - task :prepare => %w(db:test:deprecated environment load_config) do + task :prepare => %w(environment load_config) do unless ActiveRecord::Base.configurations.blank? db_namespace['test:load'].invoke end -- cgit v1.2.3 From dd986814e7f528916a44f841a02c955a4522307c Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 25 Nov 2014 10:52:33 -0700 Subject: Setting an association replaces records with the same id in memory The records weren't being replaced since equality in Active Record is defined in terms of `id` only. It is reasonable to expect that the references would be replaced in memory, even if no queries are actually executed. This change did not appear to affect any other parts of the code base. I chose not to execute callbacks since we're not actually modifying the association in a way that will be persisted. Fixes #17730 --- .../associations/collection_association.rb | 20 ++++++- .../associations/has_many_associations_test.rb | 64 ++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index d59cebb08b..2b7d39893d 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -358,6 +358,7 @@ module ActiveRecord if owner.new_record? replace_records(other_array, original_target) else + replace_common_records_in_memory(other_array, original_target) if other_array != original_target transaction { replace_records(other_array, original_target) } end @@ -385,11 +386,18 @@ module ActiveRecord target end - def add_to_target(record, skip_callbacks = false) + def add_to_target(record, skip_callbacks = false, &block) + if association_scope.distinct_value + index = @target.index(record) + end + replace_on_target(record, index, skip_callbacks, &block) + end + + def replace_on_target(record, index, skip_callbacks) callback(:before_add, record) unless skip_callbacks yield(record) if block_given? - if association_scope.distinct_value && index = @target.index(record) + if index @target[index] = record else @target << record @@ -534,6 +542,14 @@ module ActiveRecord target end + def replace_common_records_in_memory(new_target, original_target) + common_records = new_target & original_target + common_records.each do |record| + skip_callbacks = true + replace_on_target(record, @target.index(record), skip_callbacks) + end + end + def concat_records(records, should_raise = false) result = true diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 5a963b6458..8b7ab11570 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1997,4 +1997,68 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 1, car.bulbs.count assert_equal 1, car.tyres.count end + + test 'associations replace in memory when records have the same id' do + bulb = Bulb.create! + car = Car.create!(bulbs: [bulb]) + + new_bulb = Bulb.find(bulb.id) + new_bulb.name = "foo" + car.bulbs = [new_bulb] + + assert_equal "foo", car.bulbs.first.name + end + + test 'in memory replacement executes no queries' do + bulb = Bulb.create! + car = Car.create!(bulbs: [bulb]) + + new_bulb = Bulb.find(bulb.id) + + assert_no_queries do + car.bulbs = [new_bulb] + end + end + + test 'in memory replacements do not execute callbacks' do + raise_after_add = false + klass = Class.new(ActiveRecord::Base) do + self.table_name = :cars + has_many :bulbs, after_add: proc { raise if raise_after_add } + + def self.name + "Car" + end + end + bulb = Bulb.create! + car = klass.create!(bulbs: [bulb]) + + new_bulb = Bulb.find(bulb.id) + raise_after_add = true + + assert_nothing_raised do + car.bulbs = [new_bulb] + end + end + + test 'in memory replacements sets inverse instance' do + bulb = Bulb.create! + car = Car.create!(bulbs: [bulb]) + + new_bulb = Bulb.find(bulb.id) + car.bulbs = [new_bulb] + + assert_same car, new_bulb.car + end + + test 'in memory replacement maintains order' do + first_bulb = Bulb.create! + second_bulb = Bulb.create! + car = Car.create!(bulbs: [first_bulb, second_bulb]) + + same_bulb = Bulb.find(first_bulb.id) + car.bulbs = [second_bulb, same_bulb] + + assert_equal [first_bulb, second_bulb], car.bulbs + end end -- cgit v1.2.3 From d74e716b48b19a608a0445996cfcf17d304f289f Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Tue, 25 Nov 2014 13:57:06 -0700 Subject: Move PG float quoting to the correct location Not sure how we missed this case when we moved everything else to the `_quote` method. --- .../connection_adapters/postgresql/quoting.rb | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb index f95f45c689..991c41327f 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/quoting.rb @@ -14,22 +14,6 @@ module ActiveRecord @connection.unescape_bytea(value) if value end - # Quotes PostgreSQL-specific data types for SQL input. - def quote(value, column = nil) #:nodoc: - return super unless column - - case value - when Float - if value.infinite? || value.nan? - "'#{value}'" - else - super - end - else - super - end - end - # Quotes strings for use in SQL input. def quote_string(s) #:nodoc: @connection.escape(s) @@ -94,6 +78,12 @@ module ActiveRecord elsif value.hex? "X'#{value}'" end + when Float + if value.infinite? || value.nan? + "'#{value}'" + else + super + end else super end -- cgit v1.2.3 From 25c85cb6f6d8715da3f7fae08b165d01e6b34348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 25 Nov 2014 19:58:08 -0200 Subject: Use released arel --- activerecord/activerecord.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index bc10e96244..834fffeb18 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.beta2', '< 6.1' + s.add_dependency 'arel', '~> 6.0' end -- cgit v1.2.3 From 323334a775bb35d901742e4cc39f6fe7b5e53c8f Mon Sep 17 00:00:00 2001 From: Derek Prior Date: Mon, 24 Nov 2014 21:25:15 -0500 Subject: Generators add foreign keys on references If you run a generator such as: ``` rails generate model accounts supplier:references ``` The resulting migration will now add the corresponding foreign key constraint unless the reference was specified to be polymorphic. --- .../active_record/migration/templates/create_table_migration.rb | 3 +++ .../rails/generators/active_record/migration/templates/migration.rb | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb index f7bf6987c4..fb0fbb4759 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/create_table_migration.rb @@ -14,6 +14,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration end <% attributes_with_index.each do |attribute| -%> add_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> +<% end -%> +<% attributes.select(&:reference?).reject(&:polymorphic?).each do |attribute| -%> + add_foreign_key :<%= table_name %>, :<%= attribute.name.pluralize %> <% end -%> end end diff --git a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb index ae9c74fd05..7df9bcad25 100644 --- a/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb +++ b/activerecord/lib/rails/generators/active_record/migration/templates/migration.rb @@ -4,6 +4,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration <% attributes.each do |attribute| -%> <%- if attribute.reference? -%> add_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> + <%- unless attribute.polymorphic? -%> + add_foreign_key :<%= table_name %>, :<%= attribute.name.pluralize %> + <%- end -%> <%- else -%> add_column :<%= table_name %>, :<%= attribute.name %>, :<%= attribute.type %><%= attribute.inject_options %> <%- if attribute.has_index? -%> @@ -26,6 +29,9 @@ class <%= migration_class_name %> < ActiveRecord::Migration <%- if migration_action -%> <%- if attribute.reference? -%> remove_reference :<%= table_name %>, :<%= attribute.name %><%= attribute.inject_options %> + <%- unless attribute.polymorphic? -%> + remove_foreign_key :<%= table_name %>, :<%= attribute.name.pluralize %> + <%- end -%> <%- else -%> <%- if attribute.has_index? -%> remove_index :<%= table_name %>, :<%= attribute.index_name %><%= attribute.inject_index_options %> -- cgit v1.2.3 From 35c5f47d9739ed46c0fb344279f68fe0fc2e98df Mon Sep 17 00:00:00 2001 From: Jon Atack Date: Wed, 26 Nov 2014 12:23:00 +0100 Subject: Active Record change log pass [skip ci] --- activerecord/CHANGELOG.md | 78 ++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 38 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 2140b1ea83..a6ed8cdb06 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -7,7 +7,7 @@ to synchronize the schema. Plugins can use this task as a hook to provide custom behavior after the schema has been loaded. - NOTE: `test:prepare` runs before the schema was synchronized. + NOTE: `test:prepare` runs before the schema is synchronized. Fixes #17171, #15787. @@ -29,12 +29,11 @@ *Yves Senn* -* Fix includes on association with a scope containing joins along with conditions - on the joined assoiciation. +* Fix preloading of associations with a scope containing joins along with + conditions on the joined association. *Siddharth Sharma* - * Add `Table#name` to match `TableDefinition#name`. *Cody Cutrer* @@ -95,7 +94,7 @@ *Yuki Nishijima* -* Fix regression causing `after_create` callbacks to run before associated +* Fix a regression causing `after_create` callbacks to run before associated records are autosaved. Fixes #17209. @@ -192,7 +191,7 @@ *Yves Senn* -* Fixed a regression where whitespaces were stripped from DISTINCT queries in +* Fix a regression where whitespaces were stripped from DISTINCT queries in PostgreSQL. *Agis Anastasopoulos* @@ -206,7 +205,7 @@ *Agis Anastasopoulos* -* Fixed `Relation#exists?` to work with polymorphic associations. +* Fix `Relation#exists?` to work with polymorphic associations. Fixes #15821. @@ -228,7 +227,7 @@ *arthurnn* -* Fixed an issue where custom accessor methods (such as those generated by +* Fix an issue where custom accessor methods (such as those generated by `enum`) with the same name as a global method are incorrectly overridden when subclassing. @@ -292,7 +291,7 @@ *Yves Senn* -* Fixed automatic maintaining test schema to properly handle sql structure +* Fix automatic maintaining test schema to properly handle sql structure schema format. Fixes #15394. @@ -455,8 +454,8 @@ *Sean Griffin* -* Fixed error in `reset_counters` when associations have `select` scope. - (Call to `count` generates invalid SQL.) +* Fix an error in `reset_counters` when associations have `select` scope. + (Call to `count` generated invalid SQL.) *Cade Truitt* @@ -681,7 +680,7 @@ *Yves Senn* -* Fixed `columns_for_distinct` of postgresql adapter to work correctly +* Fix `columns_for_distinct` of PostgreSQL adapter to work correctly with orders without sort direction modifiers. *Nikolay Kondratyev* @@ -717,7 +716,7 @@ *arthurnn* -* Fixed serialization for records with an attribute named `format`. +* Fix serialization for records with an attribute named `format`. Fixes #15188. @@ -728,12 +727,12 @@ *Kuldeep Aggarwal* -* Fixed serialized fields returning serialized data after being updated with +* Fix serialized fields returning serialized data after being updated with `update_column`. *Simon Hørup Eskildsen* -* Fixed polymorphic eager loading when using a String as foreign key. +* Fix polymorphic eager loading when using a String as foreign key. Fixes #14734. @@ -747,7 +746,7 @@ *Brock Trappitt* -* Fixed the inferred table name of a `has_and_belongs_to_many` auxiliar +* Fix the inferred table name of a `has_and_belongs_to_many` auxiliary table inside a schema. Fixes #14824. @@ -859,7 +858,7 @@ *Yves Senn* -* Fixed has_and_belongs_to_many's CollectionAssociation size calculation. +* Fix `has_and_belongs_to_many` CollectionAssociation size calculations. `has_and_belongs_to_many` should fall back to using the normal CollectionAssociation's size calculation if the collection is not cached or loaded. @@ -964,10 +963,12 @@ *Earl St Sauver* -* Fixed unexpected behavior for `has_many :through` associations going through a scoped `has_many`. +* Fix unexpected behavior for `has_many :through` associations going through + a scoped `has_many`. - If a `has_many` association is adjusted using a scope, and another `has_many :through` - uses this association, then the scope adjustment is unexpectedly neglected. + If a `has_many` association is adjusted using a scope, and another + `has_many :through` uses this association, then the scope adjustment is + unexpectedly neglected. Fixes #14537. @@ -977,13 +978,13 @@ *Kuldeep Aggarwal* -* Fixed `has_many` association to make it support irregular inflections. +* Enable `has_many` associations to support irregular inflections. Fixes #8928. *arthurnn*, *Javier Goizueta* -* Fixed a problem where count used with a grouping was not returning a Hash. +* Fix `count` used with a grouping not returning a Hash. Fixes #14721. @@ -1038,8 +1039,8 @@ *Eileen M. Uchitelle*, *Aaron Patterson* -* Fixed error for aggregate methods (`empty?`, `any?`, `count`) with `select` - which created invalid SQL. +* Fix invalid SQL when aggregate methods (`empty?`, `any?`, `count`) used + with `select`. Fixes #13648. @@ -1070,8 +1071,8 @@ *Roderick van Domburg* -* Fixed a problem where an enum would overwrite values of another enum - with the same name in an unrelated class. +* Fix a problem where an enum would overwrite values of another enum with the + same name in an unrelated class. Fixes #14607. @@ -1106,7 +1107,7 @@ *arthurnn* -* Fixed error when using `with_options` with lambda. +* Fix error when using `with_options` with lambda. Fixes #9805. @@ -1144,14 +1145,15 @@ *Yves Senn* -* Fixed error when specifying a non-empty default value on a PostgreSQL array column. +* Fix error when specifying a non-empty default value on a PostgreSQL array + column. Fixes #10613. *Luke Steensen* -* Fixed error where .persisted? throws SystemStackError for an unsaved model with a - custom primary key that didn't save due to validation error. +* Fix error where `.persisted?` throws SystemStackError for an unsaved model with a + custom primary key that did not save due to validation error. Fixes #14393. @@ -1244,7 +1246,8 @@ *Aaron Patterson* -* Only use BINARY for MySQL case sensitive uniqueness check when column has a case insensitive collation. +* Only use BINARY for MySQL case sensitive uniqueness check when column + has a case insensitive collation. *Ryuta Kamizono* @@ -1252,8 +1255,8 @@ *arthurnn*, *Tatsuhiko Miyagawa* -* Support for Postgres `citext` data type enabling case-insensitive where - values without needing to wrap in UPPER/LOWER sql functions. +* Support for PostgreSQL `citext` data type enabling case-insensitive + `where` values without needing to wrap in UPPER/LOWER sql functions. *Troy Kruthoff*, *Lachlan Sylvester* @@ -1283,9 +1286,8 @@ *Aaron Patterson*, *Yves Senn* -* Fixed error with validation with enum fields for records where the - value for any enum attribute is always evaluated as 0 during - uniqueness validation. +* Fix error with validation with enum fields for records where the value for + any enum attribute is always evaluated as 0 during uniqueness validation. Fixes #14172. @@ -1299,8 +1301,8 @@ Fixes #14144. -* Fixed STI classes not defining an attribute method if there is a - conflicting private method defined on its ancestors. +* Fix STI classes not defining an attribute method if there is a conflicting + private method defined on its ancestors. Fixes #11569. -- cgit v1.2.3 From 3e30c5d4222f816cdffd5241d56ef9442f6d1903 Mon Sep 17 00:00:00 2001 From: Recursive Madman Date: Wed, 19 Nov 2014 12:34:25 +0100 Subject: Add #record attribute to RecordNotFound and RecordDestroyed exceptions. This allows these exceptions to be handled generically in conjunction with RecordInvalid. --- activerecord/lib/active_record/errors.rb | 19 +++++++++++++++++++ activerecord/lib/active_record/persistence.rb | 4 ++-- activerecord/test/cases/callbacks_test.rb | 6 ++++-- 3 files changed, 25 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index 5b3fdf16f5..ca4fede7a2 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -52,10 +52,29 @@ module ActiveRecord # Raised by ActiveRecord::Base.save! and ActiveRecord::Base.create! methods when record cannot be # saved because record is invalid. class RecordNotSaved < ActiveRecordError + attr_reader :record + + def initialize(record) + @record = record + super() + end end # Raised by ActiveRecord::Base.destroy! when a call to destroy would return false. + # + # begin + # complex_operation_that_internally_calls_destroy! + # rescue ActiveRecord::RecordNotDestroyed => invalid + # puts invalid.record.errors + # end + # class RecordNotDestroyed < ActiveRecordError + attr_reader :record + + def initialize(record) + @record = record + super() + end end # Superclass for all database execution errors. diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index ee3b7b6163..8d84a3acae 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -139,7 +139,7 @@ module ActiveRecord # Attributes marked as readonly are silently ignored if the record is # being updated. def save!(*) - create_or_update || raise(RecordNotSaved) + create_or_update || raise(RecordNotSaved, self) end # Deletes the record in the database and freezes this instance to @@ -181,7 +181,7 @@ module ActiveRecord # and destroy! raises ActiveRecord::RecordNotDestroyed. See # ActiveRecord::Callbacks for further details. def destroy! - destroy || raise(ActiveRecord::RecordNotDestroyed) + destroy || raise(ActiveRecord::RecordNotDestroyed, self) end # Returns an instance of the specified +klass+ with the attributes of the diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb index 5e07f8a03c..e3c3c2fcdf 100644 --- a/activerecord/test/cases/callbacks_test.rb +++ b/activerecord/test/cases/callbacks_test.rb @@ -443,7 +443,8 @@ class CallbacksTest < ActiveRecord::TestCase david = ImmutableDeveloper.find(1) assert david.valid? assert !david.save - assert_raise(ActiveRecord::RecordNotSaved) { david.save! } + exc = assert_raise(ActiveRecord::RecordNotSaved) { david.save! } + assert_equal exc.record, david david = ImmutableDeveloper.find(1) david.salary = 10_000_000 @@ -477,7 +478,8 @@ class CallbacksTest < ActiveRecord::TestCase def test_before_destroy_returning_false david = ImmutableDeveloper.find(1) assert !david.destroy - assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! } + exc = assert_raise(ActiveRecord::RecordNotDestroyed) { david.destroy! } + assert_equal exc.record, david assert_not_nil ImmutableDeveloper.find_by_id(1) someone = CallbackCancellationDeveloper.find(1) -- cgit v1.2.3 From 59190c037999d302dd95b1f53f7049c2cec3f7b9 Mon Sep 17 00:00:00 2001 From: Recursive Madman Date: Wed, 26 Nov 2014 15:05:34 +0100 Subject: various error classes: added newlines & removed :nodoc: flag from public attribute. --- activerecord/lib/active_record/errors.rb | 2 ++ activerecord/lib/active_record/validations.rb | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index ca4fede7a2..14819aab54 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -196,6 +196,7 @@ module ActiveRecord # offending attribute. class AttributeAssignmentError < ActiveRecordError attr_reader :exception, :attribute + def initialize(message, exception, attribute) super(message) @exception = exception @@ -208,6 +209,7 @@ module ActiveRecord # objects, each corresponding to the error while assigning to an attribute. class MultiparameterAssignmentErrors < ActiveRecordError attr_reader :errors + def initialize(errors) @errors = errors end diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 7f7d49cdb4..a6c8ff7f3a 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -5,13 +5,14 @@ module ActiveRecord # +record+ method to retrieve the record which did not validate. # # begin - # complex_operation_that_calls_save!_internally + # complex_operation_that_internally_calls_save! # rescue ActiveRecord::RecordInvalid => invalid # puts invalid.record.errors # end class RecordInvalid < ActiveRecordError - attr_reader :record # :nodoc: - def initialize(record) # :nodoc: + attr_reader :record + + def initialize(record) @record = record errors = @record.errors.full_messages.join(", ") super(I18n.t(:"#{@record.class.i18n_scope}.errors.messages.record_invalid", :errors => errors, :default => :"errors.messages.record_invalid")) -- cgit v1.2.3 From 9e6733d5f7867717c760c1cf4b70c36318c28d67 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Thu, 27 Nov 2014 21:27:34 +0900 Subject: Refactor `SchemaCreation#visit_AddColumn` --- .../active_record/connection_adapters/abstract/schema_creation.rb | 4 +--- .../connection_adapters/postgresql/schema_statements.rb | 6 ------ 2 files changed, 1 insertion(+), 9 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb index 6bab260f5a..792d49da6f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb @@ -15,9 +15,7 @@ module ActiveRecord end def visit_AddColumn(o) - sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale) - sql = "ADD #{quote_column_name(o.name)} #{sql_type}" - add_column_options!(sql, column_options(o)) + "ADD #{accept(o)}" end private diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 193c950261..dba905ed56 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -4,12 +4,6 @@ module ActiveRecord class SchemaCreation < AbstractAdapter::SchemaCreation private - def visit_AddColumn(o) - sql_type = type_to_sql(o.type, o.limit, o.precision, o.scale) - sql = "ADD COLUMN #{quote_column_name(o.name)} #{sql_type}" - add_column_options!(sql, column_options(o)) - end - def visit_ColumnDefinition(o) sql = super if o.primary_key? && o.type != :primary_key -- cgit v1.2.3 From 07f4bd5b60d062bda58a45ca5f832a0a6bcdc713 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 27 Nov 2014 13:50:10 -0200 Subject: StatementCache is private API It should not be used in applications --- activerecord/lib/active_record/statement_cache.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb index aece446384..767c7561cf 100644 --- a/activerecord/lib/active_record/statement_cache.rb +++ b/activerecord/lib/active_record/statement_cache.rb @@ -13,10 +13,10 @@ module ActiveRecord # # The relation returned by the block is cached, and for each +execute+ call the cached relation gets duped. # Database is queried when +to_a+ is called on the relation. - class StatementCache - class Substitute; end + class StatementCache # :nodoc: + class Substitute; end # :nodoc: - class Query + class Query # :nodoc: def initialize(sql) @sql = sql end @@ -26,7 +26,7 @@ module ActiveRecord end end - class PartialQuery < Query + class PartialQuery < Query # :nodoc: def initialize values @values = values @indexes = values.each_with_index.find_all { |thing,i| @@ -51,11 +51,11 @@ module ActiveRecord PartialQuery.new collected end - class Params + class Params # :nodoc: def bind; Substitute.new; end end - class BindMap + class BindMap # :nodoc: def initialize(bind_values) @indexes = [] @bind_values = bind_values -- cgit v1.2.3 From 8fc7eb5f21e42f1bc484c496a6431c284b9ee805 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 27 Nov 2014 14:40:19 -0200 Subject: Update the StatementCache documentation --- activerecord/lib/active_record/statement_cache.rb | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb index 767c7561cf..c68990bf99 100644 --- a/activerecord/lib/active_record/statement_cache.rb +++ b/activerecord/lib/active_record/statement_cache.rb @@ -1,18 +1,29 @@ module ActiveRecord # Statement cache is used to cache a single statement in order to avoid creating the AST again. - # Initializing the cache is done by passing the statement in the initialization block: + # Initializing the cache is done by passing the statement in the create block: # - # cache = ActiveRecord::StatementCache.new do - # Book.where(name: "my book").limit(100) + # cache = StatementCache.create(Book.connection) do |params| + # Book.where(name: "my book").where("author_id > 3") # end # # The cached statement is executed by using the +execute+ method: # - # cache.execute + # cache.execute([], Book, Book.connection) # # The relation returned by the block is cached, and for each +execute+ call the cached relation gets duped. # Database is queried when +to_a+ is called on the relation. + # + # If you want to cache the statement without the values you can use the +bind+ method of the + # block parameter. + # + # cache = StatementCache.create(Book.connection) do |params| + # Book.where(name: params.bind) + # end + # + # And pass the bind values as the first argument of +execute+ call. + # + # cache.execute(["my book"], Book, Book.connection) class StatementCache # :nodoc: class Substitute; end # :nodoc: -- cgit v1.2.3 From 5142d5411481c893f817c1431b0869be3745060f Mon Sep 17 00:00:00 2001 From: Yuki Nishijima Date: Thu, 27 Nov 2014 17:44:36 -0800 Subject: Fix a bug where AR::RecordNotSaved loses error messages Since 3e30c5d, it started ignoring the given error message. This commit changes the behavior of AR::RecordNotSaved#initialize so that it no longer loses the given error message. --- activerecord/lib/active_record/errors.rb | 4 ++-- activerecord/lib/active_record/persistence.rb | 2 +- .../cases/associations/has_many_associations_test.rb | 17 +++++++++++++---- .../associations/has_many_through_associations_test.rb | 7 +++++-- .../cases/associations/has_one_associations_test.rb | 13 ++++++++++--- 5 files changed, 31 insertions(+), 12 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index 14819aab54..fc28ab585f 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -54,9 +54,9 @@ module ActiveRecord class RecordNotSaved < ActiveRecordError attr_reader :record - def initialize(record) + def initialize(message, record = nil) @record = record - super() + super(message) end end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 8d84a3acae..99e104598b 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -139,7 +139,7 @@ module ActiveRecord # Attributes marked as readonly are silently ignored if the record is # being updated. def save!(*) - create_or_update || raise(RecordNotSaved, self) + create_or_update || raise(RecordNotSaved.new(nil, self)) end # Deletes the record in the database and freezes this instance to diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 8b7ab11570..9510d57de6 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -589,17 +589,21 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_create_with_bang_on_has_many_when_parent_is_new_raises - assert_raise(ActiveRecord::RecordNotSaved) do + error = assert_raise(ActiveRecord::RecordNotSaved) do firm = Firm.new firm.plain_clients.create! :name=>"Whoever" end + + assert_equal "You cannot call create unless the parent is saved", error.message end def test_regular_create_on_has_many_when_parent_is_new_raises - assert_raise(ActiveRecord::RecordNotSaved) do + error = assert_raise(ActiveRecord::RecordNotSaved) do firm = Firm.new firm.plain_clients.create :name=>"Whoever" end + + assert_equal "You cannot call create unless the parent is saved", error.message end def test_create_with_bang_on_has_many_raises_when_record_not_saved @@ -610,9 +614,11 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end def test_create_with_bang_on_habtm_when_parent_is_new_raises - assert_raise(ActiveRecord::RecordNotSaved) do + error = assert_raise(ActiveRecord::RecordNotSaved) do Developer.new("name" => "Aredridel").projects.create! end + + assert_equal "You cannot call create unless the parent is saved", error.message end def test_adding_a_mismatch_class @@ -1353,10 +1359,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert !account.valid? assert !orig_accounts.empty? - assert_raise ActiveRecord::RecordNotSaved do + error = assert_raise ActiveRecord::RecordNotSaved do firm.accounts = [account] end + assert_equal orig_accounts, firm.accounts + assert_equal "Failed to replace accounts because one or more of the " \ + "new records could not be saved.", error.message end def test_replace_with_same_content diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index df4a30ae9b..d523ea4f07 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -615,8 +615,11 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_create_on_new_record p = Post.new - assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(:first_name => "mew") } - assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(:first_name => "snow") } + error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create(:first_name => "mew") } + assert_equal "You cannot call create unless the parent is saved", error.message + + error = assert_raises(ActiveRecord::RecordNotSaved) { p.people.create!(:first_name => "snow") } + assert_equal "You cannot call create unless the parent is saved", error.message end def test_associate_with_create_and_invalid_options diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 2ecfcb521d..1a6d25f7d0 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -410,9 +410,11 @@ class HasOneAssociationsTest < ActiveRecord::TestCase pirate = pirates(:redbeard) new_ship = Ship.new - assert_raise(ActiveRecord::RecordNotSaved) do + error = assert_raise(ActiveRecord::RecordNotSaved) do pirate.ship = new_ship end + + assert_equal "Failed to save the new associated ship.", error.message assert_nil pirate.ship assert_nil new_ship.pirate_id end @@ -422,20 +424,25 @@ class HasOneAssociationsTest < ActiveRecord::TestCase pirate.ship.name = nil assert !pirate.ship.valid? - assert_raise(ActiveRecord::RecordNotSaved) do + error = assert_raise(ActiveRecord::RecordNotSaved) do pirate.ship = ships(:interceptor) end + assert_equal ships(:black_pearl), pirate.ship assert_equal pirate.id, pirate.ship.pirate_id + assert_equal "Failed to remove the existing associated ship. " + + "The record failed to save after its foreign key was set to nil.", error.message end def test_replacement_failure_due_to_new_record_should_raise_error pirate = pirates(:blackbeard) new_ship = Ship.new - assert_raise(ActiveRecord::RecordNotSaved) do + error = assert_raise(ActiveRecord::RecordNotSaved) do pirate.ship = new_ship end + + assert_equal "Failed to save the new associated ship.", error.message assert_equal ships(:black_pearl), pirate.ship assert_equal pirate.id, pirate.ship.pirate_id assert_equal pirate.id, ships(:black_pearl).reload.pirate_id -- cgit v1.2.3 From 91fca372b14b553a8029d81213ff51c8f3cde39b Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Thu, 27 Nov 2014 21:56:44 +0900 Subject: Refactor `add_column_options!`, to move the quoting of default value for :uuid in `quote_value`. --- .../connection_adapters/postgresql/schema_statements.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 193c950261..89b7f0a6b7 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -19,14 +19,22 @@ module ActiveRecord sql end + def column_options(o) + column_options = super + column_options[:array] = o.array + column_options + end + def add_column_options!(sql, options) - if options[:array] || options[:column].try(:array) + if options[:array] sql << '[]' end + super + end - column = options.fetch(:column) { return super } - if column.type == :uuid && options[:default] =~ /\(\)/ - sql << " DEFAULT #{options[:default]}" + def quote_value(value, column) + if column.type == :uuid && value =~ /\(\)/ + value else super end -- cgit v1.2.3 From 66cc7ce67c4f48f62377ad1a972581c35bcc8373 Mon Sep 17 00:00:00 2001 From: Ryuta Kamizono Date: Fri, 28 Nov 2014 17:30:55 +0900 Subject: Rename to `quote_default_expression` from `quote_value` --- .../lib/active_record/connection_adapters/abstract/schema_creation.rb | 4 ++-- .../active_record/connection_adapters/postgresql/schema_statements.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb index 6bab260f5a..6a684a33f8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb @@ -83,7 +83,7 @@ module ActiveRecord end def add_column_options!(sql, options) - sql << " DEFAULT #{quote_value(options[:default], options[:column])}" if options_include_default?(options) + sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options) # must explicitly check for :null to allow change_column to work on migrations if options[:null] == false sql << " NOT NULL" @@ -94,7 +94,7 @@ module ActiveRecord sql end - def quote_value(value, column) + def quote_default_expression(value, column) column.sql_type ||= type_to_sql(column.type, column.limit, column.precision, column.scale) column.cast_type ||= type_for_column(column) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 89b7f0a6b7..009b7cd682 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -32,7 +32,7 @@ module ActiveRecord super end - def quote_value(value, column) + def quote_default_expression(value, column) if column.type == :uuid && value =~ /\(\)/ value else -- cgit v1.2.3 From 6405a03c1303c4c2da7029cf9199bd17451fec4d Mon Sep 17 00:00:00 2001 From: Thorsten Ball Date: Fri, 28 Nov 2014 13:34:38 +0100 Subject: Adds preloaded_records method to NullPreloader This fixes a regression where preloading association throws an exception if one of the associations in the preloading hash doesn't exist for one record. Fixes #16070 --- activerecord/lib/active_record/associations/preloader.rb | 1 + activerecord/test/cases/associations/eager_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb index 46bccbf15a..4358f3b581 100644 --- a/activerecord/lib/active_record/associations/preloader.rb +++ b/activerecord/lib/active_record/associations/preloader.rb @@ -178,6 +178,7 @@ module ActiveRecord class NullPreloader def self.new(klass, owners, reflection, preload_scope); self; end def self.run(preloader); end + def self.preloaded_records; []; end end def preloader_for(reflection, owners, rhs_klass) diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index dd4f530791..f968a34886 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -270,6 +270,14 @@ class EagerAssociationTest < ActiveRecord::TestCase end end + def test_three_level_nested_preloading_does_not_raise_exception_when_association_does_not_exist + post_id = Comment.where(author_id: nil).where.not(post_id: nil).first.post_id + + assert_nothing_raised do + Post.preload(:comments => [{:author => :essays}]).find(post_id) + end + end + def test_nested_loading_through_has_one_association aa = AuthorAddress.all.merge!(:includes => {:author => :posts}).find(author_addresses(:david_address).id) assert_equal aa.author.posts.count, aa.author.posts.length -- cgit v1.2.3 From f25ad07f5ade46eb978fa82658463232d0247c65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 28 Nov 2014 15:00:06 -0200 Subject: Start Rails 5 development :tada: We will support only Ruby >= 2.1. But right now we don't accept pull requests with syntax changes to drop support to Ruby 1.9. --- activerecord/CHANGELOG.md | 1337 +------------------------ activerecord/lib/active_record/gem_version.rb | 6 +- 2 files changed, 4 insertions(+), 1339 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index a6ed8cdb06..38c5583708 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,1336 +1 @@ -* Bring back `db:test:prepare` to synchronize the test database schema. - - Manual synchronization using `bin/rake db:test:prepare` is required - when a migration is rolled-back, edited and reapplied. - - `ActiveRecord::Base.maintain_test_schema` now uses `db:test:prepare` - to synchronize the schema. Plugins can use this task as a hook to - provide custom behavior after the schema has been loaded. - - NOTE: `test:prepare` runs before the schema is synchronized. - - Fixes #17171, #15787. - - *Yves Senn* - -* Change `reflections` public api to return the keys as String objects. - - Fixes #16928. - - *arthurnn* - -* Renaming a table in pg also renames the primary key index. - - Fixes #12856 - - *Sean Griffin* - -* Make it possible to access fixtures excluded by a `default_scope`. - - *Yves Senn* - -* Fix preloading of associations with a scope containing joins along with - conditions on the joined association. - - *Siddharth Sharma* - -* Add `Table#name` to match `TableDefinition#name`. - - *Cody Cutrer* - -* Cache `CollectionAssociation#reader` proxies separately before and after - the owner has been saved so that the proxy is not cached without the - owner's id. - - *Ben Woosley* - -* `ActiveRecord::ReadOnlyRecord` now has a descriptive message. - - *Franky W.* - -* Fix preloading of associations which unscope a default scope. - - Fixes #11036. - - *Byron Bischoff* - -* Added SchemaDumper support for tables with jsonb columns. - - *Ted O'Meara* - -* Deprecate `sanitize_sql_hash_for_conditions` without replacement. Using a - `Relation` for performing queries and updates is the prefered API. - - *Sean Griffin* - -* Queries now properly type cast values that are part of a join statement, - even when using type decorators such as `serialize`. - - *Melanie Gilman & Sean Griffin* - -* 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"}) - # => ActiveRecord::UnknownAttributeError: unknown attribute 'name' for User. - - *Yuki Nishijima* - -* Fix a regression causing `after_create` callbacks to run before associated - records are autosaved. - - Fixes #17209. - - *Agis Anastasopoulos* - -* Honor overridden `rack.test` in Rack environment for the connection - management middleware. - - *Simon Eskildsen* - -* Add a truncate method to the connection. - - *Aaron Patterson* - -* Don't autosave unchanged has_one through records. - - *Alan Kennedy*, *Steve Parrington* - -* Do not dump foreign keys for ignored tables. - - *Yves Senn* - -* PostgreSQL adapter correctly dumps foreign keys targeting tables - outside the schema search path. - - Fixes #16907. - - *Matthew Draper*, *Yves Senn* - -* When a thread is killed, rollback the active transaction, instead of - committing it during the stack unwind. Previously, we could commit half- - completed work. This fix only works for Ruby 2.0+; on 1.9, we can't - distinguish a thread kill from an ordinary non-local (block) return, so must - default to committing. - - *Chris Hanks* - -* A `NullRelation` should represent nothing. This fixes a bug where - `Comment.where(post_id: Post.none)` returned a non-empty result. - - Fixes #15176. - - *Matthew Draper*, *Yves Senn* - -* Include default column limits in schema.rb. Allows defaults to be changed - in the future without affecting old migrations that assumed old defaults. - - *Jeremy Kemper* - -* MySQL: schema.rb now includes TEXT and BLOB column limits. - - *Jeremy Kemper* - -* MySQL: correct LONGTEXT and LONGBLOB limits from 2GB to their true 4GB. - - *Jeremy Kemper* - -* SQLite3Adapter now checks for views in `table_exists?`. Fixes #14041. - - *Girish Sonawane* - -* Introduce `connection.supports_views?` to check whether the current adapter - has support for SQL views. Connection adapters should define this method. - - *Yves Senn* - -* Allow included modules to override association methods. - - Fixes #16684. - - *Yves Senn* - -* Schema loading rake tasks (like `db:schema:load` and `db:setup`) maintain - the database connection to the current environment. - - Fixes #16757. - - *Joshua Cody*, *Yves Senn* - -* MySQL: set the connection collation along with the charset. - - Sets the connection collation to the database collation configured in - database.yml. Otherwise, `SET NAMES utf8mb4` will use the default - collation for that charset (utf8mb4_general_ci) when you may have chosen - a different collation, like utf8mb4_unicode_ci. - - This only applies to literal string comparisons, not column values, so it - is unlikely to affect you. - - *Jeremy Kemper* - -* `default_sequence_name` from the PostgreSQL adapter returns a `String`. - - *Yves Senn* - -* Fix a regression where whitespaces were stripped from DISTINCT queries in - PostgreSQL. - - *Agis Anastasopoulos* - - Fixes #16623. - -* Fix has_many :through relation merging failing when dynamic conditions are - passed as a lambda with an arity of one. - - Fixes #16128. - - *Agis Anastasopoulos* - -* Fix `Relation#exists?` to work with polymorphic associations. - - Fixes #15821. - - *Kassio Borges* - -* Currently, Active Record rescues any errors raised within - `after_rollback`/`after_create` callbacks and prints them to the logs. - Future versions of Rails will not rescue these errors anymore and - just bubble them up like the other callbacks. - - This commit adds an opt-in flag to enable not rescuing the errors. - - Example: - - # Do not swallow errors in after_commit/after_rollback callbacks. - config.active_record.raise_in_transactional_callbacks = true - - Fixes #13460. - - *arthurnn* - -* Fix an issue where custom accessor methods (such as those generated by - `enum`) with the same name as a global method are incorrectly overridden - when subclassing. - - Fixes #16288. - - *Godfrey Chan* - -* `*_was` and `changes` now work correctly for in-place attribute changes as - well. - - *Sean Griffin* - -* Fix regression on `after_commit` that did not fire with nested transactions. - - Fixes #16425. - - *arthurnn* - -* Do not try to write timestamps when a table has no timestamps columns. - - Fixes #8813. - - *Sergey Potapov* - -* `index_exists?` with `:name` option does verify specified columns. - - Example: - - add_index :articles, :title, name: "idx_title" - - # Before: - index_exists? :articles, :title, name: "idx_title" # => `true` - index_exists? :articles, :body, name: "idx_title" # => `true` - - # After: - index_exists? :articles, :title, name: "idx_title" # => `true` - index_exists? :articles, :body, name: "idx_title" # => `false` - - *Yves Senn*, *Matthew Draper* - -* When calling `update_columns` on a record that is not persisted, the error - message now reflects whether that object is a new record or has been - destroyed. - - *Lachlan Sylvester* - -* Define `id_was` to get the previous value of the primary key. - - Currently when we call `id_was` and we have a custom primary key name, - Active Record will return the current value of the primary key. This - makes it impossible to correctly do an update operation if you change the - id. - - Fixes #16413. - - *Rafael Mendonça França* - -* Deprecate `DatabaseTasks.load_schema` to act on the current connection. - Use `.load_schema_current` instead. In the future `load_schema` will - require the `configuration` to act on as an argument. - - *Yves Senn* - -* Fix automatic maintaining test schema to properly handle sql structure - schema format. - - Fixes #15394. - - *Wojciech Wnętrzak* - -* Fix type casting to Decimal from Float with large precision. - - *Tomohiro Hashidate* - -* Deprecate `Reflection#source_macro` - - `Reflection#source_macro` is no longer needed in Active Record - source so it has been deprecated. Code that used `source_macro` - was removed in #16353. - - *Eileen M. Uchtitelle*, *Aaron Patterson* - -* No verbose backtrace by `db:drop` when database does not exist. - - Fixes #16295. - - *Kenn Ejima* - -* Add support for PostgreSQL JSONB. - - Example: - - create_table :posts do |t| - t.jsonb :meta_data - end - - *Philippe Creux*, *Chris Teague* - -* `db:purge` with MySQL respects `Rails.env`. - - *Yves Senn* - -* `change_column_default :table, :column, nil` with PostgreSQL will issue a - `DROP DEFAULT` instead of a `DEFAULT NULL` query. - - Fixes #16261. - - *Matthew Draper*, *Yves Senn* - -* Allow to specify a type for the foreign key column in `references` - and `add_reference`. - - Example: - - change_table :vehicle do |t| - t.references :station, type: :uuid - end - - *Andrey Novikov*, *Łukasz Sarnacki* - -* `create_join_table` removes a common prefix when generating the join table. - This matches the existing behavior of HABTM associations. - - Fixes #13683. - - *Stefan Kanev* - -* Do not swallow errors on `compute_type` when having a bad `alias_method` on - a class. - - *arthurnn* - -* PostgreSQL invalid `uuid` are convert to nil. - - *Abdelkader Boudih* - -* Restore 4.0 behavior for using serialize attributes with `JSON` as coder. - - With 4.1.x, `serialize` started returning a string when `JSON` was passed as - the second attribute. It will now return a hash as per previous versions. - - Example: - - class Post < ActiveRecord::Base - serialize :comment, JSON - end - - class Comment - include ActiveModel::Model - attr_accessor :category, :text - end - - post = Post.create! - post.comment = Comment.new(category: "Animals", text: "This is a comment about squirrels.") - post.save! - - # 4.0 - post.comment # => {"category"=>"Animals", "text"=>"This is a comment about squirrels."} - - # 4.1 before - post.comment # => "#" - - # 4.1 after - post.comment # => {"category"=>"Animals", "text"=>"This is a comment about squirrels."} - - When using `JSON` as the coder in `serialize`, Active Record will use the - new `ActiveRecord::Coders::JSON` coder which delegates its `dump/load` to - `ActiveSupport::JSON.encode/decode`. This ensures special objects are dumped - correctly using the `#as_json` hook. - - To keep the previous behaviour, supply a custom coder instead - ([example](https://gist.github.com/jenncoop/8c4142bbe59da77daa63)). - - Fixes #15594. - - *Jenn Cooper* - -* Do not use `RENAME INDEX` syntax for MariaDB 10.0. - - Fixes #15931. - - *Jeff Browning* - -* Calling `#empty?` on a `has_many` association would use the value from the - counter cache if one exists. - - *David Verhasselt* - -* Fix the schema dump generated for tables without constraints and with - primary key with default value of custom PostgreSQL function result. - - Fixes #16111. - - *Andrey Novikov* - -* Fix the SQL generated when a `delete_all` is run on an association to not - produce an `IN` statements. - - Before: - - UPDATE "categorizations" SET "category_id" = NULL WHERE - "categorizations"."category_id" = 1 AND "categorizations"."id" IN (1, 2) - - After: - - UPDATE "categorizations" SET "category_id" = NULL WHERE - "categorizations"."category_id" = 1 - - *Eileen M. Uchitelle, Aaron Patterson* - -* Avoid type casting boolean and `ActiveSupport::Duration` values to numeric - values for string columns. Otherwise, in some database, the string column - values will be coerced to a numeric allowing false or 0.seconds match any - string starting with a non-digit. - - Example: - - App.where(apikey: false) # => SELECT * FROM users WHERE apikey = '0' - - *Dylan Thacker-Smith* - -* Add a `:required` option to singular associations, providing a nicer - API for presence validations on associations. - - *Sean Griffin* - -* Fix an error in `reset_counters` when associations have `select` scope. - (Call to `count` generated invalid SQL.) - - *Cade Truitt* - -* After a successful `reload`, `new_record?` is always false. - - Fixes #12101. - - *Matthew Draper* - -* PostgreSQL renaming table doesn't attempt to rename non existent sequences. - - *Abdelkader Boudih* - -* Move 'dependent: :destroy' handling for `belongs_to` - from `before_destroy` to `after_destroy` callback chain - - Fixes #12380. - - *Ivan Antropov* - -* Detect in-place modifications on String attributes. - - Before this change, an attribute modified in-place had to be marked as - changed in order for it to be persisted in the database. Now it is no longer - required. - - Before: - - user = User.first - user.name << ' Griffin' - user.name_will_change! - user.save - user.reload.name # => "Sean Griffin" - - After: - - user = User.first - user.name << ' Griffin' - user.save - user.reload.name # => "Sean Griffin" - - *Sean Griffin* - -* Add `ActiveRecord::Base#validate!` that raises `RecordInvalid` if the record - is invalid. - - *Bogdan Gusiev*, *Marc Schütz* - -* Support for adding and removing foreign keys. Foreign keys are now - a part of `schema.rb`. This is supported by Mysql2Adapter, MysqlAdapter - and PostgreSQLAdapter. - - Many thanks to *Matthew Higgins* for laying the foundation with his work on - [foreigner](https://github.com/matthuhiggins/foreigner). - - Example: - - # within your migrations: - add_foreign_key :articles, :authors - remove_foreign_key :articles, :authors - - *Yves Senn* - -* Fix subtle bugs regarding attribute assignment on models with no primary - key. `'id'` will no longer be part of the attributes hash. - - *Sean Griffin* - -* Deprecate automatic counter caches on `has_many :through`. The behavior was - broken and inconsistent. - - *Sean Griffin* - -* `preload` preserves readonly flag for associations. - - See #15853. - - *Yves Senn* - -* Assume numeric types have changed if they were assigned to a value that - would fail numericality validation, regardless of the old value. Previously - this would only occur if the old value was 0. - - Example: - - model = Model.create!(number: 5) - model.number = '5wibble' - model.number_changed? # => true - - Fixes #14731. - - *Sean Griffin* - -* `reload` no longer merges with the existing attributes. - The attribute hash is fully replaced. The record is put into the same state - as it would be with `Model.find(model.id)`. - - *Sean Griffin* - -* The object returned from `select_all` must respond to `column_types`. - If this is not the case a `NoMethodError` is raised. - - *Sean Griffin* - -* Detect in-place modifications of PG array types - - *Sean Griffin* - -* Add `bin/rake db:purge` task to empty the current database. - - *Yves Senn* - -* Deprecate `serialized_attributes` without replacement. - - *Sean Griffin* - -* Correctly extract IPv6 addresses from `DATABASE_URI`: the square brackets - are part of the URI structure, not the actual host. - - Fixes #15705. - - *Andy Bakun*, *Aaron Stone* - -* Ensure both parent IDs are set on join records when both sides of a - through association are new. - - *Sean Griffin* - -* `ActiveRecord::Dirty` now detects in-place changes to mutable values. - Serialized attributes on ActiveRecord models will no longer save when - unchanged. - - Fixes #8328. - - *Sean Griffin* - -* `Pluck` now works when selecting columns from different tables with the same - name. - - Fixes #15649. - - *Sean Griffin* - -* Remove `cache_attributes` and friends. All attributes are cached. - - *Sean Griffin* - -* Remove deprecated method `ActiveRecord::Base.quoted_locking_column`. - - *Akshay Vishnoi* - -* `ActiveRecord::FinderMethods.find` with block can handle proc parameter as - `Enumerable#find` does. - - Fixes #15382. - - *James Yang* - -* Make timezone aware attributes work with PostgreSQL array columns. - - Fixes #13402. - - *Kuldeep Aggarwal*, *Sean Griffin* - -* `ActiveRecord::SchemaMigration` has no primary key regardless of the - `primary_key_prefix_type` configuration. - - Fixes #15051. - - *JoseLuis Torres*, *Yves Senn* - -* `rake db:migrate:status` works with legacy migration numbers like `00018_xyz.rb`. - - Fixes #15538. - - *Yves Senn* - -* Baseclass becomes! subclass. - - Before this change, a record which changed its STI type, could not be - updated. - - Fixes #14785. - - *Matthew Draper*, *Earl St Sauver*, *Edo Balvers* - -* Remove deprecated `ActiveRecord::Migrator.proper_table_name`. Use the - `proper_table_name` instance method on `ActiveRecord::Migration` instead. - - *Akshay Vishnoi* - -* Fix regression on eager loading association based on SQL query rather than - existing column. - - Fixes #15480. - - *Lauro Caetano*, *Carlos Antonio da Silva* - -* Deprecate returning `nil` from `column_for_attribute` when no column exists. - It will return a null object in Rails 5.0 - - *Sean Griffin* - -* Implemented `ActiveRecord::Base#pretty_print` to work with PP. - - *Ethan* - -* Preserve type when dumping PostgreSQL point, bit, bit varying and money - columns. - - *Yves Senn* - -* New records remain new after YAML serialization. - - *Sean Griffin* - -* PostgreSQL support default values for enum types. Fixes #7814. - - *Yves Senn* - -* PostgreSQL `default_sequence_name` respects schema. Fixes #7516. - - *Yves Senn* - -* Fix `columns_for_distinct` of PostgreSQL adapter to work correctly - with orders without sort direction modifiers. - - *Nikolay Kondratyev* - -* PostgreSQL `reset_pk_sequence!` respects schemas. Fixes #14719. - - *Yves Senn* - -* Keep PostgreSQL `hstore` and `json` attributes as `Hash` in `@attributes`. - Fixes duplication in combination with `store_accessor`. - - Fixes #15369. - - *Yves Senn* - -* `rake railties:install:migrations` respects the order of railties. - - *Arun Agrawal* - -* Fix redefine a `has_and_belongs_to_many` inside inherited class - Fixing regression case, where redefining the same `has_and_belongs_to_many` - definition into a subclass would raise. - - Fixes #14983. - - *arthurnn* - -* Fix `has_and_belongs_to_many` public reflection. - When defining a `has_and_belongs_to_many`, internally we convert that to two has_many. - But as `reflections` is a public API, people expect to see the right macro. - - Fixes #14682. - - *arthurnn* - -* Fix serialization for records with an attribute named `format`. - - Fixes #15188. - - *Godfrey Chan* - -* When a `group` is set, `sum`, `size`, `average`, `minimum` and `maximum` - on a NullRelation should return a Hash. - - *Kuldeep Aggarwal* - -* Fix serialized fields returning serialized data after being updated with - `update_column`. - - *Simon Hørup Eskildsen* - -* Fix polymorphic eager loading when using a String as foreign key. - - Fixes #14734. - - *Lauro Caetano* - -* Change belongs_to touch to be consistent with timestamp updates - - If a model is set up with a belongs_to: touch relationship the parent - record will only be touched if the record was modified. This makes it - consistent with timestamp updating on the record itself. - - *Brock Trappitt* - -* Fix the inferred table name of a `has_and_belongs_to_many` auxiliary - table inside a schema. - - Fixes #14824. - - *Eric Chahin* - -* Remove unused `:timestamp` type. Transparently alias it to `:datetime` - in all cases. Fixes inconsistencies when column types are sent outside of - `ActiveRecord`, such as for XML Serialization. - - *Sean Griffin* - -* Fix bug that added `table_name_prefix` and `table_name_suffix` to - extension names in PostgreSQL when migrating. - - *Joao Carlos* - -* The `:index` option in migrations, which previously was only available for - `references`, now works with any column types. - - *Marc Schütz* - -* Add support for counter name to be passed as parameter on `CounterCache::ClassMethods#reset_counters`. - - *jnormore* - -* Restrict deletion of record when using `delete_all` with `uniq`, `group`, `having` - or `offset`. - - In these cases the generated query ignored them and that caused unintended - records to be deleted. - - Fixes #11985. - - *Leandro Facchinetti* - -* Floats with limit >= 25 that get turned into doubles in MySQL no longer have - their limit dropped from the schema. - - Fixes #14135. - - *Aaron Nelson* - -* Fix how to calculate associated class name when using namespaced `has_and_belongs_to_many` - association. - - Fixes #14709. - - *Kassio Borges* - -* `ActiveRecord::Relation::Merger#filter_binds` now compares equivalent symbols and - strings in column names as equal. - - This fixes a rare case in which more bind values are passed than there are - placeholders for them in the generated SQL statement, which can make PostgreSQL - throw a `StatementInvalid` exception. - - *Nat Budin* - -* Fix `stored_attributes` to correctly merge the details of stored - attributes defined in parent classes. - - Fixes #14672. - - *Brad Bennett*, *Jessica Yao*, *Lakshmi Parthasarathy* - -* `change_column_default` allows `[]` as argument to `change_column_default`. - - Fixes #11586. - - *Yves Senn* - -* Handle `name` and `"char"` column types in the PostgreSQL adapter. - - `name` and `"char"` are special character types used internally by - PostgreSQL and are used by internal system catalogs. These field types - can sometimes show up in structure-sniffing queries that feature internal system - structures or with certain PostgreSQL extensions. - - *J Smith*, *Yves Senn* - -* Fix `PostgreSQLAdapter::OID::Float#type_cast` to convert Infinity and - NaN PostgreSQL values into a native Ruby `Float::INFINITY` and `Float::NAN` - - Before: - - Point.create(value: 1.0/0) - Point.last.value # => 0.0 - - After: - - Point.create(value: 1.0/0) - Point.last.value # => Infinity - - *Innokenty Mikhailov* - -* Allow the PostgreSQL adapter to handle bigserial primary key types again. - - Fixes #10410. - - *Patrick Robertson* - -* Deprecate joining, eager loading and preloading of instance dependent - associations without replacement. These operations happen before instances - are created. The current behavior is unexpected and can result in broken - behavior. - - Fixes #15024. - - *Yves Senn* - -* Fix `has_and_belongs_to_many` CollectionAssociation size calculations. - - `has_and_belongs_to_many` should fall back to using the normal CollectionAssociation's - size calculation if the collection is not cached or loaded. - - Fixes #14913, #14914. - - *Fred Wu* - -* Return a non zero status when running `rake db:migrate:status` and migration table does - not exist. - - *Paul B.* - -* Add support for module-level `table_name_suffix` in models. - - This makes `table_name_suffix` work the same way as `table_name_prefix` when - using namespaced models. - - *Jenner LaFave* - -* Revert the behaviour of `ActiveRecord::Relation#join` changed through 4.0 => 4.1 to 4.0. - - In 4.1.0 `Relation#join` is delegated to `Arel#SelectManager`. - In 4.0 series it is delegated to `Array#join`. - - *Bogdan Gusiev* - -* Log nil binary column values correctly. - - When an object with a binary column is updated with a nil value - in that column, the SQL logger would throw an exception when trying - to log that nil value. This only occurs when updating a record - that already has a non-nil value in that column since an initial nil - value isn't included in the SQL anyway (at least, when dirty checking - is enabled.) The column's new value will now be logged as `` - to parallel the existing `` for non-nil values. - - *James Coleman* - -* Rails will now pass a custom validation context through to autosave associations - in order to validate child associations with the same context. - - Fixes #13854. - - *Eric Chahin*, *Aaron Nelson*, *Kevin Casey* - -* Stringify all variables keys of MySQL connection configuration. - - When `sql_mode` variable for MySQL adapters set in configuration as `String` - was ignored and overwritten by strict mode option. - - Fixes #14895. - - *Paul Nikitochkin* - -* Ensure SQLite3 statements are closed on errors. - - Fixes #13631. - - *Timur Alperovich* - -* Give `ActiveRecord::PredicateBuilder` private methods the privacy they deserve. - - *Hector Satre* - -* When using a custom `join_table` name on a `habtm`, rails was not saving it - on Reflections. This causes a problem when rails loads fixtures, because it - uses the reflections to set database with fixtures. - - Fixes #14845. - - *Kassio Borges* - -* Reset the cache when modifying a Relation with cached Arel. - Additionally display a warning message to make the user aware. - - *Yves Senn* - -* PostgreSQL should internally use `:datetime` consistently for TimeStamp. Assures - different spellings of timestamps are treated the same. - - Example: - - mytimestamp.simplified_type('timestamp without time zone') - # => :datetime - mytimestamp.simplified_type('timestamp(6) without time zone') - # => also :datetime (previously would be :timestamp) - - See #14513. - - *Jefferson Lai* - -* `ActiveRecord::Base.no_touching` no longer triggers callbacks or start empty transactions. - - Fixes #14841. - - *Lucas Mazza* - -* Fix name collision with `Array#select!` with `Relation#select!`. - - Fixes #14752. - - *Earl St Sauver* - -* Fix unexpected behavior for `has_many :through` associations going through - a scoped `has_many`. - - If a `has_many` association is adjusted using a scope, and another - `has_many :through` uses this association, then the scope adjustment is - unexpectedly neglected. - - Fixes #14537. - - *Jan Habermann* - -* `@destroyed` should always be set to `false` when an object is duped. - - *Kuldeep Aggarwal* - -* Enable `has_many` associations to support irregular inflections. - - Fixes #8928. - - *arthurnn*, *Javier Goizueta* - -* Fix `count` used with a grouping not returning a Hash. - - Fixes #14721. - - *Eric Chahin* - -* `sanitize_sql_like` helper method to escape a string for safe use in an SQL - LIKE statement. - - Example: - - class Article - def self.search(term) - where("title LIKE ?", sanitize_sql_like(term)) - end - end - - Article.search("20% _reduction_") - # => Query looks like "... title LIKE '20\% \_reduction\_' ..." - - *Rob Gilson*, *Yves Senn* - -* Do not quote uuid default value on `change_column`. - - Fixes #14604. - - *Eric Chahin* - -* The comparison between `Relation` and `CollectionProxy` should be consistent. - - Example: - - author.posts == Post.where(author_id: author.id) - # => true - Post.where(author_id: author.id) == author.posts - # => true - - Fixes #13506. - - *Lauro Caetano* - -* Calling `delete_all` on an unloaded `CollectionProxy` no longer - generates an SQL statement containing each id of the collection: - - Before: - - DELETE FROM `model` WHERE `model`.`parent_id` = 1 - AND `model`.`id` IN (1, 2, 3...) - - After: - - DELETE FROM `model` WHERE `model`.`parent_id` = 1 - - *Eileen M. Uchitelle*, *Aaron Patterson* - -* Fix invalid SQL when aggregate methods (`empty?`, `any?`, `count`) used - with `select`. - - Fixes #13648. - - *Simon Woker* - -* PostgreSQL adapter only warns once for every missing OID per connection. - - Fixes #14275. - - *Matthew Draper*, *Yves Senn* - -* PostgreSQL adapter automatically reloads it's type map when encountering - unknown OIDs. - - Fixes #14678. - - *Matthew Draper*, *Yves Senn* - -* Fix insertion of records via `has_many :through` association with scope. - - Fixes #3548. - - *Ivan Antropov* - -* Auto-generate stable fixture UUIDs on PostgreSQL. - - Fixes #11524. - - *Roderick van Domburg* - -* Fix a problem where an enum would overwrite values of another enum with the - same name in an unrelated class. - - Fixes #14607. - - *Evan Whalen* - -* PostgreSQL and SQLite string columns no longer have a default limit of 255. - - Fixes #13435, #9153. - - *Vladimir Sazhin*, *Toms Mikoss*, *Yves Senn* - -* Make possible to have an association called `records`. - - Fixes #11645. - - *prathamesh-sonpatki* - -* `to_sql` on an association now matches the query that is actually executed, where it - could previously have incorrectly accrued additional conditions (e.g. as a result of - a previous query). `CollectionProxy` now always defers to the association scope's - `arel` method so the (incorrect) inherited one should be entirely concealed. - - Fixes #14003. - - *Jefferson Lai* - -* Block a few default Class methods as scope name. - - For instance, this will raise: - - scope :public, -> { where(status: 1) } - - *arthurnn* - -* Fix error when using `with_options` with lambda. - - Fixes #9805. - - *Lauro Caetano* - -* Switch `sqlite3:///` URLs (which were temporarily - deprecated in 4.1) from relative to absolute. - - If you still want the previous interpretation, you should replace - `sqlite3:///my/path` with `sqlite3:my/path`. - - *Matthew Draper* - -* Treat blank UUID values as `nil`. - - Example: - - Sample.new(uuid_field: '') #=> - - *Dmitry Lavrov* - -* Enable support for materialized views on PostgreSQL >= 9.3. - - *Dave Lee* - -* The PostgreSQL adapter supports custom domains. Fixes #14305. - - *Yves Senn* - -* PostgreSQL `Column#type` is now determined through the corresponding OID. - The column types stay the same except for enum columns. They no longer have - `nil` as type but `enum`. - - See #7814. - - *Yves Senn* - -* Fix error when specifying a non-empty default value on a PostgreSQL array - column. - - Fixes #10613. - - *Luke Steensen* - -* Fix error where `.persisted?` throws SystemStackError for an unsaved model with a - custom primary key that did not save due to validation error. - - Fixes #14393. - - *Chris Finne* - -* Introduce `validate` as an alias for `valid?`. - - This is more intuitive when you want to run validations but don't care about the return value. - - *Henrik Nyh* - -* Create indexes inline in CREATE TABLE for MySQL. - - This is important, because adding an index on a temporary table after it has been created - would commit the transaction. - - It also allows creating and dropping indexed tables with fewer queries and fewer permissions - required. - - Example: - - create_table :temp, temporary: true, as: "SELECT id, name, zip FROM a_really_complicated_query" do |t| - t.index :zip - end - # => CREATE TEMPORARY TABLE temp (INDEX (zip)) AS SELECT id, name, zip FROM a_really_complicated_query - - *Cody Cutrer*, *Steve Rice*, *Rafael Mendonça Franca* - -* Use singular table name in generated migrations when - `ActiveRecord::Base.pluralize_table_names` is `false`. - - Fixes #13426. - - *Kuldeep Aggarwal* - -* `touch` accepts many attributes to be touched at once. - - Example: - - # touches :signed_at, :sealed_at, and :updated_at/on attributes. - Photo.last.touch(:signed_at, :sealed_at) - - *James Pinto* - -* `rake db:structure:dump` only dumps schema information if the schema - migration table exists. - - Fixes #14217. - - *Yves Senn* - -* Reap connections that were checked out by now-dead threads, instead - of waiting until they disconnect by themselves. Before this change, - a suitably constructed series of short-lived threads could starve - the connection pool, without ever having more than a couple alive at - the same time. - - *Matthew Draper* - -* `pk_and_sequence_for` now ensures that only the pg_depend entries - pointing to pg_class, and thus only sequence objects, are considered. - - *Josh Williams* - -* `where.not` adds `references` for `includes` like normal `where` calls do. - - Fixes #14406. - - *Yves Senn* - -* Extend fixture `$LABEL` replacement to allow string interpolation. - - Example: - - martin: - email: $LABEL@email.com - - users(:martin).email # => martin@email.com - - *Eric Steele* - -* Add support for `Relation` be passed as parameter on `QueryCache#select_all`. - - Fixes #14361. - - *arthurnn* - -* Passing an Active Record object to `find` or `exists?` is now deprecated. - Call `.id` on the object first. - - *Aaron Patterson* - -* Only use BINARY for MySQL case sensitive uniqueness check when column - has a case insensitive collation. - - *Ryuta Kamizono* - -* Support for MySQL 5.6 fractional seconds. - - *arthurnn*, *Tatsuhiko Miyagawa* - -* Support for PostgreSQL `citext` data type enabling case-insensitive - `where` values without needing to wrap in UPPER/LOWER sql functions. - - *Troy Kruthoff*, *Lachlan Sylvester* - -* Only save has_one associations if record has changes. - Previously after save related callbacks, such as `#after_commit`, were triggered when the has_one - object did not get saved to the db. - - *Alan Kennedy* - -* Allow strings to specify the `#order` value. - - Example: - - Model.order(id: 'asc').to_sql == Model.order(id: :asc).to_sql - - *Marcelo Casiraghi*, *Robin Dupret* - -* Dynamically register PostgreSQL enum OIDs. This prevents "unknown OID" - warnings on enum columns. - - *Dieter Komendera* - -* `includes` is able to detect the right preloading strategy when string - joins are involved. - - Fixes #14109. - - *Aaron Patterson*, *Yves Senn* - -* Fix error with validation with enum fields for records where the value for - any enum attribute is always evaluated as 0 during uniqueness validation. - - Fixes #14172. - - *Vilius Luneckas* *Ahmed AbouElhamayed* - -* `before_add` callbacks are fired before the record is saved on - `has_and_belongs_to_many` associations *and* on `has_many :through` - associations. Before this change, `before_add` callbacks would be fired - before the record was saved on `has_and_belongs_to_many` associations, but - *not* on `has_many :through` associations. - - Fixes #14144. - -* Fix STI classes not defining an attribute method if there is a conflicting - private method defined on its ancestors. - - Fixes #11569. - - *Godfrey Chan* - -* Coerce strings when reading attributes. Fixes #10485. - - Example: - - book = Book.new(title: 12345) - book.save! - book.title # => "12345" - - *Yves Senn* - -* Deprecate half-baked support for PostgreSQL range values with excluding beginnings. - We currently map PostgreSQL ranges to Ruby ranges. This conversion is not fully - possible because the Ruby range does not support excluded beginnings. - - The current solution of incrementing the beginning is not correct and is now - deprecated. For subtypes where we don't know how to increment (e.g. `#succ` - is not defined) it will raise an `ArgumentException` for ranges with excluding - beginnings. - - *Yves Senn* - -* Support for user created range types in PostgreSQL. - - *Yves Senn* - -Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activerecord/CHANGELOG.md) for previous changes. +Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/activerecord/CHANGELOG.md) for previous changes. diff --git a/activerecord/lib/active_record/gem_version.rb b/activerecord/lib/active_record/gem_version.rb index e820835626..a388b529c9 100644 --- a/activerecord/lib/active_record/gem_version.rb +++ b/activerecord/lib/active_record/gem_version.rb @@ -5,10 +5,10 @@ module ActiveRecord end module VERSION - MAJOR = 4 - MINOR = 2 + MAJOR = 5 + MINOR = 0 TINY = 0 - PRE = "beta4" + PRE = "alpha" STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".") end -- cgit v1.2.3 From 96d0f751f97c7eebc63d02b5dab88eee3aa3a921 Mon Sep 17 00:00:00 2001 From: claudiob Date: Fri, 28 Nov 2014 18:04:19 -0800 Subject: Bump required Ruby version to 2.1.0 [This article](http://weblog.rubyonrails.org/2014/8/20/Rails-4-2-beta1/#maintenance-consequences-and-rails-5-0) states that: > Rails 5.0 is in most likelihood going to target Ruby 2.2. Before the exact minimum version is fully decided, @arthurnn [suggests](https://github.com/rails/rails/pull/17830#issuecomment-64940383) that **at least** version 2.1.0 **must** be required by the `gemspec` files. --- activerecord/activerecord.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 834fffeb18..471769a962 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -7,7 +7,7 @@ Gem::Specification.new do |s| s.summary = 'Object-relational mapper framework (part of Rails).' s.description = 'Databases on Rails. Build a persistent domain model by mapping database tables to Ruby classes. Strong conventions for associations, validations, aggregations, migrations, and testing come baked-in.' - s.required_ruby_version = '>= 1.9.3' + s.required_ruby_version = '>= 2.1.0' s.license = 'MIT' -- cgit v1.2.3 From d1374f99bf3090f3e659687c112ed0c5c0865cfb Mon Sep 17 00:00:00 2001 From: Erik Michaels-Ober Date: Mon, 27 Oct 2014 17:28:53 +0100 Subject: Pass symbol as an argument instead of a block --- activerecord/examples/performance.rb | 4 +- activerecord/lib/active_record/associations.rb | 2 +- .../associations/collection_association.rb | 10 ++-- .../associations/has_many_through_association.rb | 4 +- .../active_record/associations/join_dependency.rb | 2 +- .../associations/preloader/has_many_through.rb | 2 +- .../lib/active_record/attribute_assignment.rb | 2 +- .../lib/active_record/autosave_association.rb | 6 +- .../abstract/connection_pool.rb | 4 +- .../abstract/schema_statements.rb | 2 +- .../connection_adapters/abstract_mysql_adapter.rb | 4 +- .../connection_adapters/mysql_adapter.rb | 4 +- .../connection_adapters/schema_cache.rb | 4 +- .../connection_adapters/sqlite3_adapter.rb | 10 ++-- activerecord/lib/active_record/fixtures.rb | 6 +- activerecord/lib/active_record/inheritance.rb | 2 +- activerecord/lib/active_record/migration.rb | 2 +- activerecord/lib/active_record/model_schema.rb | 2 +- .../lib/active_record/railties/databases.rake | 2 +- .../lib/active_record/readonly_attributes.rb | 2 +- activerecord/lib/active_record/reflection.rb | 2 +- activerecord/lib/active_record/relation.rb | 8 +-- .../lib/active_record/relation/finder_methods.rb | 2 +- activerecord/lib/active_record/serialization.rb | 2 +- activerecord/lib/active_record/statement_cache.rb | 2 +- .../lib/active_record/validations/presence.rb | 2 +- .../test/cases/adapters/mysql/connection_test.rb | 4 +- .../cases/adapters/mysql/reserved_word_test.rb | 4 +- .../test/cases/adapters/mysql/schema_test.rb | 2 +- .../test/cases/adapters/mysql2/connection_test.rb | 4 +- .../cases/adapters/mysql2/reserved_word_test.rb | 4 +- .../test/cases/adapters/mysql2/schema_test.rb | 2 +- .../cases/adapters/postgresql/connection_test.rb | 4 +- .../test/cases/adapters/postgresql/schema_test.rb | 2 +- .../cases/adapters/sqlite3/sqlite3_adapter_test.rb | 8 +-- .../associations/eager_load_nested_include_test.rb | 4 +- activerecord/test/cases/associations/eager_test.rb | 20 +++---- .../has_and_belongs_to_many_associations_test.rb | 2 +- .../associations/has_many_associations_test.rb | 2 +- .../has_many_through_associations_test.rb | 2 +- .../associations/inner_join_association_test.rb | 2 +- .../test/cases/associations/join_model_test.rb | 6 +- activerecord/test/cases/associations_test.rb | 2 +- .../test/cases/autosave_association_test.rb | 22 ++++---- activerecord/test/cases/finder_test.rb | 8 +-- .../cases/migration/column_positioning_test.rb | 12 ++-- activerecord/test/cases/migrator_test.rb | 4 +- activerecord/test/cases/nested_attributes_test.rb | 12 ++-- activerecord/test/cases/persistence_test.rb | 4 +- activerecord/test/cases/pooled_connections_test.rb | 2 +- activerecord/test/cases/reflection_test.rb | 4 +- activerecord/test/cases/relations_test.rb | 8 +-- .../test/cases/scoping/default_scoping_test.rb | 64 +++++++++++----------- activerecord/test/cases/test_case.rb | 2 +- activerecord/test/cases/transactions_test.rb | 6 +- .../test/cases/validations_repair_helper.rb | 8 +-- activerecord/test/models/customer.rb | 2 +- activerecord/test/models/pirate.rb | 4 +- activerecord/test/models/ship.rb | 2 +- 59 files changed, 156 insertions(+), 176 deletions(-) (limited to 'activerecord') diff --git a/activerecord/examples/performance.rb b/activerecord/examples/performance.rb index d3546ce948..a5a1f284a0 100644 --- a/activerecord/examples/performance.rb +++ b/activerecord/examples/performance.rb @@ -39,8 +39,8 @@ class Exhibit < ActiveRecord::Base where("notes IS NOT NULL") end - def self.look(exhibits) exhibits.each { |e| e.look } end - def self.feel(exhibits) exhibits.each { |e| e.feel } end + def self.look(exhibits) exhibits.each(&:look) end + def self.feel(exhibits) exhibits.each(&:feel) end end def progress_bar(int); print "." if (int%100).zero? ; end diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d2bcdc55bf..eb21712f96 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -57,7 +57,7 @@ module ActiveRecord through_reflection = reflection.through_reflection source_reflection_names = reflection.source_reflection_names source_associations = reflection.through_reflection.klass._reflections.keys - super("Could not find the source association(s) #{source_reflection_names.collect{ |a| a.inspect }.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => '. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?") + super("Could not find the source association(s) #{source_reflection_names.collect(&:inspect).to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)} in model #{through_reflection.klass}. Try 'has_many #{reflection.name.inspect}, :through => #{through_reflection.name.inspect}, :source => '. Is it one of #{source_associations.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ', :locale => :en)}?") end end diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 2b7d39893d..7b6aefe345 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -62,9 +62,9 @@ module ActiveRecord # Implements the ids writer method, e.g. foo.item_ids= for Foo.has_many :items def ids_writer(ids) pk_type = reflection.primary_key_type - ids = Array(ids).reject { |id| id.blank? } + ids = Array(ids).reject(&:blank?) ids.map! { |i| pk_type.type_cast_from_user(i) } - replace(klass.find(ids).index_by { |r| r.id }.values_at(*ids)) + replace(klass.find(ids).index_by(&:id).values_at(*ids)) end def reset @@ -289,7 +289,7 @@ module ActiveRecord elsif !loaded? && !association_scope.group_values.empty? load_target.size elsif !loaded? && !association_scope.distinct_value && target.is_a?(Array) - unsaved_records = target.select { |r| r.new_record? } + unsaved_records = target.select(&:new_record?) unsaved_records.size + count_records else count_records @@ -506,7 +506,7 @@ module ActiveRecord def delete_or_destroy(records, method) records = records.flatten records.each { |record| raise_on_type_mismatch!(record) } - existing_records = records.reject { |r| r.new_record? } + existing_records = records.reject(&:new_record?) if existing_records.empty? remove_records(existing_records, records, method) @@ -609,7 +609,7 @@ module ActiveRecord # specified, then #find scans the entire collection. def find_by_scan(*args) expects_array = args.first.kind_of?(Array) - ids = args.flatten.compact.map{ |arg| arg.to_s }.uniq + ids = args.flatten.compact.map(&:to_s).uniq if ids.size == 1 id = ids.first 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 6329fdfe95..5cfedcc009 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -161,9 +161,7 @@ module ActiveRecord if scope.klass.primary_key count = scope.destroy_all.length else - scope.each do |record| - record._run_destroy_callbacks - end + scope.each(&:_run_destroy_callbacks) arel = scope.arel diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index 285d0ec9af..cf63430a97 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -20,7 +20,7 @@ module ActiveRecord end def columns - @tables.flat_map { |t| t.column_aliases } + @tables.flat_map(&:column_aliases) end # An array of [column_name, alias] pairs for the table diff --git a/activerecord/lib/active_record/associations/preloader/has_many_through.rb b/activerecord/lib/active_record/associations/preloader/has_many_through.rb index 7b37b5942d..2029871f39 100644 --- a/activerecord/lib/active_record/associations/preloader/has_many_through.rb +++ b/activerecord/lib/active_record/associations/preloader/has_many_through.rb @@ -8,7 +8,7 @@ module ActiveRecord records_by_owner = super if reflection_scope.distinct_value - records_by_owner.each_value { |records| records.uniq! } + records_by_owner.each_value(&:uniq!) end records_by_owner diff --git a/activerecord/lib/active_record/attribute_assignment.rb b/activerecord/lib/active_record/attribute_assignment.rb index 2887db3bf7..bf64830417 100644 --- a/activerecord/lib/active_record/attribute_assignment.rb +++ b/activerecord/lib/active_record/attribute_assignment.rb @@ -87,7 +87,7 @@ module ActiveRecord end end unless errors.empty? - error_descriptions = errors.map { |ex| ex.message }.join(",") + error_descriptions = errors.map(&:message).join(",") raise MultiparameterAssignmentErrors.new(errors), "#{errors.size} error(s) on assignment of multiparameter attributes [#{error_descriptions}]" end end diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index a0d70435fa..c39b045a5e 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -263,9 +263,9 @@ module ActiveRecord if new_record association && association.target elsif autosave - association.target.find_all { |record| record.changed_for_autosave? } + association.target.find_all(&:changed_for_autosave?) else - association.target.find_all { |record| record.new_record? } + association.target.find_all(&:new_record?) end end @@ -275,7 +275,7 @@ module ActiveRecord self.class._reflections.values.any? do |reflection| if reflection.options[:autosave] association = association_instance_get(reflection.name) - association && Array.wrap(association.target).any? { |a| a.changed_for_autosave? } + association && Array.wrap(association.target).any?(&:changed_for_autosave?) 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 46812b75bb..3a9db4a109 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -320,9 +320,7 @@ module ActiveRecord checkin conn conn.disconnect! if conn.requires_reloading? end - @connections.delete_if do |conn| - conn.requires_reloading? - end + @connections.delete_if(&:requires_reloading?) @available.clear @connections.each do |conn| @available.add conn 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 99fa9b7d29..aa7c406050 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -781,7 +781,7 @@ module ActiveRecord version = version.to_i sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name) - migrated = select_values("SELECT version FROM #{sm_table}").map { |v| v.to_i } + migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i) paths = migrations_paths.map {|p| "#{p}/[0-9]*_*.rb" } versions = Dir[*paths].map do |filename| filename.split('/').last.split('_').first.to_i 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 c824a7b11b..bf16804aa0 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -398,7 +398,7 @@ module ActiveRecord sql << "LIKE #{quote(like)}" if like execute_and_free(sql, 'SCHEMA') do |result| - result.collect { |field| field.first } + result.collect(&:first) end end @@ -786,7 +786,7 @@ module ActiveRecord private def version - @version ||= full_version.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i } + @version ||= full_version.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map(&:to_i) end def mariadb? diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 53ad71b445..23d8389abb 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -423,9 +423,7 @@ module ActiveRecord cols = nil if metadata = stmt.result_metadata - cols = cache[:cols] ||= metadata.fetch_fields.map { |field| - field.name - } + cols = cache[:cols] ||= metadata.fetch_fields.map(&:name) metadata.free end diff --git a/activerecord/lib/active_record/connection_adapters/schema_cache.rb b/activerecord/lib/active_record/connection_adapters/schema_cache.rb index a10ce330c7..37ff4e4613 100644 --- a/activerecord/lib/active_record/connection_adapters/schema_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/schema_cache.rb @@ -61,9 +61,7 @@ module ActiveRecord end def size - [@columns, @columns_hash, @primary_keys, @tables].map { |x| - x.size - }.inject :+ + [@columns, @columns_hash, @primary_keys, @tables].map(&:size).inject :+ end # Clear out internal caches for table with +table_name+. diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 0b8b6a2bf8..c3865a8fdd 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -88,11 +88,11 @@ module ActiveRecord include Comparable def initialize(version_string) - @version = version_string.split('.').map { |v| v.to_i } + @version = version_string.split('.').map(&:to_i) end def <=>(version_string) - @version <=> version_string.split('.').map { |v| v.to_i } + @version <=> version_string.split('.').map(&:to_i) end end @@ -556,7 +556,7 @@ module ActiveRecord end copy_table_indexes(from, to, options[:rename] || {}) copy_table_contents(from, to, - @definition.columns.map {|column| column.name}, + @definition.columns.map(&:name), options[:rename] || {}) end @@ -569,7 +569,7 @@ module ActiveRecord name = name[1..-1] end - to_column_names = columns(to).map { |c| c.name } + to_column_names = columns(to).map(&:name) columns = index.columns.map {|c| rename[c] || c }.select do |column| to_column_names.include?(column) end @@ -586,7 +586,7 @@ module ActiveRecord def copy_table_contents(from, to, columns, rename = {}) #:nodoc: column_mappings = Hash[columns.map {|name| [name, name]}] rename.each { |a| column_mappings[a.last] = a.first } - from_columns = columns(from).collect {|col| col.name} + from_columns = columns(from).collect(&:name) columns = columns.find_all{|col| from_columns.include?(column_mappings[col])} quoted_columns = columns.map { |col| quote_column_name(col) } * ',' diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 7c53ee0a9a..4732462b05 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -745,7 +745,7 @@ module ActiveRecord end def column_names - @column_names ||= @connection.columns(@table_name).collect { |c| c.name } + @column_names ||= @connection.columns(@table_name).collect(&:name) end def read_fixture_files(path, model_class) @@ -868,7 +868,7 @@ module ActiveRecord fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"] fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] } else - fixture_set_names = fixture_set_names.flatten.map { |n| n.to_s } + fixture_set_names = fixture_set_names.flatten.map(&:to_s) end self.fixture_table_names |= fixture_set_names @@ -908,7 +908,7 @@ module ActiveRecord def uses_transaction(*methods) @uses_transaction = [] unless defined?(@uses_transaction) - @uses_transaction.concat methods.map { |m| m.to_s } + @uses_transaction.concat methods.map(&:to_s) end def uses_transaction?(method) diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index f58145ab05..b91e9ac137 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -192,7 +192,7 @@ module ActiveRecord def type_condition(table = arel_table) sti_column = table[inheritance_column] - sti_names = ([self] + descendants).map { |model| model.sti_name } + sti_names = ([self] + descendants).map(&:sti_name) sti_column.in(sti_names) end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 92f2951f2d..3cac465440 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -647,7 +647,7 @@ module ActiveRecord end def method_missing(method, *arguments, &block) - arg_list = arguments.map{ |a| a.inspect } * ', ' + arg_list = arguments.map(&:inspect) * ', ' say_with_time "#{method}(#{arg_list})" do unless @connection.respond_to? :revert diff --git a/activerecord/lib/active_record/model_schema.rb b/activerecord/lib/active_record/model_schema.rb index adad7774b9..92ad9c9101 100644 --- a/activerecord/lib/active_record/model_schema.rb +++ b/activerecord/lib/active_record/model_schema.rb @@ -257,7 +257,7 @@ module ActiveRecord # Returns an array of column names as strings. def column_names - @column_names ||= columns.map { |column| column.name } + @column_names ||= columns.map(&:name) end # Returns an array of column objects where the primary id, all columns ending in "_id" or "_count", diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 21b1c3f721..6c09456c4d 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -366,7 +366,7 @@ namespace :railties do namespace :install do # desc "Copies missing migrations from Railties (e.g. engines). You can specify Railties to use with FROM=railtie1,railtie2" task :migrations => :'db:load_config' do - to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map {|n| n.strip } + to_load = ENV['FROM'].blank? ? :all : ENV['FROM'].split(",").map(&:strip) railties = {} Rails.application.migration_railties.each do |railtie| next unless to_load == :all || to_load.include?(railtie.railtie_name) diff --git a/activerecord/lib/active_record/readonly_attributes.rb b/activerecord/lib/active_record/readonly_attributes.rb index 85bbac43e4..ce78f1756d 100644 --- a/activerecord/lib/active_record/readonly_attributes.rb +++ b/activerecord/lib/active_record/readonly_attributes.rb @@ -11,7 +11,7 @@ module ActiveRecord # Attributes listed as readonly will be used to create a new record but update operations will # ignore these fields. def attr_readonly(*attributes) - self._attr_readonly = Set.new(attributes.map { |a| a.to_s }) + (self._attr_readonly || []) + self._attr_readonly = Set.new(attributes.map(&:to_s)) + (self._attr_readonly || []) end # Returns an array of all the attributes that have been specified as readonly. diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 4219632596..96520d1d49 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -791,7 +791,7 @@ module ActiveRecord def source_reflection_name # :nodoc: return @source_reflection_name if @source_reflection_name - names = [name.to_s.singularize, name].collect { |n| n.to_sym }.uniq + names = [name.to_s.singularize, name].collect(&:to_sym).uniq names = names.find_all { |n| through_reflection.klass._reflect_on_association(n) } diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 561ed222d1..7e1994086f 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -400,7 +400,7 @@ module ActiveRecord if conditions where(conditions).destroy_all else - to_a.each {|object| object.destroy }.tap { reset } + to_a.each(&:destroy).tap { reset } end end @@ -644,7 +644,7 @@ module ActiveRecord preloader.preload @records, associations end - @records.each { |record| record.readonly! } if readonly_value + @records.each(&:readonly!) if readonly_value @loaded = true @records @@ -666,7 +666,7 @@ module ActiveRecord joined_tables += [table.name, table.table_alias] # always convert table names to downcase as in Oracle quoted table names are in uppercase - joined_tables = joined_tables.flatten.compact.map { |t| t.downcase }.uniq + joined_tables = joined_tables.flatten.compact.map(&:downcase).uniq (references_values - joined_tables).any? end @@ -675,7 +675,7 @@ module ActiveRecord return [] if string.blank? # always convert table names to downcase as in Oracle quoted table names are in uppercase # ignore raw_sql_ that is used by Oracle adapter as alias for limit/offset subqueries - string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map{ |s| s.downcase }.uniq - ['raw_sql_'] + string.scan(/([a-zA-Z_][.\w]+).?\./).flatten.map(&:downcase).uniq - ['raw_sql_'] end end end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index eacae73ebb..5061940d82 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -415,7 +415,7 @@ module ActiveRecord end def using_limitable_reflections?(reflections) - reflections.none? { |r| r.collection? } + reflections.none?(&:collection?) end protected diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index bd9079b596..48c12dcf9f 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -11,7 +11,7 @@ module ActiveRecord #:nodoc: def serializable_hash(options = nil) options = options.try(:clone) || {} - options[:except] = Array(options[:except]).map { |n| n.to_s } + options[:except] = Array(options[:except]).map(&:to_s) options[:except] |= Array(self.class.inheritance_column) super(options) diff --git a/activerecord/lib/active_record/statement_cache.rb b/activerecord/lib/active_record/statement_cache.rb index c68990bf99..192a19f05d 100644 --- a/activerecord/lib/active_record/statement_cache.rb +++ b/activerecord/lib/active_record/statement_cache.rb @@ -79,7 +79,7 @@ module ActiveRecord end def bind(values) - bvs = @bind_values.map { |pair| pair.dup } + bvs = @bind_values.map(&:dup) @indexes.each_with_index { |offset,i| bvs[offset][1] = values[i] } bvs end diff --git a/activerecord/lib/active_record/validations/presence.rb b/activerecord/lib/active_record/validations/presence.rb index c7aa814ba8..61b30749d9 100644 --- a/activerecord/lib/active_record/validations/presence.rb +++ b/activerecord/lib/active_record/validations/presence.rb @@ -8,7 +8,7 @@ module ActiveRecord associated_records = Array.wrap(record.send(attribute)) # Superclass validates presence. Ensure present records aren't about to be destroyed. - if associated_records.present? && associated_records.all? { |r| r.marked_for_destruction? } + if associated_records.present? && associated_records.all?(&:marked_for_destruction?) record.errors.add(attribute, :blank, options) end end diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb index 52eebe1886..ce01b16362 100644 --- a/activerecord/test/cases/adapters/mysql/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql/connection_test.rb @@ -47,9 +47,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase assert !@connection.active? # Repair all fixture connections so other tests won't break. - @fixture_connections.each do |c| - c.verify! - end + @fixture_connections.each(&:verify!) end def test_successful_reconnection_after_timeout_with_manual_reconnect diff --git a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb index 61ae0abfd1..403f7cbc74 100644 --- a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb @@ -101,7 +101,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase gs = nil assert_nothing_raised { gs = Select.find(2).groups } assert_equal gs.length, 2 - assert(gs.collect{|x| x.id}.sort == [2, 3]) + assert(gs.collect(&:id).sort == [2, 3]) end # has_and_belongs_to_many with reserved-word table name @@ -110,7 +110,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase s = nil assert_nothing_raised { s = Distinct.find(1).selects } assert_equal s.length, 2 - assert(s.collect{|x|x.id}.sort == [1, 2]) + assert(s.collect(&:id).sort == [1, 2]) end # activerecord model introspection with reserved-word table and column names diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb index 87c5277e64..ab547747df 100644 --- a/activerecord/test/cases/adapters/mysql/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql/schema_test.rb @@ -81,7 +81,7 @@ module ActiveRecord table = 'key_tests' - indexes = @connection.indexes(table).sort_by {|i| i.name} + indexes = @connection.indexes(table).sort_by(&:name) assert_equal 3,indexes.size index_a = indexes.select{|i| i.name == index_a_name}[0] diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb index 62c5abbf41..d261e2db55 100644 --- a/activerecord/test/cases/adapters/mysql2/connection_test.rb +++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb @@ -44,9 +44,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase assert !@connection.active? # Repair all fixture connections so other tests won't break. - @fixture_connections.each do |c| - c.verify! - end + @fixture_connections.each(&:verify!) end def test_successful_reconnection_after_timeout_with_manual_reconnect diff --git a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb index 799d927ee4..7f97b454bb 100644 --- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb +++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb @@ -100,7 +100,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase gs = nil assert_nothing_raised { gs = Select.find(2).groups } assert_equal gs.length, 2 - assert(gs.collect{|x| x.id}.sort == [2, 3]) + assert(gs.collect(&:id).sort == [2, 3]) end # has_and_belongs_to_many with reserved-word table name @@ -109,7 +109,7 @@ class MysqlReservedWordTest < ActiveRecord::TestCase s = nil assert_nothing_raised { s = Distinct.find(1).selects } assert_equal s.length, 2 - assert(s.collect{|x|x.id}.sort == [1, 2]) + assert(s.collect(&:id).sort == [1, 2]) end # activerecord model introspection with reserved-word table and column names diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb index 1b7e60565d..47707b7d4f 100644 --- a/activerecord/test/cases/adapters/mysql2/schema_test.rb +++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb @@ -51,7 +51,7 @@ module ActiveRecord table = 'key_tests' - indexes = @connection.indexes(table).sort_by {|i| i.name} + indexes = @connection.indexes(table).sort_by(&:name) assert_equal 3,indexes.size index_a = indexes.select{|i| i.name == index_a_name}[0] diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 7d179944d4..ab7fd3c6d5 100644 --- a/activerecord/test/cases/adapters/postgresql/connection_test.rb +++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb @@ -177,9 +177,7 @@ module ActiveRecord "successfully querying with the same connection pid." # Repair all fixture connections so other tests won't break. - @fixture_connections.each do |c| - c.verify! - end + @fixture_connections.each(&:verify!) end def test_set_session_variable_true diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 350cb3e065..92c9a13ee3 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -412,7 +412,7 @@ class SchemaTest < ActiveRecord::TestCase def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name, third_index_column_name, fourth_index_column_name) with_schema_search_path(this_schema_name) do - indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name} + indexes = @connection.indexes(TABLE_NAME).sort_by(&:name) assert_equal 4,indexes.size do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name) diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb index d83c65cf0e..9d09ff49c7 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -327,11 +327,11 @@ module ActiveRecord def test_columns with_example_table do - columns = @conn.columns('ex').sort_by { |x| x.name } + columns = @conn.columns('ex').sort_by(&:name) assert_equal 2, columns.length - assert_equal %w{ id number }.sort, columns.map { |x| x.name } - assert_equal [nil, nil], columns.map { |x| x.default } - assert_equal [true, true], columns.map { |x| x.null } + assert_equal %w{ id number }.sort, columns.map(&:name) + assert_equal [nil, nil], columns.map(&:default) + assert_equal [true, true], columns.map(&:null) end end diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index 0ff87d53ea..f571198079 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -70,9 +70,7 @@ class EagerLoadPolyAssocsTest < ActiveRecord::TestCase teardown do [Circle, Square, Triangle, PaintColor, PaintTexture, - ShapeExpression, NonPolyOne, NonPolyTwo].each do |c| - c.delete_all - end + ShapeExpression, NonPolyOne, NonPolyTwo].each(&:delete_all) end def generate_test_object_graphs diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index f968a34886..4321a198ca 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -338,31 +338,31 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_association_loading_with_belongs_to_and_limit comments = Comment.all.merge!(:includes => :post, :limit => 5, :order => 'comments.id').to_a assert_equal 5, comments.length - assert_equal [1,2,3,5,6], comments.collect { |c| c.id } + assert_equal [1,2,3,5,6], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_conditions comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 4', :limit => 3, :order => 'comments.id').to_a assert_equal 3, comments.length - assert_equal [5,6,7], comments.collect { |c| c.id } + assert_equal [5,6,7], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset comments = Comment.all.merge!(:includes => :post, :limit => 3, :offset => 2, :order => 'comments.id').to_a assert_equal 3, comments.length - assert_equal [3,5,6], comments.collect { |c| c.id } + assert_equal [3,5,6], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions comments = Comment.all.merge!(:includes => :post, :where => 'post_id = 4', :limit => 3, :offset => 1, :order => 'comments.id').to_a assert_equal 3, comments.length - assert_equal [6,7,8], comments.collect { |c| c.id } + assert_equal [6,7,8], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_conditions_array comments = Comment.all.merge!(:includes => :post, :where => ['post_id = ?',4], :limit => 3, :offset => 1, :order => 'comments.id').to_a assert_equal 3, comments.length - assert_equal [6,7,8], comments.collect { |c| c.id } + assert_equal [6,7,8], comments.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_conditions_string_with_unquoted_table_name @@ -377,7 +377,7 @@ class EagerAssociationTest < ActiveRecord::TestCase comments = Comment.all.merge!(:includes => :post, :where => {:posts => {:id => 4}}, :limit => 3, :order => 'comments.id').to_a end assert_equal 3, comments.length - assert_equal [5,6,7], comments.collect { |c| c.id } + assert_equal [5,6,7], comments.collect(&:id) assert_no_queries do comments.first.post end @@ -406,13 +406,13 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_association_loading_with_belongs_to_and_limit_and_multiple_associations posts = Post.all.merge!(:includes => [:author, :very_special_comment], :limit => 1, :order => 'posts.id').to_a assert_equal 1, posts.length - assert_equal [1], posts.collect { |p| p.id } + assert_equal [1], posts.collect(&:id) end def test_eager_association_loading_with_belongs_to_and_limit_and_offset_and_multiple_associations posts = Post.all.merge!(:includes => [:author, :very_special_comment], :limit => 1, :offset => 1, :order => 'posts.id').to_a assert_equal 1, posts.length - assert_equal [2], posts.collect { |p| p.id } + assert_equal [2], posts.collect(&:id) end def test_eager_association_loading_with_belongs_to_inferred_foreign_key_from_association_name @@ -545,13 +545,13 @@ class EagerAssociationTest < ActiveRecord::TestCase def test_eager_with_has_many_and_limit_and_conditions posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => "posts.body = 'hello'", :order => "posts.id").to_a assert_equal 2, posts.size - assert_equal [4,5], posts.collect { |p| p.id } + assert_equal [4,5], posts.collect(&:id) end def test_eager_with_has_many_and_limit_and_conditions_array posts = Post.all.merge!(:includes => [ :author, :comments ], :limit => 2, :where => [ "posts.body = ?", 'hello' ], :order => "posts.id").to_a assert_equal 2, posts.size - assert_equal [4,5], posts.collect { |p| p.id } + assert_equal [4,5], posts.collect(&:id) end def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb index 092e85949c..aea9207bfe 100644 --- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb @@ -555,7 +555,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase def test_dynamic_find_all_should_respect_readonly_access projects(:active_record).readonly_developers.each { |d| assert_raise(ActiveRecord::ReadOnlyRecord) { d.save! } if d.valid?} - projects(:active_record).readonly_developers.each { |d| d.readonly? } + projects(:active_record).readonly_developers.each(&:readonly?) end def test_new_with_values_in_collection diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 9510d57de6..c075872617 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1219,7 +1219,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert !clients.empty?, "37signals has clients after load" destroyed = companies(:first_firm).clients_of_firm.destroy_all assert_equal clients.sort_by(&:id), destroyed.sort_by(&:id) - assert destroyed.all? { |client| client.frozen? }, "destroyed clients should be frozen" + assert destroyed.all?(&:frozen?), "destroyed clients should be frozen" assert companies(:first_firm).clients_of_firm.empty?, "37signals has no clients after destroy all" assert companies(:first_firm).clients_of_firm(true).empty?, "37signals has no clients after destroy all and refresh" end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index d523ea4f07..589a232bdb 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -41,7 +41,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase def test_preload_sti_rhs_class developers = Developer.includes(:firms).all.to_a assert_no_queries do - developers.each { |d| d.firms } + developers.each(&:firms) end end diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb index 07cf65a760..9938302885 100644 --- a/activerecord/test/cases/associations/inner_join_association_test.rb +++ b/activerecord/test/cases/associations/inner_join_association_test.rb @@ -54,7 +54,7 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase def test_find_with_implicit_inner_joins_without_select_does_not_imply_readonly authors = Author.joins(:posts) assert_not authors.empty?, "expected authors to be non-empty" - assert authors.none? {|a| a.readonly? }, "expected no authors to be readonly" + assert authors.none?(&:readonly?), "expected no authors to be readonly" end def test_find_with_implicit_inner_joins_honors_readonly_with_select diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index cace7ba142..140ccdac72 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -393,18 +393,18 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase end def test_has_many_through_polymorphic_has_one - assert_equal Tagging.find(1,2).sort_by { |t| t.id }, authors(:david).taggings_2 + assert_equal Tagging.find(1,2).sort_by(&:id), authors(:david).taggings_2 end def test_has_many_through_polymorphic_has_many - assert_equal taggings(:welcome_general, :thinking_general), authors(:david).taggings.distinct.sort_by { |t| t.id } + assert_equal taggings(:welcome_general, :thinking_general), authors(:david).taggings.distinct.sort_by(&:id) end def test_include_has_many_through_polymorphic_has_many author = Author.includes(:taggings).find authors(:david).id expected_taggings = taggings(:welcome_general, :thinking_general) assert_no_queries do - assert_equal expected_taggings, author.taggings.distinct.sort_by { |t| t.id } + assert_equal expected_taggings, author.taggings.distinct.sort_by(&:id) end end diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb index 04ef50e58a..c6769edcbf 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -48,7 +48,7 @@ class AssociationsTest < ActiveRecord::TestCase assert_kind_of Firm, firm firm.clear_association_cache - assert_equal Firm.find(1).clients.collect{ |x| x.name }.sort, firm.clients.collect{ |x| x.name }.sort + assert_equal Firm.find(1).clients.collect(&:name).sort, firm.clients.collect(&:name).sort end def test_clear_association_cache_new_record diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index 71c34d816a..04d5a2869c 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -774,13 +774,13 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase def test_should_destroy_has_many_as_part_of_the_save_transaction_if_they_were_marked_for_destruction 2.times { |i| @pirate.birds.create!(:name => "birds_#{i}") } - assert !@pirate.birds.any? { |child| child.marked_for_destruction? } + assert !@pirate.birds.any?(&:marked_for_destruction?) - @pirate.birds.each { |child| child.mark_for_destruction } + @pirate.birds.each(&:mark_for_destruction) klass = @pirate.birds.first.class ids = @pirate.birds.map(&:id) - assert @pirate.birds.all? { |child| child.marked_for_destruction? } + assert @pirate.birds.all?(&:marked_for_destruction?) ids.each { |id| assert klass.find_by_id(id) } @pirate.save @@ -814,14 +814,14 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase @pirate.birds.each { |bird| bird.name = '' } assert !@pirate.valid? - @pirate.birds.each { |bird| bird.destroy } + @pirate.birds.each(&:destroy) assert @pirate.valid? end def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_has_many @pirate.birds.create!(:name => "birds_1") - @pirate.birds.each { |bird| bird.mark_for_destruction } + @pirate.birds.each(&:mark_for_destruction) assert @pirate.save @pirate.birds.each { |bird| bird.expects(:destroy).never } @@ -888,7 +888,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase association_name_with_callbacks = "birds_with_#{callback_type}_callbacks" @pirate.send(association_name_with_callbacks).create!(:name => "Crowe the One-Eyed") - @pirate.send(association_name_with_callbacks).each { |c| c.mark_for_destruction } + @pirate.send(association_name_with_callbacks).each(&:mark_for_destruction) child_id = @pirate.send(association_name_with_callbacks).first.id @pirate.ship_log.clear @@ -906,8 +906,8 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase def test_should_destroy_habtm_as_part_of_the_save_transaction_if_they_were_marked_for_destruction 2.times { |i| @pirate.parrots.create!(:name => "parrots_#{i}") } - assert !@pirate.parrots.any? { |parrot| parrot.marked_for_destruction? } - @pirate.parrots.each { |parrot| parrot.mark_for_destruction } + assert !@pirate.parrots.any?(&:marked_for_destruction?) + @pirate.parrots.each(&:mark_for_destruction) assert_no_difference "Parrot.count" do @pirate.save @@ -940,14 +940,14 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase @pirate.parrots.each { |parrot| parrot.name = '' } assert !@pirate.valid? - @pirate.parrots.each { |parrot| parrot.destroy } + @pirate.parrots.each(&:destroy) assert @pirate.valid? end def test_a_child_marked_for_destruction_should_not_be_destroyed_twice_while_saving_habtm @pirate.parrots.create!(:name => "parrots_1") - @pirate.parrots.each { |parrot| parrot.mark_for_destruction } + @pirate.parrots.each(&:mark_for_destruction) assert @pirate.save Pirate.transaction do @@ -992,7 +992,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase association_name_with_callbacks = "parrots_with_#{callback_type}_callbacks" @pirate.send(association_name_with_callbacks).create!(:name => "Crowe the One-Eyed") - @pirate.send(association_name_with_callbacks).each { |c| c.mark_for_destruction } + @pirate.send(association_name_with_callbacks).each(&:mark_for_destruction) child_id = @pirate.send(association_name_with_callbacks).first.id @pirate.ship_log.clear diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 33a59d4678..a607c1319f 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -934,7 +934,7 @@ class FinderTest < ActiveRecord::TestCase joins('LEFT JOIN developers_projects ON developers.id = developers_projects.developer_id'). where('project_id=1').to_a assert_equal 3, developers_on_project_one.length - developer_names = developers_on_project_one.map { |d| d.name } + developer_names = developers_on_project_one.map(&:name) assert developer_names.include?('David') assert developer_names.include?('Jamis') end @@ -989,7 +989,7 @@ class FinderTest < ActiveRecord::TestCase end def test_select_values - assert_equal ["1","2","3","4","5","6","7","8","9", "10", "11"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map! { |i| i.to_s } + assert_equal ["1","2","3","4","5","6","7","8","9", "10", "11"], Company.connection.select_values("SELECT id FROM companies ORDER BY id").map!(&:to_s) assert_equal ["37signals","Summit","Microsoft", "Flamboyant Software", "Ex Nihilo", "RailsCore", "Leetsoft", "Jadedpixel", "Odegy", "Ex Nihilo Part Deux", "Apex"], Company.connection.select_values("SELECT name FROM companies ORDER BY id") end @@ -1015,7 +1015,7 @@ class FinderTest < ActiveRecord::TestCase where(client_of: [2, 1, nil], name: ['37signals', 'Summit', 'Microsoft']). order('client_of DESC'). - map { |x| x.client_of } + map(&:client_of) assert client_of.include?(nil) assert_equal [2, 1].sort, client_of.compact.sort @@ -1025,7 +1025,7 @@ class FinderTest < ActiveRecord::TestCase client_of = Company. where(client_of: [nil]). order('client_of DESC'). - map { |x| x.client_of } + map(&:client_of) assert_equal [], client_of.compact end diff --git a/activerecord/test/cases/migration/column_positioning_test.rb b/activerecord/test/cases/migration/column_positioning_test.rb index 77a752f050..62186e13a5 100644 --- a/activerecord/test/cases/migration/column_positioning_test.rb +++ b/activerecord/test/cases/migration/column_positioning_test.rb @@ -25,30 +25,30 @@ module ActiveRecord if current_adapter?(:MysqlAdapter, :Mysql2Adapter) def test_column_positioning - assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name } + assert_equal %w(first second third), conn.columns(:testings).map(&:name) end def test_add_column_with_positioning conn.add_column :testings, :new_col, :integer - assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name } + assert_equal %w(first second third new_col), conn.columns(:testings).map(&:name) end def test_add_column_with_positioning_first conn.add_column :testings, :new_col, :integer, :first => true - assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name } + assert_equal %w(new_col first second third), conn.columns(:testings).map(&:name) end def test_add_column_with_positioning_after conn.add_column :testings, :new_col, :integer, :after => :first - assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name } + assert_equal %w(first new_col second third), conn.columns(:testings).map(&:name) end def test_change_column_with_positioning conn.change_column :testings, :second, :integer, :first => true - assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name } + assert_equal %w(second first third), conn.columns(:testings).map(&:name) conn.change_column :testings, :second, :integer, :after => :third - assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name } + assert_equal %w(first third second), conn.columns(:testings).map(&:name) end end end diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index f05ca900aa..c0daa83e9c 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -149,7 +149,7 @@ class MigratorTest < ActiveRecord::TestCase def test_up_calls_up migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)] ActiveRecord::Migrator.new(:up, migrations).migrate - assert migrations.all? { |m| m.went_up } + assert migrations.all?(&:went_up) assert migrations.all? { |m| !m.went_down } assert_equal 2, ActiveRecord::Migrator.current_version end @@ -160,7 +160,7 @@ class MigratorTest < ActiveRecord::TestCase migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)] ActiveRecord::Migrator.new(:down, migrations).migrate assert migrations.all? { |m| !m.went_up } - assert migrations.all? { |m| m.went_down } + assert migrations.all?(&:went_down) assert_equal 0, ActiveRecord::Migrator.current_version end diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index cf96c3fccf..5c7e8a65d2 100644 --- a/activerecord/test/cases/nested_attributes_test.rb +++ b/activerecord/test/cases/nested_attributes_test.rb @@ -13,7 +13,7 @@ require 'active_support/hash_with_indifferent_access' class TestNestedAttributesInGeneral < ActiveRecord::TestCase teardown do - Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } + Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc(&:empty?) end def test_base_should_have_an_empty_nested_attributes_options @@ -300,13 +300,13 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase end def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false - Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? } + Pirate.accepts_nested_attributes_for :ship, :allow_destroy => false, :reject_if => proc(&:empty?) @pirate.update(ship_attributes: { id: @pirate.ship.id, _destroy: '1' }) assert_equal @ship, @pirate.reload.ship - Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } + Pirate.accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc(&:empty?) end def test_should_also_work_with_a_HashWithIndifferentAccess @@ -494,12 +494,12 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase end def test_should_not_destroy_an_existing_record_if_allow_destroy_is_false - Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc { |attributes| attributes.empty? } + Ship.accepts_nested_attributes_for :pirate, :allow_destroy => false, :reject_if => proc(&:empty?) @ship.update(pirate_attributes: { id: @ship.pirate.id, _destroy: '1' }) assert_nothing_raised(ActiveRecord::RecordNotFound) { @ship.pirate.reload } ensure - Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } + Ship.accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc(&:empty?) end def test_should_work_with_update_as_well @@ -855,7 +855,7 @@ end module NestedAttributesLimitTests def teardown - Pirate.accepts_nested_attributes_for :parrots, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } + Pirate.accepts_nested_attributes_for :parrots, :allow_destroy => true, :reject_if => proc(&:empty?) end def test_limit_with_less_records diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 2bcd496415..6fc4731f01 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -127,7 +127,7 @@ class PersistenceTest < ActiveRecord::TestCase assert_difference('Topic.count', -topics_by_mary.size) do destroyed = Topic.destroy_all(conditions).sort_by(&:id) assert_equal topics_by_mary, destroyed - assert destroyed.all? { |topic| topic.frozen? }, "destroyed topics should be frozen" + assert destroyed.all?(&:frozen?), "destroyed topics should be frozen" end end @@ -137,7 +137,7 @@ class PersistenceTest < ActiveRecord::TestCase assert_difference('Client.count', -2) do destroyed = Client.destroy([2, 3]).sort_by(&:id) assert_equal clients, destroyed - assert destroyed.all? { |client| client.frozen? }, "destroyed clients should be frozen" + assert destroyed.all?(&:frozen?), "destroyed clients should be frozen" end end diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb index 8eea10143f..98888150a8 100644 --- a/activerecord/test/cases/pooled_connections_test.rb +++ b/activerecord/test/cases/pooled_connections_test.rb @@ -13,7 +13,7 @@ class PooledConnectionsTest < ActiveRecord::TestCase teardown do ActiveRecord::Base.clear_all_connections! ActiveRecord::Base.establish_connection(@connection) - @per_test_teardown.each {|td| td.call } + @per_test_teardown.each(&:call) end # Will deadlock due to lack of Monitor timeouts in 1.9 diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb index 094fcccc89..e86b892a0a 100644 --- a/activerecord/test/cases/reflection_test.rb +++ b/activerecord/test/cases/reflection_test.rb @@ -50,13 +50,13 @@ class ReflectionTest < ActiveRecord::TestCase end def test_columns_are_returned_in_the_order_they_were_declared - column_names = Topic.columns.map { |column| column.name } + column_names = Topic.columns.map(&:name) assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content important approved replies_count unique_replies_count parent_id parent_title type group created_at updated_at), column_names end def test_content_columns content_columns = Topic.content_columns - content_column_names = content_columns.map {|column| column.name} + content_column_names = content_columns.map(&:name) assert_equal 13, content_columns.length assert_equal %w(title author_name author_email_address written_on bonus_time last_read content important group approved parent_title created_at updated_at).sort, content_column_names.sort end diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ca86d58b35..fc9637a167 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -249,7 +249,7 @@ class RelationTest < ActiveRecord::TestCase def test_finding_with_reorder topics = Topic.order('author_name').order('title').reorder('id').to_a - topics_titles = topics.map{ |t| t.title } + topics_titles = topics.map(&:title) assert_equal ['The First Topic', 'The Second Topic of the day', 'The Third Topic of the day', 'The Fourth Topic of the day', 'The Fifth Topic of the day'], topics_titles end @@ -441,7 +441,7 @@ class RelationTest < ActiveRecord::TestCase where('project_id=1').to_a assert_equal 3, developers_on_project_one.length - developer_names = developers_on_project_one.map { |d| d.name } + developer_names = developers_on_project_one.map(&:name) assert developer_names.include?('David') assert developer_names.include?('Jamis') end @@ -652,8 +652,8 @@ class RelationTest < ActiveRecord::TestCase expected_taggings = taggings(:welcome_general, :thinking_general) assert_no_queries do - assert_equal expected_taggings, author.taggings.distinct.sort_by { |t| t.id } - assert_equal expected_taggings, author.taggings.uniq.sort_by { |t| t.id } + assert_equal expected_taggings, author.taggings.distinct.sort_by(&:id) + assert_equal expected_taggings, author.taggings.uniq.sort_by(&:id) end authors = Author.all diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 880d0e8293..5bdebe6ac7 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -8,8 +8,8 @@ class DefaultScopingTest < ActiveRecord::TestCase fixtures :developers, :posts, :comments def test_default_scope - expected = Developer.all.merge!(:order => 'salary DESC').to_a.collect { |dev| dev.salary } - received = DeveloperOrderedBySalary.all.collect { |dev| dev.salary } + expected = Developer.all.merge!(:order => 'salary DESC').to_a.collect(&:salary) + received = DeveloperOrderedBySalary.all.collect(&:salary) assert_equal expected, received end @@ -86,14 +86,14 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_scope_overwrites_default - expected = Developer.all.merge!(order: 'salary DESC, name DESC').to_a.collect { |dev| dev.name } - received = DeveloperOrderedBySalary.by_name.to_a.collect { |dev| dev.name } + expected = Developer.all.merge!(order: 'salary DESC, name DESC').to_a.collect(&:name) + received = DeveloperOrderedBySalary.by_name.to_a.collect(&:name) assert_equal expected, received end def test_reorder_overrides_default_scope_order - expected = Developer.order('name DESC').collect { |dev| dev.name } - received = DeveloperOrderedBySalary.reorder('name DESC').collect { |dev| dev.name } + expected = Developer.order('name DESC').collect(&:name) + received = DeveloperOrderedBySalary.reorder('name DESC').collect(&:name) assert_equal expected, received end @@ -146,34 +146,34 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_unscope_multiple_where_clauses - expected = Developer.order('salary DESC').collect { |dev| dev.name } - received = DeveloperOrderedBySalary.where(name: 'Jamis').where(id: 1).unscope(where: [:name, :id]).collect { |dev| dev.name } + expected = Developer.order('salary DESC').collect(&:name) + received = DeveloperOrderedBySalary.where(name: 'Jamis').where(id: 1).unscope(where: [:name, :id]).collect(&:name) assert_equal expected, received end def test_unscope_string_where_clauses_involved dev_relation = Developer.order('salary DESC').where("created_at > ?", 1.year.ago) - expected = dev_relation.collect { |dev| dev.name } + expected = dev_relation.collect(&:name) dev_ordered_relation = DeveloperOrderedBySalary.where(name: 'Jamis').where("created_at > ?", 1.year.ago) - received = dev_ordered_relation.unscope(where: [:name]).collect { |dev| dev.name } + received = dev_ordered_relation.unscope(where: [:name]).collect(&:name) assert_equal expected, received end def test_unscope_with_grouping_attributes - expected = Developer.order('salary DESC').collect { |dev| dev.name } - received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect { |dev| dev.name } + expected = Developer.order('salary DESC').collect(&:name) + received = DeveloperOrderedBySalary.group(:name).unscope(:group).collect(&:name) assert_equal expected, received - expected_2 = Developer.order('salary DESC').collect { |dev| dev.name } - received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect { |dev| dev.name } + expected_2 = Developer.order('salary DESC').collect(&:name) + received_2 = DeveloperOrderedBySalary.group("name").unscope(:group).collect(&:name) assert_equal expected_2, received_2 end def test_unscope_with_limit_in_query - expected = Developer.order('salary DESC').collect { |dev| dev.name } - received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect { |dev| dev.name } + expected = Developer.order('salary DESC').collect(&:name) + received = DeveloperOrderedBySalary.limit(1).unscope(:limit).collect(&:name) assert_equal expected, received end @@ -183,42 +183,42 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_unscope_reverse_order - expected = Developer.all.collect { |dev| dev.name } - received = Developer.order('salary DESC').reverse_order.unscope(:order).collect { |dev| dev.name } + expected = Developer.all.collect(&:name) + received = Developer.order('salary DESC').reverse_order.unscope(:order).collect(&:name) assert_equal expected, received end def test_unscope_select - expected = Developer.order('salary ASC').collect { |dev| dev.name } - received = Developer.order('salary DESC').reverse_order.select(:name).unscope(:select).collect { |dev| dev.name } + expected = Developer.order('salary ASC').collect(&:name) + received = Developer.order('salary DESC').reverse_order.select(:name).unscope(:select).collect(&:name) assert_equal expected, received - expected_2 = Developer.all.collect { |dev| dev.id } - received_2 = Developer.select(:name).unscope(:select).collect { |dev| dev.id } + expected_2 = Developer.all.collect(&:id) + received_2 = Developer.select(:name).unscope(:select).collect(&:id) assert_equal expected_2, received_2 end def test_unscope_offset - expected = Developer.all.collect { |dev| dev.name } - received = Developer.offset(5).unscope(:offset).collect { |dev| dev.name } + expected = Developer.all.collect(&:name) + received = Developer.offset(5).unscope(:offset).collect(&:name) assert_equal expected, received end def test_unscope_joins_and_select_on_developers_projects - expected = Developer.all.collect { |dev| dev.name } - received = Developer.joins('JOIN developers_projects ON id = developer_id').select(:id).unscope(:joins, :select).collect { |dev| dev.name } + expected = Developer.all.collect(&:name) + received = Developer.joins('JOIN developers_projects ON id = developer_id').select(:id).unscope(:joins, :select).collect(&:name) assert_equal expected, received end def test_unscope_includes - expected = Developer.all.collect { |dev| dev.name } - received = Developer.includes(:projects).select(:id).unscope(:includes, :select).collect { |dev| dev.name } + expected = Developer.all.collect(&:name) + received = Developer.includes(:projects).select(:id).unscope(:includes, :select).collect(&:name) assert_equal expected, received end def test_unscope_having - expected = DeveloperOrderedBySalary.all.collect { |dev| dev.name } - received = DeveloperOrderedBySalary.having("name IN ('Jamis', 'David')").unscope(:having).collect { |dev| dev.name } + expected = DeveloperOrderedBySalary.all.collect(&:name) + received = DeveloperOrderedBySalary.having("name IN ('Jamis', 'David')").unscope(:having).collect(&:name) assert_equal expected, received end @@ -281,8 +281,8 @@ class DefaultScopingTest < ActiveRecord::TestCase end def test_order_in_default_scope_should_not_prevail - expected = Developer.all.merge!(order: 'salary desc').to_a.collect { |dev| dev.salary } - received = DeveloperOrderedBySalary.all.merge!(order: 'salary').to_a.collect { |dev| dev.salary } + expected = Developer.all.merge!(order: 'salary desc').to_a.collect(&:salary) + received = DeveloperOrderedBySalary.all.merge!(order: 'salary').to_a.collect(&:salary) assert_equal expected, received end diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb index eb44c4a83c..5ba17359f0 100644 --- a/activerecord/test/cases/test_case.rb +++ b/activerecord/test/cases/test_case.rb @@ -43,7 +43,7 @@ module ActiveRecord patterns_to_match.each do |pattern| failed_patterns << pattern unless SQLCounter.log_all.any?{ |sql| pattern === sql } end - assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}" + assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map(&:inspect).join(', ')} not found.#{SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{SQLCounter.log.join("\n")}"}" end def assert_queries(num = 1, options = {}) diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 7160e8324d..72c07d8aea 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -13,7 +13,7 @@ class TransactionTest < ActiveRecord::TestCase fixtures :topics, :developers, :authors, :posts def setup - @first, @second = Topic.find(1, 2).sort_by { |t| t.id } + @first, @second = Topic.find(1, 2).sort_by(&:id) end def test_persisted_in_a_model_with_custom_primary_key_after_failed_save @@ -699,7 +699,7 @@ if current_adapter?(:PostgreSQLAdapter) end end - threads.each { |t| t.join } + threads.each(&:join) end end @@ -747,7 +747,7 @@ if current_adapter?(:PostgreSQLAdapter) Developer.connection.close end - threads.each { |t| t.join } + threads.each(&:join) end assert_equal original_salary, Developer.find(1).salary diff --git a/activerecord/test/cases/validations_repair_helper.rb b/activerecord/test/cases/validations_repair_helper.rb index 2bbf0f23b3..b30666d876 100644 --- a/activerecord/test/cases/validations_repair_helper.rb +++ b/activerecord/test/cases/validations_repair_helper.rb @@ -5,9 +5,7 @@ module ActiveRecord module ClassMethods def repair_validations(*model_classes) teardown do - model_classes.each do |k| - k.clear_validators! - end + model_classes.each(&:clear_validators!) end end end @@ -15,9 +13,7 @@ module ActiveRecord def repair_validations(*model_classes) yield if block_given? ensure - model_classes.each do |k| - k.clear_validators! - end + model_classes.each(&:clear_validators!) end end end diff --git a/activerecord/test/models/customer.rb b/activerecord/test/models/customer.rb index 7e8e82542f..afe4b3d707 100644 --- a/activerecord/test/models/customer.rb +++ b/activerecord/test/models/customer.rb @@ -2,7 +2,7 @@ class Customer < ActiveRecord::Base cattr_accessor :gps_conversion_was_run composed_of :address, :mapping => [ %w(address_street street), %w(address_city city), %w(address_country country) ], :allow_nil => true - composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new { |balance| balance.to_money } + composed_of :balance, :class_name => "Money", :mapping => %w(balance amount), :converter => Proc.new(&:to_money) composed_of :gps_location, :allow_nil => true composed_of :non_blank_gps_location, :class_name => "GpsLocation", :allow_nil => true, :mapping => %w(gps_location gps_location), :converter => lambda { |gps| self.gps_conversion_was_run = true; gps.blank? ? nil : GpsLocation.new(gps)} diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index 90a3c3ecee..641a33f9be 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -36,8 +36,8 @@ class Pirate < ActiveRecord::Base has_one :foo_bulb, -> { where :name => 'foo' }, :foreign_key => :car_id, :class_name => "Bulb" - accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } - accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } + accepts_nested_attributes_for :parrots, :birds, :allow_destroy => true, :reject_if => proc(&:empty?) + accepts_nested_attributes_for :ship, :allow_destroy => true, :reject_if => proc(&:empty?) accepts_nested_attributes_for :update_only_ship, :update_only => true accepts_nested_attributes_for :parrots_with_method_callbacks, :parrots_with_proc_callbacks, :birds_with_method_callbacks, :birds_with_proc_callbacks, :allow_destroy => true diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb index 77a4728d0b..5f618a50d2 100644 --- a/activerecord/test/models/ship.rb +++ b/activerecord/test/models/ship.rb @@ -6,7 +6,7 @@ class Ship < ActiveRecord::Base has_many :parts, :class_name => 'ShipPart' accepts_nested_attributes_for :parts, :allow_destroy => true - accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc { |attributes| attributes.empty? } + accepts_nested_attributes_for :pirate, :allow_destroy => true, :reject_if => proc(&:empty?) accepts_nested_attributes_for :update_only_pirate, :update_only => true validates_presence_of :name -- cgit v1.2.3 From de239066e32d654f6815a94cdf8ce4b67b058726 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Wed, 26 Nov 2014 13:22:11 -0700 Subject: Stop using `Arel::Table.engine` We never actually make use of it on the table, since we're constructing the select manager manually. It looks like if we ever actually were grabbing it from the table, we're grossly misusing it since it's meant to vary by AR class. Its existence on `Arel::Table` appears to be purely for convenience methods that are never used outside of tests. However, in production code it just complicates construction of the tables on the rails side, and the plan is to remove it from `Arel::Table` entirely. I'm not convinced it needs to live on `SelectManager`, etc either. --- activerecord/lib/active_record/core.rb | 2 +- activerecord/lib/active_record/relation/predicate_builder.rb | 4 ++-- activerecord/lib/active_record/relation/query_methods.rb | 2 +- activerecord/lib/active_record/sanitization.rb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 89d8932e9e..c812e7842a 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -235,7 +235,7 @@ module ActiveRecord # scope :published_and_commented, -> { published.and(self.arel_table[:comments_count].gt(0)) } # end def arel_table # :nodoc: - @arel_table ||= Arel::Table.new(table_name, arel_engine) + @arel_table ||= Arel::Table.new(table_name) end # Returns the Arel engine. diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index e4b6b49087..938bf55c6d 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -25,7 +25,7 @@ module ActiveRecord if value.empty? queries << '1=0' else - table = Arel::Table.new(column, default_table.engine) + table = Arel::Table.new(column) association = klass._reflect_on_association(column) value.each do |k, v| @@ -37,7 +37,7 @@ module ActiveRecord if column.include?('.') table_name, column = column.split('.', 2) - table = Arel::Table.new(table_name, default_table.engine) + table = Arel::Table.new(table_name) end queries.concat expand(klass, table, column, value) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 6e384facce..0edd2b1e48 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -858,7 +858,7 @@ module ActiveRecord private def build_arel - arel = Arel::SelectManager.new(table.engine, table) + arel = Arel::SelectManager.new(klass.arel_engine, table) build_joins(arel, joins_values.flatten) unless joins_values.empty? diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index 6a130c145b..88dfddebcc 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -93,7 +93,7 @@ sanitize_sql_hash_for_conditions is deprecated, and will be removed in Rails 5.0 attrs = PredicateBuilder.resolve_column_aliases self, attrs attrs = expand_hash_conditions_for_aggregates(attrs) - table = Arel::Table.new(table_name, arel_engine).alias(default_table_name) + table = Arel::Table.new(table_name).alias(default_table_name) PredicateBuilder.build_from_hash(self, attrs, table).map { |b| connection.visitor.compile b }.join(' AND ') -- cgit v1.2.3 From a975407a0a4f8bb06f8d4696de6a8b5e5a086622 Mon Sep 17 00:00:00 2001 From: Sean Griffin Date: Sat, 29 Nov 2014 17:23:10 -0700 Subject: Update Arel usage for rails/arel#98fc259 `where_sql` now requires that we pass it an engine. None of the manager classes take an engine in their constructor. --- .../active_record/associations/has_many_through_association.rb | 2 +- activerecord/lib/active_record/relation.rb | 4 ++-- activerecord/lib/active_record/relation/finder_methods.rb | 8 ++++---- activerecord/lib/active_record/relation/query_methods.rb | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) (limited to 'activerecord') 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 5cfedcc009..3f4d3bfc08 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -165,7 +165,7 @@ module ActiveRecord arel = scope.arel - stmt = Arel::DeleteManager.new arel.engine + stmt = Arel::DeleteManager.new stmt.from scope.klass.arel_table stmt.wheres = arel.constraints diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 7e1994086f..7df50f7c97 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -327,7 +327,7 @@ module ActiveRecord def update_all(updates) raise ArgumentError, "Empty list of attributes to change" if updates.blank? - stmt = Arel::UpdateManager.new(arel.engine) + stmt = Arel::UpdateManager.new stmt.set Arel.sql(@klass.send(:sanitize_sql_for_assignment, updates)) stmt.table(table) @@ -465,7 +465,7 @@ module ActiveRecord if conditions where(conditions).delete_all else - stmt = Arel::DeleteManager.new(arel.engine) + stmt = Arel::DeleteManager.new stmt.from(table) if joins_values.any? diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 5061940d82..357861caaa 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -108,7 +108,7 @@ module ActiveRecord # Same as +take+ but raises ActiveRecord::RecordNotFound if no record # is found. Note that take! accepts no arguments. def take! - take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") + take or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]") end # Find the first record (or first N records if a parameter is supplied). @@ -176,7 +176,7 @@ module ActiveRecord # Same as +last+ but raises ActiveRecord::RecordNotFound if no record # is found. Note that last! accepts no arguments. def last! - last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") + last or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]") end # Find the second record. @@ -323,7 +323,7 @@ module ActiveRecord # the expected number of results should be provided in the +expected_size+ # argument. def raise_record_not_found_exception!(ids, result_size, expected_size) #:nodoc: - conditions = arel.where_sql + conditions = arel.where_sql(@klass.arel_engine) conditions = " [#{conditions}]" if conditions if Array(ids).size == 1 @@ -498,7 +498,7 @@ module ActiveRecord end def find_nth!(index) - find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql}]") + find_nth(index, offset_index) or raise RecordNotFound.new("Couldn't find #{@klass.name} with [#{arel.where_sql(@klass.arel_engine)}]") end def find_nth_with_limit(offset, limit) diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 0edd2b1e48..d58af01339 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -858,7 +858,7 @@ module ActiveRecord private def build_arel - arel = Arel::SelectManager.new(klass.arel_engine, table) + arel = Arel::SelectManager.new(table) build_joins(arel, joins_values.flatten) unless joins_values.empty? -- cgit v1.2.3 From 6874133f9e5ca79f30f43632da4338d7b82ddeff Mon Sep 17 00:00:00 2001 From: Hendy Tanata Date: Wed, 26 Nov 2014 09:16:44 -0800 Subject: Fix grammar on ActiveRecord::AttributeMethods doc. [ci skip] --- activerecord/lib/active_record/attribute_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index d766996d37..83fcefa64d 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -218,7 +218,7 @@ module ActiveRecord # A Person object with a name attribute can ask person.respond_to?(:name), # person.respond_to?(:name=), and person.respond_to?(:name?) - # which will all return +true+. It also define the attribute methods if they have + # which will all return +true+. It also defines the attribute methods if they have # not been generated. # # class Person < ActiveRecord::Base -- cgit v1.2.3