From d038a72bf3fb202d909337872467278586731411 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Thu, 20 Feb 2014 10:34:08 -0500 Subject: Revert "speed up the collection proxy reader method, but slow down the constructor" This reverts commit f9e4c3c7c0c4152b62fe9202a9d12262884bb118. [fixes #14116] --- .../lib/active_record/associations/collection_association.rb | 6 +----- 1 file changed, 1 insertion(+), 5 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 03ca00fa70..89b7945c78 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -24,10 +24,6 @@ module ActiveRecord # If you need to work on all current children, new and existing records, # +load_target+ and the +loaded+ flag are your friends. class CollectionAssociation < Association #:nodoc: - def initialize(owner, reflection) - super - @proxy = CollectionProxy.create(klass, self) - end # Implements the reader method, e.g. foo.items for Foo.has_many :items def reader(force_reload = false) @@ -37,7 +33,7 @@ module ActiveRecord reload end - @proxy + @proxy ||= CollectionProxy.create(klass, self) end # Implements the writer method, e.g. foo.items= for Foo.has_many :items -- cgit v1.2.3 From f96831e5ec200d957265adb7df765214123fbd6e Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Thu, 20 Feb 2014 10:34:57 -0500 Subject: Add regression test for collection proxy reader [related #14116] --- .../test/cases/associations/has_many_through_associations_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'activerecord') 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 47592f312e..026a7fe635 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -817,6 +817,13 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase assert author.named_categories(true).include?(category) end + def test_collection_exists + author = authors(:mary) + category = Category.create!(author_ids: [author.id], name: "Primary") + assert category.authors.exists?(id: author.id) + assert category.reload.authors.exists?(id: author.id) + end + def test_collection_delete_with_nonstandard_primary_key_on_belongs_to author = authors(:mary) category = author.named_categories.create(:name => "Primary") -- cgit v1.2.3 From 358802b850df595e071abe3b9d392225a0481d6b Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 20 Feb 2014 10:46:53 -0800 Subject: Revert "context in validation goes through has many relationship" This reverts commit 5e3d466d52fa4e9a42c3a1f8773a7c31da875e48. --- activerecord/lib/active_record/autosave_association.rb | 2 +- .../test/cases/associations/has_many_associations_test.rb | 10 ---------- activerecord/test/models/pirate.rb | 8 -------- activerecord/test/models/ship.rb | 8 -------- 4 files changed, 1 insertion(+), 27 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 4f58d06f35..e9622ca0c1 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -301,7 +301,7 @@ module ActiveRecord def association_valid?(reflection, record) return true if record.destroyed? || record.marked_for_destruction? - unless valid = record.valid?(self.validation_context) + unless valid = record.valid? if reflection.options[:autosave] record.errors.each do |attribute, message| attribute = "#{reflection.name}.#{attribute}" diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 321440cab7..a86fb15719 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -22,8 +22,6 @@ require 'models/engine' require 'models/categorization' require 'models/minivan' require 'models/speedometer' -require 'models/pirate' -require 'models/ship' class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase fixtures :authors, :posts, :comments @@ -1832,12 +1830,4 @@ class HasManyAssociationsTest < ActiveRecord::TestCase end end end - - test 'has_many_association passes context validation to validate children' do - pirate = FamousPirate.new - pirate.famous_ships << ship = FamousShip.new - assert_equal true, pirate.valid? - assert_equal false, pirate.valid?(:conference) - assert_equal "can't be blank", ship.errors[:name].first - end end diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb index 8510c596a7..7bb0caf44b 100644 --- a/activerecord/test/models/pirate.rb +++ b/activerecord/test/models/pirate.rb @@ -85,11 +85,3 @@ end class DestructivePirate < Pirate has_one :dependent_ship, :class_name => 'Ship', :foreign_key => :pirate_id, :dependent => :destroy end - -class FamousPirate < ActiveRecord::Base - self.table_name = 'pirates' - - has_many :famous_ships - - validates_presence_of :catchphrase, on: :conference -end diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb index 7a369b9d9a..3da031946f 100644 --- a/activerecord/test/models/ship.rb +++ b/activerecord/test/models/ship.rb @@ -17,11 +17,3 @@ class Ship < ActiveRecord::Base false end end - -class FamousShip < ActiveRecord::Base - self.table_name = 'ships' - - belongs_to :famous_pirate - - validates_presence_of :name, on: :conference -end -- cgit v1.2.3 From 7eaebd85798c8f56baea4a09d7e08b17d5dcc02c Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 20 Feb 2014 11:25:48 -0800 Subject: adding a test for #14106 --- .../test/cases/autosave_association_test.rb | 25 ++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index c55dd685a1..d2f97df0fc 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -21,6 +21,31 @@ require 'models/electron' require 'models/molecule' class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase + def test_autosave_validation + person = Class.new(ActiveRecord::Base) { + self.table_name = 'people' + validate :should_be_cool, :on => :create + def self.name; 'Person'; end + + private + + def should_be_cool + unless self.first_name == 'cool' + errors.add :first_name, "not cool" + end + end + } + reference = Class.new(ActiveRecord::Base) { + self.table_name = "references" + def self.name; 'Reference'; end + belongs_to :person, autosave: true, class: person + } + + u = person.create!(first_name: 'cool') + u.update_attributes!(first_name: 'nah') # still valid because validation only applies on 'create' + assert reference.create!(person: u).persisted? + end + def test_should_not_add_the_same_callbacks_multiple_times_for_has_one assert_no_difference_when_adding_callbacks_twice_for Pirate, :ship end -- cgit v1.2.3 From dbf9ec071497cd3935c8e5cd59df57f3fd9bbb17 Mon Sep 17 00:00:00 2001 From: Wojtek Kruszewski Date: Fri, 21 Feb 2014 14:47:39 +0100 Subject: Prevent foreign_key_for? from type casting all attributes --- activerecord/lib/active_record/associations/association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 4e46256862..9ad2d2fb12 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -232,7 +232,7 @@ module ActiveRecord # Returns true if record contains the foreign_key def foreign_key_for?(record) - record.attributes.has_key? reflection.foreign_key + record.has_attribute?(reflection.foreign_key) end # This should be implemented to return the values of the relevant key(s) on the owner, -- cgit v1.2.3 From 283a2edec2f8ccdf90fb58025608f02a63948fa0 Mon Sep 17 00:00:00 2001 From: schneems Date: Fri, 21 Feb 2014 16:04:21 -0600 Subject: Handle missing environment from non empty config If using a `DATABASE_URL` and a `database.yml`. The connection information in `DATABASE_URL` should be merged into whatever environment we are in. As released in 4.1.0rc1 if someone has a database.yml but is missing a key like production: ```yml development: host: localhost ``` Then the check for blank config will return false so the information from the `DATABASE_URL` will not be used when attempting to connect to the `production` database and the connection will incorrectly fail. This commit fixes this problem and adds a test for the behavior. In addition the ability to specify a connection url in a `database.yml` like this: ``` production: postgres://localhost/foo ``` Was introduced in 4.1.0rc1 though should not be used, instead using a url sub key ``` production: url: postgres://localhost/foo ``` This url sub key was also introduced in 4.1.0rc1 though the `production: postgres://localhost/foo` was not removed. As a result we should not test this behavior. --- .../lib/active_record/connection_handling.rb | 14 +++++-------- .../connection_adapters/connection_handler_test.rb | 23 ++++++++-------------- 2 files changed, 13 insertions(+), 24 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index 11f6a47158..4ba4e09777 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -93,16 +93,12 @@ module ActiveRecord # the connection URL. This hash responds to any string key with # resolved connection information. def default_url_hash - if @raw_config.blank? - Hash.new do |hash, key| - hash[key] = if key.is_a? String - ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(@url).to_hash - else - nil - end + Hash.new do |hash, key| + hash[key] = if key.is_a? String + ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(@url).to_hash + else + nil end - else - {} end end end diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index 318cc5a32c..599e8c762c 100644 --- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb +++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb @@ -17,6 +17,14 @@ module ActiveRecord ENV["DATABASE_URL"] = @previous_database_url end + def test_environment_does_not_exist_in_config_url_does_exist + ENV['DATABASE_URL'] = "postgres://localhost/foo" + config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } } + actual = klass.new(config).resolve + expect_prod = { "adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost" } + assert_equal expect_prod, actual["production"] + end + def test_string_connection config = { "production" => "postgres://localhost/foo" } actual = klass.new(config).resolve @@ -69,21 +77,6 @@ module ActiveRecord assert_equal nil, actual[:test] end - def test_sting_with_database_url - ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO" - - config = { "production" => "postgres://localhost/foo" } - actual = klass.new(config).resolve - - expected = { "production" => - { "adapter" => "postgresql", - "database" => "foo", - "host" => "localhost" - } - } - assert_equal expected, actual - end - def test_url_sub_key_with_database_url ENV['DATABASE_URL'] = "NOT-POSTGRES://localhost/NOT_FOO" -- cgit v1.2.3 From 58ba48e4b35c840f56b4fcbb6c8e4b489d785e15 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Sat, 22 Feb 2014 09:18:53 -0700 Subject: Distinguish ConnectionNotEstablished messages: no conn pool for the class, or no conn available from the pool --- .../active_record/connection_adapters/abstract/connection_pool.rb | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'activerecord') 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 759e162e19..fd185de589 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -538,7 +538,10 @@ module ActiveRecord # for (not necessarily the current class). def retrieve_connection(klass) #:nodoc: pool = retrieve_connection_pool(klass) - (pool && pool.connection) or raise ConnectionNotEstablished + raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool + conn = pool.connection + raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn + conn end # Returns true if a connection that's accessible to this class has -- cgit v1.2.3 From 5ea67a29c66fb872202cac2bb734241cec34e0a1 Mon Sep 17 00:00:00 2001 From: Vajrasky Kok Date: Sun, 23 Feb 2014 12:17:44 +0800 Subject: Fixed typo in comment about MAX_ID. --- activerecord/lib/active_record/fixtures.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 297792aeec..59467636d7 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -549,7 +549,7 @@ module ActiveRecord end # Returns a consistent, platform-independent identifier for +label+. - # Identifiers are positive integers less than 2^32. + # Identifiers are positive integers less than 2^30. def self.identify(label) Zlib.crc32(label.to_s) % MAX_ID end -- cgit v1.2.3 From 40a9d8987711f7973c5e95087bcc0c59e167294b Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Sun, 23 Feb 2014 11:14:05 +0000 Subject: Add tests for default scope behaviour change See #13875 --- .../test/cases/scoping/default_scoping_test.rb | 18 ++++++++++++++++++ activerecord/test/models/developer.rb | 2 ++ 2 files changed, 20 insertions(+) (limited to 'activerecord') diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb index 170e9a49eb..9a4d8c6740 100644 --- a/activerecord/test/cases/scoping/default_scoping_test.rb +++ b/activerecord/test/cases/scoping/default_scoping_test.rb @@ -395,4 +395,22 @@ class DefaultScopingTest < ActiveRecord::TestCase threads.each(&:join) end end + + test "additional conditions are ANDed with the default scope" do + scope = DeveloperCalledJamis.where(name: "David") + assert_equal 2, scope.where_values.length + assert_equal [], scope.to_a + end + + test "additional conditions in a scope are ANDed with the default scope" do + scope = DeveloperCalledJamis.david + assert_equal 2, scope.where_values.length + assert_equal [], scope.to_a + end + + test "a scope can remove the condition from the default scope" do + scope = DeveloperCalledJamis.david2 + assert_equal 1, scope.where_values.length + assert_equal Developer.where(name: "David").map(&:id), scope.map(&:id) + end end diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb index 2e2d8a0d37..762259ffa3 100644 --- a/activerecord/test/models/developer.rb +++ b/activerecord/test/models/developer.rb @@ -165,6 +165,8 @@ class DeveloperCalledJamis < ActiveRecord::Base default_scope { where(:name => 'Jamis') } scope :poor, -> { where('salary < 150000') } + scope :david, -> { where name: "David" } + scope :david2, -> { unscoped.where name: "David" } end class PoorDeveloperCalledJamis < ActiveRecord::Base -- cgit v1.2.3 From 4cb47167e747e8f9dc12b0ddaf82bdb68c03e032 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Tue, 21 Jan 2014 12:14:11 +0100 Subject: dynamically define PostgreSQL OID range types. This gets AR working with custom defined range types. It also removes the need for subtype specific branches in `OID::Range`. This expands the interface of all `OID` types with the `infinity` method. It's responsible to provide a value for positive and negative infinity. --- activerecord/CHANGELOG.md | 4 ++ .../connection_adapters/postgresql/oid.rb | 50 ++++++--------------- .../connection_adapters/postgresql_adapter.rb | 28 +++++++++--- .../test/cases/adapters/postgresql/range_test.rb | 51 +++++++++++++++++----- 4 files changed, 81 insertions(+), 52 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 7efd75a239..24b21ccc65 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,7 @@ +* Support for user created range types in PostgreSQL. + + *Yves Senn* + * Default scopes are no longer overriden by chained conditions. Before this change when you defined a `default_scope` in a model diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index fae260a921..32f86bf01c 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -6,6 +6,10 @@ module ActiveRecord module OID class Type def type; end + + def infinity(options = {}) + ::Float::INFINITY * (options[:negative] ? -1 : 1) + end end class Identity < Type @@ -109,23 +113,19 @@ module ActiveRecord def extract_bounds(value) from, to = value[1..-2].split(',') { - from: (value[1] == ',' || from == '-infinity') ? infinity(:negative => true) : from, - to: (value[-2] == ',' || to == 'infinity') ? infinity : to, + from: (value[1] == ',' || from == '-infinity') ? @subtype.infinity(negative: true) : from, + to: (value[-2] == ',' || to == 'infinity') ? @subtype.infinity : to, exclude_start: (value[0] == '('), exclude_end: (value[-1] == ')') } end - def infinity(options = {}) - ::Float::INFINITY * (options[:negative] ? -1 : 1) - end - def infinity?(value) value.respond_to?(:infinite?) && value.infinite? end - def to_integer(value) - infinity?(value) ? value : value.to_i + def type_cast_single(value) + infinity?(value) ? value : @subtype.type_cast(value) end def type_cast(value) @@ -133,27 +133,8 @@ module ActiveRecord return value if value.is_a?(::Range) extracted = extract_bounds(value) - - case @subtype - when :date - from = ConnectionAdapters::Column.value_to_date(extracted[:from]) - from -= 1.day if extracted[:exclude_start] - to = ConnectionAdapters::Column.value_to_date(extracted[:to]) - when :decimal - from = BigDecimal.new(extracted[:from].to_s) - # FIXME: add exclude start for ::Range, same for timestamp ranges - to = BigDecimal.new(extracted[:to].to_s) - when :time - from = ConnectionAdapters::Column.string_to_time(extracted[:from]) - to = ConnectionAdapters::Column.string_to_time(extracted[:to]) - when :integer - from = to_integer(extracted[:from]) rescue value ? 1 : 0 - from -= 1 if extracted[:exclude_start] - to = to_integer(extracted[:to]) rescue value ? 1 : 0 - else - return value - end - + from = type_cast_single extracted[:from] + to = type_cast_single extracted[:to] ::Range.new(from, to, extracted[:exclude_end]) end end @@ -222,6 +203,10 @@ module ActiveRecord ConnectionAdapters::Column.value_to_decimal value end + + def infinity(options = {}) + BigDecimal.new("Infinity") * (options[:negative] ? -1 : 1) + end end class Hstore < Type @@ -331,13 +316,6 @@ module ActiveRecord alias_type 'int8', 'int2' alias_type 'oid', 'int2' - register_type 'daterange', OID::Range.new(:date) - register_type 'numrange', OID::Range.new(:decimal) - register_type 'tsrange', OID::Range.new(:time) - register_type 'int4range', OID::Range.new(:integer) - alias_type 'tstzrange', 'tsrange' - alias_type 'int8range', 'int4range' - register_type 'numeric', OID::Decimal.new register_type 'text', OID::Identity.new alias_type 'varchar', 'text' diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 36c7462419..d1fb132b60 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -785,18 +785,29 @@ module ActiveRecord end def initialize_type_map(type_map) - result = execute('SELECT oid, typname, typelem, typdelim, typinput FROM pg_type', 'SCHEMA') - leaves, nodes = result.partition { |row| row['typelem'] == '0' } + if supports_ranges? + result = execute(<<-SQL, 'SCHEMA') + SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput, r.rngsubtype + FROM pg_type as t + LEFT JOIN pg_range as r ON oid = rngtypid + SQL + else + result = execute(<<-SQL, 'SCHEMA') + SELECT t.oid, t.typname, t.typelem, t.typdelim, t.typinput + FROM pg_type as t + SQL + end + ranges, nodes = result.partition { |row| row['typinput'] == 'range_in' } + leaves, nodes = nodes.partition { |row| row['typelem'] == '0' } + arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' } - # populate the leaf nodes + # populate the base types leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row| type_map[row['oid'].to_i] = OID::NAMES[row['typname']] end records_by_oid = result.group_by { |row| row['oid'] } - arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' } - # populate composite types nodes.each do |row| add_oid row, records_by_oid, type_map @@ -807,6 +818,13 @@ module ActiveRecord array = OID::Array.new type_map[row['typelem'].to_i] type_map[row['oid'].to_i] = array end + + # populate range types + ranges.find_all { |row| type_map.key? row['rngsubtype'].to_i }.each do |row| + subtype = type_map[row['rngsubtype'].to_i] + range = OID::Range.new type_map[row['rngsubtype'].to_i] + type_map[row['oid'].to_i] = range + end end FEATURE_NOT_SUPPORTED = "0A000" #:nodoc: diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb index 5c2d8e1c1d..b621a1468a 100644 --- a/activerecord/test/cases/adapters/postgresql/range_test.rb +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -10,12 +10,22 @@ if ActiveRecord::Base.connection.supports_ranges? class PostgresqlRangeTest < ActiveRecord::TestCase def teardown @connection.execute 'DROP TABLE IF EXISTS postgresql_ranges' + @connection.execute 'DROP TYPE IF EXISTS floatrange' end def setup - @connection = ActiveRecord::Base.connection + @connection = PostgresqlRange.connection begin @connection.transaction do + @connection.execute 'DROP TABLE IF EXISTS postgresql_ranges' + @connection.execute 'DROP TYPE IF EXISTS floatrange' + @connection.execute <<_SQL + CREATE TYPE floatrange AS RANGE ( + subtype = float8, + subtype_diff = float8mi + ); +_SQL + @connection.create_table('postgresql_ranges') do |t| t.daterange :date_range t.numrange :num_range @@ -24,7 +34,11 @@ if ActiveRecord::Base.connection.supports_ranges? t.int4range :int4_range t.int8range :int8_range end + + @connection.add_column 'postgresql_ranges', 'float_range', 'floatrange' end + @connection.send :reload_type_map + PostgresqlRange.reset_column_information rescue ActiveRecord::StatementInvalid skip "do not test on PG without range" end @@ -35,15 +49,17 @@ if ActiveRecord::Base.connection.supports_ranges? ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'']", tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']", int4_range: "[1, 10]", - int8_range: "[10, 100]") + int8_range: "[10, 100]", + float_range: "[0.5, 0.7]") insert_range(id: 102, date_range: "(''2012-01-02'', ''2012-01-04'')", - num_range: "[0.1, 0.2)", - ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'')", - tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')", + num_range: "(0.1, 0.2)", + ts_range: "(''2010-01-01 14:30'', ''2011-01-01 14:30'')", + tstz_range: "(''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')", int4_range: "(1, 10)", - int8_range: "(10, 100)") + int8_range: "(10, 100)", + float_range: "(0.5, 0.7)") insert_range(id: 103, date_range: "(''2012-01-02'',]", @@ -51,7 +67,8 @@ if ActiveRecord::Base.connection.supports_ranges? ts_range: "[''2010-01-01 14:30'',]", tstz_range: "[''2010-01-01 14:30:00+05'',]", int4_range: "(1,]", - int8_range: "(10,]") + int8_range: "(10,]", + float_range: "[0.5,]") insert_range(id: 104, date_range: "[,]", @@ -59,7 +76,8 @@ if ActiveRecord::Base.connection.supports_ranges? ts_range: "[,]", tstz_range: "[,]", int4_range: "[,]", - int8_range: "[,]") + int8_range: "[,]", + float_range: "[,]") insert_range(id: 105, date_range: "(''2012-01-02'', ''2012-01-02'')", @@ -67,7 +85,8 @@ if ActiveRecord::Base.connection.supports_ranges? ts_range: "(''2010-01-01 14:30'', ''2010-01-01 14:30'')", tstz_range: "(''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')", int4_range: "(1, 1)", - int8_range: "(10, 10)") + int8_range: "(10, 10)", + float_range: "(0.5, 0.5)") @new_range = PostgresqlRange.new @first_range = PostgresqlRange.find(101) @@ -133,6 +152,14 @@ if ActiveRecord::Base.connection.supports_ranges? assert_nil @empty_range.tstz_range end + def test_custom_range_values + assert_equal 0.5..0.7, @first_range.float_range + assert_equal 0.5...0.7, @second_range.float_range + assert_equal 0.5...Float::INFINITY, @third_range.float_range + assert_equal -Float::INFINITY...Float::INFINITY, @fourth_range.float_range + assert_nil @empty_range.float_range + end + def test_create_tstzrange tstzrange = Time.parse('2010-01-01 14:30:00 +0100')...Time.parse('2011-02-02 14:30:00 CDT') round_trip(@new_range, :tstz_range, tstzrange) @@ -229,7 +256,8 @@ if ActiveRecord::Base.connection.supports_ranges? ts_range, tstz_range, int4_range, - int8_range + int8_range, + float_range ) VALUES ( #{values[:id]}, '#{values[:date_range]}', @@ -237,7 +265,8 @@ if ActiveRecord::Base.connection.supports_ranges? '#{values[:ts_range]}', '#{values[:tstz_range]}', '#{values[:int4_range]}', - '#{values[:int8_range]}' + '#{values[:int8_range]}', + '#{values[:float_range]}' ) SQL end -- cgit v1.2.3 From 91949e48cf41af9f3e4ffba3e5eecf9b0a08bfc3 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Wed, 22 Jan 2014 14:20:01 +0100 Subject: deprecate support for pg ranges with excluding beginnings. The Ruby Range object does not support excluding beginnings. We currently support excluding beginnings for some subtypes using manually by incrementing them (now using the `#succ` method). This is approach is flawed as it's not equal to an excluding beginning. This commit deprecates the current support for excluding beginnings. It also raises an `ArgumentError` for subtypes that do not implement the `succ` method. This is a temporary solution to get rid of the broken state. We might still add complete support for excluding beginnings afterwards. (Probably with a new `PGRange` object, which acts like a `Range` but has excluding beginnings. --- activerecord/CHANGELOG.md | 11 +++ .../connection_adapters/postgresql/oid.rb | 12 ++++ .../test/cases/adapters/postgresql/range_test.rb | 78 +++++++++++++++------- 3 files changed, 78 insertions(+), 23 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 24b21ccc65..881a4d2d85 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,14 @@ +* 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* diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index 32f86bf01c..6f9b60b6c4 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -135,6 +135,18 @@ module ActiveRecord extracted = extract_bounds(value) from = type_cast_single extracted[:from] to = type_cast_single extracted[:to] + + if !infinity?(from) && extracted[:exclude_start] + if from.respond_to?(:succ) + from = from.succ + ActiveSupport::Deprecation.warn <<-MESSAGE +Excluding the beginning of a Range is only partialy supported through `#succ`. +This is not reliable and will be removed in the future. + MESSAGE + else + raise ArgumentError, "The Ruby Range object does not support excluding the beginning of a Range. (unsupported value: '#{value}')" + end + end ::Range.new(from, to, extracted[:exclude_end]) end end diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb index b621a1468a..306bb09a0d 100644 --- a/activerecord/test/cases/adapters/postgresql/range_test.rb +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -53,21 +53,21 @@ _SQL float_range: "[0.5, 0.7]") insert_range(id: 102, - date_range: "(''2012-01-02'', ''2012-01-04'')", - num_range: "(0.1, 0.2)", - ts_range: "(''2010-01-01 14:30'', ''2011-01-01 14:30'')", - tstz_range: "(''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')", - int4_range: "(1, 10)", - int8_range: "(10, 100)", - float_range: "(0.5, 0.7)") + date_range: "[''2012-01-02'', ''2012-01-04'')", + num_range: "[0.1, 0.2)", + ts_range: "[''2010-01-01 14:30'', ''2011-01-01 14:30'')", + tstz_range: "[''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'')", + int4_range: "[1, 10)", + int8_range: "[10, 100)", + float_range: "[0.5, 0.7)") insert_range(id: 103, - date_range: "(''2012-01-02'',]", + date_range: "[''2012-01-02'',]", num_range: "[0.1,]", ts_range: "[''2010-01-01 14:30'',]", tstz_range: "[''2010-01-01 14:30:00+05'',]", - int4_range: "(1,]", - int8_range: "(10,]", + int4_range: "[1,]", + int8_range: "[10,]", float_range: "[0.5,]") insert_range(id: 104, @@ -80,13 +80,13 @@ _SQL float_range: "[,]") insert_range(id: 105, - date_range: "(''2012-01-02'', ''2012-01-02'')", - num_range: "(0.1, 0.1)", - ts_range: "(''2010-01-01 14:30'', ''2010-01-01 14:30'')", - tstz_range: "(''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')", - int4_range: "(1, 1)", - int8_range: "(10, 10)", - float_range: "(0.5, 0.5)") + date_range: "[''2012-01-02'', ''2012-01-02'')", + num_range: "[0.1, 0.1)", + ts_range: "[''2010-01-01 14:30'', ''2010-01-01 14:30'')", + tstz_range: "[''2010-01-01 14:30:00+05'', ''2010-01-01 06:30:00-03'')", + int4_range: "[1, 1)", + int8_range: "[10, 10)", + float_range: "[0.5, 0.5)") @new_range = PostgresqlRange.new @first_range = PostgresqlRange.find(101) @@ -107,24 +107,24 @@ _SQL def test_int4range_values assert_equal 1...11, @first_range.int4_range - assert_equal 2...10, @second_range.int4_range - assert_equal 2...Float::INFINITY, @third_range.int4_range + assert_equal 1...10, @second_range.int4_range + assert_equal 1...Float::INFINITY, @third_range.int4_range assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int4_range) assert_nil @empty_range.int4_range end def test_int8range_values assert_equal 10...101, @first_range.int8_range - assert_equal 11...100, @second_range.int8_range - assert_equal 11...Float::INFINITY, @third_range.int8_range + assert_equal 10...100, @second_range.int8_range + assert_equal 10...Float::INFINITY, @third_range.int8_range assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.int8_range) assert_nil @empty_range.int8_range end def test_daterange_values assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 5), @first_range.date_range - assert_equal Date.new(2012, 1, 3)...Date.new(2012, 1, 4), @second_range.date_range - assert_equal Date.new(2012, 1, 3)...Float::INFINITY, @third_range.date_range + assert_equal Date.new(2012, 1, 2)...Date.new(2012, 1, 4), @second_range.date_range + assert_equal Date.new(2012, 1, 2)...Float::INFINITY, @third_range.date_range assert_equal(-Float::INFINITY...Float::INFINITY, @fourth_range.date_range) assert_nil @empty_range.date_range end @@ -230,6 +230,38 @@ _SQL assert_nil_round_trip(@first_range, :int8_range, 39999...39999) end + def test_exclude_beginning_for_subtypes_with_succ_method_is_deprecated + tz = ::ActiveRecord::Base.default_timezone + + silence_warnings { + assert_deprecated { + range = PostgresqlRange.create!(date_range: "(''2012-01-02'', ''2012-01-04'']") + assert_equal Date.new(2012, 1, 3)..Date.new(2012, 1, 4), range.date_range + } + assert_deprecated { + range = PostgresqlRange.create!(ts_range: "(''2010-01-01 14:30'', ''2011-01-01 14:30'']") + assert_equal Time.send(tz, 2010, 1, 1, 14, 30, 1)..Time.send(tz, 2011, 1, 1, 14, 30, 0), range.ts_range + } + assert_deprecated { + range = PostgresqlRange.create!(tstz_range: "(''2010-01-01 14:30:00+05'', ''2011-01-01 14:30:00-03'']") + assert_equal Time.parse('2010-01-01 09:30:01 UTC')..Time.parse('2011-01-01 17:30:00 UTC'), range.tstz_range + } + assert_deprecated { + range = PostgresqlRange.create!(int4_range: "(1, 10]") + assert_equal 2..10, range.int4_range + } + assert_deprecated { + range = PostgresqlRange.create!(int8_range: "(10, 100]") + assert_equal 11..100, range.int8_range + } + } + end + + def test_exclude_beginning_for_subtypes_without_succ_method_is_not_supported + assert_raises(ArgumentError) { PostgresqlRange.create!(num_range: "(0.1, 0.2]") } + assert_raises(ArgumentError) { PostgresqlRange.create!(float_range: "(0.5, 0.7]") } + end + private def assert_equal_round_trip(range, attribute, value) round_trip(range, attribute, value) -- cgit v1.2.3 From c554d170e6b3e7ebe783ab356e16abc9bfd56615 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Sun, 23 Feb 2014 13:14:43 +0100 Subject: update version to 4.2.0.alpha --- activerecord/lib/active_record/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index 7795561e51..5eb7f7e205 100644 --- a/activerecord/lib/active_record/version.rb +++ b/activerecord/lib/active_record/version.rb @@ -1,7 +1,7 @@ module ActiveRecord # Returns the version of the currently loaded ActiveRecord as a Gem::Version def self.version - Gem::Version.new "4.1.0.beta2" + Gem::Version.new "4.2.0.alpha" end module VERSION #:nodoc: -- cgit v1.2.3 From d9314b4c0a8b32f242ed4d394dcc3d45ddfb4c62 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Wed, 7 Aug 2013 18:56:49 +0200 Subject: Coerce strings when reading attributes. --- activerecord/CHANGELOG.md | 11 +++++++++++ .../lib/active_record/connection_adapters/column.rb | 10 ++++++++-- .../connection_adapters/postgresql/oid.rb | 18 +++++++++++++----- activerecord/test/cases/attribute_methods_test.rb | 18 ++++++++++++++++++ 4 files changed, 50 insertions(+), 7 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 881a4d2d85..7be5278a54 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,14 @@ +* 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. diff --git a/activerecord/lib/active_record/connection_adapters/column.rb b/activerecord/lib/active_record/connection_adapters/column.rb index f2fbd5a8f2..187eefb9e4 100644 --- a/activerecord/lib/active_record/connection_adapters/column.rb +++ b/activerecord/lib/active_record/connection_adapters/column.rb @@ -87,7 +87,7 @@ module ActiveRecord end end - # Casts value (which is a String) to an appropriate instance. + # Casts value to an appropriate instance. def type_cast(value) return nil if value.nil? return coder.load(value) if encoded? @@ -95,7 +95,13 @@ module ActiveRecord klass = self.class case type - when :string, :text then value + when :string, :text + case value + when TrueClass; "1" + when FalseClass; "0" + else + value.to_s + end when :integer then klass.value_to_integer(value) when :float then value.to_f when :decimal then klass.value_to_decimal(value) diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index 6f9b60b6c4..e7df073627 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -18,6 +18,14 @@ module ActiveRecord end end + class Text < Type + def type_cast(value) + return if value.nil? + + value.to_s + end + end + class Bit < Type def type_cast(value) if String === value @@ -329,7 +337,7 @@ This is not reliable and will be removed in the future. alias_type 'oid', 'int2' register_type 'numeric', OID::Decimal.new - register_type 'text', OID::Identity.new + register_type 'text', OID::Text.new alias_type 'varchar', 'text' alias_type 'char', 'text' alias_type 'bpchar', 'text' @@ -355,13 +363,13 @@ This is not reliable and will be removed in the future. register_type 'date', OID::Date.new register_type 'time', OID::Time.new - register_type 'path', OID::Identity.new + register_type 'path', OID::Text.new register_type 'point', OID::Point.new - register_type 'polygon', OID::Identity.new - register_type 'circle', OID::Identity.new + register_type 'polygon', OID::Text.new + register_type 'circle', OID::Text.new register_type 'hstore', OID::Hstore.new register_type 'json', OID::Json.new - register_type 'ltree', OID::Identity.new + register_type 'ltree', OID::Text.new register_type 'cidr', OID::Cidr.new alias_type 'inet', 'cidr' diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 6c581a432f..38a43de05a 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -555,6 +555,24 @@ class AttributeMethodsTest < ActiveRecord::TestCase end end + def test_converted_values_are_returned_after_assignment + developer = Developer.new(name: 1337, salary: "50000") + + assert_equal "50000", developer.salary_before_type_cast + assert_equal 1337, developer.name_before_type_cast + + assert_equal 50000, developer.salary + assert_equal "1337", developer.name + + developer.save! + + assert_equal "50000", developer.salary_before_type_cast + assert_equal 1337, developer.name_before_type_cast + + assert_equal 50000, developer.salary + assert_equal "1337", developer.name + end + def test_write_nil_to_time_attributes in_time_zone "Pacific Time (US & Canada)" do record = @target.new -- cgit v1.2.3 From 41554319f8454f667b5e315339d3e286dbf6e1a4 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Thu, 13 Feb 2014 11:49:19 -0800 Subject: Fixed STI classes not defining an attribute method if there is a conflicting private method defined on its ancestors. The problem is that `method_defined_within?(name, klass, superklass)` only works correclty when `klass` and `superklass` are both `Class`es. If both `klass` and `superklass` are both `Class`es, they share the same inheritance chain, so if a method is defined on `klass` but not `superklass`, this method must be introduced at some point between `klass` and `superklass`. This does not work when `superklass` is a `Module`. A `Module`'s inheritance chain contains just itself. So if a method is defined on `klass` but not on `superklass`, the method could still be defined somewhere upstream, e.g. in `Object`. This fix works by avoiding calling `method_defined_within?` with a module while still fufilling the requirement (checking that the method is defined withing `superclass` but not is not a generated attribute method). 4d8ee288 is likely an attempted partial fix for this problem. This unrolls that fix and properly check the `superclass` as intended. Fixes #11569. --- activerecord/CHANGELOG.md | 7 +++++ .../lib/active_record/attribute_methods.rb | 5 ++-- activerecord/test/cases/attribute_methods_test.rb | 30 ++++++++++++++-------- activerecord/test/fixtures/computers.yml | 1 + activerecord/test/schema/schema.rb | 1 + 5 files changed, 31 insertions(+), 13 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 881a4d2d85..7476b82e5f 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,10 @@ +* Fixed STI classes not defining an attribute method if there is a + conflicting private method defined on its ancestors. + + Fixes #11569. + + *Godfrey Chan* + * 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. diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 9326c9c117..57ceec2fc5 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -105,8 +105,9 @@ module ActiveRecord super else # If B < A and A defines its own attribute method, then we don't want to overwrite that. - defined = method_defined_within?(method_name, superclass, superclass.generated_attribute_methods) - defined && !ActiveRecord::Base.method_defined?(method_name) || super + defined = method_defined_within?(method_name, superclass) && + superclass.instance_method(method_name).owner != superclass.generated_attribute_methods + defined || super end end diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 6c581a432f..aec1ea3972 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -728,19 +728,27 @@ class AttributeMethodsTest < ActiveRecord::TestCase assert "unknown attribute: hello", error.message end - def test_read_attribute_overwrites_private_method_not_considered_implemented - # simulate a model with a db column that shares its name an inherited - # private method (e.g. Object#system) - # - Object.class_eval do - private - def title; "private!"; end + def test_global_methods_are_overwritten + klass = Class.new(ActiveRecord::Base) do + self.table_name = 'computers' + end + + assert !klass.instance_method_already_implemented?(:system) + computer = klass.new + assert_nil computer.system + end + + def test_global_methods_are_overwritte_when_subclassing + klass = Class.new(ActiveRecord::Base) { self.abstract_class = true } + + subklass = Class.new(klass) do + self.table_name = 'computers' end - assert !@target.instance_method_already_implemented?(:title) - topic = @target.new - assert_nil topic.title - Object.send(:undef_method, :title) # remove test method from object + assert !klass.instance_method_already_implemented?(:system) + assert !subklass.instance_method_already_implemented?(:system) + computer = subklass.new + assert_nil computer.system end def test_instance_method_should_be_defined_on_the_base_class diff --git a/activerecord/test/fixtures/computers.yml b/activerecord/test/fixtures/computers.yml index daf969d7da..7281a4d768 100644 --- a/activerecord/test/fixtures/computers.yml +++ b/activerecord/test/fixtures/computers.yml @@ -1,4 +1,5 @@ workstation: id: 1 + system: 'Linux' developer: 1 extendedWarranty: 1 diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 99a53434f6..5f2d6acbcb 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -198,6 +198,7 @@ ActiveRecord::Schema.define do end create_table :computers, force: true do |t| + t.string :system t.integer :developer, null: false t.integer :extendedWarranty, null: false end -- cgit v1.2.3 From be37b0c143da6dc682fd53eab744e0c1115063d1 Mon Sep 17 00:00:00 2001 From: Yannick Schutz Date: Tue, 25 Feb 2014 13:09:26 +0100 Subject: Add missing parantheses in index_exists? --- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 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 ad069f5e53..7f530ec5e4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -40,7 +40,7 @@ module ActiveRecord # index_exists?(:suppliers, :company_id, unique: true) # # # Check an index with a custom name exists - # index_exists?(:suppliers, :company_id, name: "idx_company_id" + # index_exists?(:suppliers, :company_id, name: "idx_company_id") # def index_exists?(table_name, column_name, options = {}) column_names = Array(column_name) -- cgit v1.2.3 From 71b3910a7d6c9c9a94af31510683390c2b3a1b23 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Tue, 25 Feb 2014 09:14:35 -0300 Subject: Point master changelogs to 4-1-stable branch Remove 4-1 related entries from master [ci skip] --- activerecord/CHANGELOG.md | 1859 +-------------------------------------------- 1 file changed, 1 insertion(+), 1858 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 7be5278a54..58ad2b84fd 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -24,1861 +24,4 @@ *Yves Senn* -* Default scopes are no longer overriden by chained conditions. - - Before this change when you defined a `default_scope` in a model - it was overriden by chained conditions in the same field. Now it - is merged like any other scope. - - Before: - - class User < ActiveRecord::Base - default_scope { where state: 'pending' } - scope :active, -> { where state: 'active' } - scope :inactive, -> { where state: 'inactive' } - end - - User.all - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' - - User.active - # SELECT "users".* FROM "users" WHERE "users"."state" = 'active' - - User.where(state: 'inactive') - # SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' - - After: - - class User < ActiveRecord::Base - default_scope { where state: 'pending' } - scope :active, -> { where state: 'active' } - scope :inactive, -> { where state: 'inactive' } - end - - User.all - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' - - User.active - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'active' - - User.where(state: 'inactive') - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' AND "users"."state" = 'inactive' - - To get the previous behavior it is needed to explicitly remove the - `default_scope` condition using `unscoped`, `unscope`, `rewhere` or - `except`. - - Example: - - class User < ActiveRecord::Base - default_scope { where state: 'pending' } - scope :active, -> { unscope(where: :state).where(state: 'active') } - scope :inactive, -> { rewhere state: 'inactive' } - end - - User.all - # SELECT "users".* FROM "users" WHERE "users"."state" = 'pending' - - User.active - # SELECT "users".* FROM "users" WHERE "users"."state" = 'active' - - User.inactive - # SELECT "users".* FROM "users" WHERE "users"."state" = 'inactive' - -* Perform necessary deeper encoding when hstore is inside an array. - - Fixes #11135. - - *Josh Goodall*, *Genadi Samokovarov* - -* Properly detect if a connection is still active before using it - in multi-threaded environments. - - Fixes #12867. - - *Kevin Casey*, *Matthew Draper*, *William (B.J.) Snow Orvis* - -* When inverting add_index use the index name if present instead of - the columns. - - If there are two indices with matching columns and one of them is - explicitly named then reverting the migration adding the named one - would instead drop the unnamed one. - - The inversion of add_index will now drop the index by its name if - it is present. - - *Hubert Dąbrowski* - -* Add flag to disable schema dump after migration. - - Add a config parameter on Active Record named `dump_schema_after_migration` - which is true by default. Now schema dump does not happen at the - end of migration rake task if `dump_schema_after_migration` is false. - - *Emil Soman* - -* `find_in_batches`, `find_each`, `Result#each` and `Enumerable#index_by` now - return an `Enumerator` that can calculate its size. - - See also #13938. - - *Marc-André Lafortune* - -* Make sure transaction state gets reset after a commit operation on the record. - - If a new transaction was open inside a callback, the record was loosing track - of the transaction level state, and it was leaking that state. - - Fixes #12566. - - *arthurnn* - -* Pass `has_and_belongs_to_many` `:autosave` option to - the underlying `has_many :through` association. - - Fixes #13923. - - *Yves Senn* - -* PostgreSQL implementation of `SchemaStatements#index_name_exists?`. - - The database agnostic implementation does not detect with indexes that are - not supported by the ActiveRecord schema dumper. For example, expressions - indexes would not be detected. - - Fixes #11018. - - *Jonathan Baudanza* - -* Parsing PostgreSQL arrays with empty strings now works correctly. - - Previously, if you tried to parse `{"1","","2","","3"}` the result - would be `["1","2","3"]`, removing the empty strings from the array, - which would be incorrect. Now it will correctly produce `["1","","2","","3"]` - as the result of parsing the above PostgreSQL array. - - Fixes #13907. - - *Maurício Linhares* - -* Associations now raise `ArgumentError` on name conflicts. - - Dangerous association names conflicts include instance or class methods already - defined by `ActiveRecord::Base`. - - Example: - - class Car < ActiveRecord::Base - has_many :errors - end - # Will raise ArgumentError. - - Fixes #13217. - - *Lauro Caetano* - -* Fix regressions on `select_*` methods. - When `select_*` methods receive a `Relation` object, they should be able to - get the arel/binds from it. - Also fix regressions on `select_rows` that was ignoring the binds. - - Fixes #7538, #12017, #13731, #12056. - - *arthurnn* - -* Active Record objects can now be correctly dumped, loaded and dumped again - without issues. - - Previously, if you did `YAML.dump`, `YAML.load` and then `YAML.dump` again - in an Active Record model that used serialization it would fail at the last - dump due to the fields not being correctly serialized before being dumped - to YAML. Now it is possible to dump and load the same object as many times - as needed without any issues. - - Fixes #13861. - - *Maurício Linhares* - -* `find_in_batches` now returns an `Enumerator` when called without a block, so that it - can be chained with other `Enumerable` methods. - - *Marc-André Lafortune* - -* `enum` now raises on "dangerous" name conflicts. - - Dangerous name conflicts includes instance or class method conflicts - with methods defined within `ActiveRecord::Base` but not its ancestors, - as well as conflicts with methods generated by other enums on the same - class. - - Fixes #13389. - - *Godfrey Chan* - -* `scope` now raises on "dangerous" name conflicts. - - Similar to dangerous attribute methods, a scope name conflict is - dangerous if it conflicts with an existing class method defined within - `ActiveRecord::Base` but not its ancestors. - - See also #13389. - - *Godfrey Chan*, *Philippe Creux* - -* Correctly send an user provided statement to a `lock!()` call. - - person.lock! 'FOR SHARE NOWAIT' - # Before: SELECT * ... LIMIT 1 FOR UPDATE - # After: SELECT * ... LIMIT 1 FOR SHARE NOWAIT - - Fixes #13788. - - *Maurício Linhares* - -* Handle aliased attributes `select()`, `order()` and `reorder()`. - - *Tsutomu Kuroda* - -* Reset the collection association when calling `reset` on it. - - Before: - - post.comments.loaded? # => true - post.comments.reset - post.comments.loaded? # => true - - After: - - post.comments.loaded? # => true - post.comments.reset - post.comments.loaded? # => false - - Fixes #13777. - - *Kelsey Schlarman* - -* Make enum fields work as expected with the `ActiveModel::Dirty` API. - - Before this change, using the dirty API would have surprising results: - - conversation = Conversation.new - conversation.status = :active - conversation.status = :archived - conversation.status_was # => 0 - - After this change, the same code would result in: - - conversation = Conversation.new - conversation.status = :active - conversation.status = :archived - conversation.status_was # => "active" - - *Rafael Mendonça França* - -* `has_one` and `belongs_to` accessors don't add ORDER BY to the queries - anymore. - - Since Rails 4.0, we add an ORDER BY in the `first` method to ensure - consistent results among different database engines. But for singular - associations this behavior is not needed since we will have one record to - return. As this ORDER BY option can lead some performance issues we are - removing it for singular associations accessors. - - Fixes #12623. - - *Rafael Mendonça França* - -* Prepend table name for column names passed to `Relation#select`. - - Example: - - Post.select(:id) - # Before: => SELECT id FROM "posts" - # After: => SELECT "posts"."id" FROM "posts" - - *Yves Senn* - -* Fail early with "Primary key not included in the custom select clause" - in `find_in_batches`. - - Before this patch, the exception was raised after the first batch was - yielded to the block. This means that you only get it, when you hit the - `batch_size` treshold. This could shadow the issue in development. - - *Alexander Balashov* - -* Ensure `second` through `fifth` methods act like the `first` finder. - - The famous ordinal Array instance methods defined in ActiveSupport - (`first`, `second`, `third`, `fourth`, and `fifth`) are now available as - full-fledged finders in ActiveRecord. The biggest benefit of this is ordering - of the records returned now defaults to the table's primary key in ascending order. - - Fixes #13743. - - Example: - - User.all.second - - # Before - # => 'SELECT "users".* FROM "users"' - - # After - # => SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 OFFSET 1' - - User.offset(3).second - - # Before - # => 'SELECT "users".* FROM "users" LIMIT -1 OFFSET 3' # sqlite3 gem - # => 'SELECT "users".* FROM "users" OFFSET 3' # pg gem - # => 'SELECT `users`.* FROM `users` LIMIT 18446744073709551615 OFFSET 3' # mysql2 gem - - # After - # => SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 OFFSET 4' - - *Jason Meller* - -* ActiveRecord states are now correctly restored after a rollback for - models that did not define any transactional callbacks (i.e. - `after_commit`, `after_rollback` or `after_create`). - - Fixes #13744. - - *Godfrey Chan* - -* Make `touch` fire the `after_commit` and `after_rollback` callbacks. - - *Harry Brundage* - -* Enable partial indexes for `sqlite >= 3.8.0`. - - See http://www.sqlite.org/partialindex.html - - *Cody Cutrer* - -* Don't try to get the subclass if the inheritance column doesn't exist - - The `subclass_from_attrs` method is called even if the column specified by - the `inheritance_column` setting doesn't exist. This prevents setting associations - via the attributes hash if the association name clashes with the value of the setting, - typically `:type`. This worked previously in Rails 3.2. - - *Ujjwal Thaakar* - -* Enum mappings are now exposed via class methods instead of constants. - - Example: - - class Conversation < ActiveRecord::Base - enum status: [ :active, :archived ] - end - - Before: - - Conversation::STATUS # => { "active" => 0, "archived" => 1 } - - After: - - Conversation.statuses # => { "active" => 0, "archived" => 1 } - - *Godfrey Chan* - -* Set `NameError#name` when STI-class-lookup fails. - - *Chulki Lee* - -* Fix bug in `becomes!` when changing from the base model to a STI sub-class. - - Fixes #13272. - - *the-web-dev*, *Yves Senn* - -* Currently Active Record can be configured via the environment variable - `DATABASE_URL` or by manually injecting a hash of values which is what Rails does, - reading in `database.yml` and setting Active Record appropriately. Active Record - expects to be able to use `DATABASE_URL` without the use of Rails, and we cannot - rip out this functionality without deprecating. This presents a problem though - when both config is set, and a `DATABASE_URL` is present. Currently the - `DATABASE_URL` should "win" and none of the values in `database.yml` are - used. This is somewhat unexpected, if one were to set values such as - `pool` in the `production:` group of `database.yml` they are ignored. - - There are many ways that Active Record initiates a connection today: - - - Stand Alone (without rails) - - `rake db:` - - `ActiveRecord.establish_connection` - - - With Rails - - `rake db:` - - `rails | ` - - `rails dbconsole` - - Now all of these behave exactly the same way. The best way to do - this is to put all of this logic in one place so it is guaranteed to be used. - - Here is the matrix of how this behavior works: - - ``` - No database.yml - No DATABASE_URL - => Error - ``` - - ``` - database.yml present - No DATABASE_URL - => Use database.yml configuration - ``` - - ``` - No database.yml - DATABASE_URL present - => use DATABASE_URL configuration - ``` - - ``` - database.yml present - DATABASE_URL present - => Merged into `url` sub key. If both specify `url` sub key, the `database.yml` `url` - sub key "wins". If other paramaters `adapter` or `database` are specified in YAML, - they are discarded as the `url` sub key "wins". - ``` - - Current implementation uses `ActiveRecord::Base.configurations` to resolve and merge - all connection information before returning. This is achieved through a utility - class: `ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig`. - - To understand the exact behavior of this class, it is best to review the - behavior in `activerecord/test/cases/connection_adapters/connection_handler_test.rb`. - - *Richard Schneeman* - -* Make `change_column_null` revertable. Fixes #13576. - - *Yves Senn*, *Nishant Modak*, *Prathamesh Sonpatki* - -* Don't create/drop the test database if RAILS_ENV is specified explicitly. - - Previously, when the environment was development, we would always - create or drop both the test and development databases. - - Now, if RAILS_ENV is explicitly defined as development, we don't create - the test database. - - *Damien Mathieu* - -* Initialize version on Migration objects so that it can be used in a migration, - and it will be included in the announce message. - - *Dylan Thacker-Smith* - -* `change_table` now uses the current adapter's `update_table_definition` - method to retrieve a specific table definition. - This ensures that `change_table` and `create_table` will use - similar objects. - - Fixes #13577, #13503. - - *Nishant Modak*, *Prathamesh Sonpatki*, *Rafael Mendonça França* - -* Fixed ActiveRecord::Store nil conversion TypeError when using YAML coder. - In case the YAML passed as paramter is nil, uses an empty string. - - Fixes #13570. - - *Thales Oliveira* - -* Deprecate unused `ActiveRecord::Base.symbolized_base_class` - and `ActiveRecord::Base.symbolized_sti_name` without replacement. - - *Yves Senn* - -* Since the `test_help.rb` file in Railties now automatically maintains - your test schema, the `rake db:test:*` tasks are deprecated. This - doesn't stop you manually running other tasks on your test database - if needed: - - rake db:schema:load RAILS_ENV=test - - *Jon Leighton* - -* Fix presence validator for association when the associated record responds to `to_a`. - - *gmarik* - -* Fixed regression on preload/includes with multiple arguments failing in certain conditions, - raising a NoMethodError internally by calling `reflect_on_association` for `NilClass:Class`. - - Fixes #13437. - - *Vipul A M*, *khustochka* - -* Add the ability to nullify the `enum` column. - - Example: - - class Conversation < ActiveRecord::Base - enum gender: [:female, :male] - end - - Conversation::GENDER # => { female: 0, male: 1 } - - # conversation.update! gender: 0 - conversation.female! - conversation.female? # => true - conversation.gender # => "female" - - # conversation.update! gender: nil - conversation.gender = nil - conversation.gender.nil? # => true - conversation.gender # => nil - - *Amr Tamimi* - -* Connection specification now accepts a "url" key. The value of this - key is expected to contain a database URL. The database URL will be - expanded into a hash and merged. - - *Richard Schneeman* - -* An `ArgumentError` is now raised on a call to `Relation#where.not(nil)`. - - Example: - - User.where.not(nil) - - # Before - # => 'SELECT `users`.* FROM `users` WHERE (NOT (NULL))' - - # After - # => ArgumentError, 'Invalid argument for .where.not(), got nil.' - - *Kuldeep Aggarwal* - -* Deprecated use of string argument as a configuration lookup in - `ActiveRecord::Base.establish_connection`. Instead, a symbol must be given. - - *José Valim* - -* Fixed `update_column`, `update_columns`, and `update_all` to correctly serialize - values for `array`, `hstore` and `json` column types in PostgreSQL. - - Fixes #12261. - - *Tadas Tamosauskas*, *Carlos Antonio da Silva* - -* Do not consider PostgreSQL array columns as number or text columns. - - The code uses these checks in several places to know what to do with a - particular column, for instance AR attribute query methods has a branch - like this: - - if column.number? - !value.zero? - end - - This should never be true for array columns, since it would be the same - as running [].zero?, which results in a NoMethodError exception. - - Fixing this by ensuring that array columns in PostgreSQL never return - true for number?/text? checks. - - *Carlos Antonio da Silva* - -* When connecting to a non-existant database, the error: - `ActiveRecord::NoDatabaseError` will now be raised. When being used with Rails - the error message will include information on how to create a database: - `rake db:create`. Supported adapters: postgresql, mysql, mysql2, sqlite3 - - *Richard Schneeman* - -* Do not raise `'cannot touch on a new record object'` exception on destroying - already destroyed `belongs_to` association with `touch: true` option. - - Fixes #13445. - - Example: - - # Given Comment has belongs_to :post, touch: true - comment.post.destroy - comment.destroy # no longer raises an error - - *Paul Nikitochkin* - -* Fix a bug when assigning an array containing string numbers to a - PostgreSQL integer array column. - - Fixes #13444. - - Example: - - # Given Book#ratings is of type :integer, array: true - Book.new(ratings: [1, 2]) # worked before - Book.new(ratings: ['1', '2']) # now works as well - - *Damien Mathieu* - -* Fix `PostgreSQL` insert to properly extract table name from multiline string SQL. - - Previously, executing an insert SQL in `PostgreSQL` with a command like this: - - insert into articles( - number) - values( - 5152 - ) - - would not work because the adapter was unable to extract the correct `articles` - table name. - - *Kuldeep Aggarwal* - -* Correctly escape PostgreSQL arrays. - - Fixes: CVE-2014-0080 - -* `Relation` no longer has mutator methods like `#map!` and `#delete_if`. Convert - to an `Array` by calling `#to_a` before using these methods. - - It intends to prevent odd bugs and confusion in code that call mutator - methods directly on the `Relation`. - - Example: - - # Instead of this - Author.where(name: 'Hank Moody').compact! - - # Now you have to do this - authors = Author.where(name: 'Hank Moody').to_a - authors.compact! - - *Lauro Caetano* - -* Better support for `where()` conditions that use a `belongs_to` - association name. - - Using the name of an association in `where` previously worked only - if the value was a single `ActiveRecord::Base` object. e.g. - - Post.where(author: Author.first) - - Any other values, including `nil`, would cause invalid SQL to be - generated. This change supports arguments in the `where` query - conditions where the key is a `belongs_to` association name and the - value is `nil`, an `Array` of `ActiveRecord::Base` objects, or an - `ActiveRecord::Relation` object. - - class Post < ActiveRecord::Base - belongs_to :author - end - - `nil` value finds records where the association is not set: - - Post.where(author: nil) - # SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IS NULL - - `Array` values find records where the association foreign key - matches the ids of the passed ActiveRecord models, resulting - in the same query as `Post.where(author_id: [1,2])`: - - authors_array = [Author.find(1), Author.find(2)] - Post.where(author: authors_array) - # SELECT "posts".* FROM "posts" WHERE "posts"."author_id" IN (1, 2) - - `ActiveRecord::Relation` values find records using the same - query as `Post.where(author_id: Author.where(last_name: "Emde"))` - - Post.where(author: Author.where(last_name: "Emde")) - # SELECT "posts".* FROM "posts" - # WHERE "posts"."author_id" IN ( - # SELECT "authors"."id" FROM "authors" - # WHERE "authors"."last_name" = 'Emde') - - Polymorphic `belongs_to` associations will continue to be handled - appropriately, with the polymorphic `association_type` field added - to the query to match the base class of the value. This feature - previously only worked when the value was a single `ActveRecord::Base`. - - class Post < ActiveRecord::Base - belongs_to :author, polymorphic: true - end - - Post.where(author: Author.where(last_name: "Emde")) - # Generates a query similar to: - Post.where(author_id: Author.where(last_name: "Emde"), author_type: "Author") - - *Martin Emde* - -* Respect temporary option when dropping tables with MySQL. - - Normal DROP TABLE also works, but commits the transaction. - - drop_table :temporary_table, temporary: true - - *Cody Cutrer* - -* Add option to create tables from a query. - - create_table(:long_query, temporary: true, - as: "SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id") - - Generates: - - CREATE TEMPORARY TABLE long_query AS - SELECT * FROM orders INNER JOIN line_items ON order_id=orders.id - - *Cody Cutrer* - -* `db:test:clone` and `db:test:prepare` must load Rails environment. - - `db:test:clone` and `db:test:prepare` use `ActiveRecord::Base`. configurations, - so we need to load the Rails environment, otherwise the config wont be in place. - - *arthurnn* - -* Use the right column to type cast grouped calculations with custom expressions. - - Fixes #13230. - - Example: - - # Before - Account.group(:firm_name).sum('0.01 * credit_limit') - # => { '37signals' => '0.5' } - - # After - Account.group(:firm_name).sum('0.01 * credit_limit') - # => { '37signals' => 0.5 } - - *Paul Nikitochkin* - -* Polymorphic `belongs_to` associations with the `touch: true` option set update the timestamps of - the old and new owner correctly when moved between owners of different types. - - Example: - - class Rating < ActiveRecord::Base - belongs_to :rateable, polymorphic: true, touch: true - end - - rating = Rating.create rateable: Song.find(1) - rating.update_attributes rateable: Book.find(2) # => timestamps of Song(1) and Book(2) are updated - - *Severin Schoepke* - -* Improve formatting of migration exception messages: make them easier to read - with line breaks before/after, and improve the error for pending migrations. - - *John Bachir* - -* Fix `last` with `offset` to return the proper record instead of always the last one. - - Example: - - Model.offset(4).last - # => returns the 4th record from the end. - - Fixes #7441. - - *kostya*, *Lauro Caetano* - -* `type_to_sql` returns a `String` for unmapped columns. This fixes an error - when using unmapped PostgreSQL array types. - - Example: - - change_colum :table, :column, :bigint, array: true - - Fixes #13146. - - *Jens Fahnenbruck*, *Yves Senn* - -* Fix `QueryCache` to work with nested blocks, so that it will only clear the existing cache - after leaving the outer block instead of clearing it right after the inner block is finished. - - *Vipul A M* - -* The ERB in fixture files is no longer evaluated in the context of the main - object. Helper methods used by multiple fixtures should be defined on the - class object returned by `ActiveRecord::FixtureSet.context_class`. - - *Victor Costan* - -* Previously, the `has_one` macro incorrectly accepted the `counter_cache` - option, but never actually supported it. Now it will raise an `ArgumentError` - when using `has_one` with `counter_cache`. - - *Godfrey Chan* - -* Implement `rename_index` natively for MySQL >= 5.7. - - *Cody Cutrer* - -* Fix bug when validating the uniqueness of an aliased attribute. - - Fixes #12402. - - *Lauro Caetano* - -* Update counter cache on a `has_many` relationship regardless of default scope. - - Fixes #12952. - - *Uku Taht* - -* `rename_index` adds the new index before removing the old one. This allows to - rename indexes on columns with a foreign key and prevents the following error: - - Cannot drop index 'index_engines_on_car_id': needed in a foreign key constraint - - *Cody Cutrer*, *Yves Senn* - -* Raise `ActiveRecord::RecordNotDestroyed` when a replaced child - marked with `dependent: destroy` fails to be destroyed. - - Fixes #12812. - - *Brian Thomas Storti* - -* Fix validation on uniqueness of empty association. - - *Evgeny Li* - -* Make `ActiveRecord::Relation#unscope` affect relations it is merged in to. - - *Jon Leighton* - -* Use strings to represent non-string `order_values`. - - *Yves Senn* - -* Checks to see if the record contains the foreign key to set the inverse automatically. - - *Edo Balvers* - -* Added `ActiveRecord::Base.to_param` for convenient "pretty" URLs derived from a model's attribute or method. - - Example: - - class User < ActiveRecord::Base - to_param :name - end - - user = User.find_by(name: 'Fancy Pants') - user.id # => 123 - user.to_param # => "123-fancy-pants" - - *Javan Makhmali* - -* Added `ActiveRecord::Base.no_touching`, which allows ignoring touch on models. - - Example: - - Post.no_touching do - Post.first.touch - end - - *Sam Stephenson*, *Damien Mathieu* - -* Prevent the counter cache from being decremented twice when destroying - a record on a `has_many :through` association. - - Fixes #11079. - - *Dmitry Dedov* - -* Unify boolean type casting for `MysqlAdapter` and `Mysql2Adapter`. - `type_cast` will return `1` for `true` and `0` for `false`. - - Fixes #11119. - - *Adam Williams*, *Yves Senn* - -* Fix bug where `has_one` association record update result in crash, when replaced with itself. - - Fixes #12834. - - *Denis Redozubov*, *Sergio Cambra* - -* Log bind variables after they are type casted. This makes it more - transparent what values are actually sent to the database. - - irb(main):002:0> Event.find("im-no-integer") - # Before: ... WHERE "events"."id" = $1 LIMIT 1 [["id", "im-no-integer"]] - # After: ... WHERE "events"."id" = $1 LIMIT 1 [["id", 0]] - - *Yves Senn* - -* Fix uninitialized constant `TransactionState` error when `Marshall.load` is used on an Active Record result. - - Fixes #12790. - - *Jason Ayre* - -* `.unscope` now removes conditions specified in `default_scope`. - - *Jon Leighton* - -* Added `ActiveRecord::QueryMethods#rewhere` which will overwrite an existing, named where condition. - - Examples: - - Post.where(trashed: true).where(trashed: false) #=> WHERE `trashed` = 1 AND `trashed` = 0 - Post.where(trashed: true).rewhere(trashed: false) #=> WHERE `trashed` = 0 - Post.where(active: true).where(trashed: true).rewhere(trashed: false) #=> WHERE `active` = 1 AND `trashed` = 0 - - *DHH* - -* Extend `ActiveRecord::Base#cache_key` to take an optional list of timestamp attributes of which the highest will be used. - - Example: - - # last_reviewed_at will be used, if that's more recent than updated_at, or vice versa - Person.find(5).cache_key(:updated_at, :last_reviewed_at) - - *DHH* - -* Added `ActiveRecord::Base#enum` for declaring enum attributes where the values map to integers in the database, but can be queried by name. - - Example: - - class Conversation < ActiveRecord::Base - enum status: [:active, :archived] - end - - Conversation::STATUS # => { active: 0, archived: 1 } - - # conversation.update! status: 0 - conversation.active! - conversation.active? # => true - conversation.status # => "active" - - # conversation.update! status: 1 - conversation.archived! - conversation.archived? # => true - conversation.status # => "archived" - - # conversation.update! status: 1 - conversation.status = :archived - - *DHH* - -* `ActiveRecord::Base#attribute_for_inspect` now truncates long arrays (more than 10 elements). - - *Jan Bernacki* - -* Allow for the name of the `schema_migrations` table to be configured. - - *Jerad Phelps* - -* Do not add to scope includes values from through associations. - Fixed bug when providing `includes` in through association scope, and fetching targets. - - Example: - - class Vendor < ActiveRecord::Base - has_many :relationships, -> { includes(:user) } - has_many :users, through: :relationships - end - - vendor = Vendor.first - - # Before - - vendor.users.to_a # => Raises exception: not found `:user` for `User` - - # After - - vendor.users.to_a # => No exception is raised - - Fixes #12242, #9517, #10240. - - *Paul Nikitochkin* - -* Type cast json values on write, so that the value is consistent - with reading from the database. - - Example: - - x = JsonDataType.new tags: {"string" => "foo", :symbol => :bar} - - # Before: - x.tags # => {"string" => "foo", :symbol => :bar} - - # After: - x.tags # => {"string" => "foo", "symbol" => "bar"} - - *Severin Schoepke* - -* `ActiveRecord::Store` works together with PostgreSQL `hstore` columns. - - Fixes #12452. - - *Yves Senn* - -* Fix bug where `ActiveRecord::Store` used a global `Hash` to keep track of - all registered `stored_attributes`. Now every subclass of - `ActiveRecord::Base` has it's own `Hash`. - - *Yves Senn* - -* Save `has_one` association when primary key is manually set. - - Fixes #12302. - - *Lauro Caetano* - -* Allow any version of BCrypt when using `has_secure_password`. - - *Mike Perham* - -* Sub-query generated for `Relation` passed as array condition did not take in account - bind values and have invalid syntax. - - Generate sub-query with inline bind values. - - Fixes #12586. - - *Paul Nikitochkin* - -* Fix a bug where rake db:structure:load crashed when the path contained - spaces. - - *Kevin Mook* - -* `ActiveRecord::QueryMethods#unscope` unscopes negative equality - - Allows you to call `#unscope` on a relation with negative equality - operators, i.e. `Arel::Nodes::NotIn` and `Arel::Nodes::NotEqual` that have - been generated through the use of `where.not`. - - *Eric Hankins* - -* Raise an exception when model without primary key calls `.find_with_ids`. - - *Shimpei Makimoto* - -* Make `Relation#empty?` use `exists?` instead of `count`. - - *Szymon Nowak* - -* `rake db:structure:dump` no longer crashes when the port was specified as `Fixnum`. - - *Kenta Okamoto* - -* `NullRelation#pluck` takes a list of columns - - The method signature in `NullRelation` was updated to mimic that in - `Calculations`. - - *Derek Prior* - -* `scope_chain` should not be mutated for other reflections. - - Currently `scope_chain` uses same array for building different - `scope_chain` for different associations. During processing - these arrays are sometimes mutated and because of in-place - mutation the changed `scope_chain` impacts other reflections. - - Fix is to dup the value before adding to the `scope_chain`. - - Fixes #3882. - - *Neeraj Singh* - -* Prevent the inversed association from being reloaded on save. - - Fixes #9499. - - *Dmitry Polushkin* - -* Generate subquery for `Relation` if it passed as array condition for `where` - method. - - Example: - - # Before - Blog.where('id in (?)', Blog.where(id: 1)) - # => SELECT "blogs".* FROM "blogs" WHERE "blogs"."id" = 1 - # => SELECT "blogs".* FROM "blogs" WHERE (id IN (1)) - - # After - Blog.where('id in (?)', Blog.where(id: 1).select(:id)) - # => SELECT "blogs".* FROM "blogs" - # WHERE "blogs"."id" IN (SELECT "blogs"."id" FROM "blogs" WHERE "blogs"."id" = 1) - - Fixes #12415. - - *Paul Nikitochkin* - -* For missed association exception message - which is raised in `ActiveRecord::Associations::Preloader` class - added owner record class name in order to simplify to find problem code. - - *Paul Nikitochkin* - -* `has_and_belongs_to_many` is now transparently implemented in terms of - `has_many :through`. Behavior should remain the same, if not, it is a bug. - -* `create_savepoint`, `rollback_to_savepoint` and `release_savepoint` accept - a savepoint name. - - *Yves Senn* - -* Make `next_migration_number` accessible for third party generators. - - *Yves Senn* - -* Objects instantiated using a null relationship will now retain the - attributes of the where clause. - - Fixes #11676, #11675, #11376. - - *Paul Nikitochkin*, *Peter Brown*, *Nthalk* - -* Fixed `ActiveRecord::Associations::CollectionAssociation#find` - when using `has_many` association with `:inverse_of` and finding an array of one element, - it should return an array of one element too. - - *arthurnn* - -* Callbacks on has_many should access the in memory parent if a inverse_of is set. - - *arthurnn* - -* `ActiveRecord::ConnectionAdapters.string_to_time` respects - string with timezone (e.g. Wed, 04 Sep 2013 20:30:00 JST). - - Fixes #12278. - - *kennyj* - -* Calling `update_attributes` will now throw an `ArgumentError` whenever it - gets a `nil` argument. More specifically, it will throw an error if the - argument that it gets passed does not respond to to `stringify_keys`. - - Example: - - @my_comment.update_attributes(nil) # => raises ArgumentError - - *John Wang* - -* Deprecate `quoted_locking_column` method, which isn't used anywhere. - - *kennyj* - -* Migration dump UUID default functions to schema.rb. - - Fixes #10751. - - *kennyj* - -* Fixed a bug in `ActiveRecord::Associations::CollectionAssociation#find_by_scan` - when using `has_many` association with `:inverse_of` option and UUID primary key. - - Fixes #10450. - - *kennyj* - -* Fix: joins association, with defined in the scope block constraints by using several - where constraints and at least of them is not `Arel::Nodes::Equality`, - generates invalid SQL expression. - - Fixes #11963. - - *Paul Nikitochkin* - -* `CollectionAssociation#first`/`#last` (e.g. `has_many`) use a `LIMIT`ed - query to fetch results rather than loading the entire collection. - - *Lann Martin* - -* Make possible to run SQLite rake tasks without the `Rails` constant defined. - - *Damien Mathieu* - -* Allow Relation#from to accept other relations with bind values. - - *Ryan Wallace* - -* Fix inserts with prepared statements disabled. - - Fixes #12023. - - *Rafael Mendonça França* - -* Setting a has_one association on a new record no longer causes an empty - transaction. - - *Dylan Thacker-Smith* - -* Fix `AR::Relation#merge` sometimes failing to preserve `readonly(false)` flag. - - *thedarkone* - -* Re-use `order` argument pre-processing for `reorder`. - - *Paul Nikitochkin* - -* Fix PredicateBuilder so polymorphic association keys in `where` clause can - accept objects other than direct descendants of `ActiveRecord::Base` (decorated - models, for example). - - *Mikhail Dieterle* - -* PostgreSQL adapter recognizes negative money values formatted with - parentheses (eg. `($1.25) # => -1.25`)). - Fixes #11899. - - *Yves Senn* - -* Stop interpreting SQL 'string' columns as :string type because there is no - common STRING datatype in SQL. - - *Ben Woosley* - -* `ActiveRecord::FinderMethods#exists?` returns `true`/`false` in all cases. - - *Xavier Noria* - -* Assign inet/cidr attribute with `nil` value for invalid address. - - Example: - - record = User.new - record.logged_in_from_ip # is type of an inet or a cidr - - # Before: - record.logged_in_from_ip = 'bad ip address' # raise exception - - # After: - record.logged_in_from_ip = 'bad ip address' # do not raise exception - record.logged_in_from_ip # => nil - record.logged_in_from_ip_before_type_cast # => 'bad ip address' - - *Paul Nikitochkin* - -* `add_to_target` now accepts a second optional `skip_callbacks` argument - - If truthy, it will skip the :before_add and :after_add callbacks. - - *Ben Woosley* - -* Fix interactions between `:before_add` callbacks and nested attributes - assignment of `has_many` associations, when the association was not - yet loaded: - - - A `:before_add` callback was being called when a nested attributes - assignment assigned to an existing record. - - - Nested Attributes assignment did not affect the record in the - association target when a `:before_add` callback triggered the - loading of the association - - *Jörg Schray* - -* Allow enable_extension migration method to be revertible. - - *Eric Tipton* - -* Type cast hstore values on write, so that the value is consistent - with reading from the database. - - Example: - - x = Hstore.new tags: {"bool" => true, "number" => 5} - - # Before: - x.tags # => {"bool" => true, "number" => 5} - - # After: - x.tags # => {"bool" => "true", "number" => "5"} - - *Yves Senn* , *Severin Schoepke* - -* Fix multidimensional PostgreSQL arrays containing non-string items. - - *Yves Senn* - -* Fixes bug when using includes combined with select, the select statement was overwritten. - - Fixes #11773. - - *Edo Balvers* - -* Load fixtures from linked folders. - - *Kassio Borges* - -* Create a directory for sqlite3 file if not present on the system. - - *Richard Schneeman* - -* Removed redundant override of `xml` column definition for PostgreSQL, - in order to use `xml` column type instead of `text`. - - *Paul Nikitochkin*, *Michael Nikitochkin* - -* Revert `ActiveRecord::Relation#order` change that make new order - prepend the old one. - - Before: - - User.order("name asc").order("created_at desc") - # SELECT * FROM users ORDER BY created_at desc, name asc - - After: - - User.order("name asc").order("created_at desc") - # SELECT * FROM users ORDER BY name asc, created_at desc - - This also affects order defined in `default_scope` or any kind of associations. - -* Add ability to define how a class is converted to Arel predicates. - For example, adding a very vendor specific regex implementation: - - regex_handler = proc do |column, value| - Arel::Nodes::InfixOperation.new('~', column, value.source) - end - ActiveRecord::PredicateBuilder.register_handler(Regexp, regex_handler) - - *Sean Griffin & @joannecheng* - -* Don't allow `quote_value` to be called without a column. - - Some adapters require column information to do their job properly. - By enforcing the provision of the column for this internal method - we ensure that those using adapters that require column information - will always get the proper behavior. - - *Ben Woosley* - -* When using optimistic locking, `update` was not passing the column to `quote_value` - to allow the connection adapter to properly determine how to quote the value. This was - affecting certain databases that use specific column types. - - Fixes #6763. - - *Alfred Wong* - -* rescue from all exceptions in `ConnectionManagement#call` - - Fixes #11497. - - As `ActiveRecord::ConnectionAdapters::ConnectionManagement` middleware does - not rescue from Exception (but only from StandardError), the Connection - Pool quickly runs out of connections when multiple erroneous Requests come - in right after each other. - - Rescuing from all exceptions and not just StandardError, fixes this - behaviour. - - *Vipul A M* - -* `change_column` for PostgreSQL adapter respects the `:array` option. - - *Yves Senn* - -* Remove deprecation warning from `attribute_missing` for attributes that are columns. - - *Arun Agrawal* - -* Remove extra decrement of transaction deep level. - - Fixes #4566. - - *Paul Nikitochkin* - -* Reset @column_defaults when assigning `locking_column`. - We had a potential problem. For example: - - class Post < ActiveRecord::Base - self.column_defaults # if we call this unintentionally before setting locking_column ... - self.locking_column = 'my_locking_column' - end - - Post.column_defaults["my_locking_column"] - => nil # expected value is 0 ! - - *kennyj* - -* Remove extra select and update queries on save/touch/destroy ActiveRecord model - with belongs to reflection with option `touch: true`. - - Fixes #11288. - - *Paul Nikitochkin* - -* Remove deprecated nil-passing to the following `SchemaCache` methods: - `primary_keys`, `tables`, `columns` and `columns_hash`. - - *Yves Senn* - -* Remove deprecated block filter from `ActiveRecord::Migrator#migrate`. - - *Yves Senn* - -* Remove deprecated String constructor from `ActiveRecord::Migrator`. - - *Yves Senn* - -* Remove deprecated `scope` use without passing a callable object. - - *Arun Agrawal* - -* Remove deprecated `transaction_joinable=` in favor of `begin_transaction` - with `:joinable` option. - - *Arun Agrawal* - -* Remove deprecated `decrement_open_transactions`. - - *Arun Agrawal* - -* Remove deprecated `increment_open_transactions`. - - *Arun Agrawal* - -* Remove deprecated `PostgreSQLAdapter#outside_transaction?` - method. You can use `#transaction_open?` instead. - - *Yves Senn* - -* Remove deprecated `ActiveRecord::Fixtures.find_table_name` in favor of - `ActiveRecord::Fixtures.default_fixture_model_name`. - - *Vipul A M* - -* Removed deprecated `columns_for_remove` from `SchemaStatements`. - - *Neeraj Singh* - -* Remove deprecated `SchemaStatements#distinct`. - - *Francesco Rodriguez* - -* Move deprecated `ActiveRecord::TestCase` into the rails test - suite. The class is no longer public and is only used for internal - Rails tests. - - *Yves Senn* - -* Removed support for deprecated option `:restrict` for `:dependent` - in associations. - - *Neeraj Singh* - -* Removed support for deprecated `delete_sql` in associations. - - *Neeraj Singh* - -* Removed support for deprecated `insert_sql` in associations. - - *Neeraj Singh* - -* Removed support for deprecated `finder_sql` in associations. - - *Neeraj Singh* - -* Support array as root element in JSON fields. - - *Alexey Noskov & Francesco Rodriguez* - -* Removed support for deprecated `counter_sql` in associations. - - *Neeraj Singh* - -* Do not invoke callbacks when `delete_all` is called on collection. - - Method `delete_all` should not be invoking callbacks and this - feature was deprecated in Rails 4.0. This is being removed. - `delete_all` will continue to honor the `:dependent` option. However - if `:dependent` value is `:destroy` then the `:delete_all` deletion - strategy for that collection will be applied. - - User can also force a deletion strategy by passing parameter to - `delete_all`. For example you can do `@post.comments.delete_all(:nullify)`. - - *Neeraj Singh* - -* Calling default_scope without a proc will now raise `ArgumentError`. - - *Neeraj Singh* - -* Removed deprecated method `type_cast_code` from Column. - - *Neeraj Singh* - -* Removed deprecated options `delete_sql` and `insert_sql` from HABTM - association. - - Removed deprecated options `finder_sql` and `counter_sql` from - collection association. - - *Neeraj Singh* - -* Remove deprecated `ActiveRecord::Base#connection` method. - Make sure to access it via the class. - - *Yves Senn* - -* Remove deprecation warning for `auto_explain_threshold_in_seconds`. - - *Yves Senn* - -* Remove deprecated `:distinct` option from `Relation#count`. - - *Yves Senn* - -* Removed deprecated methods `partial_updates`, `partial_updates?` and - `partial_updates=`. - - *Neeraj Singh* - -* Removed deprecated method `scoped`. - - *Neeraj Singh* - -* Removed deprecated method `default_scopes?`. - - *Neeraj Singh* - -* Remove implicit join references that were deprecated in 4.0. - - Example: - - # before with implicit joins - Comment.where('posts.author_id' => 7) - - # after - Comment.references(:posts).where('posts.author_id' => 7) - - *Yves Senn* - -* Apply default scope when joining associations. For example: - - class Post < ActiveRecord::Base - default_scope -> { where published: true } - end - - class Comment - belongs_to :post - end - - When calling `Comment.joins(:post)`, we expect to receive only - comments on published posts, since that is the default scope for - posts. - - Before this change, the default scope from `Post` was not applied, - so we'd get comments on unpublished posts. - - *Jon Leighton* - -* Remove `activerecord-deprecated_finders` as a dependency. - - *Łukasz Strzałkowski* - -* Remove Oracle / Sqlserver / Firebird database tasks that were deprecated in 4.0. - - *kennyj* - -* `find_each` now returns an `Enumerator` when called without a block, so that it - can be chained with other `Enumerable` methods. - - *Ben Woosley* - -* `ActiveRecord::Result.each` now returns an `Enumerator` when called without - a block, so that it can be chained with other `Enumerable` methods. - - *Ben Woosley* - -* Flatten merged join_values before building the joins. - - While joining_values special treatment is given to string values. - By flattening the array it ensures that string values are detected - as strings and not arrays. - - Fixes #10669. - - *Neeraj Singh and iwiznia* - -* Do not load all child records for inverse case. - - currently `post.comments.find(Comment.first.id)` would load all - comments for the given post to set the inverse association. - - This has a huge performance penalty. Because if post has 100k - records and all these 100k records would be loaded in memory - even though the comment id was supplied. - - Fix is to use in-memory records only if loaded? is true. Otherwise - load the records using full sql. - - Fixes #10509. - - *Neeraj Singh* - -* `inspect` on Active Record model classes does not initiate a - new connection. This means that calling `inspect`, when the - database is missing, will no longer raise an exception. - Fixes #10936. - - Example: - - Author.inspect # => "Author(no database connection)" - - *Yves Senn* - -* Handle single quotes in PostgreSQL default column values. - Fixes #10881. - - *Dylan Markow* - -* Log the sql that is actually sent to the database. - - If I have a query that produces sql - `WHERE "users"."name" = 'a b'` then in the log all the - whitespace is being squeezed. So the sql that is printed in the - log is `WHERE "users"."name" = 'a b'`. - - Do not squeeze whitespace out of sql queries. Fixes #10982. - - *Neeraj Singh* - -* Fixture setup no longer depends on `ActiveRecord::Base.configurations`. - This is relevant when `ENV["DATABASE_URL"]` is used in place of a `database.yml`. - - *Yves Senn* - -* Fix mysql2 adapter raises the correct exception when executing a query on a - closed connection. - - *Yves Senn* - -* Ambiguous reflections are on :through relationships are no longer supported. - For example, you need to change this: - - class Author < ActiveRecord::Base - has_many :posts - has_many :taggings, through: :posts - end - - class Post < ActiveRecord::Base - has_one :tagging - has_many :taggings - end - - class Tagging < ActiveRecord::Base - end - - To this: - - class Author < ActiveRecord::Base - has_many :posts - has_many :taggings, through: :posts, source: :tagging - end - - class Post < ActiveRecord::Base - has_one :tagging - has_many :taggings - end - - class Tagging < ActiveRecord::Base - end - - *Aaron Patterson* - -* Remove column restrictions for `count`, let the database raise if the SQL is - invalid. The previous behavior was untested and surprising for the user. - Fixes #5554. - - Example: - - User.select("name, username").count - # Before => SELECT count(*) FROM users - # After => ActiveRecord::StatementInvalid - - # you can still use `count(:all)` to perform a query unrelated to the - # selected columns - User.select("name, username").count(:all) # => SELECT count(*) FROM users - - *Yves Senn* - -* Rails now automatically detects inverse associations. If you do not set the - `:inverse_of` option on the association, then Active Record will guess the - inverse association based on heuristics. - - Note that automatic inverse detection only works on `has_many`, `has_one`, - and `belongs_to` associations. Extra options on the associations will - also prevent the association's inverse from being found automatically. - - The automatic guessing of the inverse association uses a heuristic based - on the name of the class, so it may not work for all associations, - especially the ones with non-standard names. - - You can turn off the automatic detection of inverse associations by setting - the `:inverse_of` option to `false` like so: - - class Taggable < ActiveRecord::Base - belongs_to :tag, inverse_of: false - end - - *John Wang* - -* Fix `add_column` with `array` option when using PostgreSQL. Fixes #10432. - - *Adam Anderson* - -* Usage of `implicit_readonly` is being removed`. Please use `readonly` method - explicitly to mark records as `readonly. - Fixes #10615. - - Example: - - user = User.joins(:todos).select("users.*, todos.title as todos_title").readonly(true).first - user.todos_title = 'clean pet' - user.save! # will raise error - - *Yves Senn* - -* Fix the `:primary_key` option for `has_many` associations. - - Fixes #10693. - - *Yves Senn* - -* Fix bug where tiny types are incorrectly coerced as boolean when the length is more than 1. - - Fixes #10620. - - *Aaron Patterson* - -* Also support extensions in PostgreSQL 9.1. This feature has been supported since 9.1. - - *kennyj* - -* Deprecate `ConnectionAdapters::SchemaStatements#distinct`, - as it is no longer used by internals. - - *Ben Woosley* - -* Fix pending migrations error when loading schema and `ActiveRecord::Base.table_name_prefix` - is not blank. - - Call `assume_migrated_upto_version` on connection to prevent it from first - being picked up in `method_missing`. - - In the base class, `Migration`, `method_missing` expects the argument to be a - table name, and calls `proper_table_name` on the arguments before sending to - `connection`. If `table_name_prefix` or `table_name_suffix` is used, the schema - version changes to `prefix_version_suffix`, breaking `rake test:prepare`. - - Fixes #10411. - - *Kyle Stevens* - -* Method `read_attribute_before_type_cast` should accept input as symbol. - - *Neeraj Singh* - -* Confirm a record has not already been destroyed before decrementing counter cache. - - *Ben Tucker* - -* Fixed a bug in `ActiveRecord#sanitize_sql_hash_for_conditions` in which - `self.class` is an argument to `PredicateBuilder#build_from_hash` - causing `PredicateBuilder` to call non-existent method - `Class#reflect_on_association`. - - *Zach Ohlgren* - -* While removing index if column option is missing then raise IrreversibleMigration exception. - - Following code should raise `IrreversibleMigration`. But the code was - failing since options is an array and not a hash. - - def change - change_table :users do |t| - t.remove_index [:name, :email] - end - end - - Fix was to check if the options is a Hash before operating on it. - - Fixes #10419. - - *Neeraj Singh* - -* Do not overwrite manually built records during one-to-one nested attribute assignment - - For one-to-one nested associations, if you build the new (in-memory) - child object yourself before assignment, then the NestedAttributes - module will not overwrite it, e.g.: - - class Member < ActiveRecord::Base - has_one :avatar - accepts_nested_attributes_for :avatar - - def avatar - super || build_avatar(width: 200) - end - end - - member = Member.new - member.avatar_attributes = {icon: 'sad'} - member.avatar.width # => 200 - - *Olek Janiszewski* - -* fixes bug introduced by #3329. Now, when autosaving associations, - deletions happen before inserts and saves. This prevents a 'duplicate - unique value' database error that would occur if a record being created had - the same value on a unique indexed field as that of a record being destroyed. - - *Johnny Holton* - -* Handle aliased attributes in ActiveRecord::Relation. - - When using symbol keys, ActiveRecord will now translate aliased attribute names to the actual column name used in the database: - - With the model - - class Topic - alias_attribute :heading, :title - end - - The call - - Topic.where(heading: 'The First Topic') - - should yield the same result as - - Topic.where(title: 'The First Topic') - - This also applies to ActiveRecord::Relation::Calculations calls such as `Model.sum(:aliased)` and `Model.pluck(:aliased)`. - - This will not work with SQL fragment strings like `Model.sum('DISTINCT aliased')`. - - *Godfrey Chan* - -* Mute `psql` output when running rake db:schema:load. - - *Godfrey Chan* - -* Trigger a save on `has_one association=(associate)` when the associate contents have changed. - - Fixes #8856. - - *Chris Thompson* - -* Abort a rake task when missing db/structure.sql like `db:schema:load` task. - - *kennyj* - -* rake:db:test:prepare falls back to original environment after execution. - - *Slava Markevich* - -Please check [4-0-stable](https://github.com/rails/rails/blob/4-0-stable/activerecord/CHANGELOG.md) for previous changes. +Please check [4-1-stable](https://github.com/rails/rails/blob/4-1-stable/activerecord/CHANGELOG.md) for previous changes. -- cgit v1.2.3 From d64673c8fe00539a5e5c21c334d92f689e916444 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Mon, 24 Feb 2014 22:33:43 +0900 Subject: Skip test_migrate_revert_add_index_with_name if databases do not allow to create duplicate indexes on the same columns --- .../test/cases/invertible_migration_test.rb | 23 ++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index debacf815c..2b1749cf70 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -271,16 +271,19 @@ module ActiveRecord ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = '' end - def test_migrate_revert_add_index_with_name - RevertNamedIndexMigration1.new.migrate(:up) - RevertNamedIndexMigration2.new.migrate(:up) - RevertNamedIndexMigration2.new.migrate(:down) - - connection = ActiveRecord::Base.connection - assert connection.index_exists?(:horses, :content), - "index on content should exist" - assert !connection.index_exists?(:horses, :content, name: "horses_index_named"), - "horses_index_named index should not exist" + # MySQL 5.7 and Oracle do not allow to create duplicate indexes on the same columns + unless current_adapter?(:MysqlAdapter, :Mysql2Adapter, :OracleAdapter) + def test_migrate_revert_add_index_with_name + RevertNamedIndexMigration1.new.migrate(:up) + RevertNamedIndexMigration2.new.migrate(:up) + RevertNamedIndexMigration2.new.migrate(:down) + + connection = ActiveRecord::Base.connection + assert connection.index_exists?(:horses, :content), + "index on content should exist" + assert !connection.index_exists?(:horses, :content, name: "horses_index_named"), + "horses_index_named index should not exist" + end end end -- cgit v1.2.3 From b1656fa6305a5c8237027ab8165d7292751c0e86 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Tue, 25 Feb 2014 14:57:06 -0800 Subject: let `insert_record` actuall save the object. `before_add` callbacks are fired before the record is saved on `has_and_belongs_to_many` assocations *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 --- activerecord/CHANGELOG.md | 8 ++++++++ .../associations/collection_association.rb | 4 ++-- .../associations/has_many_through_association.rb | 3 +-- .../test/cases/associations/callbacks_test.rb | 21 +++++++++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) (limited to 'activerecord') diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 128628ccee..58f680a03f 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,11 @@ +* `before_add` callbacks are fired before the record is saved on + `has_and_belongs_to_many` assocations *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 + * Fixed STI classes not defining an attribute method if there is a conflicting private method defined on its ancestors. diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 89b7945c78..270871c866 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -513,13 +513,13 @@ module ActiveRecord target end - def concat_records(records) + def concat_records(records, should_raise = false) result = true records.flatten.each do |record| raise_on_type_mismatch!(record) add_to_target(record) do |rec| - result &&= insert_record(rec) unless owner.new_record? + result &&= insert_record(rec, true, should_raise) unless owner.new_record? end 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 31b8d27892..64bc98c642 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -30,7 +30,6 @@ module ActiveRecord unless owner.new_record? records.flatten.each do |record| raise_on_type_mismatch!(record) - record.save! if record.new_record? end end @@ -40,7 +39,7 @@ module ActiveRecord def concat_records(records) ensure_not_nested - records = super + records = super(records, true) if owner.new_record? && records records.flatten.each do |record| diff --git a/activerecord/test/cases/associations/callbacks_test.rb b/activerecord/test/cases/associations/callbacks_test.rb index 2d0d4541b4..e555c52281 100644 --- a/activerecord/test/cases/associations/callbacks_test.rb +++ b/activerecord/test/cases/associations/callbacks_test.rb @@ -101,6 +101,27 @@ class AssociationCallbacksTest < ActiveRecord::TestCase "after_adding#{david.id}"], ar.developers_log end + def test_has_and_belongs_to_many_before_add_called_before_save + dev = nil + new_dev = nil + klass = Class.new(Project) do + def self.name; Project.name; end + has_and_belongs_to_many :developers_with_callbacks, + :class_name => "Developer", + :before_add => lambda { |o,r| + dev = r + new_dev = r.new_record? + } + end + rec = klass.create! + alice = Developer.new(:name => 'alice') + rec.developers_with_callbacks << alice + assert_equal alice, dev + assert_not_nil new_dev + assert new_dev, "record should not have been saved" + assert_not alice.new_record? + end + def test_has_and_belongs_to_many_after_add_called_after_save ar = projects(:active_record) assert ar.developers_log.empty? -- cgit v1.2.3 From cfb22381451b8984c57f3be2bd39819a0e8ad858 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Tue, 11 Feb 2014 14:41:11 +0100 Subject: test case for custom PostgreSQL enum type. This test currently outputs the following warning: ``` unknown OID: current_mood(3567879) (SELECT "postgresql_enums".* FROM "postgresql_enums" ORDER BY "postgresql_enums"."id" ASC LIMIT 1) unknown OID: current_mood(3567879) (SELECT "postgresql_enums".* FROM "postgresql_enums" WHERE "postgresql_enums"."id" = $1 LIMIT 1) ``` We have an open PR to deal with this issue. It will dynamically register the OID for enum columns. This test case is merely to exhibit the current behavior of PostgreSQL enum columns. --- .../test/cases/adapters/postgresql/enum_test.rb | 38 ++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 activerecord/test/cases/adapters/postgresql/enum_test.rb (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb new file mode 100644 index 0000000000..bf8dbd6c3f --- /dev/null +++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +require "cases/helper" +require 'active_record/base' +require 'active_record/connection_adapters/postgresql_adapter' + +class PostgresqlEnumTest < ActiveRecord::TestCase + class PostgresqlEnum < ActiveRecord::Base + self.table_name = "postgresql_enums" + end + + def teardown + @connection.execute 'DROP TABLE IF EXISTS postgresql_enums' + @connection.execute 'DROP TYPE IF EXISTS mood' + end + + def setup + @connection = ActiveRecord::Base.connection + @connection.transaction do + @connection.execute <<-SQL + CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); + SQL + @connection.create_table('postgresql_enums') do |t| + t.column :current_mood, :mood + end + end + end + + def test_enum_mapping + @connection.execute "INSERT INTO postgresql_enums VALUES (1, 'sad');" + enum = PostgresqlEnum.first + assert_equal "sad", enum.current_mood + + enum.current_mood = "happy" + enum.save! + + assert_equal "happy", enum.reload.current_mood + end +end -- cgit v1.2.3 From 50be1d0d20f6e1a487ad329374d105e931a60ebb Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Wed, 26 Feb 2014 15:56:11 +0100 Subject: move PostgreSQL UUID tests from `datatype_test.rb` to `uuid_test.rb`. --- .../cases/adapters/postgresql/datatype_test.rb | 17 +---------- .../test/cases/adapters/postgresql/uuid_test.rb | 35 ++++++++++++++++++++++ 2 files changed, 36 insertions(+), 16 deletions(-) (limited to 'activerecord') diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb index 5c3a797c41..158bc6ee89 100644 --- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb +++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb @@ -27,9 +27,6 @@ end class PostgresqlTimestampWithZone < ActiveRecord::Base end -class PostgresqlUUID < ActiveRecord::Base -end - class PostgresqlLtree < ActiveRecord::Base end @@ -68,14 +65,11 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase @first_oid = PostgresqlOid.find(1) @connection.execute("INSERT INTO postgresql_timestamp_with_zones (id, time) VALUES (1, '2010-01-01 10:00:00-1')") - - @connection.execute("INSERT INTO postgresql_uuids (id, guid, compact_guid) VALUES(1, 'd96c3da0-96c1-012f-1316-64ce8f32c6d8', 'f06c715096c1012f131764ce8f32c6d8')") - @first_uuid = PostgresqlUUID.find(1) end def teardown [PostgresqlArray, PostgresqlTsvector, PostgresqlMoney, PostgresqlNumber, PostgresqlTime, PostgresqlNetworkAddress, - PostgresqlBitString, PostgresqlOid, PostgresqlTimestampWithZone, PostgresqlUUID].each(&:delete_all) + PostgresqlBitString, PostgresqlOid, PostgresqlTimestampWithZone].each(&:delete_all) end def test_array_escaping @@ -124,10 +118,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase assert_equal :integer, @first_oid.column_for_attribute(:obj_id).type end - def test_data_type_of_uuid_types - assert_equal :uuid, @first_uuid.column_for_attribute(:guid).type - end - def test_array_values assert_equal [35000,21000,18000,17000], @first_array.commission_by_quarter assert_equal ['foo','bar','baz'], @first_array.nicknames @@ -180,11 +170,6 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase assert_equal '01:23:45:67:89:0a', @first_network_address.mac_address end - def test_uuid_values - assert_equal 'd96c3da0-96c1-012f-1316-64ce8f32c6d8', @first_uuid.guid - assert_equal 'f06c7150-96c1-012f-1317-64ce8f32c6d8', @first_uuid.compact_guid - end - def test_bit_string_values assert_equal '00010101', @first_bit_string.bit_string assert_equal '00010101', @first_bit_string.bit_string_varying diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 3f5d981444..f3625c2fc8 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -5,6 +5,41 @@ require 'active_record/base' require 'active_record/connection_adapters/postgresql_adapter' class PostgresqlUUIDTest < ActiveRecord::TestCase + class UUIDType < ActiveRecord::Base + self.table_name = "uuid_data_type" + end + + def setup + @connection = ActiveRecord::Base.connection + @connection.transaction do + @connection.create_table "uuid_data_type" do |t| + t.uuid 'guid' + end + end + end + + def teardown + @connection.execute 'drop table if exists uuid_data_type' + end + + def test_data_type_of_uuid_types + assert_equal :uuid, UUIDType.columns_hash["guid"].type + end + + def test_uuid_formats + ["A0EEBC99-9C0B-4EF8-BB6D-6BB9BD380A11", + "{a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11}", + "a0eebc999c0b4ef8bb6d6bb9bd380a11", + "a0ee-bc99-9c0b-4ef8-bb6d-6bb9-bd38-0a11", + "{a0eebc99-9c0b4ef8-bb6d6bb9-bd380a11}"].each do |valid_uuid| + UUIDType.create(guid: valid_uuid) + uuid = UUIDType.last + assert_equal "a0eebc99-9c0b-4ef8-bb6d-6bb9bd380a11", uuid.guid + end + end +end + +class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase class UUID < ActiveRecord::Base self.table_name = 'pg_uuids' end -- cgit v1.2.3 From 671e997e5a71fbe0ba44d589b34c48b6c6502323 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Wed, 26 Feb 2014 16:04:37 +0100 Subject: extract common code in `uuid_test.rb`. --- .../test/cases/adapters/postgresql/uuid_test.rb | 117 ++++++++++----------- 1 file changed, 57 insertions(+), 60 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 f3625c2fc8..f581c4cf39 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -4,22 +4,40 @@ require "cases/helper" require 'active_record/base' require 'active_record/connection_adapters/postgresql_adapter' +module PostgresqlUUIDHelper + def connection + @connection ||= ActiveRecord::Base.connection + end + + def enable_uuid_ossp + unless connection.extension_enabled?('uuid-ossp') + connection.enable_extension 'uuid-ossp' + connection.commit_db_transaction + end + + connection.reconnect! + end + + def drop_table(name) + connection.execute "drop table if exists #{name}" + end +end + class PostgresqlUUIDTest < ActiveRecord::TestCase + include PostgresqlUUIDHelper + class UUIDType < ActiveRecord::Base self.table_name = "uuid_data_type" end - def setup - @connection = ActiveRecord::Base.connection - @connection.transaction do - @connection.create_table "uuid_data_type" do |t| - t.uuid 'guid' - end + setup do + connection.create_table "uuid_data_type" do |t| + t.uuid 'guid' end end - def teardown - @connection.execute 'drop table if exists uuid_data_type' + teardown do + drop_table "uuid_data_type" end def test_data_type_of_uuid_types @@ -40,30 +58,23 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase end class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase + include PostgresqlUUIDHelper + class UUID < ActiveRecord::Base self.table_name = 'pg_uuids' end - def setup - @connection = ActiveRecord::Base.connection + setup do + enable_uuid_ossp - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction - end - - @connection.reconnect! - - @connection.transaction do - @connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t| - t.string 'name' - t.uuid 'other_uuid', default: 'uuid_generate_v4()' - end + connection.create_table('pg_uuids', id: :uuid, default: 'uuid_generate_v1()') do |t| + t.string 'name' + t.uuid 'other_uuid', default: 'uuid_generate_v4()' end end - def teardown - @connection.execute 'drop table if exists pg_uuids' + teardown do + drop_table "pg_uuids" end if ActiveRecord::Base.connection.supports_extensions? @@ -84,14 +95,14 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase end def test_pk_and_sequence_for_uuid_primary_key - pk, seq = @connection.pk_and_sequence_for('pg_uuids') + pk, seq = connection.pk_and_sequence_for('pg_uuids') assert_equal 'id', pk assert_equal nil, seq end def test_schema_dumper_for_uuid_primary_key schema = StringIO.new - ActiveRecord::SchemaDumper.dump(@connection, schema) + 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) end @@ -99,34 +110,24 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase end class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase - class UUID < ActiveRecord::Base - self.table_name = 'pg_uuids' - end + include PostgresqlUUIDHelper - def setup - @connection = ActiveRecord::Base.connection - @connection.reconnect! + setup do + enable_uuid_ossp - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction - end - - @connection.transaction do - @connection.create_table('pg_uuids', id: false) do |t| - t.primary_key :id, :uuid, default: nil - t.string 'name' - end + connection.create_table('pg_uuids', id: false) do |t| + t.primary_key :id, :uuid, default: nil + t.string 'name' end end - def teardown - @connection.execute 'drop table if exists pg_uuids' + teardown do + drop_table "pg_uuids" end if ActiveRecord::Base.connection.supports_extensions? def test_id_allows_default_override_via_nil - col_desc = @connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default + col_desc = connection.execute("SELECT pg_get_expr(d.adbin, d.adrelid) as default FROM pg_attribute a LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum WHERE a.attname='id' AND a.attrelid = 'pg_uuids'::regclass").first @@ -136,6 +137,8 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase end class PostgresqlUUIDTestInverseOf < ActiveRecord::TestCase + include PostgresqlUUIDHelper + class UuidPost < ActiveRecord::Base self.table_name = 'pg_uuid_posts' has_many :uuid_comments, inverse_of: :uuid_post @@ -146,30 +149,24 @@ class PostgresqlUUIDTestInverseOf < ActiveRecord::TestCase belongs_to :uuid_post end - def setup - @connection = ActiveRecord::Base.connection - @connection.reconnect! - - unless @connection.extension_enabled?('uuid-ossp') - @connection.enable_extension 'uuid-ossp' - @connection.commit_db_transaction - end + setup do + enable_uuid_ossp - @connection.transaction do - @connection.create_table('pg_uuid_posts', id: :uuid) do |t| + connection.transaction do + connection.create_table('pg_uuid_posts', id: :uuid) do |t| t.string 'title' end - @connection.create_table('pg_uuid_comments', id: :uuid) do |t| + connection.create_table('pg_uuid_comments', id: :uuid) do |t| t.uuid :uuid_post_id, default: 'uuid_generate_v4()' t.string 'content' end end end - def teardown - @connection.transaction do - @connection.execute 'drop table if exists pg_uuid_comments' - @connection.execute 'drop table if exists pg_uuid_posts' + teardown do + connection.transaction do + drop_table "pg_uuid_comments" + drop_table "pg_uuid_posts" end end -- cgit v1.2.3 From 3a707e21109abfbecd499c445345707d2eb944cd Mon Sep 17 00:00:00 2001 From: Logan Hasson Date: Wed, 26 Feb 2014 11:27:04 -0500 Subject: [ci skip] Move association class method notes Make explanation of association class methods clearer by moving notes to beginning of each example section. --- activerecord/lib/active_record/associations.rb | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'activerecord') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 714f623af3..d6692afa75 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1034,6 +1034,9 @@ module ActiveRecord # Specifies a one-to-many association. The following methods for retrieval and query of # collections of associated objects will be added: # + # +collection+ is a placeholder for the symbol passed as the first argument, so + # has_many :clients would add among others clients.empty?. + # # [collection(force_reload = false)] # Returns an array of all the associated objects. # An empty array is returned if none are found. @@ -1092,9 +1095,6 @@ module ActiveRecord # Does the same as collection.create, but raises ActiveRecord::RecordInvalid # if the record is invalid. # - # (*Note*: +collection+ is replaced with the symbol passed as the first argument, so - # has_many :clients would add among others clients.empty?.) - # # === Example # # A Firm class declares has_many :clients, which will add: @@ -1209,6 +1209,9 @@ module ActiveRecord # # The following methods for retrieval and query of a single associated object will be added: # + # +association+ is a placeholder for the symbol passed as the first argument, so + # has_one :manager would add among others manager.nil?. + # # [association(force_reload = false)] # Returns the associated object. +nil+ is returned if none is found. # [association=(associate)] @@ -1226,9 +1229,6 @@ module ActiveRecord # Does the same as create_association, but raises ActiveRecord::RecordInvalid # if the record is invalid. # - # (+association+ is replaced with the symbol passed as the first argument, so - # has_one :manager would add among others manager.nil?.) - # # === Example # # An Account class declares has_one :beneficiary, which will add: @@ -1314,6 +1314,9 @@ module ActiveRecord # Methods will be added for retrieval and query for a single associated object, for which # this object holds an id: # + # +association+ is a placeholder for the symbol passed as the first argument, so + # belongs_to :author would add among others author.nil?. + # # [association(force_reload = false)] # Returns the associated object. +nil+ is returned if none is found. # [association=(associate)] @@ -1329,9 +1332,6 @@ module ActiveRecord # Does the same as create_association, but raises ActiveRecord::RecordInvalid # if the record is invalid. # - # (+association+ is replaced with the symbol passed as the first argument, so - # belongs_to :author would add among others author.nil?.) - # # === Example # # A Post class declares belongs_to :author, which will add: @@ -1451,6 +1451,9 @@ module ActiveRecord # # Adds the following methods for retrieval and query: # + # +collection+ is a placeholder for the symbol passed as the first argument, so + # has_and_belongs_to_many :categories would add among others categories.empty?. + # # [collection(force_reload = false)] # Returns an array of all the associated objects. # An empty array is returned if none are found. @@ -1492,9 +1495,6 @@ module ActiveRecord # with +attributes+, linked to this object through the join table, and that has already been # saved (if it passed the validation). # - # (+collection+ is replaced with the symbol passed as the first argument, so - # has_and_belongs_to_many :categories would add among others categories.empty?.) - # # === Example # # A Developer class declares has_and_belongs_to_many :projects, which will add: -- cgit v1.2.3