From a8ede3664055f33c102b3f229cf280b0bf69c540 Mon Sep 17 00:00:00 2001 From: Dan Erikson Date: Mon, 8 Apr 2013 00:41:16 -0600 Subject: Changed ActiveRecord::Associations::CollectionProxy#select to take multiple arguments. This makes the arguments the same as ActiveRecord::QueryMethods::select. --- .../lib/active_record/associations/collection_association.rb | 4 ++-- activerecord/lib/active_record/associations/collection_proxy.rb | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 2a00ac1386..5b08d07425 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -67,11 +67,11 @@ module ActiveRecord @target = [] end - def select(select = nil) + def select(*fields) if block_given? load_target.select.each { |e| yield e } else - scope.select(select) + scope.select(*fields) end end diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 8a5b312862..ef2acfce89 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -76,7 +76,7 @@ module ActiveRecord # # # # # ] # - # person.pets.select([:id, :name]) + # person.pets.select(:id, :name ) # # => [ # # #, # # #, @@ -107,8 +107,8 @@ module ActiveRecord # # #, # # # # # ] - def select(select = nil, &block) - @association.select(select, &block) + def select(*fields, &block) + @association.select(*fields, &block) end # Finds an object in the collection responding to the +id+. Uses the same -- cgit v1.2.3 From e9bf87f08e77a457e1b82c2409abcaf4aef5f97c Mon Sep 17 00:00:00 2001 From: thedarkone Date: Sun, 29 Sep 2013 18:56:32 +0200 Subject: Fix AR#method_missing re-dispatching into overwritten attribute methods. This was happening when a `super` call in an overwritten attribute method was triggering a method_missing fallback, because attribute methods haven't been generated yet. class Topic < ActiveRecord::Base def title # `super` would re-invoke this method if define_attribute_methods # hasn't been called yet resulting in double '!' appending super + '!' end end --- activerecord/lib/active_record/attribute_methods.rb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index bf270c1829..f90c11dcf7 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -128,6 +128,16 @@ module ActiveRecord end end + def find_generated_attribute_method(method_name) # :nodoc: + klass = self + until klass == Base + gen_methods = klass.generated_attribute_methods + return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object) + klass = klass.superclass + end + nil + end + # Returns +true+ if +attribute+ is an attribute method and table exists, # +false+ otherwise. # @@ -163,7 +173,14 @@ module ActiveRecord def method_missing(method, *args, &block) # :nodoc: if self.class.define_attribute_methods if respond_to_without_attributes?(method) - send(method, *args, &block) + # make sure to invoke the correct attribute method, as we might have gotten here via a `super` + # call in a overwritten attribute method + if attribute_method = self.class.find_generated_attribute_method(method) + # this is probably horribly slow, but should only happen at most once for a given AR class + attribute_method.bind(self).call(*args, &block) + else + send(method, *args, &block) + end else super end -- cgit v1.2.3 From 49ff20d9b164693ed7fee880b69cc14b141678b3 Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Sun, 1 Dec 2013 03:19:56 -0500 Subject: Use genderless pronouns in API docs --- activerecord/lib/active_record/validations/uniqueness.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index e2b132ca81..bdb42c3724 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -204,7 +204,7 @@ module ActiveRecord # exception. You can either choose to let this error propagate (which # will result in the default Rails exception page being shown), or you # can catch it and restart the transaction (e.g. by telling the user - # that the title already exists, and asking him to re-enter the title). + # that the title already exists, and asking them to re-enter the title). # This technique is also known as # {optimistic concurrency control}[http://en.wikipedia.org/wiki/Optimistic_concurrency_control]. # -- cgit v1.2.3 From 5827824a6fdc210165cec15f1aa65879493d0d7a Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Mon, 2 Dec 2013 00:34:52 -0500 Subject: Cleanups in API docs: his => their --- activerecord/lib/active_record/validations/uniqueness.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index bdb42c3724..7ebe9dfec0 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -174,11 +174,11 @@ module ActiveRecord # WHERE title = 'My Post' | # | # | # User 2 does the same thing and also - # | # infers that his title is unique. + # | # infers that their title is unique. # | SELECT * FROM comments # | WHERE title = 'My Post' # | - # # User 1 inserts his comment. | + # # User 1 inserts their comment. | # INSERT INTO comments | # (title, content) VALUES | # ('My Post', 'hi!') | -- cgit v1.2.3 From 6329d9fa8b2f86a178151be264cccdb805bfaaac Mon Sep 17 00:00:00 2001 From: Genadi Samokovarov Date: Mon, 2 Dec 2013 23:36:58 +0200 Subject: Remove deprecated cattr_* requires --- activerecord/lib/active_record/base.rb | 2 +- activerecord/lib/active_record/migration.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e05e22ebb0..1d3ec75aa1 100644 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -4,7 +4,7 @@ require 'active_support/benchmarkable' require 'active_support/dependencies' require 'active_support/descendants_tracker' require 'active_support/time' -require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/module/attribute_accessors' require 'active_support/core_ext/class/delegating_attributes' require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/hash/deep_merge' diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index a4247fb6f4..f7e0a388d7 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -1,4 +1,4 @@ -require "active_support/core_ext/class/attribute_accessors" +require "active_support/core_ext/module/attribute_accessors" require 'set' module ActiveRecord -- cgit v1.2.3 From ddf27acbc285a892842866cde04951cdad52c5c9 Mon Sep 17 00:00:00 2001 From: Victor Costan Date: Sun, 24 Nov 2013 12:52:53 -0500 Subject: Introduce a context for rendering fixtures ERB. Fixture files are passed through an ERB renderer before being read as YAML. The rendering is currently done in the context of the main object, so method definitons leak into other fixtures, and there is no clean place to define fixture helpers. After this commit, the ERB renderer will use a new subclass of ActiveRecord::FixtureSet.context_class each time a fixture is rendered. --- activerecord/lib/active_record/fixture_set/file.rb | 3 +- activerecord/lib/active_record/fixtures.rb | 32 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/fixture_set/file.rb b/activerecord/lib/active_record/fixture_set/file.rb index fbd7a4d891..8132310c91 100644 --- a/activerecord/lib/active_record/fixture_set/file.rb +++ b/activerecord/lib/active_record/fixture_set/file.rb @@ -38,7 +38,8 @@ module ActiveRecord end def render(content) - ERB.new(content).result + context = ActiveRecord::FixtureSet::RenderContext.create_subclass.new + ERB.new(content).result(context.get_binding) end # Validate our unmarshalled data. diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 8601414209..d241788a9b 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -119,6 +119,23 @@ module ActiveRecord # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values # in fixtures are to be considered a code smell. # + # Helper methods defined in a fixture will not be available in other fixtures, to prevent against + # unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module + # that is included in ActiveRecord::FixtureSet.context_class. + # + # - define a helper method in `test_helper.rb` + # class FixtureFileHelpers + # def file_sha(path) + # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path))) + # end + # end + # ActiveRecord::FixtureSet.context_class.send :include, FixtureFileHelpers + # + # - use the helper method in a fixture + # photo: + # name: kitten.png + # sha: <%= file_sha 'files/kitten.png' %> + # # = Transactional Fixtures # # Test cases can use begin+rollback to isolate their changes to the database instead of having to @@ -529,6 +546,11 @@ module ActiveRecord Zlib.crc32(label.to_s) % MAX_ID end + # Superclass for the evaluation contexts used by ERB fixtures. + def self.context_class + @context_class ||= Class.new + end + attr_reader :table_name, :name, :fixtures, :model_class, :config def initialize(connection, name, class_name, path, config = ActiveRecord::Base) @@ -989,3 +1011,13 @@ module ActiveRecord end end end + +class ActiveRecord::FixtureSet::RenderContext # :nodoc: + def self.create_subclass + Class.new ActiveRecord::FixtureSet.context_class do + def get_binding + binding() + end + end + end +end -- cgit v1.2.3 From 1ed81e85ca1d2518400b56c167f3c196c51afede Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Mon, 29 Jul 2013 01:12:34 +0530 Subject: Currently, we clear query_cache in cache block finish, even if we may already have cache true. This commit takes into account the last cache_enabled value, before clearing query_cache. --- .../lib/active_record/connection_adapters/abstract/query_cache.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb index 8399232d73..adc23a6674 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -31,8 +31,8 @@ module ActiveRecord old, @query_cache_enabled = @query_cache_enabled, true yield ensure - clear_query_cache @query_cache_enabled = old + clear_query_cache unless @query_cache_enabled end def enable_query_cache! -- cgit v1.2.3 From edd93a53fe9e4609f68cf0057d83ecd499bbf817 Mon Sep 17 00:00:00 2001 From: joker1007 Date: Tue, 3 Dec 2013 23:11:53 +0900 Subject: Fix ActiveRecord::Callbacks sample code [ci skip] Callback caller class uses `after_initialize`, but Callback callee defines `after_find`. Current sample code causes following error. NoMethodError: undefined method `after_initialize' for # --- activerecord/lib/active_record/callbacks.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index 128a9377c1..35f19f0bc0 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -128,7 +128,7 @@ module ActiveRecord # record.credit_card_number = decrypt(record.credit_card_number) # end # - # alias_method :after_find, :after_save + # alias_method :after_initialize, :after_save # # private # def encrypt(value) @@ -163,7 +163,7 @@ module ActiveRecord # record.send("#{@attribute}=", decrypt(record.send("#{@attribute}"))) # end # - # alias_method :after_find, :after_save + # alias_method :after_initialize, :after_save # # private # def encrypt(value) -- cgit v1.2.3 From b1b9a0aeca879b1c1bc2c8a74f2c9cabd143b9bb Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Tue, 3 Dec 2013 12:04:25 -0200 Subject: Typos. return -> returns. [ci skip] --- .../lib/active_record/connection_adapters/abstract/connection_pool.rb | 2 +- .../active_record/connection_adapters/abstract/database_statements.rb | 2 +- activerecord/lib/active_record/fixtures.rb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') 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 cfdcae7f63..cebe741daa 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -86,7 +86,7 @@ module ActiveRecord end end - # Return the number of threads currently waiting on this + # Returns the number of threads currently waiting on this # queue. def num_waiting synchronize do diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index 06a2ddb8b7..223eb552e4 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -350,7 +350,7 @@ module ActiveRecord protected - # Return a subquery for the given key using the join information. + # Returns a subquery for the given key using the join information. def subquery_for(key, select) subselect = select.clone subselect.projections = [key] diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 8601414209..55423565e8 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -582,7 +582,7 @@ module ActiveRecord } end - # Return a hash of rows to be inserted. The key is the table, the value is + # Returns a hash of rows to be inserted. The key is the table, the value is # a list of rows to insert to that table. def table_rows now = config.default_timezone == :utc ? Time.now.utc : Time.now -- cgit v1.2.3 From be5527b8e8fcc25946b128fe78db10d5bee2a483 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Tue, 3 Dec 2013 17:54:25 +0100 Subject: `connection.type_to_sql` returns a `String` for unmapped types. Closes #13146. This fixes an error when using: ``` change_colum :table, :column, :bigint, array: true ``` --- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') 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 6268ae4875..51a6929dab 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -690,7 +690,7 @@ module ActiveRecord column_type_sql else - type + type.to_s end end -- cgit v1.2.3 From f606153755eb2498ccb8d87b72bbd6f3f848ed2f Mon Sep 17 00:00:00 2001 From: John Joseph Bachir Date: Mon, 7 Oct 2013 21:25:35 -0400 Subject: ActiveRecord migration exception message formatting --- activerecord/lib/active_record/migration.rb | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index f7e0a388d7..7d7e97e6c9 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -2,40 +2,47 @@ require "active_support/core_ext/module/attribute_accessors" require 'set' module ActiveRecord + class MigrationError < ActiveRecordError#:nodoc: + def initialize(message = nil) + message = "\n\n#{message}\n\n" if message + super + end + end + # Exception that can be raised to stop migrations from going backwards. - class IrreversibleMigration < ActiveRecordError + class IrreversibleMigration < MigrationError end - class DuplicateMigrationVersionError < ActiveRecordError#:nodoc: + class DuplicateMigrationVersionError < MigrationError#:nodoc: def initialize(version) super("Multiple migrations have the version number #{version}") end end - class DuplicateMigrationNameError < ActiveRecordError#:nodoc: + class DuplicateMigrationNameError < MigrationError#:nodoc: def initialize(name) super("Multiple migrations have the name #{name}") end end - class UnknownMigrationVersionError < ActiveRecordError #:nodoc: + class UnknownMigrationVersionError < MigrationError #:nodoc: def initialize(version) super("No migration with version number #{version}") end end - class IllegalMigrationNameError < ActiveRecordError#:nodoc: + class IllegalMigrationNameError < MigrationError#:nodoc: def initialize(name) super("Illegal name for migration file: #{name}\n\t(only lower case letters, numbers, and '_' allowed)") end end - class PendingMigrationError < ActiveRecordError#:nodoc: + class PendingMigrationError < MigrationError#:nodoc: def initialize if defined?(Rails) - super("Migrations are pending; run 'bin/rake db:migrate RAILS_ENV=#{::Rails.env}' to resolve this issue.") + super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate RAILS_ENV=#{::Rails.env}") else - super("Migrations are pending; run 'bin/rake db:migrate' to resolve this issue.") + super("Migrations are pending. To resolve this issue, run:\n\n\tbin/rake db:migrate") end end end -- cgit v1.2.3 From 2a517e7291d6f93b6ca3d8c56fc8559c48089c28 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Tue, 3 Dec 2013 16:33:34 -0200 Subject: Fix offset with last. Closes #7441 --- activerecord/lib/active_record/relation/finder_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index d91d6367a3..3963f2b3e0 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -384,7 +384,7 @@ module ActiveRecord @records.last else @last ||= - if offset_value || limit_value + if limit_value to_a.last else reverse_order.limit(1).to_a.first -- cgit v1.2.3 From c9223dc366f17b61d0cffeff14a7e670ece9d0d4 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 4 Dec 2013 16:48:37 -0800 Subject: make the type_map per connection. fixes #13182 --- .../postgresql/schema_statements.rb | 2 +- .../connection_adapters/postgresql_adapter.rb | 27 +++++++++++++--------- 2 files changed, 17 insertions(+), 12 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb index 5dc70a5ad1..571257f6dd 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb @@ -172,7 +172,7 @@ module ActiveRecord def columns(table_name) # Limit, precision, and scale are all handled by the superclass. column_definitions(table_name).map do |column_name, type, default, notnull, oid, fmod| - oid = OID::TYPE_MAP.fetch(oid.to_i, fmod.to_i) { + oid = type_map.fetch(oid.to_i, fmod.to_i) { OID::Identity.new } PostgreSQLColumn.new(column_name, default, oid, type, notnull == 'f') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index adeb57d913..1ec1490263 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -565,7 +565,8 @@ module ActiveRecord raise "Your version of PostgreSQL (#{postgresql_version}) is too old, please upgrade!" end - initialize_type_map + @type_map = OID::TypeMap.new + initialize_type_map(type_map) @local_tz = execute('SHOW TIME ZONE', 'SCHEMA').first["TimeZone"] @use_insert_returning = @config.key?(:insert_returning) ? self.class.type_cast_config_to_boolean(@config[:insert_returning]) : true end @@ -738,39 +739,43 @@ module ActiveRecord private + def type_map + @type_map + end + def reload_type_map - OID::TYPE_MAP.clear - initialize_type_map + type_map.clear + initialize_type_map(type_map) end - def initialize_type_map + 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' } # populate the leaf nodes leaves.find_all { |row| OID.registered_type? row['typname'] }.each do |row| - OID::TYPE_MAP[row['oid'].to_i] = OID::NAMES[row['typname']] + type_map[row['oid'].to_i] = OID::NAMES[row['typname']] end arrays, nodes = nodes.partition { |row| row['typinput'] == 'array_in' } # populate composite types - nodes.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row| + nodes.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row| if OID.registered_type? row['typname'] # this composite type is explicitly registered vector = OID::NAMES[row['typname']] else # use the default for composite types - vector = OID::Vector.new row['typdelim'], OID::TYPE_MAP[row['typelem'].to_i] + vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i] end - OID::TYPE_MAP[row['oid'].to_i] = vector + type_map[row['oid'].to_i] = vector end # populate array types - arrays.find_all { |row| OID::TYPE_MAP.key? row['typelem'].to_i }.each do |row| - array = OID::Array.new OID::TYPE_MAP[row['typelem'].to_i] - OID::TYPE_MAP[row['oid'].to_i] = array + arrays.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row| + array = OID::Array.new type_map[row['typelem'].to_i] + type_map[row['oid'].to_i] = array end end -- cgit v1.2.3 From f1a646fa74290db32fb52bb373791891ab73d693 Mon Sep 17 00:00:00 2001 From: Severin Schoepke Date: Thu, 5 Dec 2013 15:57:05 +0100 Subject: polymorphic belongs_to association with touch: true updates old record correctly Example: Given you have a comments model with a polymorphic commentable association (e.g. books and songs) with the touch option set. Every time you update a comment its commentable should be touched. This was working when you changed attributes on the comment or when you moved the comment from one book to another. However, it was not working when moving a comment from a book to a song. This is now fixed. --- activerecord/lib/active_record/associations/builder/belongs_to.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb index ac387d377d..e8e36e7cd0 100644 --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb @@ -91,7 +91,13 @@ module ActiveRecord::Associations::Builder old_foreign_id = o.changed_attributes[foreign_key] if old_foreign_id - klass = o.association(name).klass + association = o.association(name) + reflection = association.reflection + if reflection.polymorphic? + klass = o.public_send("#{reflection.foreign_type}_was").constantize + else + klass = association.klass + end old_record = klass.find_by(klass.primary_key => old_foreign_id) if old_record -- cgit v1.2.3 From a2fa06aa5d205a742cf2036261b99da0c343119f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Thu, 5 Dec 2013 19:16:41 -0200 Subject: Use the right type_map --- .../connection_adapters/postgresql/database_statements.rb | 2 +- .../lib/active_record/connection_adapters/postgresql/oid.rb | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb index fa173d13a2..f349c37724 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/database_statements.rb @@ -142,7 +142,7 @@ module ActiveRecord fields.each_with_index do |fname, i| ftype = result.ftype i fmod = result.fmod i - types[fname] = OID::TYPE_MAP.fetch(ftype, fmod) { |oid, mod| + types[fname] = type_map.fetch(ftype, fmod) { |oid, mod| warn "unknown OID: #{fname}(#{oid}) (#{sql})" OID::Identity.new } diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb index 2a9547fd10..fae260a921 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/oid.rb @@ -301,11 +301,9 @@ module ActiveRecord end end - TYPE_MAP = TypeMap.new # :nodoc: - - # When the PG adapter connects, the pg_type table is queried. The + # When the PG adapter connects, the pg_type table is queried. The # key of this hash maps to the `typname` column from the table. - # TYPE_MAP is then dynamically built with oids as the key and type + # type_map is then dynamically built with oids as the key and type # objects as values. NAMES = Hash.new { |h,k| # :nodoc: h[k] = OID::Identity.new -- cgit v1.2.3 From dbdbda9a4a5cb9cd22e7a5a5625caa84edae6d5d Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 5 Dec 2013 14:48:11 -0800 Subject: fix pg warnings on geometric types --- .../connection_adapters/postgresql_adapter.rb | 33 +++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 1ec1490263..d23a24589c 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -748,6 +748,25 @@ module ActiveRecord initialize_type_map(type_map) end + def add_oid(row, records_by_oid, type_map) + return type_map if type_map.key? row['type_elem'].to_i + + if OID.registered_type? row['typname'] + # this composite type is explicitly registered + vector = OID::NAMES[row['typname']] + else + # use the default for composite types + unless type_map.key? row['typelem'].to_i + add_oid records_by_oid[row['typelem']], records_by_oid, type_map + end + + vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i] + end + + type_map[row['oid'].to_i] = vector + type_map + 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' } @@ -757,19 +776,13 @@ module ActiveRecord 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.find_all { |row| type_map.key? row['typelem'].to_i }.each do |row| - if OID.registered_type? row['typname'] - # this composite type is explicitly registered - vector = OID::NAMES[row['typname']] - else - # use the default for composite types - vector = OID::Vector.new row['typdelim'], type_map[row['typelem'].to_i] - end - - type_map[row['oid'].to_i] = vector + nodes.each do |row| + add_oid row, records_by_oid, type_map end # populate array types -- cgit v1.2.3 From fa414e6877cd6d68d95e6b7472a1df4ab31e382c Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Thu, 5 Dec 2013 00:41:09 -0800 Subject: Make clear that the enum array should not be changed once defined. [ci skip] --- activerecord/lib/active_record/enum.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 5fcc0382d8..34c17f11a9 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -26,12 +26,23 @@ module ActiveRecord # # Good practice is to let the first declared status be the default. # - # Finally, it's also possible to explicitly map the relation between attribute and database integer: + # Finally, it's also possible to explicitly map the relation between attribute and + # database integer with a +Hash+: # # class Conversation < ActiveRecord::Base # enum status: { active: 0, archived: 1 } # end # + # Note that when an +Array+ is used, the implicit mapping from the values to database + # integers is derived from the order the values appear in the array. In the example, + # :active is mapped to +0+ as it's the first elemet, and :archived + # is mapped to +1+. In general, the +i+-th element is mapped to i-1 in the + # database. + # + # Therefore, once a value is added to the enum array, its position in the array must + # be maintained, and new values should only be added to the end of the array. To + # remove unused values, the explicit +Hash+ syntax should be used. + # # In rare circumstances you might need to access the mapping directly. # The mappings are exposed through a constant with the attributes name: # -- cgit v1.2.3 From 4b5e91956a432c6df5cc744b02416330524259e6 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Thu, 5 Dec 2013 18:12:42 -0800 Subject: _enum_methods_module should be kept private --- activerecord/lib/active_record/enum.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 34c17f11a9..b42039e722 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -88,12 +88,13 @@ module ActiveRecord end end - def _enum_methods_module - @_enum_methods_module ||= begin - mod = Module.new - include mod - mod + private + def _enum_methods_module + @_enum_methods_module ||= begin + mod = Module.new + include mod + mod + end end - end end end -- cgit v1.2.3 From 8cfab46837f66587c3cdb5ea40a0d968fd3e1b15 Mon Sep 17 00:00:00 2001 From: Robin Dupret Date: Fri, 6 Dec 2013 20:21:12 +0100 Subject: Very tiny typo fix [ci skip] --- activerecord/lib/active_record/enum.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index b42039e722..837989aaa7 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -35,7 +35,7 @@ module ActiveRecord # # Note that when an +Array+ is used, the implicit mapping from the values to database # integers is derived from the order the values appear in the array. In the example, - # :active is mapped to +0+ as it's the first elemet, and :archived + # :active is mapped to +0+ as it's the first element, and :archived # is mapped to +1+. In general, the +i+-th element is mapped to i-1 in the # database. # -- cgit v1.2.3 From 4b6b6c8d8a65956d5521c3b4159a4cf3f615b357 Mon Sep 17 00:00:00 2001 From: Scott M Date: Sat, 7 Dec 2013 01:03:28 -0500 Subject: fix typo, "state_state" should be "stale_state" [ci skip] --- activerecord/lib/active_record/associations/association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index 02f45731c9..ce7d41cf54 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -69,7 +69,7 @@ module ActiveRecord # The target is stale if the target no longer points to the record(s) that the # relevant foreign_key(s) refers to. If stale, the association accessor method # on the owner will reload the target. It's up to subclasses to implement the - # state_state method if relevant. + # stale_state method if relevant. # # Note that if the target has not been loaded, it is not considered stale. def stale_target? -- cgit v1.2.3 From b6655885ef13cf8d1705dc9b5232846f0207febd Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Fri, 6 Dec 2013 04:56:42 +0900 Subject: Remove `DEFAULT NULL` for primary key column to support MySQL 5.7.3 Since MySQL 5.7.3 m13 does now allow primary key column is null. --- .../lib/active_record/connection_adapters/abstract_mysql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index e3270e734f..1b9f865666 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -148,7 +148,7 @@ module ActiveRecord QUOTED_TRUE, QUOTED_FALSE = '1', '0' NATIVE_DATABASE_TYPES = { - :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY", + :primary_key => "int(11) auto_increment PRIMARY KEY", :string => { :name => "varchar", :limit => 255 }, :text => { :name => "text" }, :integer => { :name => "int", :limit => 4 }, -- cgit v1.2.3 From 2a7fe7ae9b09024acea3c5d525c89b91bdb264a1 Mon Sep 17 00:00:00 2001 From: Paul Nikitochkin Date: Mon, 9 Dec 2013 01:23:38 +0200 Subject: Fix type cast on group sum with custom expression For PG adapters with custom expression and grouped result of aggregate functions have not found correct column type for it. Extract column type from query result. Closes: #13230 --- activerecord/lib/active_record/relation/calculations.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 2d267183ce..cf24d10a8d 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -311,7 +311,9 @@ module ActiveRecord } key = key.first if key.size == 1 key = key_records[key] if associated - [key, type_cast_calculated_value(row[aggregate_alias], column_for(column_name), operation)] + + column_type = calculated_data.column_types.fetch(aggregate_alias) { column_for(column_name) } + [key, type_cast_calculated_value(row[aggregate_alias], column_type, operation)] end] end -- cgit v1.2.3 From 23ce3e5fde42d0af9b77ad014bde8f36401fa3b6 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Wed, 11 Dec 2013 00:05:16 -0200 Subject: Prevent invalid code when using dynamic finders with Ruby's reserved words. The dynamic finder was creating the method signature with the parameters name, which may have reserved words and this way creating invalid Ruby code. Closes: #13261 Example: # Before Dog.find_by_alias('dog name') # Was creating this method def self.find_by_alias(alias, options = {}) # After Dog.find_by_alias('dog name') # Will create this method def self.find_by_alias(_alias, options = {}) --- activerecord/lib/active_record/dynamic_matchers.rb | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/dynamic_matchers.rb b/activerecord/lib/active_record/dynamic_matchers.rb index e650ebcf64..5caab09038 100644 --- a/activerecord/lib/active_record/dynamic_matchers.rb +++ b/activerecord/lib/active_record/dynamic_matchers.rb @@ -84,13 +84,18 @@ module ActiveRecord "#{finder}(#{attributes_hash})" end + # The parameters in the signature may have reserved Ruby words, in order + # to prevent errors, we start each param name with `_`. + # # Extended in activerecord-deprecated_finders def signature - attribute_names.join(', ') + attribute_names.map { |name| "_#{name}" }.join(', ') end + # Given that the parameters starts with `_`, the finder needs to use the + # same parameter name. def attributes_hash - "{" + attribute_names.map { |name| ":#{name} => #{name}" }.join(',') + "}" + "{" + attribute_names.map { |name| ":#{name} => _#{name}" }.join(',') + "}" end def finder -- cgit v1.2.3 From 4c0cddc92c680490f8cafc889a92541ac80dde39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Tue, 10 Dec 2013 14:12:36 -0200 Subject: Bring back the valid_options class accessor It is need in activerecord-deprecated_finders --- .../lib/active_record/associations/builder/association.rb | 13 +++++++++---- .../lib/active_record/associations/builder/belongs_to.rb | 2 +- .../associations/builder/collection_association.rb | 2 +- .../lib/active_record/associations/builder/has_many.rb | 2 +- .../lib/active_record/associations/builder/has_one.rb | 2 +- .../associations/builder/singular_association.rb | 2 +- 6 files changed, 14 insertions(+), 9 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb index 37ba1c73b1..f86d975592 100644 --- a/activerecord/lib/active_record/associations/builder/association.rb +++ b/activerecord/lib/active_record/associations/builder/association.rb @@ -1,3 +1,5 @@ +require 'active_support/core_ext/module/attribute_accessors' + # This is the parent Association class which defines the variables # used by all associations. # @@ -16,7 +18,10 @@ module ActiveRecord::Associations::Builder end self.extensions = [] - VALID_OPTIONS = [:class_name, :class, :foreign_key, :validate] + # TODO: This class accessor is needed to make activerecord-deprecated_finders work. + # We can move it to a constant in 5.0. + cattr_accessor :valid_options, instance_accessor: false + self.valid_options = [:class_name, :class, :foreign_key, :validate] def self.build(model, name, scope, options, &block) extension = define_extensions model, name, &block @@ -63,12 +68,12 @@ module ActiveRecord::Associations::Builder raise NotImplementedError end - def self.valid_options(options) - VALID_OPTIONS + Association.extensions.flat_map(&:valid_options) + def self.build_valid_options(options) + self.valid_options + Association.extensions.flat_map(&:valid_options) end def self.validate_options(options) - options.assert_valid_keys(valid_options(options)) + options.assert_valid_keys(build_valid_options(options)) end def self.define_extensions(model, name) diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb index e8e36e7cd0..7dd8a7deb1 100644 --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb @@ -4,7 +4,7 @@ module ActiveRecord::Associations::Builder :belongs_to end - def self.valid_options(options) + def self.build_valid_options(options) super + [:foreign_type, :polymorphic, :touch, :counter_cache] end diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb index 2ff67f904d..b696914883 100644 --- a/activerecord/lib/active_record/associations/builder/collection_association.rb +++ b/activerecord/lib/active_record/associations/builder/collection_association.rb @@ -7,7 +7,7 @@ module ActiveRecord::Associations::Builder CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove] - def self.valid_options(options) + def self.build_valid_options(options) super + [:table_name, :before_add, :after_add, :before_remove, :after_remove, :extend] end diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb index 227184cd19..4ec808bc77 100644 --- a/activerecord/lib/active_record/associations/builder/has_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_many.rb @@ -4,7 +4,7 @@ module ActiveRecord::Associations::Builder :has_many end - def self.valid_options(options) + def self.build_valid_options(options) super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache] end diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb index 064a3c8b51..216d6abec1 100644 --- a/activerecord/lib/active_record/associations/builder/has_one.rb +++ b/activerecord/lib/active_record/associations/builder/has_one.rb @@ -4,7 +4,7 @@ module ActiveRecord::Associations::Builder :has_one end - def self.valid_options(options) + def self.build_valid_options(options) valid = super + [:order, :as] valid += [:through, :source, :source_type] if options[:through] valid diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb index a6e8300642..6a1a0073e6 100644 --- a/activerecord/lib/active_record/associations/builder/singular_association.rb +++ b/activerecord/lib/active_record/associations/builder/singular_association.rb @@ -2,7 +2,7 @@ module ActiveRecord::Associations::Builder class SingularAssociation < Association #:nodoc: - def self.valid_options(options) + def self.build_valid_options(options) super + [:remote, :dependent, :primary_key, :inverse_of] end -- cgit v1.2.3 From e1ce005942f20c4f64cf78a0dc15e662e0353f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 11 Dec 2013 13:29:21 -0200 Subject: Revert "Merge pull request #12518 from vipulnsward/remove_count_options" It is needed for activerecord-depecated_finders This reverts commit dcff027a5242b20c0c90eb062dddb22ccf51aed9, reversing changes made to 3a2093984ff49d86db1efeff0c7581e788ecfb9f. --- .../associations/collection_association.rb | 4 +++- .../active_record/associations/collection_proxy.rb | 4 ++-- activerecord/lib/active_record/null_relation.rb | 2 +- .../lib/active_record/relation/calculations.rb | 25 +++++++++++----------- 4 files changed, 19 insertions(+), 16 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 62f23f54f9..109ad71ff9 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -193,7 +193,9 @@ module ActiveRecord # Count all records using SQL. Construct options and pass them with # scope to the target class's +count+. - def count(column_name = nil) + def count(column_name = nil, count_options = {}) + column_name, count_options = nil, column_name if column_name.is_a?(Hash) + relation = scope if association_scope.distinct_value # This is needed because 'SELECT count(DISTINCT *)..' is not valid SQL. diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 0b37ecf5b7..6e2b559e4b 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -669,8 +669,8 @@ module ActiveRecord # # #, # # # # # ] - def count(column_name = nil) - @association.count(column_name) + def count(column_name = nil, options = {}) + @association.count(column_name, options) end # Returns the size of the collection. If the collection hasn't been loaded, diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb index 080b20134d..62c6bbc413 100644 --- a/activerecord/lib/active_record/null_relation.rb +++ b/activerecord/lib/active_record/null_relation.rb @@ -50,7 +50,7 @@ module ActiveRecord 0 end - def calculate(_operation, _column_name) + def calculate(_operation, _column_name, _options = {}) if _operation == :count 0 else diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index cf24d10a8d..1d81b50abb 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -19,16 +19,17 @@ module ActiveRecord # # Person.group(:city).count # # => { 'Rome' => 5, 'Paris' => 3 } - def count(column_name = nil) - calculate(:count, column_name) + def count(column_name = nil, options = {}) + column_name, options = nil, column_name if column_name.is_a?(Hash) + calculate(:count, column_name, options) end # Calculates the average value on a given column. Returns +nil+ if there's # no row. See +calculate+ for examples with options. # # Person.average(:age) # => 35.8 - def average(column_name) - calculate(:average, column_name) + def average(column_name, options = {}) + calculate(:average, column_name, options) end # Calculates the minimum value on a given column. The value is returned @@ -36,8 +37,8 @@ module ActiveRecord # +calculate+ for examples with options. # # Person.minimum(:age) # => 7 - def minimum(column_name) - calculate(:minimum, column_name) + def minimum(column_name, options = {}) + calculate(:minimum, column_name, options) end # Calculates the maximum value on a given column. The value is returned @@ -45,8 +46,8 @@ module ActiveRecord # +calculate+ for examples with options. # # Person.maximum(:age) # => 93 - def maximum(column_name) - calculate(:maximum, column_name) + def maximum(column_name, options = {}) + calculate(:maximum, column_name, options) end # Calculates the sum of values on a given column. The value is returned @@ -89,15 +90,15 @@ module ActiveRecord # Person.group(:last_name).having("min(age) > 17").minimum(:age) # # Person.sum("2 * age") - def calculate(operation, column_name) + def calculate(operation, column_name, options = {}) if column_name.is_a?(Symbol) && attribute_alias?(column_name) column_name = attribute_alias(column_name) end if has_include?(column_name) - construct_relation_for_association_calculations.calculate(operation, column_name) + construct_relation_for_association_calculations.calculate(operation, column_name, options) else - perform_calculation(operation, column_name) + perform_calculation(operation, column_name, options) end end @@ -180,7 +181,7 @@ module ActiveRecord eager_loading? || (includes_values.present? && (column_name || references_eager_loaded_tables?)) end - def perform_calculation(operation, column_name) + def perform_calculation(operation, column_name, options = {}) operation = operation.to_s.downcase # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count) -- cgit v1.2.3 From 929c2261cdb279d6184cc288d7ab014c1414799c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 11 Dec 2013 13:35:34 -0200 Subject: Mark the arguments needed by activerecord-deprecated_finders with a TODO --- .../lib/active_record/associations/collection_association.rb | 2 ++ .../lib/active_record/associations/collection_proxy.rb | 2 ++ activerecord/lib/active_record/null_relation.rb | 6 ++++-- activerecord/lib/active_record/relation/calculations.rb | 12 ++++++++++++ 4 files changed, 20 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 109ad71ff9..e7bcc59354 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -194,6 +194,8 @@ module ActiveRecord # Count all records using SQL. Construct options and pass them with # scope to the target class's +count+. def count(column_name = nil, count_options = {}) + # TODO: Remove count_options argument as soon we remove support to + # activerecord-deprecated_finders. column_name, count_options = nil, column_name if column_name.is_a?(Hash) relation = scope diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 6e2b559e4b..0dabe256e3 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -670,6 +670,8 @@ module ActiveRecord # # # # # ] def count(column_name = nil, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. @association.count(column_name, options) end diff --git a/activerecord/lib/active_record/null_relation.rb b/activerecord/lib/active_record/null_relation.rb index 62c6bbc413..5b255c3fe5 100644 --- a/activerecord/lib/active_record/null_relation.rb +++ b/activerecord/lib/active_record/null_relation.rb @@ -50,8 +50,10 @@ module ActiveRecord 0 end - def calculate(_operation, _column_name, _options = {}) - if _operation == :count + def calculate(operation, _column_name, _options = {}) + # TODO: Remove _options argument as soon we remove support to + # activerecord-deprecated_finders. + if operation == :count 0 else nil diff --git a/activerecord/lib/active_record/relation/calculations.rb b/activerecord/lib/active_record/relation/calculations.rb index 1d81b50abb..45ffb99868 100644 --- a/activerecord/lib/active_record/relation/calculations.rb +++ b/activerecord/lib/active_record/relation/calculations.rb @@ -20,6 +20,8 @@ module ActiveRecord # Person.group(:city).count # # => { 'Rome' => 5, 'Paris' => 3 } def count(column_name = nil, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. column_name, options = nil, column_name if column_name.is_a?(Hash) calculate(:count, column_name, options) end @@ -29,6 +31,8 @@ module ActiveRecord # # Person.average(:age) # => 35.8 def average(column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. calculate(:average, column_name, options) end @@ -38,6 +42,8 @@ module ActiveRecord # # Person.minimum(:age) # => 7 def minimum(column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. calculate(:minimum, column_name, options) end @@ -47,6 +53,8 @@ module ActiveRecord # # Person.maximum(:age) # => 93 def maximum(column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. calculate(:maximum, column_name, options) end @@ -91,6 +99,8 @@ module ActiveRecord # # Person.sum("2 * age") def calculate(operation, column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. if column_name.is_a?(Symbol) && attribute_alias?(column_name) column_name = attribute_alias(column_name) end @@ -182,6 +192,8 @@ module ActiveRecord end def perform_calculation(operation, column_name, options = {}) + # TODO: Remove options argument as soon we remove support to + # activerecord-deprecated_finders. operation = operation.to_s.downcase # If #count is used with #distinct / #uniq it is considered distinct. (eg. relation.distinct.count) -- cgit v1.2.3 From 7a036ebd30a6333f22684bdd33dcf5ad4d101d26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 11 Dec 2013 19:28:32 -0200 Subject: Revert the whole refactoring in the association builder classes. This is to get activerecord-deprecated_finders work again --- .../associations/builder/association.rb | 56 +++++++++++----------- .../associations/builder/belongs_to.rb | 6 ++- .../associations/builder/collection_association.rb | 24 +++++++--- .../builder/has_and_belongs_to_many.rb | 10 ++-- .../active_record/associations/builder/has_many.rb | 4 +- .../active_record/associations/builder/has_one.rb | 6 ++- .../associations/builder/singular_association.rb | 2 +- 7 files changed, 62 insertions(+), 46 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb index f86d975592..3973570da4 100644 --- a/activerecord/lib/active_record/associations/builder/association.rb +++ b/activerecord/lib/active_record/associations/builder/association.rb @@ -15,23 +15,26 @@ module ActiveRecord::Associations::Builder class Association #:nodoc: class << self attr_accessor :extensions + # TODO: This class accessor is needed to make activerecord-deprecated_finders work. + # We can move it to a constant in 5.0. + attr_accessor :valid_options end self.extensions = [] - # TODO: This class accessor is needed to make activerecord-deprecated_finders work. - # We can move it to a constant in 5.0. - cattr_accessor :valid_options, instance_accessor: false self.valid_options = [:class_name, :class, :foreign_key, :validate] + attr_reader :name, :scope, :options + def self.build(model, name, scope, options, &block) - extension = define_extensions model, name, &block - reflection = create_reflection model, name, scope, options, extension + builder = create_builder model, name, scope, options, &block + reflection = builder.build(model) define_accessors model, reflection define_callbacks model, reflection + builder.define_extensions model reflection end - def self.create_reflection(model, name, scope, options, extension = nil) + def self.create_builder(model, name, scope, options, &block) raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol) if scope.is_a?(Hash) @@ -39,44 +42,39 @@ module ActiveRecord::Associations::Builder scope = nil end - validate_options(options) - - scope = build_scope(scope, extension) - - ActiveRecord::Reflection.create(macro, name, scope, options, model) + new(model, name, scope, options, &block) end - def self.build_scope(scope, extension) - new_scope = scope + def initialize(model, name, scope, options) + # TODO: Remove this model argument as soon we drop support to activerecord-deprecated_finders. + @name = name + @scope = scope + @options = options - if scope && scope.arity == 0 - new_scope = proc { instance_exec(&scope) } - end + validate_options - if extension - new_scope = wrap_scope new_scope, extension + if scope && scope.arity == 0 + @scope = proc { instance_exec(&scope) } end - - new_scope end - def self.wrap_scope(scope, extension) - scope + def build(model) + ActiveRecord::Reflection.create(macro, name, scope, options, model) end - def self.macro + def macro raise NotImplementedError end - def self.build_valid_options(options) - self.valid_options + Association.extensions.flat_map(&:valid_options) + def valid_options + Association.valid_options + Association.extensions.flat_map(&:valid_options) end - def self.validate_options(options) - options.assert_valid_keys(build_valid_options(options)) + def validate_options + options.assert_valid_keys(valid_options) end - def self.define_extensions(model, name) + def define_extensions(model) end def self.define_callbacks(model, reflection) @@ -119,6 +117,8 @@ module ActiveRecord::Associations::Builder raise NotImplementedError end + private + def self.add_before_destroy_callbacks(model, reflection) unless valid_dependent_options.include? reflection.options[:dependent] raise ArgumentError, "The :dependent option must be one of #{valid_dependent_options}, but is :#{reflection.options[:dependent]}" diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb index 7dd8a7deb1..62cc1e3a8d 100644 --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb @@ -1,10 +1,10 @@ module ActiveRecord::Associations::Builder class BelongsTo < SingularAssociation #:nodoc: - def self.macro + def macro :belongs_to end - def self.build_valid_options(options) + def valid_options super + [:foreign_type, :polymorphic, :touch, :counter_cache] end @@ -23,6 +23,8 @@ module ActiveRecord::Associations::Builder add_counter_cache_methods mixin end + private + def self.add_counter_cache_methods(mixin) return if mixin.method_defined? :belongs_to_counter_cache_after_create diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb index b696914883..bc15a49996 100644 --- a/activerecord/lib/active_record/associations/builder/collection_association.rb +++ b/activerecord/lib/active_record/associations/builder/collection_association.rb @@ -7,11 +7,22 @@ module ActiveRecord::Associations::Builder CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove] - def self.build_valid_options(options) + def valid_options super + [:table_name, :before_add, :after_add, :before_remove, :after_remove, :extend] end + attr_reader :block_extension + + def initialize(model, name, scope, options) + super + @mod = nil + if block_given? + @mod = Module.new(&Proc.new) + @scope = wrap_scope @scope, @mod + end + end + def self.define_callbacks(model, reflection) super name = reflection.name @@ -21,11 +32,10 @@ module ActiveRecord::Associations::Builder } end - def self.define_extensions(model, name) - if block_given? + def define_extensions(model) + if @mod extension_module_name = "#{model.name.demodulize}#{name.to_s.camelize}AssociationExtension" - extension = Module.new(&Proc.new) - model.parent.const_set(extension_module_name, extension) + model.parent.const_set(extension_module_name, @mod) end end @@ -68,7 +78,9 @@ module ActiveRecord::Associations::Builder CODE end - def self.wrap_scope(scope, mod) + private + + def wrap_scope(scope, mod) if scope proc { |owner| instance_exec(owner, &scope).extending(mod) } else diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index 1c9c04b044..af596a3a64 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -84,11 +84,11 @@ module ActiveRecord::Associations::Builder middle_name = [lhs_model.name.downcase.pluralize, association_name].join('_').gsub(/::/, '_').to_sym middle_options = middle_options join_model - - HasMany.create_reflection(lhs_model, - middle_name, - nil, - middle_options) + hm_builder = HasMany.create_builder(lhs_model, + middle_name, + nil, + middle_options) + hm_builder.build lhs_model end private diff --git a/activerecord/lib/active_record/associations/builder/has_many.rb b/activerecord/lib/active_record/associations/builder/has_many.rb index 4ec808bc77..7909b93622 100644 --- a/activerecord/lib/active_record/associations/builder/has_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_many.rb @@ -1,10 +1,10 @@ module ActiveRecord::Associations::Builder class HasMany < CollectionAssociation #:nodoc: - def self.macro + def macro :has_many end - def self.build_valid_options(options) + def valid_options super + [:primary_key, :dependent, :as, :through, :source, :source_type, :inverse_of, :counter_cache] end diff --git a/activerecord/lib/active_record/associations/builder/has_one.rb b/activerecord/lib/active_record/associations/builder/has_one.rb index 216d6abec1..f359efd496 100644 --- a/activerecord/lib/active_record/associations/builder/has_one.rb +++ b/activerecord/lib/active_record/associations/builder/has_one.rb @@ -1,10 +1,10 @@ module ActiveRecord::Associations::Builder class HasOne < SingularAssociation #:nodoc: - def self.macro + def macro :has_one end - def self.build_valid_options(options) + def valid_options valid = super + [:order, :as] valid += [:through, :source, :source_type] if options[:through] valid @@ -14,6 +14,8 @@ module ActiveRecord::Associations::Builder [:destroy, :delete, :nullify, :restrict_with_error, :restrict_with_exception] end + private + def self.add_before_destroy_callbacks(model, reflection) super unless reflection.options[:through] end diff --git a/activerecord/lib/active_record/associations/builder/singular_association.rb b/activerecord/lib/active_record/associations/builder/singular_association.rb index 6a1a0073e6..e655c389a6 100644 --- a/activerecord/lib/active_record/associations/builder/singular_association.rb +++ b/activerecord/lib/active_record/associations/builder/singular_association.rb @@ -2,7 +2,7 @@ module ActiveRecord::Associations::Builder class SingularAssociation < Association #:nodoc: - def self.build_valid_options(options) + def valid_options super + [:remote, :dependent, :primary_key, :inverse_of] end -- cgit v1.2.3 From 683df716f3261121af4f8e3e03cc1ce70782ac30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Wed, 11 Dec 2013 20:22:57 -0200 Subject: Move the parameter normalization to the initialize method activerecord-deprecated_finders expects the parameters denormalized in its initialize method --- .../lib/active_record/associations/builder/association.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/builder/association.rb b/activerecord/lib/active_record/associations/builder/association.rb index 3973570da4..3911d1b520 100644 --- a/activerecord/lib/active_record/associations/builder/association.rb +++ b/activerecord/lib/active_record/associations/builder/association.rb @@ -37,15 +37,16 @@ module ActiveRecord::Associations::Builder def self.create_builder(model, name, scope, options, &block) raise ArgumentError, "association names must be a Symbol" unless name.kind_of?(Symbol) + new(model, name, scope, options, &block) + end + + def initialize(model, name, scope, options) + # TODO: Move this to create_builder as soon we drop support to activerecord-deprecated_finders. if scope.is_a?(Hash) options = scope scope = nil end - new(model, name, scope, options, &block) - end - - def initialize(model, name, scope, options) # TODO: Remove this model argument as soon we drop support to activerecord-deprecated_finders. @name = name @scope = scope -- cgit v1.2.3 From 5fccd77b6c71e26fcbf879657c1f532999171900 Mon Sep 17 00:00:00 2001 From: Akshay Vishnoi Date: Thu, 12 Dec 2013 18:28:34 +0530 Subject: Spelling and Grammar checks --- activerecord/lib/active_record/counter_cache.rb | 8 ++++---- activerecord/lib/active_record/errors.rb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/counter_cache.rb b/activerecord/lib/active_record/counter_cache.rb index 7e3bef9431..dcbdf75627 100644 --- a/activerecord/lib/active_record/counter_cache.rb +++ b/activerecord/lib/active_record/counter_cache.rb @@ -82,10 +82,10 @@ module ActiveRecord # Increment a numeric field by one, via a direct SQL update. # - # This method is used primarily for maintaining counter_cache columns used to - # store aggregate values. For example, a DiscussionBoard may cache posts_count - # and comments_count to avoid running an SQL query to calculate the number of - # posts and comments there are each time it is displayed. + # This method is used primarily for maintaining counter_cache columns that are + # used to store aggregate values. For example, a DiscussionBoard may cache + # posts_count and comments_count to avoid running an SQL query to calculate the + # number of posts and comments there are, each time it is displayed. # # ==== Parameters # diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index 7e38719811..2602f79db9 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -188,7 +188,7 @@ module ActiveRecord end end - # Raised when a primary key is needed, but there is not one specified in the schema or model. + # Raised when a primary key is needed, but not specified in the schema or model. class UnknownPrimaryKey < ActiveRecordError attr_reader :model -- cgit v1.2.3 From 89622b92afaa5bde4cd2a3dea70c49e24e15211f Mon Sep 17 00:00:00 2001 From: Josef Stribny Date: Thu, 12 Dec 2013 11:27:20 +0100 Subject: Translate new unique constraint for sqlite >= 3.8.2 --- .../lib/active_record/connection_adapters/sqlite3_adapter.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index e5ad08b6b0..19fa8502d3 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -595,7 +595,11 @@ module ActiveRecord def translate_exception(exception, message) case exception.message - when /column(s)? .* (is|are) not unique/ + # SQLite 3.8.2 returns a newly formatted error message: + # UNIQUE constraint failed: *table_name*.*column_name* + # Older versions of SQLite return: + # column *column_name* is not unique + when /column(s)? .* (is|are) not unique/, /UNIQUE constraint failed: .*/ RecordNotUnique.new(message, exception) else super -- cgit v1.2.3 From da3891c898fd79ab28dad3ce4c9e52d876c9e4e9 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 12 Dec 2013 10:43:36 -0800 Subject: make sure cached table name is a string. fixes #12582 --- .../lib/active_record/associations/builder/has_and_belongs_to_many.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb index af596a3a64..e472277374 100644 --- a/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb +++ b/activerecord/lib/active_record/associations/builder/has_and_belongs_to_many.rb @@ -20,7 +20,7 @@ module ActiveRecord::Associations::Builder def self.build(lhs_class, name, options) if options[:join_table] - KnownTable.new options[:join_table] + KnownTable.new options[:join_table].to_s else class_name = options.fetch(:class_name) { name.to_s.camelize.singularize -- cgit v1.2.3 From c76549d6de3716782d227c370a2169450274ed97 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 12 Dec 2013 11:19:32 -0800 Subject: remove the nil check from set_inverse_instance methods that call set_inverse_instance with a record will not have to pay the cost of a nil check on every call --- activerecord/lib/active_record/associations/association.rb | 3 ++- activerecord/lib/active_record/associations/belongs_to_association.rb | 2 +- .../lib/active_record/associations/preloader/singular_association.rb | 2 +- activerecord/lib/active_record/associations/singular_association.rb | 4 +++- 4 files changed, 7 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/association.rb b/activerecord/lib/active_record/associations/association.rb index ce7d41cf54..67ea489b22 100644 --- a/activerecord/lib/active_record/associations/association.rb +++ b/activerecord/lib/active_record/associations/association.rb @@ -104,11 +104,12 @@ module ActiveRecord # Set the inverse association, if possible def set_inverse_instance(record) - if record && invertible_for?(record) + if invertible_for?(record) inverse = record.association(inverse_reflection_for(record).name) inverse.target = owner inverse.inversed = true end + record end # Returns the class of the target. belongs_to polymorphic overrides this to look at the diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index e1fa5225b5..5ddff13711 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -12,7 +12,7 @@ module ActiveRecord update_counters(record) replace_keys(record) - set_inverse_instance(record) + set_inverse_instance(record) if record @updated = true if record diff --git a/activerecord/lib/active_record/associations/preloader/singular_association.rb b/activerecord/lib/active_record/associations/preloader/singular_association.rb index 2b5cfda8ce..f60647a81e 100644 --- a/activerecord/lib/active_record/associations/preloader/singular_association.rb +++ b/activerecord/lib/active_record/associations/preloader/singular_association.rb @@ -11,7 +11,7 @@ module ActiveRecord association = owner.association(reflection.name) association.target = record - association.set_inverse_instance(record) + association.set_inverse_instance(record) if record end end diff --git a/activerecord/lib/active_record/associations/singular_association.rb b/activerecord/lib/active_record/associations/singular_association.rb index 02dc464536..e4500af5b2 100644 --- a/activerecord/lib/active_record/associations/singular_association.rb +++ b/activerecord/lib/active_record/associations/singular_association.rb @@ -39,7 +39,9 @@ module ActiveRecord end def find_target - scope.first.tap { |record| set_inverse_instance(record) } + if record = scope.first + set_inverse_instance record + end end def replace(record) -- cgit v1.2.3 From 1678e959e973de32287b65c52ebc6cce87148951 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 12 Dec 2013 11:29:02 -0800 Subject: remove nil check calling replace_keys could possibly do a nil check twice, this commit reduces it to once. --- .../active_record/associations/belongs_to_association.rb | 16 ++++++++++------ .../associations/belongs_to_polymorphic_association.rb | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 5ddff13711..7d96f0c372 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -11,7 +11,11 @@ module ActiveRecord raise_on_type_mismatch!(record) if record update_counters(record) - replace_keys(record) + if record + replace_keys(record) + else + remove_keys + end set_inverse_instance(record) if record @updated = true if record @@ -58,11 +62,11 @@ module ActiveRecord end def replace_keys(record) - if record - owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)] - else - owner[reflection.foreign_key] = nil - end + owner[reflection.foreign_key] = record[reflection.association_primary_key(record.class)] + end + + def remove_keys + owner[reflection.foreign_key] = nil end def foreign_key_present? diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb index eae5eed3a1..81d4abfa68 100644 --- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb @@ -11,7 +11,7 @@ module ActiveRecord def replace_keys(record) super - owner[reflection.foreign_type] = record && record.class.base_class.name + owner[reflection.foreign_type] = record.class.base_class.name end def different_target?(record) -- cgit v1.2.3 From 93ee9f0cba607f1050610cddcfa509e5faa7e731 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 12 Dec 2013 11:31:11 -0800 Subject: remove more nil checks --- .../lib/active_record/associations/belongs_to_association.rb | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 7d96f0c372..7dc817fc66 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -8,17 +8,16 @@ module ActiveRecord end def replace(record) - raise_on_type_mismatch!(record) if record - - update_counters(record) if record + raise_on_type_mismatch!(record) + update_counters(record) replace_keys(record) + set_inverse_instance(record) + @updated = true else + update_counters(record) remove_keys end - set_inverse_instance(record) if record - - @updated = true if record self.target = record end -- cgit v1.2.3 From aa85bdba68eb981588cecfef9dea0c0fa3e1c673 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Tue, 3 Dec 2013 18:30:13 -0200 Subject: Use a whitelist to delegate methods to array --- .../lib/active_record/relation/delegation.rb | 24 ++++++---------------- 1 file changed, 6 insertions(+), 18 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 1e15bddcdf..af6be24351 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -36,7 +36,11 @@ module ActiveRecord # may vary depending on the klass of a relation, so we create a subclass of Relation # for each different klass, and the delegations are compiled into that subclass only. - delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :to => :to_a + delegate :&, :+, :[], :all?, :collect, :detect, :each, :each_cons, + :each_with_index, :flat_map, :group_by, :include?, :length, + :map, :none?, :one?, :reverse, :sample, :second, :sort, :sort_by, + :to_ary, :to_set, :to_xml, :to_yaml, :to => :to_a + delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :columns_hash, :to => :klass @@ -84,9 +88,6 @@ module ActiveRecord if @klass.respond_to?(method) self.class.delegate_to_scoped_klass(method) scoping { @klass.send(method, *args, &block) } - elsif array_delegable?(method) - self.class.delegate method, :to => :to_a - to_a.send(method, *args, &block) elsif arel.respond_to?(method) self.class.delegate method, :to => :arel arel.send(method, *args, &block) @@ -109,28 +110,15 @@ module ActiveRecord end def respond_to?(method, include_private = false) - super || array_delegable?(method) || - @klass.respond_to?(method, include_private) || + super || @klass.respond_to?(method, include_private) || arel.respond_to?(method, include_private) end protected - def array_delegable?(method) - defined = Array.method_defined?(method) - if defined && method.to_s.ends_with?('!') - ActiveSupport::Deprecation.warn( - "Association will no longer delegate #{method} to #to_a as of Rails 4.2. You instead must first call #to_a on the association to expose the array to be acted on." - ) - end - defined - end - def method_missing(method, *args, &block) if @klass.respond_to?(method) scoping { @klass.send(method, *args, &block) } - elsif array_delegable?(method) - to_a.send(method, *args, &block) elsif arel.respond_to?(method) arel.send(method, *args, &block) else -- cgit v1.2.3 From 1244aa7c5f871da5e907a39242b63a7aee3308a3 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Thu, 12 Dec 2013 20:19:04 -0200 Subject: Use `public_send` instead of just use `send`. --- activerecord/lib/active_record/relation/delegation.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index af6be24351..603e5a9df5 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -68,7 +68,7 @@ module ActiveRecord RUBY else define_method method do |*args, &block| - scoping { @klass.send(method, *args, &block) } + scoping { @klass.public_send(method, *args, &block) } end end end @@ -87,10 +87,10 @@ module ActiveRecord def method_missing(method, *args, &block) if @klass.respond_to?(method) self.class.delegate_to_scoped_klass(method) - scoping { @klass.send(method, *args, &block) } + scoping { @klass.public_send(method, *args, &block) } elsif arel.respond_to?(method) self.class.delegate method, :to => :arel - arel.send(method, *args, &block) + arel.public_send(method, *args, &block) else super end @@ -118,9 +118,9 @@ module ActiveRecord def method_missing(method, *args, &block) if @klass.respond_to?(method) - scoping { @klass.send(method, *args, &block) } + scoping { @klass.public_send(method, *args, &block) } elsif arel.respond_to?(method) - arel.send(method, *args, &block) + arel.public_send(method, *args, &block) else super end -- cgit v1.2.3 From 6772d5e74aaa3d35e91a4f5b6e46831c03315093 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 12 Dec 2013 14:02:05 -0800 Subject: pull a nil check up one frame --- .../active_record/associations/belongs_to_association.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 7dc817fc66..2fd2cba8fc 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -40,7 +40,15 @@ module ActiveRecord def update_counters(record) counter_cache_name = reflection.counter_cache_column - if counter_cache_name && owner.persisted? && different_target?(record) + return unless counter_cache_name && owner.persisted? + + diff_target = if record + different_target?(record) + else + owner[reflection.foreign_key] + end + + if diff_target if record record.class.increment_counter(counter_cache_name, record.id) end @@ -53,11 +61,7 @@ module ActiveRecord # Checks whether record is different to the current target, without loading it def different_target?(record) - if record.nil? - owner[reflection.foreign_key] - else - record.id != owner[reflection.foreign_key] - end + record.id != owner[reflection.foreign_key] end def replace_keys(record) -- cgit v1.2.3 From 9798a11c2bc9cef5d66efe5006b607c9d858da76 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 12 Dec 2013 14:13:51 -0800 Subject: extract methods out of the cache update method --- .../associations/belongs_to_association.rb | 36 +++++++++++++--------- 1 file changed, 22 insertions(+), 14 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 2fd2cba8fc..2c5ad9d1c0 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -42,20 +42,28 @@ module ActiveRecord return unless counter_cache_name && owner.persisted? - diff_target = if record - different_target?(record) - else - owner[reflection.foreign_key] - end - - if diff_target - if record - record.class.increment_counter(counter_cache_name, record.id) - end - - if foreign_key_present? - klass.decrement_counter(counter_cache_name, target_id) - end + if record + update_with_record record, counter_cache_name + else + update_without_record counter_cache_name + end + end + + def update_with_record record, counter_cache_name + return unless different_target? record + + record.class.increment_counter(counter_cache_name, record.id) + + decrement_counter counter_cache_name + end + + def update_without_record counter_cache_name + decrement_counter counter_cache_name + end + + def decrement_counter counter_cache_name + if foreign_key_present? + klass.decrement_counter(counter_cache_name, target_id) end end -- cgit v1.2.3 From 45940e7112cab8414b15baeb4ef5783247e9f576 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 12 Dec 2013 14:23:28 -0800 Subject: extract a method for updating without a record object --- .../active_record/associations/belongs_to_association.rb | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 2c5ad9d1c0..5ce2a5dcb7 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -15,7 +15,7 @@ module ActiveRecord set_inverse_instance(record) @updated = true else - update_counters(record) + update_counters_without_record remove_keys end @@ -42,11 +42,15 @@ module ActiveRecord return unless counter_cache_name && owner.persisted? - if record - update_with_record record, counter_cache_name - else - update_without_record counter_cache_name - end + update_with_record record, counter_cache_name + end + + def update_counters_without_record + counter_cache_name = reflection.counter_cache_column + + return unless counter_cache_name && owner.persisted? + + update_without_record counter_cache_name end def update_with_record record, counter_cache_name -- cgit v1.2.3 From e2be6eacb86aa11f2c606d45a639802e946e911a Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 12 Dec 2013 14:26:10 -0800 Subject: remove duplicate code --- .../associations/belongs_to_association.rb | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 5ce2a5dcb7..625a2efabd 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -15,7 +15,7 @@ module ActiveRecord set_inverse_instance(record) @updated = true else - update_counters_without_record + decrement_counters remove_keys end @@ -41,19 +41,6 @@ module ActiveRecord counter_cache_name = reflection.counter_cache_column return unless counter_cache_name && owner.persisted? - - update_with_record record, counter_cache_name - end - - def update_counters_without_record - counter_cache_name = reflection.counter_cache_column - - return unless counter_cache_name && owner.persisted? - - update_without_record counter_cache_name - end - - def update_with_record record, counter_cache_name return unless different_target? record record.class.increment_counter(counter_cache_name, record.id) @@ -61,7 +48,11 @@ module ActiveRecord decrement_counter counter_cache_name end - def update_without_record counter_cache_name + def decrement_counters + counter_cache_name = reflection.counter_cache_column + + return unless counter_cache_name && owner.persisted? + decrement_counter counter_cache_name end -- cgit v1.2.3 From 94cd08be38dc086714be8f6cbe17e77f778e1ecc Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 12 Dec 2013 14:30:55 -0800 Subject: extract cache counter logic to one method --- .../associations/belongs_to_association.rb | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/belongs_to_association.rb b/activerecord/lib/active_record/associations/belongs_to_association.rb index 625a2efabd..8272a5584c 100644 --- a/activerecord/lib/active_record/associations/belongs_to_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_association.rb @@ -37,23 +37,22 @@ module ActiveRecord !loaded? && foreign_key_present? && klass end - def update_counters(record) + def with_cache_name counter_cache_name = reflection.counter_cache_column - return unless counter_cache_name && owner.persisted? - return unless different_target? record - - record.class.increment_counter(counter_cache_name, record.id) + yield counter_cache_name + end - decrement_counter counter_cache_name + def update_counters(record) + with_cache_name do |name| + return unless different_target? record + record.class.increment_counter(name, record.id) + decrement_counter name + end end def decrement_counters - counter_cache_name = reflection.counter_cache_column - - return unless counter_cache_name && owner.persisted? - - decrement_counter counter_cache_name + with_cache_name { |name| decrement_counter name } end def decrement_counter counter_cache_name -- cgit v1.2.3 From 0b142a6f842051e3f1f3c146d1e1318050274352 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 12 Dec 2013 21:10:03 -0700 Subject: Add a bunch of Relation -> Array delegate methods to the whitelist. This won't last - aim to switch back to a blacklist for mutator methods. --- activerecord/lib/active_record/relation/delegation.rb | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 603e5a9df5..87f5e8e684 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -36,10 +36,18 @@ module ActiveRecord # may vary depending on the klass of a relation, so we create a subclass of Relation # for each different klass, and the delegations are compiled into that subclass only. - delegate :&, :+, :[], :all?, :collect, :detect, :each, :each_cons, - :each_with_index, :flat_map, :group_by, :include?, :length, - :map, :none?, :one?, :reverse, :sample, :second, :sort, :sort_by, - :to_ary, :to_set, :to_xml, :to_yaml, :to => :to_a + # TODO: This is not going to work. Brittle, painful. We'll switch to a blacklist + # to disallow mutator methods like map!, pop, and delete_if instead. + ARRAY_DELEGATES = [ + :+, :-, :|, :&, :[], + :all?, :collect, :detect, :each, :each_cons, :each_with_index, + :exclude?, :find_all, :flat_map, :group_by, :include?, :length, + :map, :none?, :one?, :partition, :reject, :reverse, + :sample, :second, :sort, :sort_by, :third, + :to_ary, :to_set, :to_xml, :to_yaml + ] + + delegate *ARRAY_DELEGATES, to: :to_a delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :columns_hash, :to => :klass -- cgit v1.2.3 From b8569b9337309c2d6c72566a3426994b6da443d7 Mon Sep 17 00:00:00 2001 From: Kassio Borges Date: Fri, 13 Dec 2013 11:37:19 -0200 Subject: Fix mysql to support duplicated column names This will fix the [broken test](https://github.com/rails/rails/commit/4a2650836680f51490e999c3c8441a2f9adff96e) `test_with_limiting_with_custom_select`. The query's result was built in a hash with column name as key, if the result have a duplicated column name the last value was overriding the first one. --- .../lib/active_record/connection_adapters/mysql_adapter.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index c4dcba0501..760f1435eb 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -419,14 +419,19 @@ module ActiveRecord if result types = {} + fields = [] result.fetch_fields.each { |field| + field_name = field.name + fields << field_name + if field.decimals > 0 - types[field.name] = Fields::Decimal.new + types[field_name] = Fields::Decimal.new else - types[field.name] = Fields.find_type field + types[field_name] = Fields.find_type field end } - result_set = ActiveRecord::Result.new(types.keys, result.to_a, types) + + result_set = ActiveRecord::Result.new(fields, result.to_a, types) result.free else result_set = ActiveRecord::Result.new([], []) -- cgit v1.2.3 From c2f1796c6dfa1f37581471dea0a126d3efa2a7fe Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Fri, 13 Dec 2013 17:19:20 +0100 Subject: argument prefix warning removed * interpreted as a argument prefix --- activerecord/lib/active_record/relation/delegation.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 87f5e8e684..246c5db5bd 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -47,7 +47,7 @@ module ActiveRecord :to_ary, :to_set, :to_xml, :to_yaml ] - delegate *ARRAY_DELEGATES, to: :to_a + delegate(*ARRAY_DELEGATES, to: :to_a) delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :columns_hash, :to => :klass -- cgit v1.2.3 From 75a2e4a8dff8c01dad913048d3676b993a8e23a2 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Fri, 13 Dec 2013 15:20:03 -0700 Subject: support creating temporary tables from queries also override drop_table in AbstractMySQLAdapter to properly drop temporary tables without committing the transaction --- .../abstract/schema_creation.rb | 7 +++-- .../abstract/schema_definitions.rb | 5 ++-- .../abstract/schema_statements.rb | 35 +++++++++++++++------- .../connection_adapters/abstract_mysql_adapter.rb | 4 +++ .../connection_adapters/postgresql_adapter.rb | 4 +-- 5 files changed, 38 insertions(+), 17 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb index 7c330a2f25..a51691bfa8 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_creation.rb @@ -34,9 +34,10 @@ module ActiveRecord def visit_TableDefinition(o) create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE " - create_sql << "#{quote_table_name(o.name)} (" - create_sql << o.columns.map { |c| accept c }.join(', ') - create_sql << ") #{o.options}" + create_sql << "#{quote_table_name(o.name)} " + create_sql << "(#{o.columns.map { |c| accept c }.join(', ')}) " unless o.as + create_sql << "#{o.options}" + create_sql << " AS #{@conn.to_sql(o.as)}" if o.as create_sql end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb index 063b19871a..c39bf15e83 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -49,14 +49,15 @@ module ActiveRecord # An array of ColumnDefinition objects, representing the column changes # that have been defined. attr_accessor :indexes - attr_reader :name, :temporary, :options + attr_reader :name, :temporary, :options, :as - def initialize(types, name, temporary, options) + def initialize(types, name, temporary, options, as = nil) @columns_hash = {} @indexes = {} @native = types @temporary = temporary @options = options + @as = as @name = name end diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 51a6929dab..e8ea96c89f 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -131,6 +131,9 @@ module ActiveRecord # [:force] # Set to true to drop the table before creating it. # Defaults to false. + # [:as] + # SQL to use to generate the table. When this option is used, the block is + # ignored, as are the :id and :primary_key options. # # ====== Add a backend specific option to the generated SQL (MySQL) # @@ -169,19 +172,31 @@ module ActiveRecord # supplier_id int # ) # + # ====== Create a temporary table based on 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 + # # See also TableDefinition#column for details on how to create columns. def create_table(table_name, options = {}) - td = create_table_definition table_name, options[:temporary], options[:options] + td = create_table_definition table_name, options[:temporary], options[:options], options[:as] - unless options[:id] == false - pk = options.fetch(:primary_key) { - Base.get_primary_key table_name.to_s.singularize - } + if !options[:as] + unless options[:id] == false + pk = options.fetch(:primary_key) { + Base.get_primary_key table_name.to_s.singularize + } - td.primary_key pk, options.fetch(:id, :primary_key), options - end + td.primary_key pk, options.fetch(:id, :primary_key), options + end - yield td if block_given? + yield td if block_given? + end if options[:force] && table_exists?(table_name) drop_table(table_name, options) @@ -826,8 +841,8 @@ module ActiveRecord end private - def create_table_definition(name, temporary, options) - TableDefinition.new native_database_types, name, temporary, options + def create_table_definition(name, temporary, options, as = nil) + TableDefinition.new native_database_types, name, temporary, options, as end def create_alter_table(name) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 1b9f865666..59a788397e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -492,6 +492,10 @@ module ActiveRecord rename_table_indexes(table_name, new_name) end + def drop_table(table_name, options = {}) + execute "DROP#{' TEMPORARY' if options[:temporary]} TABLE #{quote_table_name(table_name)}" + end + def rename_index(table_name, old_name, new_name) if (version[0] == 5 && version[1] >= 7) || version[0] >= 6 execute "ALTER TABLE #{quote_table_name(table_name)} RENAME INDEX #{quote_table_name(old_name)} TO #{quote_table_name(new_name)}" diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index d23a24589c..2912a208ee 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -973,8 +973,8 @@ module ActiveRecord $1.strip if $1 end - def create_table_definition(name, temporary, options) - TableDefinition.new native_database_types, name, temporary, options + def create_table_definition(name, temporary, options, as = nil) + TableDefinition.new native_database_types, name, temporary, options, as end def update_table_definition(table_name, base) -- cgit v1.2.3 From b3a806b7b2179fc588465fee728fe837cf8d4940 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Fri, 13 Dec 2013 16:04:46 -0500 Subject: db:test:clone and prepare must load 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. --- activerecord/lib/active_record/railties/databases.rake | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 52b3d3e5e6..0fdfed991c 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -343,7 +343,7 @@ db_namespace = namespace :db do end # desc "Recreate the test database from a fresh schema" - task :clone do + task :clone => :environment do case ActiveRecord::Base.schema_format when :ruby db_namespace["test:clone_schema"].invoke @@ -364,7 +364,7 @@ db_namespace = namespace :db do end # desc 'Check for pending migrations and load the test schema' - task :prepare => :load_config do + task :prepare => [:environment, :load_config] do unless ActiveRecord::Base.configurations.blank? db_namespace['test:load'].invoke end @@ -401,4 +401,3 @@ namespace :railties do end task 'test:prepare' => ['db:test:prepare', 'db:test:load', 'db:abort_if_pending_migrations'] - -- cgit v1.2.3 From c758093eca303704f52b45fd0660e13600b9b315 Mon Sep 17 00:00:00 2001 From: Akshay Vishnoi Date: Mon, 16 Dec 2013 00:44:37 +0530 Subject: Spelling and Grammar check [ci skip] --- activerecord/lib/active_record/relation/query_methods.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index cacc787eba..4f4961d273 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -427,7 +427,7 @@ module ActiveRecord # === string # # A single string, without additional arguments, is passed to the query - # constructor as a SQL fragment, and used in the where clause of the query. + # constructor as an SQL fragment, and used in the where clause of the query. # # Client.where("orders_count = '2'") # # SELECT * from clients where orders_count = '2'; @@ -656,7 +656,7 @@ module ActiveRecord # when 'Reviewer' # Post.published # when 'Bad User' - # Post.none # => returning [] instead breaks the previous code + # Post.none # It can't be chained if [] is returned. # end # end # -- cgit v1.2.3 From 4b4aeabb3605bd0cbd7dde10c1d2ac990c65379a Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Mon, 16 Dec 2013 11:14:54 -0200 Subject: Fix missing closing rdoc tag [ci skip] --- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') 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 e8ea96c89f..00383bad3b 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -131,7 +131,7 @@ module ActiveRecord # [:force] # Set to true to drop the table before creating it. # Defaults to false. - # [:as] + # [:as] # SQL to use to generate the table. When this option is used, the block is # ignored, as are the :id and :primary_key options. # -- cgit v1.2.3 From 8062a307942cb3f7a83bfc1a8cd81e3a1f8edc5b Mon Sep 17 00:00:00 2001 From: Martin Emde Date: Mon, 16 Dec 2013 14:16:15 -0800 Subject: Better support for `where()` conditions that use an association name. Using the name of an association in `where` previously worked only if the value was a single `ActiveRecrd::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. # Given the Post model 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) # 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")) 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") --- .../lib/active_record/relation/predicate_builder.rb | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index c60cd27a83..1252af7635 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -55,9 +55,9 @@ module ActiveRecord # # For polymorphic relationships, find the foreign key and type: # PriceEstimate.where(estimate_of: treasure) - if klass && value.is_a?(Base) && reflection = klass.reflect_on_association(column.to_sym) - if reflection.polymorphic? - queries << build(table[reflection.foreign_type], value.class.base_class) + if klass && reflection = klass.reflect_on_association(column.to_sym) + if reflection.polymorphic? && base_class = polymorphic_base_class_from_value(value) + queries << build(table[reflection.foreign_type], base_class) end column = reflection.foreign_key @@ -67,6 +67,18 @@ module ActiveRecord queries end + def self.polymorphic_base_class_from_value(value) + case value + when Relation + value.klass.base_class + when Array + val = value.compact.first + val.class.base_class if val.is_a?(Base) + when Base + value.class.base_class + end + end + def self.references(attributes) attributes.map do |key, value| if value.is_a?(Hash) -- cgit v1.2.3 From c28d0f2031d31aeb5289b73acbb5c1adb7bd71d4 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Tue, 17 Dec 2013 01:56:35 -0700 Subject: MySQL: remove the old-school 'packets out of order' message Blast from the past, MySQL 4 era, when the password hashing style changed. --- .../lib/active_record/connection_adapters/abstract_mysql_adapter.rb | 6 ------ 1 file changed, 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index 59a788397e..7768c24967 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -303,12 +303,6 @@ module ActiveRecord else log(sql, name) { @connection.query(sql) } end - rescue ActiveRecord::StatementInvalid => exception - if exception.message.split(":").first =~ /Packets out of order/ - raise ActiveRecord::StatementInvalid.new("'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings.", exception.original_exception) - else - raise - end end # MysqlAdapter has to free a result after using it, so we use this method to write -- cgit v1.2.3 From d4ee09cda135da9c36a5ddadd2d8c2d35c116be3 Mon Sep 17 00:00:00 2001 From: Lauro Caetano Date: Fri, 13 Dec 2013 22:11:39 -0200 Subject: Create a blacklist to disallow mutator methods to be delegated to `Array`. This change was necessary because the whitelist wouldn't work. It would be painful for users trying to update their applications. This blacklist intent to prevent odd bugs and confusion in code that call mutator methods directely on the `Relation`. --- .../lib/active_record/relation/delegation.rb | 25 ++++++++++++---------- 1 file changed, 14 insertions(+), 11 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 246c5db5bd..21beed332f 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -1,3 +1,4 @@ +require 'set' require 'active_support/concern' require 'active_support/deprecation' @@ -36,18 +37,13 @@ module ActiveRecord # may vary depending on the klass of a relation, so we create a subclass of Relation # for each different klass, and the delegations are compiled into that subclass only. - # TODO: This is not going to work. Brittle, painful. We'll switch to a blacklist - # to disallow mutator methods like map!, pop, and delete_if instead. - ARRAY_DELEGATES = [ - :+, :-, :|, :&, :[], - :all?, :collect, :detect, :each, :each_cons, :each_with_index, - :exclude?, :find_all, :flat_map, :group_by, :include?, :length, - :map, :none?, :one?, :partition, :reject, :reverse, - :sample, :second, :sort, :sort_by, :third, - :to_ary, :to_set, :to_xml, :to_yaml - ] + BLACKLISTED_ARRAY_METHODS = [ + :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!, + :shuffle!, :slice!, :sort!, :sort_by!, :delete_if, + :keep_if, :pop, :shift, :delete_at, :compact + ].to_set # :nodoc: - delegate(*ARRAY_DELEGATES, to: :to_a) + delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, to: :to_a delegate :table_name, :quoted_table_name, :primary_key, :quoted_primary_key, :connection, :columns_hash, :to => :klass @@ -119,14 +115,21 @@ module ActiveRecord def respond_to?(method, include_private = false) super || @klass.respond_to?(method, include_private) || + array_delegable?(method) || arel.respond_to?(method, include_private) end protected + def array_delegable?(method) + Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method) + end + def method_missing(method, *args, &block) if @klass.respond_to?(method) scoping { @klass.public_send(method, *args, &block) } + elsif array_delegable?(method) + to_a.public_send(method, *args, &block) elsif arel.respond_to?(method) arel.public_send(method, *args, &block) else -- cgit v1.2.3 From c0a2d474c50a3a096229f956d9696d9e91f1557a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Tue, 17 Dec 2013 16:05:28 -0800 Subject: Get ready to release 4.1.0.beta1 --- activerecord/lib/active_record/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/version.rb b/activerecord/lib/active_record/version.rb index de5fd05468..863c3ebe4d 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.beta" + Gem::Version.new "4.1.0.beta1" end module VERSION #:nodoc: -- cgit v1.2.3 From db765ec741911ca8d9dfc9403f532d239301e435 Mon Sep 17 00:00:00 2001 From: Akshay Vishnoi Date: Wed, 18 Dec 2013 11:52:54 +0530 Subject: #none documentation updated [ci skip] --- activerecord/lib/active_record/relation/query_methods.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 4f4961d273..e7b8809fe0 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -631,12 +631,11 @@ module ActiveRecord self end - # Returns a chainable relation with zero records, specifically an - # instance of the ActiveRecord::NullRelation class. + # Returns a chainable relation with zero records. # - # The returned ActiveRecord::NullRelation inherits from Relation and implements the - # Null Object pattern. It is an object with defined null behavior and always returns an empty - # array of records without querying the database. + # The returned relation implements the Null Object pattern. It is an + # object with defined null behavior and always returns an empty array of + # records without querying the database. # # Any subsequent condition chained to the returned relation will continue # generating an empty relation and will not fire any query to the database. -- cgit v1.2.3 From 01162b3d77b50057791526d29b97c16a891b6048 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Wed, 18 Dec 2013 04:52:24 -0700 Subject: Fix nil assignment to polymorphic belongs_to Assigning nil to a polymorphic belongs_to would nullify its _id field by not its _type field. Fixes failing test from c141dfc838a5dca9f197814410fa5d44c143129c. Regression from 1678e959e973de32287b65c52ebc6cce87148951. --- .../active_record/associations/belongs_to_polymorphic_association.rb | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb index 81d4abfa68..b710cf6bdb 100644 --- a/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb +++ b/activerecord/lib/active_record/associations/belongs_to_polymorphic_association.rb @@ -14,6 +14,11 @@ module ActiveRecord owner[reflection.foreign_type] = record.class.base_class.name end + def remove_keys + super + owner[reflection.foreign_type] = nil + end + def different_target?(record) super || record.class != klass end -- cgit v1.2.3 From ee738f960439e511285bc53b7207cebacda8db36 Mon Sep 17 00:00:00 2001 From: rono23 Date: Thu, 19 Dec 2013 01:35:30 +0900 Subject: Fix to_param when attribute has multibyte character --- activerecord/lib/active_record/integration.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/integration.rb b/activerecord/lib/active_record/integration.rb index 27576b1e61..31e2518540 100644 --- a/activerecord/lib/active_record/integration.rb +++ b/activerecord/lib/active_record/integration.rb @@ -98,8 +98,10 @@ module ActiveRecord super() else define_method :to_param do - if (default = super()) && (result = send(method_name).to_s).present? - "#{default}-#{result.squish.truncate(20, separator: /\s/, omission: nil).parameterize}" + if (default = super()) && + (result = send(method_name).to_s).present? && + (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present? + "#{default}-#{param}" else default end -- cgit v1.2.3 From ed768ee5c1cb4e6debbd744b246388ac694daea7 Mon Sep 17 00:00:00 2001 From: Alex Ghiculescu Date: Thu, 19 Dec 2013 16:13:18 +1000 Subject: Fix typo: data --> date --- activerecord/lib/active_record/attribute_methods/read.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index c152a246b5..d01e9aea59 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -102,7 +102,7 @@ module ActiveRecord end # Returns the value of the attribute identified by attr_name after - # it has been typecast (for example, "2004-12-12" in a data column is cast + # it has been typecast (for example, "2004-12-12" in a date column is cast # to a date object, like Date.new(2004, 12, 12)). def read_attribute(attr_name) # If it's cached, just return it -- cgit v1.2.3 From 517caa85c97b0addba0d27fffa03aec40f70ff1f Mon Sep 17 00:00:00 2001 From: nishant-cyro Date: Thu, 19 Dec 2013 12:02:01 +0530 Subject: fix typos and grammar mistake [ci skip] --- activerecord/lib/active_record/attribute_methods.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 217fc52dd5..73761520f7 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -330,7 +330,7 @@ module ActiveRecord end # Returns the value of the attribute identified by attr_name after it has been typecast (for example, - # "2004-12-12" in a data column is cast to a date object, like Date.new(2004, 12, 12)). It raises + # "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises # ActiveModel::MissingAttributeError if the identified attribute is missing. # # Alias for the read_attribute method. -- cgit v1.2.3 From b082bece5ed99a1bb1ff9d4af5f9d97a3dcaf6c1 Mon Sep 17 00:00:00 2001 From: Kuldeep Aggarwal Date: Thu, 19 Dec 2013 18:46:17 +0530 Subject: 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. --- .../lib/active_record/connection_adapters/postgresql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 2912a208ee..dd3bfa5546 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -969,7 +969,7 @@ module ActiveRecord end def extract_table_ref_from_insert_sql(sql) - sql[/into\s+([^\(]*).*values\s*\(/i] + sql[/into\s+([^\(]*).*values\s*\(/im] $1.strip if $1 end -- cgit v1.2.3 From 847e9a95da59f1263e6e5c15cd5ce5c9ec8260a6 Mon Sep 17 00:00:00 2001 From: Cody Cutrer Date: Mon, 16 Dec 2013 21:26:37 -0700 Subject: fix default select when from is used --- activerecord/lib/active_record/relation/query_methods.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index e7b8809fe0..3d0709266a 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -982,8 +982,10 @@ module ActiveRecord end def build_select(arel, selects) - unless selects.empty? + if !selects.empty? arel.project(*selects) + elsif from_value + arel.project(Arel.star) else arel.project(@klass.arel_table[Arel.star]) end -- cgit v1.2.3 From 4a720a2cdefc1926e0bfa23b8febd71869093c59 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Thu, 19 Dec 2013 16:51:28 -0500 Subject: quoting: Check if id is a valid method before using it Need to check if valud also respond_to :id before calling it, otherwise things could explode. --- .../lib/active_record/connection_adapters/abstract/quoting.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb index 552a22d28a..75501852ed 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb @@ -43,7 +43,9 @@ module ActiveRecord # SQLite does not understand dates, so this method will convert a Date # to a String. def type_cast(value, column) - return value.id if value.respond_to?(:quoted_id) + if value.respond_to?(:quoted_id) && value.respond_to?(:id) + return value.id + end case value when String, ActiveSupport::Multibyte::Chars -- cgit v1.2.3 From 855ded667975a797cc10b0f38c2986c76b18e251 Mon Sep 17 00:00:00 2001 From: Ben Angel Date: Thu, 19 Dec 2013 18:35:24 -0800 Subject: change CollectionAssociation's comments to say Association instead of AssociationProxy to match changes for 3.1 removing Association proxy. --- activerecord/lib/active_record/associations/collection_association.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 62f23f54f9..c7315b12bd 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -4,7 +4,7 @@ module ActiveRecord # # CollectionAssociation is an abstract class that provides common stuff to # ease the implementation of association proxies that represent - # collections. See the class hierarchy in AssociationProxy. + # collections. See the class hierarchy in Association. # # CollectionAssociation: # HasManyAssociation => has_many -- cgit v1.2.3 From fbb79b517f3127ba620fedd01849f9628b78d6ce Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Fri, 20 Dec 2013 16:13:34 -0800 Subject: fix url connections for sqlite3 --- .../connection_adapters/connection_specification.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 64fc9e95d8..66d7f04fc3 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -69,11 +69,22 @@ module ActiveRecord config = URI.parse url adapter = config.scheme adapter = "postgresql" if adapter == "postgres" + + database = if adapter == 'sqlite3' + if '/:memory:' == config.path + ':memory:' + else + config.path + end + else + config.path.sub(%r{^/},"") + end + spec = { :adapter => adapter, :username => config.user, :password => config.password, :port => config.port, - :database => config.path.sub(%r{^/},""), + :database => database, :host => config.host } spec.reject!{ |_,value| value.blank? } -- cgit v1.2.3 From 7432d32f527f9a06ed51f576985987a1f87c5c78 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Sun, 22 Dec 2013 03:53:39 -0800 Subject: Use the same enum definition in the docs and in the comments [ci skip] --- activerecord/lib/active_record/enum.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 837989aaa7..a657452813 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -55,12 +55,12 @@ module ActiveRecord def enum(definitions) klass = self definitions.each do |name, values| - # DIRECTION = { } + # STATUS = { } enum_values = _enum_methods_module.const_set name.to_s.upcase, ActiveSupport::HashWithIndifferentAccess.new name = name.to_sym _enum_methods_module.module_eval do - # def direction=(value) self[:direction] = DIRECTION[value] end + # def status=(value) self[:status] = STATUS[value] end define_method("#{name}=") { |value| unless enum_values.has_key?(value) raise ArgumentError, "'#{value}' is not a valid #{name}" @@ -68,20 +68,20 @@ module ActiveRecord self[name] = enum_values[value] } - # def direction() DIRECTION.key self[:direction] end + # def status() STATUS.key self[:status] end define_method(name) { enum_values.key self[name] } pairs = values.respond_to?(:each_pair) ? values.each_pair : values.each_with_index pairs.each do |value, i| enum_values[value] = i - # scope :incoming, -> { where direction: 0 } + # scope :active, -> { where status: 0 } klass.scope value, -> { klass.where name => i } - # def incoming?() direction == 0 end + # def active?() status == 0 end define_method("#{value}?") { self[name] == i } - # def incoming! update! direction: :incoming end + # def active! update! status: :active end define_method("#{value}!") { update! name => value } end end -- cgit v1.2.3 From 1f6a9b50ee26d6c4520a416c66ef999d92570698 Mon Sep 17 00:00:00 2001 From: Damien Mathieu <42@dmathieu.com> Date: Sun, 22 Dec 2013 17:00:40 +0100 Subject: fix quoting non-strings Closes #13444 --- activerecord/lib/active_record/connection_adapters/postgresql/cast.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb index bf34f2bdae..35ce881302 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql/cast.rb @@ -144,7 +144,7 @@ module ActiveRecord def quote_and_escape(value) case value - when "NULL" + when "NULL", Numeric value else "\"#{value.gsub(/"/,"\\\"")}\"" -- cgit v1.2.3 From 2bcf7158d346e1b619aebbbec360ee0153ef8d06 Mon Sep 17 00:00:00 2001 From: Paul Nikitochkin Date: Sun, 22 Dec 2013 18:18:40 +0200 Subject: On destroying do not touch destroyed belongs to association. Fixes: #13445 --- activerecord/lib/active_record/associations/builder/belongs_to.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/builder/belongs_to.rb b/activerecord/lib/active_record/associations/builder/belongs_to.rb index 62cc1e3a8d..5ccaa55a32 100644 --- a/activerecord/lib/active_record/associations/builder/belongs_to.rb +++ b/activerecord/lib/active_record/associations/builder/belongs_to.rb @@ -112,7 +112,7 @@ module ActiveRecord::Associations::Builder end record = o.send name - unless record.nil? || record.new_record? + if record && record.persisted? if touch != true record.touch touch else -- cgit v1.2.3 From b4d7be95e635da64b99e833a93c345bb4d6f2ba2 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Mon, 23 Dec 2013 13:18:06 -0200 Subject: Fix comment example in AR enum [ci skip] --- activerecord/lib/active_record/enum.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index a657452813..2f8439892b 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -81,7 +81,7 @@ module ActiveRecord # def active?() status == 0 end define_method("#{value}?") { self[name] == i } - # def active! update! status: :active end + # def active!() update! status: :active end define_method("#{value}!") { update! name => value } end end -- cgit v1.2.3 From 0ec45cd15d0a2f5aebc75e23d841b6c12f3ba763 Mon Sep 17 00:00:00 2001 From: schneems Date: Mon, 11 Nov 2013 15:56:09 -0500 Subject: Tell how to Create a Database in Error Message Currently if you attempt to use a database that does not exist you get an error: ``` PG::ConnectionBad FATAL: database "db_error" does not exist ``` The solution is easy, create and migrate your database however new developers may not know these commands by memory. Instead of requiring the developer to search for a solution, tell them how to fix the problem in the error message: ``` ActiveRecord::NoDatabase: FATAL: database "db_error" does not exist Run `$ bin/rake db:create db:migrate` to create your database ``` Active Record should not know about `rake db:migrate` so this additional information needs to come from the railtie. Potential alternative implementation suggestions are welcome. --- .../active_record/connection_adapters/postgresql_adapter.rb | 6 ++++++ activerecord/lib/active_record/errors.rb | 12 ++++++++++++ activerecord/lib/active_record/railtie.rb | 8 ++++++++ 3 files changed, 26 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index dd3bfa5546..95cd69d5ad 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -865,6 +865,12 @@ module ActiveRecord PostgreSQLColumn.money_precision = (postgresql_version >= 80300) ? 19 : 10 configure_connection + rescue ::PG::Error => error + if error.message.include?("does not exist") + raise ActiveRecord::NoDatabaseError.new(error.message) + else + raise error + end end # Configures the encoding, verbosity, schema search path, and time zone of the connection. diff --git a/activerecord/lib/active_record/errors.rb b/activerecord/lib/active_record/errors.rb index 2602f79db9..7f6228131f 100644 --- a/activerecord/lib/active_record/errors.rb +++ b/activerecord/lib/active_record/errors.rb @@ -94,6 +94,18 @@ module ActiveRecord class PreparedStatementInvalid < ActiveRecordError end + # Raised when a given database does not exist + class NoDatabaseError < ActiveRecordError + def initialize(message) + super extend_message(message) + end + + # can be over written to add additional error information. + def extend_message(message) + message + end + end + # Raised on attempt to save stale record. Record is stale when it's being saved in another query after # instantiation, for example, when two users edit the same wiki page and one starts editing and saves # the page before the other. diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index eef08aea88..ff98c829f4 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -122,6 +122,14 @@ module ActiveRecord # and then establishes the connection. initializer "active_record.initialize_database" do |app| ActiveSupport.on_load(:active_record) do + + class ActiveRecord::NoDatabaseError + def extend_message(message) + message << "Run `$ bin/rake db:create db:migrate` to create your database" + message + end + end + self.configurations = app.config.database_configuration || {} establish_connection end -- cgit v1.2.3 From 56510091fb8d178ec7c2391bed9fbdb9e3a54287 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Thu, 19 Dec 2013 17:39:20 -0200 Subject: Do not consider PG 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. Since most of the array support was based on the postgres_ext lib, it's worth noting it does the same thing for numeric array columns too: https://github.com/dockyard/postgres_ext/blob/v1.0.0/lib/postgres_ext/active_record/connection_adapters/postgres_adapter.rb#L72 This extended the same logic for text columns to ensure consistency. --- .../active_record/connection_adapters/postgresql_adapter.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 95cd69d5ad..11a5eba464 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -46,7 +46,7 @@ module ActiveRecord # PostgreSQL-specific extensions to column definitions in a table. class PostgreSQLColumn < Column #:nodoc: attr_accessor :array - # Instantiates a new PostgreSQL column definition in a table. + def initialize(name, default, oid_type, sql_type = nil, null = true) @oid_type = oid_type default_value = self.class.extract_value_from_default(default) @@ -62,6 +62,14 @@ module ActiveRecord @default_function = default if has_default_function?(default_value, default) end + def number? + !array && super + end + + def text? + !array && super + end + # :stopdoc: class << self include ConnectionAdapters::PostgreSQLColumn::Cast -- cgit v1.2.3 From 73bba4c1e1f7fa23aa1a126971338d94ae42398f Mon Sep 17 00:00:00 2001 From: Tadas Tamosauskas Date: Sat, 28 Sep 2013 16:10:59 +0100 Subject: Serialize postgres' hstore, json and array types correctly in AR update methods. Fixes #12261. Closes #12395. Conflicts: activerecord/CHANGELOG.md activerecord/test/cases/adapters/postgresql/array_test.rb activerecord/test/cases/adapters/postgresql/json_test.rb --- activerecord/lib/active_record/sanitization.rb | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index cab8fd745a..bc78c3858a 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -101,7 +101,7 @@ module ActiveRecord # # => "status = NULL , group_id = 1" def sanitize_sql_hash_for_assignment(attrs, table) attrs.map do |attr, value| - "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value)}" + "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, connection, columns_hash[attr.to_s])}" end.join(', ') end @@ -152,15 +152,16 @@ module ActiveRecord end end - def quote_bound_value(value, c = connection) #:nodoc: - if value.respond_to?(:map) && !value.acts_like?(:string) + def quote_bound_value(value, c = connection, column = nil) #:nodoc: + db_native_type = column && (column.respond_to?(:array) && column.array || [:json, :hstore].include?(column.type) ) + if value.respond_to?(:map) && !value.acts_like?(:string) && !db_native_type if value.respond_to?(:empty?) && value.empty? c.quote(nil) else value.map { |v| c.quote(v) }.join(',') end else - c.quote(value) + c.quote(value, column) end end -- cgit v1.2.3 From 9e1740af9c1a00980b30c66aba3c223c33e646e4 Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Fri, 20 Dec 2013 08:45:54 -0200 Subject: Tidy up fix for PG extensions quoting Always pass in the column for quote_bound_value and quote using it in case it exists there. --- activerecord/lib/active_record/sanitization.rb | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/sanitization.rb b/activerecord/lib/active_record/sanitization.rb index bc78c3858a..dacaec26b7 100644 --- a/activerecord/lib/active_record/sanitization.rb +++ b/activerecord/lib/active_record/sanitization.rb @@ -100,8 +100,9 @@ module ActiveRecord # { status: nil, group_id: 1 } # # => "status = NULL , group_id = 1" def sanitize_sql_hash_for_assignment(attrs, table) + c = connection attrs.map do |attr, value| - "#{connection.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, connection, columns_hash[attr.to_s])}" + "#{c.quote_table_name_for_assignment(table, attr)} = #{quote_bound_value(value, c, columns_hash[attr.to_s])}" end.join(', ') end @@ -153,15 +154,16 @@ module ActiveRecord end def quote_bound_value(value, c = connection, column = nil) #:nodoc: - db_native_type = column && (column.respond_to?(:array) && column.array || [:json, :hstore].include?(column.type) ) - if value.respond_to?(:map) && !value.acts_like?(:string) && !db_native_type + if column + c.quote(value, column) + elsif value.respond_to?(:map) && !value.acts_like?(:string) if value.respond_to?(:empty?) && value.empty? c.quote(nil) else value.map { |v| c.quote(v) }.join(',') end else - c.quote(value, column) + c.quote(value) end end -- cgit v1.2.3 From c390e60811b2e11bfd5d79b15bfb43690c1a1339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 23 Dec 2013 20:15:52 +0100 Subject: Guarantee the connection resolver handles string values This commit also cleans up the rake tasks that were checking for DATABASE_URL in different places. In fact, it would be nice to deprecate DATABASE_URL usage in the long term, considering the direction we are moving of allowing those in .yml files. --- .../connection_specification.rb | 49 +++++++++++++--------- .../lib/active_record/connection_handling.rb | 4 +- activerecord/lib/active_record/railtie.rb | 15 ++++++- .../lib/active_record/railties/databases.rake | 12 +----- .../lib/active_record/tasks/database_tasks.rb | 19 +-------- 5 files changed, 48 insertions(+), 51 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 66d7f04fc3..a87eed5243 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -16,34 +16,43 @@ module ActiveRecord ## # Builds a ConnectionSpecification from user input class Resolver # :nodoc: - attr_reader :config, :klass, :configurations + attr_reader :configurations - def initialize(config, configurations) - @config = config + def initialize(configurations) @configurations = configurations end - def spec - case config - when nil - raise AdapterNotSpecified unless defined?(Rails.env) - resolve_string_connection Rails.env - when Symbol, String - resolve_string_connection config.to_s - when Hash - resolve_hash_connection config + def resolve(config) + if config + resolve_connection config + elsif defined?(Rails.env) + resolve_env_connection Rails.env + else + raise AdapterNotSpecified end end private - def resolve_string_connection(spec) # :nodoc: - hash = configurations.fetch(spec) do |k| - connection_url_to_hash(k) - end - raise(AdapterNotSpecified, "#{spec} database is not configured") unless hash + def resolve_connection(spec) #:nodoc: + case spec + when Symbol, String + resolve_env_connection spec.to_s + when Hash + resolve_hash_connection spec + end + end - resolve_hash_connection hash + def resolve_env_connection(spec) # :nodoc: + # Rails has historically accepted a string to mean either + # an environment key or a url spec. So we support both for + # now but it would be nice to limit the environment key only + # for symbols. + spec = configurations.fetch(spec.to_s) do + resolve_string_connection(spec) if spec.is_a?(String) + end + raise(AdapterNotSpecified, "#{spec} database is not configured") unless spec + resolve_connection spec end def resolve_hash_connection(spec) # :nodoc: @@ -65,8 +74,8 @@ module ActiveRecord ConnectionSpecification.new(spec, adapter_method) end - def connection_url_to_hash(url) # :nodoc: - config = URI.parse url + def resolve_string_connection(spec) # :nodoc: + config = URI.parse spec adapter = config.scheme adapter = "postgresql" if adapter == "postgres" diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index a1943dfcb0..6035e674ce 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -35,8 +35,8 @@ module ActiveRecord # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError # may be returned on an error. def establish_connection(spec = ENV["DATABASE_URL"]) - resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new spec, configurations - spec = resolver.spec + resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations + spec = resolver.resolve(spec) unless respond_to?(spec.adapter_method) raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index ff98c829f4..2c796f97e6 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -43,11 +43,24 @@ module ActiveRecord namespace :db do task :load_config do ActiveRecord::Tasks::DatabaseTasks.db_dir = Rails.application.config.paths["db"].first - ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration ActiveRecord::Tasks::DatabaseTasks.migrations_paths = Rails.application.paths['db/migrate'].to_a ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures' ActiveRecord::Tasks::DatabaseTasks.root = Rails.root + configuration = if ENV["DATABASE_URL"] + { Rails.env => ENV["DATABASE_URL"] } + else + Rails.application.config.database_configuration || {} + end + + resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configuration) + + configuration.each do |key, value| + configuration[key] = resolver.resolve(value) if value + end + + ActiveRecord::Tasks::DatabaseTasks.database_configuration = configuration + if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH) if engine.paths['db/migrate'].existent ActiveRecord::Tasks::DatabaseTasks.migrations_paths += engine.paths['db/migrate'].to_a diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 0fdfed991c..90dfc177e5 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -14,11 +14,7 @@ db_namespace = namespace :db do desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all databases in the config)' task :create => [:load_config] do - if ENV['DATABASE_URL'] - ActiveRecord::Tasks::DatabaseTasks.create_database_url - else - ActiveRecord::Tasks::DatabaseTasks.create_current - end + ActiveRecord::Tasks::DatabaseTasks.create_current end namespace :drop do @@ -29,11 +25,7 @@ db_namespace = namespace :db do desc 'Drops the database using DATABASE_URL or the current Rails.env (use db:drop:all to drop all databases)' task :drop => [:load_config] do - if ENV['DATABASE_URL'] - ActiveRecord::Tasks::DatabaseTasks.drop_database_url - else - ActiveRecord::Tasks::DatabaseTasks.drop_current - end + ActiveRecord::Tasks::DatabaseTasks.drop_current end desc "Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)." diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index be7d496d15..06592eece2 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -56,11 +56,7 @@ module ActiveRecord if options.has_key?(:config) @current_config = options[:config] else - @current_config ||= if ENV['DATABASE_URL'] - database_url_config - else - ActiveRecord::Base.configurations[options[:env]] - end + @current_config ||= ActiveRecord::Base.configurations[options[:env]] end end @@ -85,10 +81,6 @@ module ActiveRecord ActiveRecord::Base.establish_connection environment end - def create_database_url - create database_url_config - end - def drop(*arguments) configuration = arguments.first class_for_adapter(configuration['adapter']).new(*arguments).drop @@ -107,10 +99,6 @@ module ActiveRecord } end - def drop_database_url - drop database_url_config - end - def charset_current(environment = env) charset ActiveRecord::Base.configurations[environment] end @@ -165,11 +153,6 @@ module ActiveRecord private - def database_url_config - @database_url_config ||= - ConnectionAdapters::ConnectionSpecification::Resolver.new(ENV["DATABASE_URL"], {}).spec.config.stringify_keys - end - def class_for_adapter(adapter) key = @tasks.keys.detect { |pattern| adapter[pattern] } unless key -- cgit v1.2.3 From d8336cab32d0d8e8c2877cac26111cbecb5ac872 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 24 Dec 2013 09:26:34 +0100 Subject: Fix build failures related to the new ENV options in yml --- .../active_record/connection_adapters/connection_specification.rb | 6 +++--- activerecord/lib/active_record/railtie.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index a87eed5243..5f50ca6aae 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -48,11 +48,11 @@ module ActiveRecord # an environment key or a url spec. So we support both for # now but it would be nice to limit the environment key only # for symbols. - spec = configurations.fetch(spec.to_s) do + config = configurations.fetch(spec.to_s) do resolve_string_connection(spec) if spec.is_a?(String) end - raise(AdapterNotSpecified, "#{spec} database is not configured") unless spec - resolve_connection spec + raise(AdapterNotSpecified, "#{spec} database is not configured") unless config + resolve_connection config end def resolve_hash_connection(spec) # :nodoc: diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 2c796f97e6..19e27ccb37 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -56,7 +56,7 @@ module ActiveRecord resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configuration) configuration.each do |key, value| - configuration[key] = resolver.resolve(value) if value + configuration[key] = resolver.resolve(value).config.stringify_keys if value end ActiveRecord::Tasks::DatabaseTasks.database_configuration = configuration -- cgit v1.2.3 From d2ed433b0af948da78e971bf342c506b27f6072f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 24 Dec 2013 10:02:07 +0100 Subject: Only build a ConnectionSpecification if required --- .../connection_specification.rb | 54 +++++++++++----------- .../lib/active_record/connection_handling.rb | 2 +- activerecord/lib/active_record/railtie.rb | 2 +- 3 files changed, 30 insertions(+), 28 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 5f50ca6aae..a3c645c53c 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -32,6 +32,24 @@ module ActiveRecord end end + def spec(config) + spec = resolve(config).symbolize_keys + + raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter) + + path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter" + begin + require path_to_adapter + rescue Gem::LoadError => e + raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)." + rescue LoadError => e + raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace + end + + adapter_method = "#{spec[:adapter]}_connection" + ConnectionSpecification.new(spec, adapter_method) + end + private def resolve_connection(spec) #:nodoc: @@ -52,30 +70,15 @@ module ActiveRecord resolve_string_connection(spec) if spec.is_a?(String) end raise(AdapterNotSpecified, "#{spec} database is not configured") unless config - resolve_connection config + resolve_connection(config) end def resolve_hash_connection(spec) # :nodoc: - spec = spec.symbolize_keys - - raise(AdapterNotSpecified, "database configuration does not specify adapter") unless spec.key?(:adapter) - - path_to_adapter = "active_record/connection_adapters/#{spec[:adapter]}_adapter" - begin - require path_to_adapter - rescue Gem::LoadError => e - raise Gem::LoadError, "Specified '#{spec[:adapter]}' for database adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by ActiveRecord)." - rescue LoadError => e - raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/database.yml is valid. If you use an adapter other than 'mysql', 'mysql2', 'postgresql' or 'sqlite3' add the necessary adapter gem to the Gemfile.", e.backtrace - end - - adapter_method = "#{spec[:adapter]}_connection" - - ConnectionSpecification.new(spec, adapter_method) + spec end def resolve_string_connection(spec) # :nodoc: - config = URI.parse spec + config = URI.parse spec adapter = config.scheme adapter = "postgresql" if adapter == "postgres" @@ -89,12 +92,12 @@ module ActiveRecord config.path.sub(%r{^/},"") end - spec = { :adapter => adapter, - :username => config.user, - :password => config.password, - :port => config.port, - :database => database, - :host => config.host } + spec = { "adapter" => adapter, + "username" => config.user, + "password" => config.password, + "port" => config.port, + "database" => database, + "host" => config.host } spec.reject!{ |_,value| value.blank? } @@ -103,8 +106,7 @@ module ActiveRecord spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) } if config.query - options = Hash[config.query.split("&").map{ |pair| pair.split("=") }].symbolize_keys - + options = Hash[config.query.split("&").map{ |pair| pair.split("=") }] spec.merge!(options) end diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index 6035e674ce..f274f74619 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -36,7 +36,7 @@ module ActiveRecord # may be returned on an error. def establish_connection(spec = ENV["DATABASE_URL"]) resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations - spec = resolver.resolve(spec) + spec = resolver.spec(spec) unless respond_to?(spec.adapter_method) raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 19e27ccb37..2c796f97e6 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -56,7 +56,7 @@ module ActiveRecord resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configuration) configuration.each do |key, value| - configuration[key] = resolver.resolve(value).config.stringify_keys if value + configuration[key] = resolver.resolve(value) if value end ActiveRecord::Tasks::DatabaseTasks.database_configuration = configuration -- cgit v1.2.3 From ec11807368318845a2542e8f5f51f3f5280135f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 24 Dec 2013 10:18:54 +0100 Subject: Deprecate use of string in establish_connection as connection lookup --- .../connection_specification.rb | 25 ++++++++++++++-------- .../lib/active_record/connection_handling.rb | 7 ++++++ 2 files changed, 23 insertions(+), 9 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index a3c645c53c..7e16156408 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -26,7 +26,7 @@ module ActiveRecord if config resolve_connection config elsif defined?(Rails.env) - resolve_env_connection Rails.env + resolve_env_connection Rails.env.to_sym else raise AdapterNotSpecified end @@ -55,7 +55,7 @@ module ActiveRecord def resolve_connection(spec) #:nodoc: case spec when Symbol, String - resolve_env_connection spec.to_s + resolve_env_connection spec when Hash resolve_hash_connection spec end @@ -63,14 +63,21 @@ module ActiveRecord def resolve_env_connection(spec) # :nodoc: # Rails has historically accepted a string to mean either - # an environment key or a url spec. So we support both for - # now but it would be nice to limit the environment key only - # for symbols. - config = configurations.fetch(spec.to_s) do - resolve_string_connection(spec) if spec.is_a?(String) + # an environment key or a url spec, so we have deprecated + # this ambiguous behaviour and in the future this function + # can be removed in favor of resolve_string_connection and + # resolve_symbol_connection. + if config = configurations[spec.to_s] + if spec.is_a?(String) + ActiveSupport::Deprecation.warn "Passing a string to ActiveRecord::Base.establish_connection " \ + "for a configuration lookup is deprecated, please pass a symbol (#{spec.to_sym.inspect}) instead" + end + resolve_connection(config) + elsif spec.is_a?(String) + resolve_string_connection(spec) + else + raise(AdapterNotSpecified, "#{spec} database is not configured") end - raise(AdapterNotSpecified, "#{spec} database is not configured") unless config - resolve_connection(config) end def resolve_hash_connection(spec) # :nodoc: diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index f274f74619..c4afadbd9b 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -32,6 +32,13 @@ module ActiveRecord # "postgres://myuser:mypass@localhost/somedatabase" # ) # + # In case ActiveRecord::Base.configurations is set (Rails + # automatically loads the contents of config/database.yml into it), + # a symbol can also be given as argument, representing a key in the + # configuration hash: + # + # ActiveRecord::Base.establish_connection(:production) + # # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError # may be returned on an error. def establish_connection(spec = ENV["DATABASE_URL"]) -- cgit v1.2.3 From f0311c24876f78ed2054fb1e5a24d38a4d0db4ac Mon Sep 17 00:00:00 2001 From: schneems Date: Tue, 24 Dec 2013 10:12:08 -0500 Subject: Raise NoDatabaseError when db does not exist Building on the work of #13427 this PR adds a helpful error message to the adapters: mysql, mysql2, and sqlite3 --- .../lib/active_record/connection_adapters/mysql2_adapter.rb | 6 ++++++ activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 6 ++++++ .../lib/active_record/connection_adapters/sqlite3_adapter.rb | 6 ++++++ 3 files changed, 18 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb index e790f731ea..6d8e994654 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql2_adapter.rb @@ -18,6 +18,12 @@ module ActiveRecord client = Mysql2::Client.new(config) options = [config[:host], config[:username], config[:password], config[:database], config[:port], config[:socket], 0] ConnectionAdapters::Mysql2Adapter.new(client, logger, options, config) + rescue Mysql2::Error => error + if error.message.include?("Unknown database") + raise ActiveRecord::NoDatabaseError.new(error.message) + else + raise error + end end end diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index 760f1435eb..7dbaa272a3 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -34,6 +34,12 @@ module ActiveRecord default_flags |= Mysql::CLIENT_FOUND_ROWS if Mysql.const_defined?(:CLIENT_FOUND_ROWS) options = [host, username, password, database, port, socket, default_flags] ConnectionAdapters::MysqlAdapter.new(mysql, logger, options, config) + rescue Mysql::Error => error + if error.message.include?("Unknown database") + raise ActiveRecord::NoDatabaseError.new(error.message) + else + raise error + end end end diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index a02eda5603..92bb70ba53 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -31,6 +31,12 @@ module ActiveRecord db.busy_timeout(ConnectionAdapters::SQLite3Adapter.type_cast_config_to_integer(config[:timeout])) if config[:timeout] ConnectionAdapters::SQLite3Adapter.new(db, logger, config) + rescue Errno::ENOENT => error + if error.message.include?("No such file or directory") + raise ActiveRecord::NoDatabaseError.new(error.message) + else + raise error + end end end -- cgit v1.2.3 From db7065b53ca58bff030931db128745f40a45630b Mon Sep 17 00:00:00 2001 From: Aayush khandelwal Date: Wed, 25 Dec 2013 18:22:26 +0530 Subject: Typo rectified commom => common[ci skip] --- activerecord/lib/active_record/persistence.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 35fbad466e..0cd5c09399 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -361,7 +361,7 @@ module ActiveRecord # assert_equal 25, account.credit # check it is updated in memory # assert_equal 25, account.reload.credit # check it is also persisted # - # Another commom use case is optimistic locking handling: + # Another common use case is optimistic locking handling: # # def with_optimistic_retry # begin -- cgit v1.2.3 From 7a9d2925d7ed05d905161bd4ff3927ef3f42709d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Wed, 25 Dec 2013 23:04:20 +0100 Subject: Avoid getting redefined method warning Warning: ~/projects/rails/activerecord/lib/active_record/railtie.rb:140: warning: method redefined; discarding old extend_message ~/projects/rails/activerecord/lib/active_record/errors.rb:104: warning: previous definition of extend_message was here --- activerecord/lib/active_record/railtie.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 2c796f97e6..2a8cd83285 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -137,6 +137,7 @@ module ActiveRecord ActiveSupport.on_load(:active_record) do class ActiveRecord::NoDatabaseError + remove_possible_method :extend_message def extend_message(message) message << "Run `$ bin/rake db:create db:migrate` to create your database" message -- cgit v1.2.3 From dd5503377718e510e9f036261dd9877958713181 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Strza=C5=82kowski?= Date: Wed, 25 Dec 2013 23:09:31 +0100 Subject: Cast env to symbol, fixes deprecation warning Warning: DEPRECATION WARNING: Passing a string to ActiveRecord::Base.establish_connection for a configuration lookup is deprecated, please pass a symbol (:development) instead. --- activerecord/lib/active_record/tasks/database_tasks.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index 06592eece2..acb7c65af5 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -78,7 +78,7 @@ module ActiveRecord each_current_configuration(environment) { |configuration| create configuration } - ActiveRecord::Base.establish_connection environment + ActiveRecord::Base.establish_connection(environment.to_sym) end def drop(*arguments) -- cgit v1.2.3 From b7bf025374324c2b73b3ab270cd4153f18a942b0 Mon Sep 17 00:00:00 2001 From: Jonathan del Strother Date: Tue, 24 Dec 2013 17:23:47 +0000 Subject: Copy changed_attributes across to newly become'd records Without this, the original record's values won't get saved, since the partial insertions support (https://github.com/rails/rails/commit/144e8691cbfb8bba77f18cfe68d5e7fd48887f5e) checks for changed values and thinks there are none. --- activerecord/lib/active_record/persistence.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 35fbad466e..38264cdb7d 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -181,6 +181,7 @@ module ActiveRecord became = klass.new became.instance_variable_set("@attributes", @attributes) became.instance_variable_set("@attributes_cache", @attributes_cache) + became.instance_variable_set("@changed_attributes", @changed_attributes) became.instance_variable_set("@new_record", new_record?) became.instance_variable_set("@destroyed", destroyed?) became.instance_variable_set("@errors", errors) -- cgit v1.2.3 From e8253d3a642d396d9646d082f97eb233a2b2396e Mon Sep 17 00:00:00 2001 From: Kuldeep Aggarwal Date: Sat, 28 Dec 2013 17:05:51 +0530 Subject: setting `changed_attributes` instance variable if it is already initialized. --- activerecord/lib/active_record/persistence.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 309bb980e3..b94ef18fd5 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -181,7 +181,7 @@ module ActiveRecord became = klass.new became.instance_variable_set("@attributes", @attributes) became.instance_variable_set("@attributes_cache", @attributes_cache) - became.instance_variable_set("@changed_attributes", @changed_attributes) + became.instance_variable_set("@changed_attributes", @changed_attributes) if defined?(@changed_attributes) became.instance_variable_set("@new_record", new_record?) became.instance_variable_set("@destroyed", destroyed?) became.instance_variable_set("@errors", errors) -- cgit v1.2.3 From a2985e206709f7947ef427a527d72ff89824931a Mon Sep 17 00:00:00 2001 From: Kuldeep Aggarwal Date: Mon, 30 Dec 2013 01:53:02 +0530 Subject: raise `ArgumentError` exception if `Model.where.not` is called with `nil` argument --- activerecord/lib/active_record/relation/query_methods.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 3d0709266a..78da6a83ec 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -37,6 +37,8 @@ module ActiveRecord def not(opts, *rest) where_value = @scope.send(:build_where, opts, rest).map do |rel| case rel + when NilClass + raise ArgumentError, 'Invalid argument for .where.not(), got nil.' when Arel::Nodes::In Arel::Nodes::NotIn.new(rel.left, rel.right) when Arel::Nodes::Equality -- cgit v1.2.3 From 137c0e573b9fe03245d4dd2e2093da12ba732598 Mon Sep 17 00:00:00 2001 From: schneems Date: Sun, 29 Dec 2013 18:56:32 -0500 Subject: Extract db url connection logic to class --- .../connection_specification.rb | 113 ++++++++++++++------- 1 file changed, 79 insertions(+), 34 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 7e16156408..a1664509df 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -13,6 +13,83 @@ module ActiveRecord @config = original.config.dup end + # Expands a connection string into a hash + class ConnectionUrlResolver # :nodoc: + + # == Example + # url = 'postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000' + # ConnectionUrlResolver.new(url).to_hash + # # => { + # "adapter" => "postgresql", + # "host" => "localhost", + # "port" => 9000, + # "database" => "foo_test", + # "username" => "foo", + # "password" => "bar", + # "pool" => "5", + # "timeout" => "3000" + # } + def initialize(url) + raise "Database URL cannot be empty" if url.blank? + @uri = URI.parse(url) + @adapter = @uri.scheme + @adapter = "postgresql" if @adapter == "postgres" + @query = @uri.query || '' + end + + # Converts the given url to a full connection hash + def to_hash + config = raw_config.reject { |_,value| value.blank? } + config.map { |key,value| config[key] = uri_parser.unescape(value) if value.is_a? String } + config + end + + private + + def uri + @uri + end + + def uri_parser + @uri_parser ||= URI::Parser.new + end + + # Converts the query parameters of the uri into a hash + # "localhost?pool=5&reap_frequency=2" + # # => {"pool" => "5", "reap_frequency" => "2"} + # + # returns empty hash if no query present + # "localhost" + # # => {} + def query_hash + Hash[@query.split("&").map { |pair| pair.split("=") }] + end + + def raw_config + query_hash.merge({ + "adapter" => @adapter, + "username" => uri.user, + "password" => uri.password, + "port" => uri.port, + "database" => database, + "host" => uri.host }) + end + + # Returns name of the database + # sqlite3 expects this to be a full path or `:memory` + def database + if @adapter == 'sqlite3' + if '/:memory:' == uri.path + ':memory:' + else + uri.path + end + else + uri.path.sub(%r{^/},"") + end + end + end + ## # Builds a ConnectionSpecification from user input class Resolver # :nodoc: @@ -84,40 +161,8 @@ module ActiveRecord spec end - def resolve_string_connection(spec) # :nodoc: - config = URI.parse spec - adapter = config.scheme - adapter = "postgresql" if adapter == "postgres" - - database = if adapter == 'sqlite3' - if '/:memory:' == config.path - ':memory:' - else - config.path - end - else - config.path.sub(%r{^/},"") - end - - spec = { "adapter" => adapter, - "username" => config.user, - "password" => config.password, - "port" => config.port, - "database" => database, - "host" => config.host } - - spec.reject!{ |_,value| value.blank? } - - uri_parser = URI::Parser.new - - spec.map { |key,value| spec[key] = uri_parser.unescape(value) if value.is_a?(String) } - - if config.query - options = Hash[config.query.split("&").map{ |pair| pair.split("=") }] - spec.merge!(options) - end - - spec + def resolve_string_connection(url) # :nodoc: + ConnectionUrlResolver.new(url).to_hash end end end -- cgit v1.2.3 From 3d865c1bcdb5a9c1ecf7a968312752d8f6178f8b Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Mon, 30 Dec 2013 09:59:13 +0100 Subject: Remove deprecation warning for FIXTURES_PATH FIXTURES_PATH has a bit different case than DatabaseTasks.fixtures_path, which was added along with deprecation. A use case for FIXTURES_PATH could be loading fixtures from a different directory just for a given test run. The implementation is fairly simple, so leaving it as it is won't hurt. --- activerecord/lib/active_record/railties/databases.rake | 6 ------ 1 file changed, 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 90dfc177e5..4b769aa11c 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -179,9 +179,6 @@ db_namespace = namespace :db do require 'active_record/fixtures' base_dir = if ENV['FIXTURES_PATH'] - STDERR.puts "Using FIXTURES_PATH env variable is deprecated, please use " + - "ActiveRecord::Tasks::DatabaseTasks.fixtures_path = '/path/to/fixtures' " + - "instead." File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten else ActiveRecord::Tasks::DatabaseTasks.fixtures_path @@ -204,9 +201,6 @@ db_namespace = namespace :db do puts %Q(The fixture ID for "#{label}" is #{ActiveRecord::FixtureSet.identify(label)}.) if label base_dir = if ENV['FIXTURES_PATH'] - STDERR.puts "Using FIXTURES_PATH env variable is deprecated, please use " + - "ActiveRecord::Tasks::DatabaseTasks.fixtures_path = '/path/to/fixtures' " + - "instead." File.join [Rails.root, ENV['FIXTURES_PATH'] || %w{test fixtures}].flatten else ActiveRecord::Tasks::DatabaseTasks.fixtures_path -- cgit v1.2.3 From 8c82ee838ec10fcfde45f99129c4376f8435dd06 Mon Sep 17 00:00:00 2001 From: schneems Date: Sun, 29 Dec 2013 21:57:55 -0500 Subject: [ci skip] ConnectionSpecification::Resolver Docs Document the internal interfaces of `ConnectionSpecification::Resolver` Change method name from `config` to `env` to better match the most common use case. --- .../connection_specification.rb | 94 ++++++++++++++++++---- 1 file changed, 80 insertions(+), 14 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index a1664509df..bea9a22442 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -13,11 +13,12 @@ module ActiveRecord @config = original.config.dup end - # Expands a connection string into a hash + # Expands a connection string into a hash. class ConnectionUrlResolver # :nodoc: # == Example - # url = 'postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000' + # + # url = "postgresql://foo:bar@localhost:9000/foo_test?pool=5&timeout=3000" # ConnectionUrlResolver.new(url).to_hash # # => { # "adapter" => "postgresql", @@ -37,7 +38,7 @@ module ActiveRecord @query = @uri.query || '' end - # Converts the given url to a full connection hash + # Converts the given URL to a full connection hash. def to_hash config = raw_config.reject { |_,value| value.blank? } config.map { |key,value| config[key] = uri_parser.unescape(value) if value.is_a? String } @@ -54,11 +55,13 @@ module ActiveRecord @uri_parser ||= URI::Parser.new end - # Converts the query parameters of the uri into a hash + # Converts the query parameters of the URI into a hash. + # # "localhost?pool=5&reap_frequency=2" - # # => {"pool" => "5", "reap_frequency" => "2"} + # # => { "pool" => "5", "reap_frequency" => "2" } + # + # returns empty hash if no query present. # - # returns empty hash if no query present # "localhost" # # => {} def query_hash @@ -75,8 +78,8 @@ module ActiveRecord "host" => uri.host }) end - # Returns name of the database - # sqlite3 expects this to be a full path or `:memory` + # Returns name of the database. + # Sqlite3 expects this to be a full path or `:memory`. def database if @adapter == 'sqlite3' if '/:memory:' == uri.path @@ -91,14 +94,32 @@ module ActiveRecord end ## - # Builds a ConnectionSpecification from user input + # Builds a ConnectionSpecification from user input. class Resolver # :nodoc: attr_reader :configurations + # Accepts a hash two layers deep, keys on the first layer represent + # environments such as "production". Keys must be strings. def initialize(configurations) @configurations = configurations end + # Returns a hash with database connection information. + # + # == Examples + # + # Full hash Configuration. + # + # configurations = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } } + # Resolver.new(configurations).resolve(:production) + # # => {host: "localhost", database: "foo", adapter: "sqlite3"} + # + # Initialized with URL configuration strings. + # + # configurations = { "production" => "postgresql://localhost/foo" } + # Resolver.new(configurations).resolve(:production) + # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" } + # def resolve(config) if config resolve_connection config @@ -109,6 +130,18 @@ module ActiveRecord end end + # Returns an instance of ConnectionSpecification for a given adapter. + # Accepts a hash one layer deep that contains all connection information. + # + # == Example + # + # config = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } } + # spec = Resolver.new(config).spec(:production) + # spec.adapter_method + # # => "sqlite3" + # spec.config + # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } + # def spec(config) spec = resolve(config).symbolize_keys @@ -129,7 +162,27 @@ module ActiveRecord private - def resolve_connection(spec) #:nodoc: + # Returns fully resolved connection, accepts hash, string or symbol. + # Always returns a hash. + # + # == Examples + # + # Symbol representing current environment. + # + # Resolver.new("production" => {}).resolve_connection(:production) + # # => {} + # + # One layer deep hash of connection values. + # + # Resolver.new({}).resolve_connection("adapter" => "sqlite3") + # # => { "adapter" => "sqlite3" } + # + # Connection URL. + # + # Resolver.new({}).resolve_connection("postgresql://localhost/foo") + # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" } + # + def resolve_connection(spec) case spec when Symbol, String resolve_env_connection spec @@ -138,9 +191,22 @@ module ActiveRecord end end - def resolve_env_connection(spec) # :nodoc: + # Takes the environment such as `:production` or `:development`. + # This requires that the @configurations was initialized with a key that + # matches. + # + # + # Resolver.new("production" => {}).resolve_env_connection(:production) + # # => {} + # + # Takes a connection URL. + # + # Resolver.new({}).resolve_env_connection("postgresql://localhost/foo") + # # => { "host" => "localhost", "database" => "foo", "adapter" => "postgresql" } + # + def resolve_env_connection(spec) # Rails has historically accepted a string to mean either - # an environment key or a url spec, so we have deprecated + # an environment key or a URL spec, so we have deprecated # this ambiguous behaviour and in the future this function # can be removed in favor of resolve_string_connection and # resolve_symbol_connection. @@ -157,11 +223,11 @@ module ActiveRecord end end - def resolve_hash_connection(spec) # :nodoc: + def resolve_hash_connection(spec) spec end - def resolve_string_connection(url) # :nodoc: + def resolve_string_connection(url) ConnectionUrlResolver.new(url).to_hash end end -- cgit v1.2.3 From 5b96027ef6ac6e507ec9caf9973069345ce6a7cd Mon Sep 17 00:00:00 2001 From: schneems Date: Mon, 30 Dec 2013 11:26:28 -0500 Subject: Allow "url" sub key in database.yml configuration Currently a developer can pass in a YAML configuration that fully specifies connection information: ``` production: database: triage_production adapter: password pool: 5 ``` They can also pass in a string that specifies a connection URL directly to an environment key: ``` production: postgresql://localhost/foo ``` This PR allows the use of both a connection url and specifying connection attributes via YAML through the use of the "url" sub key: ``` production: url: postgresql://localhost/foo pool: 3 ``` This will allow developers to inherit Active Record options such as `pool` from `&defaults` and still use a secure connection url such as `<%= ENV['DATABASE_URL'] %>`. The URL is expanded into a hash and then merged back into the YAML hash. If there are any conflicts, the values from the connection URL are preferred. Talked this over with @josevalim --- .../active_record/connection_adapters/connection_specification.rb | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index bea9a22442..049768effc 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -223,7 +223,15 @@ module ActiveRecord end end + # Accepts a hash. Expands the "url" key that contains a + # URL database connection to a full connection + # hash and merges with the rest of the hash. + # Connection details inside of the "url" key win any merge conflicts def resolve_hash_connection(spec) + if url = spec.delete("url") + connection_hash = resolve_string_connection(url) + spec.merge!(connection_hash) + end spec end -- cgit v1.2.3 From bb17c3b2105da30ef3260c3b489da85e4865eaa5 Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Tue, 31 Dec 2013 02:44:27 +0530 Subject: https://github.com/rails/rails/commit/2075f39d726cef361170218fd16421fc52bed5a8 introduced a regression in includes/preloades by calling `read_attribute` on an association when preloading takes places, instead of using loaded records in `association.target`. tl;dr Records are not made properly available via `read_attribute` when preloding in simultaneous, but value of `@loaded` is already set true, and records concatenated in `association.target` on an association object. When `@loaded` is true we return an object of `AlreadyLoaded` in preload_for. In `AlreadyLoaded` to return preloaded records we make wrong use of `read_attribute`, instead of `target` records. The regression is fixed by making use of the loaded records in `association.target` when the preloading takes place. Fixes #13437 --- activerecord/lib/active_record/associations/preloader.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/preloader.rb b/activerecord/lib/active_record/associations/preloader.rb index 2393667ac8..83637a0409 100644 --- a/activerecord/lib/active_record/associations/preloader.rb +++ b/activerecord/lib/active_record/associations/preloader.rb @@ -183,7 +183,7 @@ module ActiveRecord def run(preloader); end def preloaded_records - owners.flat_map { |owner| owner.read_attribute reflection.name } + owners.flat_map { |owner| owner.association(reflection.name).target } end end -- cgit v1.2.3 From 8e1f26c66c5cd1ceb23baca5fb48440a8c9a06c1 Mon Sep 17 00:00:00 2001 From: Washington Luiz Date: Sat, 28 Dec 2013 23:46:21 -0300 Subject: Make outer joins on proper parent Outer joins were being built on the root relation klass rather than the one specified in the join dependency root --- activerecord/lib/active_record/associations/join_dependency.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations/join_dependency.rb b/activerecord/lib/active_record/associations/join_dependency.rb index 9506960be3..295dccf34e 100644 --- a/activerecord/lib/active_record/associations/join_dependency.rb +++ b/activerecord/lib/active_record/associations/join_dependency.rb @@ -114,7 +114,7 @@ module ActiveRecord walk join_root, oj.join_root else oj.join_root.children.flat_map { |child| - make_outer_joins join_root, child + make_outer_joins oj.join_root, child } end } -- cgit v1.2.3 From 8ea00ed91a89997098edc9d83b8ecb26ed72f8fc Mon Sep 17 00:00:00 2001 From: gmarik Date: Tue, 31 Dec 2013 11:51:33 -0600 Subject: Use `Array#wrap` instead `Array()` - since `Array()` calls `to_ary` or `to_a` on a subject - the intent is to 'wrap' subject into an array --- activerecord/lib/active_record/validations/presence.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/validations/presence.rb b/activerecord/lib/active_record/validations/presence.rb index 6b14c39686..9a19483da3 100644 --- a/activerecord/lib/active_record/validations/presence.rb +++ b/activerecord/lib/active_record/validations/presence.rb @@ -5,7 +5,7 @@ module ActiveRecord super attributes.each do |attribute| next unless record.class.reflect_on_association(attribute) - associated_records = Array(record.send(attribute)) + associated_records = Array.wrap(record.send(attribute)) # Superclass validates presence. Ensure present records aren't about to be destroyed. if associated_records.present? && associated_records.all? { |r| r.marked_for_destruction? } -- cgit v1.2.3 From e0ad9ae27e9b6dc5460d91761ce6fbd7a92b08f0 Mon Sep 17 00:00:00 2001 From: Amr Tamimi Date: Fri, 20 Dec 2013 14:12:59 +0200 Subject: Add the ability to nullify the `enum` column --- activerecord/lib/active_record/enum.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 2f8439892b..1d484f7c15 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -18,6 +18,11 @@ module ActiveRecord # # conversation.update! status: 1 # conversation.status = "archived" # + # # conversation.update! status: nil + # conversation.status = nil + # conversation.status.nil? # => true + # conversation.status # => nil + # # You can set the default value from the database declaration, like: # # create_table :conversations do |t| @@ -62,7 +67,7 @@ module ActiveRecord _enum_methods_module.module_eval do # def status=(value) self[:status] = STATUS[value] end define_method("#{name}=") { |value| - unless enum_values.has_key?(value) + unless enum_values.has_key?(value) || value.blank? raise ArgumentError, "'#{value}' is not a valid #{name}" end self[name] = enum_values[value] -- cgit v1.2.3 From 7aebcb67b040bca353cd2e97a468c8ffe4594665 Mon Sep 17 00:00:00 2001 From: Robin Dupret Date: Mon, 30 Dec 2013 16:56:13 +0100 Subject: Fix the enums writer methods Previously, the writer methods would simply check whether the passed argument was the symbol representing the integer value of an enum field. Therefore, it was not possible to specify the numeric value itself but the dynamically defined scopes generate where clauses relying on this kind of values so a chained call to a method like `find_or_initialize_by` would trigger an `ArgumentError`. Reference #13530 --- activerecord/lib/active_record/enum.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 1d484f7c15..aebb061717 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -67,7 +67,7 @@ module ActiveRecord _enum_methods_module.module_eval do # def status=(value) self[:status] = STATUS[value] end define_method("#{name}=") { |value| - unless enum_values.has_key?(value) || value.blank? + unless enum_values.has_key?(value) || enum_values.has_value?(value) || value.blank? raise ArgumentError, "'#{value}' is not a valid #{name}" end self[name] = enum_values[value] -- cgit v1.2.3 From 50060e969b422e6cd8f90181ac3d92f915a96f69 Mon Sep 17 00:00:00 2001 From: Robin Dupret Date: Mon, 30 Dec 2013 17:47:11 +0100 Subject: Improve enum documentation Add a mention about the automatic generation of scopes based on the allowed values of the field on the enum documentation. --- activerecord/lib/active_record/enum.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index aebb061717..c042a2f232 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -1,5 +1,6 @@ module ActiveRecord - # Declare an enum attribute where the values map to integers in the database, but can be queried by name. Example: + # Declare an enum attribute where the values map to integers in the database, + # but can be queried by name. Example: # # class Conversation < ActiveRecord::Base # enum status: [ :active, :archived ] @@ -23,6 +24,10 @@ module ActiveRecord # conversation.status.nil? # => true # conversation.status # => nil # + # Scopes based on the allowed values of the enum field will be provided + # as well. With the above example, it will create an +active+ and +archived+ + # scope. + # # You can set the default value from the database declaration, like: # # create_table :conversations do |t| -- cgit v1.2.3 From 98cb3e69afd687f7c0d4bc48eabf284da691abcc Mon Sep 17 00:00:00 2001 From: Vipul A M Date: Wed, 1 Jan 2014 23:59:49 +0530 Subject: update copyright notices to 2014. [ci skip] --- activerecord/lib/active_record.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record.rb b/activerecord/lib/active_record.rb index cbac2ef3c6..f856c482d6 100644 --- a/activerecord/lib/active_record.rb +++ b/activerecord/lib/active_record.rb @@ -1,5 +1,5 @@ #-- -# Copyright (c) 2004-2013 David Heinemeier Hansson +# Copyright (c) 2004-2014 David Heinemeier Hansson # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the -- cgit v1.2.3 From ff7ab3bc78abc3e8439a57ef7d755c5aa5b069f4 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Sun, 29 Dec 2013 11:05:04 +0000 Subject: Automatically maintain test database schema * Move check from generated helper to test_help.rb, so that all applications can benefit * Rather than just raising when the test schema has pending migrations, try to load in the schema and only raise if there are pending migrations afterwards * Opt out of the check by setting config.active_record.maintain_test_schema = false * Deprecate db:test:* tasks. The test helper is now fully responsible for maintaining the test schema, so we don't need rake tasks for this. This is also a speed improvement since we're no longer reloading the test database on every call to "rake test". --- activerecord/lib/active_record/core.rb | 3 ++ activerecord/lib/active_record/migration.rb | 13 +++++++ activerecord/lib/active_record/railtie.rb | 9 +---- .../lib/active_record/railties/databases.rake | 34 ++++++++--------- .../lib/active_record/tasks/database_tasks.rb | 44 ++++++++++++++++++++-- 5 files changed, 75 insertions(+), 28 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 8808ad5a4c..18ee77f6fe 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -69,6 +69,9 @@ module ActiveRecord mattr_accessor :timestamped_migrations, instance_writer: false self.timestamped_migrations = true + # :nodoc: + mattr_accessor :maintain_test_schema, instance_accessor: false + def self.disable_implicit_join_references=(value) ActiveSupport::Deprecation.warn("Implicit join references were removed with Rails 4.1." \ "Make sure to remove this configuration because it does nothing.") diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 7d7e97e6c9..fc84b907a5 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -389,6 +389,19 @@ module ActiveRecord raise ActiveRecord::PendingMigrationError if ActiveRecord::Migrator.needs_migration? end + def load_schema_if_pending! + if ActiveRecord::Migrator.needs_migration? + ActiveRecord::Tasks::DatabaseTasks.load_schema + check_pending! + end + end + + def maintain_test_schema! # :nodoc: + if ActiveRecord::Base.maintain_test_schema + suppress_messages { load_schema_if_pending! } + end + end + def method_missing(name, *args, &block) # :nodoc: (delegate || superclass.delegate).send(name, *args, &block) end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 2a8cd83285..ec85b3c843 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -31,22 +31,15 @@ module ActiveRecord config.active_record.use_schema_cache_dump = true + config.active_record.maintain_test_schema = true config.eager_load_namespaces << ActiveRecord rake_tasks do require "active_record/base" - ActiveRecord::Tasks::DatabaseTasks.seed_loader = Rails.application - ActiveRecord::Tasks::DatabaseTasks.env = Rails.env - namespace :db do task :load_config do - ActiveRecord::Tasks::DatabaseTasks.db_dir = Rails.application.config.paths["db"].first - ActiveRecord::Tasks::DatabaseTasks.migrations_paths = Rails.application.paths['db/migrate'].to_a - ActiveRecord::Tasks::DatabaseTasks.fixtures_path = File.join Rails.root, 'test', 'fixtures' - ActiveRecord::Tasks::DatabaseTasks.root = Rails.root - configuration = if ENV["DATABASE_URL"] { Rails.env => ENV["DATABASE_URL"] } else diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 4b769aa11c..25d42bbf2d 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -234,9 +234,7 @@ db_namespace = namespace :db do desc 'Load a schema.rb file into the database' task :load => [:environment, :load_config] do - file = ENV['SCHEMA'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, 'schema.rb') - ActiveRecord::Tasks::DatabaseTasks.check_schema_file(file) - load(file) + ActiveRecord::Tasks::DatabaseTasks.load_schema(:ruby, ENV['SCHEMA']) end task :load_if_ruby => ['db:create', :environment] do @@ -281,10 +279,7 @@ db_namespace = namespace :db do # desc "Recreate the databases from the structure.sql file" task :load => [:environment, :load_config] do - filename = ENV['DB_STRUCTURE'] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, "structure.sql") - ActiveRecord::Tasks::DatabaseTasks.check_schema_file(filename) - current_config = ActiveRecord::Tasks::DatabaseTasks.current_config - ActiveRecord::Tasks::DatabaseTasks.structure_load(current_config, filename) + ActiveRecord::Tasks::DatabaseTasks.load_schema(:sql, ENV['DB_STRUCTURE']) end task :load_if_sql => ['db:create', :environment] do @@ -294,8 +289,15 @@ db_namespace = namespace :db do namespace :test do + task :deprecated do + Rake.application.top_level_tasks.grep(/^db:test:/).each do |task| + $stderr.puts "WARNING: #{task} is deprecated. The Rails test helper now maintains " \ + "your test schema automatically, see the release notes for details." + end + end + # desc "Recreate the test database from the current schema" - task :load => 'db:test:purge' do + task :load => %w(db:test:deprecated db:test:purge) do case ActiveRecord::Base.schema_format when :ruby db_namespace["test:load_schema"].invoke @@ -305,7 +307,7 @@ db_namespace = namespace :db do end # desc "Recreate the test database from an existent schema.rb file" - task :load_schema => 'db:test:purge' do + task :load_schema => %w(db:test:deprecated db:test:purge) do begin should_reconnect = ActiveRecord::Base.connection_pool.active_connection? ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) @@ -319,7 +321,7 @@ db_namespace = namespace :db do end # desc "Recreate the test database from an existent structure.sql file" - task :load_structure => 'db:test:purge' do + task :load_structure => %w(db:test:deprecated db:test:purge) do begin ActiveRecord::Tasks::DatabaseTasks.current_config(:config => ActiveRecord::Base.configurations['test']) db_namespace["structure:load"].invoke @@ -329,7 +331,7 @@ db_namespace = namespace :db do end # desc "Recreate the test database from a fresh schema" - task :clone => :environment do + task :clone => %w(db:test:deprecated environment) do case ActiveRecord::Base.schema_format when :ruby db_namespace["test:clone_schema"].invoke @@ -339,18 +341,18 @@ db_namespace = namespace :db do end # desc "Recreate the test database from a fresh schema.rb file" - task :clone_schema => ["db:schema:dump", "db:test:load_schema"] + task :clone_schema => %w(db:test:deprecated db:schema:dump db:test:load_schema) # desc "Recreate the test database from a fresh structure.sql file" - task :clone_structure => [ "db:structure:dump", "db:test:load_structure" ] + task :clone_structure => %w(db:structure:dump db:test:load_structure) # desc "Empty the test database" - task :purge => [:environment, :load_config] do + task :purge => %w(db:test:deprecated environment load_config) do ActiveRecord::Tasks::DatabaseTasks.purge ActiveRecord::Base.configurations['test'] end # desc 'Check for pending migrations and load the test schema' - task :prepare => [:environment, :load_config] do + task :prepare => %w(db:test:deprecated environment load_config) do unless ActiveRecord::Base.configurations.blank? db_namespace['test:load'].invoke end @@ -385,5 +387,3 @@ namespace :railties do end end end - -task 'test:prepare' => ['db:test:prepare', 'db:test:load', 'db:abort_if_pending_migrations'] diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index acb7c65af5..afd493011e 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -36,9 +36,8 @@ module ActiveRecord module DatabaseTasks extend self - attr_writer :current_config - attr_accessor :database_configuration, :migrations_paths, :seed_loader, :db_dir, - :fixtures_path, :env, :root + attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader + attr_accessor :database_configuration LOCAL_HOSTS = ['127.0.0.1', 'localhost'] @@ -51,6 +50,30 @@ module ActiveRecord register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks) register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks) + def db_dir + @db_dir ||= Rails.application.config.paths["db"].first + end + + def migrations_paths + @migrations_paths ||= Rails.application.paths['db/migrate'].to_a + end + + def fixtures_path + @fixtures_path ||= File.join(root, 'test', 'fixtures') + end + + def root + @root ||= Rails.root + end + + def env + @env ||= Rails.env + end + + def seed_loader + @seed_loader ||= Rails.application + end + def current_config(options = {}) options.reverse_merge! :env => env if options.has_key?(:config) @@ -133,6 +156,21 @@ module ActiveRecord class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename) end + def load_schema(format = ActiveRecord::Base.schema_format, file = nil) + case format + when :ruby + file ||= File.join(db_dir, "schema.rb") + check_schema_file(file) + load(file) + when :sql + file ||= File.join(db_dir, "structure.sql") + check_schema_file(file) + structure_load(current_config, file) + else + raise ArgumentError, "unknown format #{format.inspect}" + end + end + def check_schema_file(filename) unless File.exist?(filename) message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.} -- cgit v1.2.3 From 97e7ca48c139ea5cce2fa9b4be631946252a1ebd Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 3 Jan 2014 16:31:20 +0100 Subject: Deprecate unused `symbolized_base_class` and `symbolized_sti_name`. These methods were only used for the `IdentityMap` which was removed. They are no longer used internally and should be removed without replacement. As they were not `:nodoc:`'ed it's better to deprecate them before removal. --- activerecord/lib/active_record/inheritance.rb | 2 ++ 1 file changed, 2 insertions(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index 7e1e120288..f144d84946 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -45,10 +45,12 @@ module ActiveRecord end def symbolized_base_class + ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_base_class is deprecated and will be removed without replacement.") @symbolized_base_class ||= base_class.to_s.to_sym end def symbolized_sti_name + ActiveSupport::Deprecation.warn("ActiveRecord::Base.symbolized_sti_name is deprecated and will be removed without replacement.") @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class end -- cgit v1.2.3 From 788bb40e3887e2718f07be769b80818d653638f0 Mon Sep 17 00:00:00 2001 From: Godfrey Chan Date: Fri, 3 Jan 2014 09:31:01 -0800 Subject: Building new records with enum scopes now works as expected Previously, this would give an `ArgumentError`: class Issue < ActiveRecord::Base enum :status, [:open, :finished] end Issue.open.build # => ArgumentError: '0' is not a valid status Issue.open.create # => ArgumentError: '0' is not a valid status PR #13542 muted the error, but the issue remains. This commit fixes the issue by allowing the enum value to be written directly via the setter: Issue.new.status = 0 # This now sets status to :open Assigning a value directly via the setter like this is not part of the documented public API, so users should not rely on this behavior. Closes #13530. --- activerecord/lib/active_record/enum.rb | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index c042a2f232..3dd093b8a2 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -72,10 +72,16 @@ module ActiveRecord _enum_methods_module.module_eval do # def status=(value) self[:status] = STATUS[value] end define_method("#{name}=") { |value| - unless enum_values.has_key?(value) || enum_values.has_value?(value) || value.blank? + if enum_values.has_key?(value)|| value.blank? + self[name] = enum_values[value] + elsif enum_values.has_value?(value) + # Assigning a value directly is not a end-user fetaure, hence it's not documented. + # This is used internally to make building objects from the generated scopes work + # as expected, i.e. +Conversation.archived.build.archived?+ should be true. + self[name] = value + else raise ArgumentError, "'#{value}' is not a valid #{name}" end - self[name] = enum_values[value] } # def status() STATUS.key self[:status] end -- cgit v1.2.3 From 96c4bd69c101b866b80a3ca5424439c87791af5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20Mendon=C3=A7a=20Fran=C3=A7a?= Date: Fri, 3 Jan 2014 16:33:39 -0200 Subject: Whitespace [ci skip] --- activerecord/lib/active_record/enum.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index 3dd093b8a2..c43b311223 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -72,7 +72,7 @@ module ActiveRecord _enum_methods_module.module_eval do # def status=(value) self[:status] = STATUS[value] end define_method("#{name}=") { |value| - if enum_values.has_key?(value)|| value.blank? + if enum_values.has_key?(value) || value.blank? self[name] = enum_values[value] elsif enum_values.has_value?(value) # Assigning a value directly is not a end-user fetaure, hence it's not documented. -- cgit v1.2.3 From 05a81c5fe2efe08a171cd3dbef3873c1f0e5fae3 Mon Sep 17 00:00:00 2001 From: Robin Dupret Date: Fri, 3 Jan 2014 22:33:21 +0100 Subject: Minor typo fix [ci skip] --- activerecord/lib/active_record/enum.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/enum.rb b/activerecord/lib/active_record/enum.rb index c43b311223..9ad718a02a 100644 --- a/activerecord/lib/active_record/enum.rb +++ b/activerecord/lib/active_record/enum.rb @@ -75,7 +75,7 @@ module ActiveRecord if enum_values.has_key?(value) || value.blank? self[name] = enum_values[value] elsif enum_values.has_value?(value) - # Assigning a value directly is not a end-user fetaure, hence it's not documented. + # Assigning a value directly is not a end-user feature, hence it's not documented. # This is used internally to make building objects from the generated scopes work # as expected, i.e. +Conversation.archived.build.archived?+ should be true. self[name] = value -- cgit v1.2.3 From 72bb3fc297a3548e6748867bfb55a077b7b7728c Mon Sep 17 00:00:00 2001 From: "T.J. Schuck" Date: Fri, 3 Jan 2014 17:02:31 -0500 Subject: Change all "can not"s to the correct "cannot". --- activerecord/lib/active_record/associations.rb | 4 ++-- activerecord/lib/active_record/associations/collection_proxy.rb | 2 +- activerecord/lib/active_record/inheritance.rb | 2 +- activerecord/lib/active_record/persistence.rb | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 74e2774626..714f623af3 100644 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -75,13 +75,13 @@ module ActiveRecord class EagerLoadPolymorphicError < ActiveRecordError #:nodoc: def initialize(reflection) - super("Can not eagerly load the polymorphic association #{reflection.name.inspect}") + super("Cannot eagerly load the polymorphic association #{reflection.name.inspect}") end end class ReadOnlyAssociation < ActiveRecordError #:nodoc: def initialize(reflection) - super("Can not add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.") + super("Cannot add to a has_many :through association. Try adding to #{reflection.through_reflection.name.inspect}.") end end diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index a28cc2993b..e3fc908444 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -112,7 +112,7 @@ module ActiveRecord # Finds an object in the collection responding to the +id+. Uses the same # rules as ActiveRecord::Base.find. Returns ActiveRecord::RecordNotFound - # error if the object can not be found. + # error if the object cannot be found. # # class Person < ActiveRecord::Base # has_many :pets diff --git a/activerecord/lib/active_record/inheritance.rb b/activerecord/lib/active_record/inheritance.rb index f144d84946..949e7678a5 100644 --- a/activerecord/lib/active_record/inheritance.rb +++ b/activerecord/lib/active_record/inheritance.rb @@ -16,7 +16,7 @@ module ActiveRecord # instance of the given subclass instead of the base class. def new(*args, &block) if abstract_class? || self == Base - raise NotImplementedError, "#{self} is an abstract class and can not be instantiated." + raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated." end if (attrs = args.first).is_a?(Hash) if subclass = subclass_from_attrs(attrs) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index b94ef18fd5..4669c072a1 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -265,7 +265,7 @@ module ActiveRecord # This method raises an +ActiveRecord::ActiveRecordError+ when called on new # objects, or when at least one of the attributes is marked as readonly. def update_columns(attributes) - raise ActiveRecordError, "can not update on a new record object" unless persisted? + raise ActiveRecordError, "cannot update on a new record object" unless persisted? attributes.each_key do |key| verify_readonly_attribute(key.to_s) @@ -427,7 +427,7 @@ module ActiveRecord # ball.touch(:updated_at) # => raises ActiveRecordError # def touch(name = nil) - raise ActiveRecordError, "can not touch on a new record object" unless persisted? + raise ActiveRecordError, "cannot touch on a new record object" unless persisted? attributes = timestamp_attributes_for_update_in_model attributes << name if name -- cgit v1.2.3 From 9ea54b2d8e8180c59f31b035765e058e743660bb Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Sat, 4 Jan 2014 09:29:20 +0000 Subject: Add deprecation I accidentally missed --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 25d42bbf2d..3d795ca689 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -344,7 +344,7 @@ db_namespace = namespace :db do task :clone_schema => %w(db:test:deprecated db:schema:dump db:test:load_schema) # desc "Recreate the test database from a fresh structure.sql file" - task :clone_structure => %w(db:structure:dump db:test:load_structure) + task :clone_structure => %w(db:test:deprecated db:structure:dump db:test:load_structure) # desc "Empty the test database" task :purge => %w(db:test:deprecated environment load_config) do -- cgit v1.2.3 From 28161b7c884d4e780775657ad32f0f2b26294b2f Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Mon, 6 Jan 2014 09:07:44 -0200 Subject: Fix typo [ci skip] --- .../lib/active_record/connection_adapters/connection_specification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 049768effc..b316107e37 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -79,7 +79,7 @@ module ActiveRecord end # Returns name of the database. - # Sqlite3 expects this to be a full path or `:memory`. + # Sqlite3 expects this to be a full path or `:memory:`. def database if @adapter == 'sqlite3' if '/:memory:' == uri.path -- cgit v1.2.3 From 5d238ea926560ad0cb1287c6bcc34d2d3d7f982d Mon Sep 17 00:00:00 2001 From: Carlos Antonio da Silva Date: Mon, 6 Jan 2014 09:15:11 -0200 Subject: Fix AR connection resolver docs to return a hash with string keys [ci skip] --- .../lib/active_record/connection_adapters/connection_specification.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index b316107e37..9f210c5f33 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -112,7 +112,7 @@ module ActiveRecord # # configurations = { "production" => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3" } } # Resolver.new(configurations).resolve(:production) - # # => {host: "localhost", database: "foo", adapter: "sqlite3"} + # # => { "host" => "localhost", "database" => "foo", "adapter" => "sqlite3"} # # Initialized with URL configuration strings. # -- cgit v1.2.3 From 901a0c8b4a3b1de8ff1ec8feb4b67f727c07dc8b Mon Sep 17 00:00:00 2001 From: Thales Oliveira Date: Sun, 5 Jan 2014 20:55:02 -0200 Subject: Fix: ActiveRecord::Store TypeError conversion when using YAML coder Renaming the test accordingly to its behaviour Adding 'Fixes' statement to changelog Improving tests legibility & changelog Undoing mistakenly removed empty line & further improving changelog --- activerecord/lib/active_record/store.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/store.rb b/activerecord/lib/active_record/store.rb index 186a737561..79a6ccbda0 100644 --- a/activerecord/lib/active_record/store.rb +++ b/activerecord/lib/active_record/store.rb @@ -178,7 +178,7 @@ module ActiveRecord end def load(yaml) - self.class.as_indifferent_hash(@coder.load(yaml)) + self.class.as_indifferent_hash(@coder.load(yaml || '')) end def self.as_indifferent_hash(obj) -- cgit v1.2.3 From 06ace1e2b5cdccf67ff969ff0d355fd87f46dcf0 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Tue, 17 Dec 2013 16:02:52 -0500 Subject: activerecord: Initialize Migration with version from MigrationProxy. --- activerecord/lib/active_record/migration.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index fc84b907a5..b57da73969 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -759,7 +759,7 @@ module ActiveRecord def load_migration require(File.expand_path(filename)) - name.constantize.new + name.constantize.new(name, version) end end -- cgit v1.2.3 From eb589fed6f4950d441bc6aed8dfaaeffec061322 Mon Sep 17 00:00:00 2001 From: Nishant Modak Date: Mon, 6 Jan 2014 14:34:22 +0530 Subject: Make change_table use object of current database adapter - Earlier, change_table was creating database-agnostic object. - After this change, it will create correct object based on current database adapter. - This will ensure that create_table and change_table will get same objects. - This makes update_table_definition method public and nodoc. - Fixes #13577 and #13503 --- .../connection_adapters/abstract/schema_statements.rb | 10 +++++----- .../active_record/connection_adapters/postgresql_adapter.rb | 10 +++++----- activerecord/lib/active_record/migration/command_recorder.rb | 10 ++++++---- 3 files changed, 16 insertions(+), 14 deletions(-) (limited to 'activerecord/lib') 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 00383bad3b..88bf15bc18 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -714,7 +714,7 @@ module ActiveRecord # require the order columns appear in the SELECT. # # columns_for_distinct("posts.id", ["posts.created_at desc"]) - def columns_for_distinct(columns, orders) # :nodoc: + def columns_for_distinct(columns, orders) #:nodoc: columns end @@ -736,6 +736,10 @@ module ActiveRecord remove_column table_name, :created_at end + def update_table_definition(table_name, base) #:nodoc: + Table.new(table_name, base) + end + protected def add_index_sort_order(option_strings, column_names, options = {}) if options.is_a?(Hash) && order = options[:order] @@ -848,10 +852,6 @@ module ActiveRecord def create_alter_table(name) AlterTable.new create_table_definition(name, false, {}) end - - def update_table_definition(table_name, base) - Table.new(table_name, base) - end end end end diff --git a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb index 11a5eba464..7e188907e1 100644 --- a/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/postgresql_adapter.rb @@ -721,6 +721,10 @@ module ActiveRecord !native_database_types[type].nil? end + def update_table_definition(table_name, base) #:nodoc: + Table.new(table_name, base) + end + protected # Returns the version of the connected PostgreSQL server. @@ -800,7 +804,7 @@ module ActiveRecord end end - FEATURE_NOT_SUPPORTED = "0A000" # :nodoc: + FEATURE_NOT_SUPPORTED = "0A000" #:nodoc: def exec_no_cache(sql, name, binds) log(sql, name, binds) { @connection.async_exec(sql) } @@ -990,10 +994,6 @@ module ActiveRecord def create_table_definition(name, temporary, options, as = nil) TableDefinition.new native_database_types, name, temporary, options, as end - - def update_table_definition(table_name, base) - Table.new(table_name, base) - end end end end diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 01c73be849..17300f0598 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -86,7 +86,7 @@ module ActiveRecord alias :remove_belongs_to :remove_reference def change_table(table_name, options = {}) - yield ConnectionAdapters::Table.new(table_name, self) + yield delegate.update_table_definition(table_name, self) end private @@ -159,9 +159,11 @@ module ActiveRecord # Forwards any missing method call to the \target. def method_missing(method, *args, &block) - @delegate.send(method, *args, &block) - rescue NoMethodError => e - raise e, e.message.sub(/ for #<.*$/, " via proxy for #{@delegate}") + if @delegate.respond_to?(method) + @delegate.send(method, *args, &block) + else + super + end end end end -- cgit v1.2.3 From a334425caff9b2140d5e99fcfc2eb8c4ab10bdfa Mon Sep 17 00:00:00 2001 From: Damien Mathieu <42@dmathieu.com> Date: Wed, 8 Jan 2014 11:08:08 +0100 Subject: create/drop test and development databases only if RAILS_ENV is nil Closes #13625 --- activerecord/lib/active_record/railties/databases.rake | 4 ++-- activerecord/lib/active_record/tasks/database_tasks.rb | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 3d795ca689..58dfa2c5a5 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -12,7 +12,7 @@ db_namespace = namespace :db do end end - desc 'Create the database from DATABASE_URL or config/database.yml for the current Rails.env (use db:create:all to create all databases in the config)' + desc 'Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV it defaults to creating the development and test databases.' task :create => [:load_config] do ActiveRecord::Tasks::DatabaseTasks.create_current end @@ -23,7 +23,7 @@ db_namespace = namespace :db do end end - desc 'Drops the database using DATABASE_URL or the current Rails.env (use db:drop:all to drop all databases)' + desc 'Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:all to drop all databases in the config). Without RAILS_ENV it defaults to dropping the development and test databases.' task :drop => [:load_config] do ActiveRecord::Tasks::DatabaseTasks.drop_current end diff --git a/activerecord/lib/active_record/tasks/database_tasks.rb b/activerecord/lib/active_record/tasks/database_tasks.rb index afd493011e..6ce0495f6f 100644 --- a/activerecord/lib/active_record/tasks/database_tasks.rb +++ b/activerecord/lib/active_record/tasks/database_tasks.rb @@ -201,7 +201,8 @@ module ActiveRecord def each_current_configuration(environment) environments = [environment] - environments << 'test' if environment == 'development' + # add test environment only if no RAILS_ENV was specified. + environments << 'test' if environment == 'development' && ENV['RAILS_ENV'].nil? configurations = ActiveRecord::Base.configurations.values_at(*environments) configurations.compact.each do |configuration| -- cgit v1.2.3 From 724509a9d5322ff502aefa90dd282ba33a281a96 Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Wed, 8 Jan 2014 15:51:06 +0100 Subject: make `change_column_null` reversible. Closes #13576. Closes #13623. --- activerecord/lib/active_record/migration/command_recorder.rb | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/migration/command_recorder.rb b/activerecord/lib/active_record/migration/command_recorder.rb index 17300f0598..9139ad953c 100644 --- a/activerecord/lib/active_record/migration/command_recorder.rb +++ b/activerecord/lib/active_record/migration/command_recorder.rb @@ -74,7 +74,7 @@ module ActiveRecord :rename_index, :rename_column, :add_index, :remove_index, :add_timestamps, :remove_timestamps, :change_column_default, :add_reference, :remove_reference, :transaction, :drop_join_table, :drop_table, :execute_block, :enable_extension, - :change_column, :execute, :remove_columns, # irreversible methods need to be here too + :change_column, :execute, :remove_columns, :change_column_null # irreversible methods need to be here too ].each do |method| class_eval <<-EOV, __FILE__, __LINE__ + 1 def #{method}(*args, &block) # def create_table(*args, &block) @@ -157,6 +157,11 @@ module ActiveRecord alias :invert_add_belongs_to :invert_add_reference alias :invert_remove_belongs_to :invert_remove_reference + def invert_change_column_null(args) + args[2] = !args[2] + [:change_column_null, args] + end + # Forwards any missing method call to the \target. def method_missing(method, *args, &block) if @delegate.respond_to?(method) -- cgit v1.2.3 From da65fe9e11b7f445847228753e6df46d376e29ab Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 9 Jan 2014 14:17:04 -0800 Subject: Revert "ask the fixture set for the sql statements" This reverts commit 026d0555685087845b74dd87a0417b5a164b1c13. Conflicts: activerecord/lib/active_record/fixtures.rb Fixes #13383 --- .../abstract/database_statements.rb | 6 +----- activerecord/lib/active_record/fixtures.rb | 22 ++++++++++------------ 2 files changed, 11 insertions(+), 17 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb index e196df079b..c90915c509 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/database_statements.rb @@ -286,10 +286,6 @@ module ActiveRecord # Inserts the given fixture into the table. Overridden in adapters that require # something beyond a simple insert (eg. Oracle). def insert_fixture(fixture, table_name) - execute fixture_sql(fixture, table_name), 'Fixture Insert' - end - - def fixture_sql(fixture, table_name) columns = schema_cache.columns_hash(table_name) key_list = [] @@ -298,7 +294,7 @@ module ActiveRecord quote(value, columns[name]) end - "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})" + execute "INSERT INTO #{quote_table_name(table_name)} (#{key_list.join(', ')}) VALUES (#{value_list.join(', ')})", 'Fixture Insert' end def empty_insert_statement_value diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index a7a54483bc..fee3f51d9e 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -521,8 +521,16 @@ module ActiveRecord connection.transaction(:requires_new => true) do fixture_sets.each do |fs| conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection - fs.fixture_sql(conn).each do |stmt| - conn.execute stmt + table_rows = fs.table_rows + + table_rows.keys.each do |table| + conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete' + end + + table_rows.each do |fixture_set_name, rows| + rows.each do |row| + conn.insert_fixture(row, fixture_set_name) + end end end @@ -594,16 +602,6 @@ module ActiveRecord fixtures.size end - def fixture_sql(conn) - table_rows = self.table_rows - - table_rows.keys.map { |table| - "DELETE FROM #{conn.quote_table_name(table)}" - }.concat table_rows.flat_map { |fixture_set_name, rows| - rows.map { |row| conn.fixture_sql(row, fixture_set_name) } - } - end - # Returns a hash of rows to be inserted. The key is the table, the value is # a list of rows to insert to that table. def table_rows -- cgit v1.2.3 From 6cc03675d30b58e28f585720dad14e947a57ff5b Mon Sep 17 00:00:00 2001 From: schneems Date: Wed, 1 Jan 2014 17:33:59 -0500 Subject: Ensure Active Record connection consistency 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 to me if I 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` We should make 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 my prosed matrix of how this behavior should work: ``` 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". ``` ### Implementation 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 though it should match the above proposal. --- .../connection_specification.rb | 15 ++++-- .../lib/active_record/connection_handling.rb | 60 ++++++++++++++++++++-- activerecord/lib/active_record/core.rb | 9 +++- activerecord/lib/active_record/railtie.rb | 16 +----- .../lib/active_record/railties/databases.rake | 2 +- 5 files changed, 80 insertions(+), 22 deletions(-) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/connection_adapters/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/connection_specification.rb index 9f210c5f33..3f8b14bf67 100644 --- a/activerecord/lib/active_record/connection_adapters/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/connection_specification.rb @@ -123,13 +123,22 @@ module ActiveRecord def resolve(config) if config resolve_connection config - elsif defined?(Rails.env) - resolve_env_connection Rails.env.to_sym + elsif env = ActiveRecord::ConnectionHandling::RAILS_ENV.call + resolve_env_connection env.to_sym else raise AdapterNotSpecified end end + # Expands each key in @configurations hash into fully resolved hash + def resolve_all + config = configurations.dup + config.each do |key, value| + config[key] = resolve(value) if value + end + config + end + # Returns an instance of ConnectionSpecification for a given adapter. # Accepts a hash one layer deep that contains all connection information. # @@ -219,7 +228,7 @@ module ActiveRecord elsif spec.is_a?(String) resolve_string_connection(spec) else - raise(AdapterNotSpecified, "#{spec} database is not configured") + raise(AdapterNotSpecified, "'#{spec}' database is not configured. Available configuration: #{configurations.inspect}") end end diff --git a/activerecord/lib/active_record/connection_handling.rb b/activerecord/lib/active_record/connection_handling.rb index c4afadbd9b..11f6a47158 100644 --- a/activerecord/lib/active_record/connection_handling.rb +++ b/activerecord/lib/active_record/connection_handling.rb @@ -1,5 +1,8 @@ module ActiveRecord module ConnectionHandling + RAILS_ENV = -> { Rails.env if defined?(Rails) } + DEFAULT_ENV = -> { RAILS_ENV.call || "default_env" } + # Establishes the connection to the database. Accepts a hash as input where # the :adapter key must be specified with the name of a database adapter (in lower-case) # example for regular databases (MySQL, Postgresql, etc): @@ -41,9 +44,10 @@ module ActiveRecord # # The exceptions AdapterNotSpecified, AdapterNotFound and ArgumentError # may be returned on an error. - def establish_connection(spec = ENV["DATABASE_URL"]) - resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations - spec = resolver.spec(spec) + def establish_connection(spec = nil) + spec ||= DEFAULT_ENV.call.to_sym + resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new configurations + spec = resolver.spec(spec) unless respond_to?(spec.adapter_method) raise AdapterNotFound, "database configuration specifies nonexistent #{spec.config[:adapter]} adapter" @@ -53,6 +57,56 @@ module ActiveRecord connection_handler.establish_connection self, spec end + class MergeAndResolveDefaultUrlConfig # :nodoc: + def initialize(raw_configurations, url = ENV['DATABASE_URL']) + @raw_config = raw_configurations.dup + @url = url + end + + # Returns fully resolved connection hashes. + # Merges connection information from `ENV['DATABASE_URL']` if available. + def resolve + ConnectionAdapters::ConnectionSpecification::Resolver.new(config).resolve_all + end + + private + def config + if @url + raw_merged_into_default + else + @raw_config + end + end + + def raw_merged_into_default + default = default_url_hash + + @raw_config.each do |env, values| + default[env] = values || {} + default[env].merge!("url" => @url) { |h, v1, v2| v1 || v2 } if default[env].is_a?(Hash) + end + default + end + + # When the raw configuration is not present and ENV['DATABASE_URL'] + # is available we return a hash with the connection information in + # 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 + end + else + {} + end + end + end + # Returns the connection currently associated with the class. This can # also be used to "borrow" the connection to do database work unrelated # to any of the specific Active Records. diff --git a/activerecord/lib/active_record/core.rb b/activerecord/lib/active_record/core.rb index 18ee77f6fe..cd8690d500 100644 --- a/activerecord/lib/active_record/core.rb +++ b/activerecord/lib/active_record/core.rb @@ -42,9 +42,16 @@ module ActiveRecord # 'database' => 'db/production.sqlite3' # } # } - mattr_accessor :configurations, instance_writer: false + def self.configurations=(config) + @@configurations = ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve + end self.configurations = {} + # Returns fully resolved configurations hash + def self.configurations + @@configurations + end + ## # :singleton-method: # Determines whether to use Time.utc (using :utc) or Time.local (using :local) when pulling diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index ec85b3c843..11b564f8f9 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -40,19 +40,7 @@ module ActiveRecord namespace :db do task :load_config do - configuration = if ENV["DATABASE_URL"] - { Rails.env => ENV["DATABASE_URL"] } - else - Rails.application.config.database_configuration || {} - end - - resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new(configuration) - - configuration.each do |key, value| - configuration[key] = resolver.resolve(value) if value - end - - ActiveRecord::Tasks::DatabaseTasks.database_configuration = configuration + ActiveRecord::Tasks::DatabaseTasks.database_configuration = Rails.application.config.database_configuration if defined?(ENGINE_PATH) && engine = Rails::Engine.find(ENGINE_PATH) if engine.paths['db/migrate'].existent @@ -137,7 +125,7 @@ module ActiveRecord end end - self.configurations = app.config.database_configuration || {} + self.configurations = Rails.application.config.database_configuration establish_connection end end diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 58dfa2c5a5..561387a179 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -2,7 +2,7 @@ require 'active_record' db_namespace = namespace :db do task :load_config do - ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration || {} + ActiveRecord::Base.configurations = ActiveRecord::Tasks::DatabaseTasks.database_configuration || {} ActiveRecord::Migrator.migrations_paths = ActiveRecord::Tasks::DatabaseTasks.migrations_paths end -- cgit v1.2.3 From 35cc32841ed343e3013cdf67e7330647d50d668b Mon Sep 17 00:00:00 2001 From: Yves Senn Date: Fri, 10 Jan 2014 09:59:19 +0100 Subject: doc, API example on how to use `Model#exists?` with multiple IDs. [ci skip] Refs #13658 --- activerecord/lib/active_record/relation/finder_methods.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib') diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index 3963f2b3e0..4984dbd277 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -195,6 +195,7 @@ module ActiveRecord # Person.exists?(5) # Person.exists?('5') # Person.exists?(['name LIKE ?', "%#{query}%"]) + # Person.exists?(id: [1, 4, 8]) # Person.exists?(name: 'David') # Person.exists?(false) # Person.exists? -- cgit v1.2.3