From 6fbe9ef2ffb1858027130789246f3ae24a0a182f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sun, 3 Jan 2010 20:39:42 +0100 Subject: Use namespaces in notifications. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- activerecord/lib/active_record/railtie.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index d09aa3c4d2..ba12dcd9ce 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -201,7 +201,7 @@ module ActiveRecord protected def log(sql, name) result = nil - ActiveSupport::Notifications.instrument(:sql, :sql => sql, :name => name) do + ActiveSupport::Notifications.instrument("activerecord.sql", :sql => sql, :name => name) do @runtime += Benchmark.ms { result = yield } end result diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 657ee738c0..51eb04c9d0 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -62,7 +62,7 @@ module ActiveRecord initializer "active_record.notifications" do require 'active_support/notifications' - ActiveSupport::Notifications.subscribe("sql") do |name, before, after, instrumenter_id, payload| + ActiveSupport::Notifications.subscribe("activerecord.sql") do |name, before, after, instrumenter_id, payload| ActiveRecord::Base.connection.log_info(payload[:sql], payload[:name], (after - before) * 1000) end end -- cgit v1.2.3 From a68165833a7ba50a1e3d731afe8934d19e3ced99 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Mon, 4 Jan 2010 03:50:16 +0530 Subject: Add Relation#create_with to explictily specify create scope --- activerecord/lib/active_record/relation.rb | 16 +++++++++------- activerecord/lib/active_record/relation/query_methods.rb | 4 ++++ activerecord/lib/active_record/relation/spawn_methods.rb | 10 ++++++++++ 3 files changed, 23 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 6b9925d4e7..487b54f27d 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -6,7 +6,7 @@ module ActiveRecord attr_reader :relation, :klass attr_writer :readonly, :table - attr_accessor :preload_associations, :eager_load_associations, :includes_associations + attr_accessor :preload_associations, :eager_load_associations, :includes_associations, :create_with_attributes def initialize(klass, relation) @klass, @relation = klass, relation @@ -124,7 +124,7 @@ module ActiveRecord end def reset - @first = @last = @create_scope = @to_sql = @order_clause = nil + @first = @last = @to_sql = @order_clause = @scope_for_create = nil @records = [] self end @@ -163,13 +163,15 @@ module ActiveRecord end def with_create_scope - @klass.send(:with_scope, :create => create_scope) { yield } + @klass.send(:with_scope, :create => scope_for_create) { yield } end - def create_scope - @create_scope ||= wheres.inject({}) do |hash, where| - hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality) - hash + def scope_for_create + @scope_for_create ||= begin + @create_with_attributes || wheres.inject({}) do |hash, where| + hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality) + hash + end end end diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 525a9cb365..5d7bf0b7bc 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -17,6 +17,10 @@ module ActiveRecord spawn.tap {|r| r.readonly = status } end + def create_with(attributes = {}) + spawn.tap {|r| r.create_with_attributes = attributes } + end + def select(selects) if selects.present? relation = spawn(@relation.project(selects)) diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index a637e97155..4ecee8c634 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -6,6 +6,7 @@ module ActiveRecord relation.preload_associations = @preload_associations relation.eager_load_associations = @eager_load_associations relation.includes_associations = @includes_associations + relation.create_with_attributes = @create_with_attributes relation.table = table relation end @@ -32,6 +33,14 @@ module ActiveRecord merged_order = relation_order.present? ? relation_order : order_clause merged_relation = merged_relation.order(merged_order) + merged_relation.create_with_attributes = @create_with_attributes + + if @create_with_attributes && r.create_with_attributes + merged_relation.create_with_attributes = @create_with_attributes.merge(r.create_with_attributes) + else + merged_relation.create_with_attributes = r.create_with_attributes || @create_with_attributes + end + merged_wheres = @relation.wheres r.wheres.each do |w| @@ -56,6 +65,7 @@ module ActiveRecord end result.readonly = self.readonly unless skips.include?(:readonly) + result.create_with_attributes = @create_with_attributes unless skips.include?(:create_with) result = result.joins(@relation.joins(@relation)) unless skips.include?(:joins) result = result.group(@relation.groupings) unless skips.include?(:group) -- cgit v1.2.3 From 3990310a2bedd0dff5753e3e9b1282e686cff0cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 4 Jan 2010 00:03:56 +0100 Subject: Use underscore in notification namespaces. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- activerecord/lib/active_record/railtie.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index ba12dcd9ce..5eedf448a4 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -201,7 +201,7 @@ module ActiveRecord protected def log(sql, name) result = nil - ActiveSupport::Notifications.instrument("activerecord.sql", :sql => sql, :name => name) do + ActiveSupport::Notifications.instrument("active_record.sql", :sql => sql, :name => name) do @runtime += Benchmark.ms { result = yield } end result diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 51eb04c9d0..a35edace19 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -62,7 +62,7 @@ module ActiveRecord initializer "active_record.notifications" do require 'active_support/notifications' - ActiveSupport::Notifications.subscribe("activerecord.sql") do |name, before, after, instrumenter_id, payload| + ActiveSupport::Notifications.subscribe("active_record.sql") do |name, before, after, instrumenter_id, payload| ActiveRecord::Base.connection.log_info(payload[:sql], payload[:name], (after - before) * 1000) end end -- cgit v1.2.3 From 1459c8cc1d6baca324bf2350d8f89f4faa054f18 Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sun, 3 Jan 2010 19:30:28 -0800 Subject: Changed ActiveRecord::Base.store_full_sti_class to be true by default reflecting the previously announced Rails 3 default [DHH] --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7cefba2b82..78b92be849 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -550,7 +550,7 @@ module ActiveRecord #:nodoc: # Determine whether to store the full constant name including namespace when using STI superclass_delegating_accessor :store_full_sti_class - self.store_full_sti_class = false + self.store_full_sti_class = true # Stores the default scope for the class class_inheritable_accessor :default_scoping, :instance_writer => false -- cgit v1.2.3 From 2601a16ede92566c651c06942294250ea653bd85 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 4 Jan 2010 16:22:39 -0600 Subject: Autoload AS test case --- activerecord/lib/active_record/fixtures.rb | 1 - activerecord/lib/active_record/test_case.rb | 2 -- 2 files changed, 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 99b812b5fc..9da36a87ec 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -3,7 +3,6 @@ require 'yaml' require 'csv' require 'zlib' require 'active_support/dependencies' -require 'active_support/test_case' require 'active_support/core_ext/logger' if RUBY_VERSION < '1.9' diff --git a/activerecord/lib/active_record/test_case.rb b/activerecord/lib/active_record/test_case.rb index 2dfe2c09ea..0a77ad5fd7 100644 --- a/activerecord/lib/active_record/test_case.rb +++ b/activerecord/lib/active_record/test_case.rb @@ -1,5 +1,3 @@ -require "active_support/test_case" - module ActiveRecord class TestCase < ActiveSupport::TestCase #:nodoc: def assert_date_from_db(expected, actual, message = nil) -- cgit v1.2.3 From e5ed62deea3f281f9dafc8e7c9ae4354b5ad6a27 Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 4 Jan 2010 16:50:01 -0600 Subject: Autoload AR test case --- activerecord/lib/active_record/railties/databases.rake | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index a35a6c156b..492d26c268 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -46,7 +46,7 @@ namespace :db do $stderr.puts "Couldn't create database for #{config.inspect}" end end - return # Skip the else clause of begin/rescue + return # Skip the else clause of begin/rescue else ActiveRecord::Base.establish_connection(config) ActiveRecord::Base.connection @@ -245,7 +245,6 @@ namespace :db do namespace :fixtures do desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." task :load => :environment do - require 'active_record/fixtures' ActiveRecord::Base.establish_connection(Rails.env) base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures') fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir @@ -257,8 +256,6 @@ namespace :db do desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." task :identify => :environment do - require "active_record/fixtures" - label, id = ENV["LABEL"], ENV["ID"] raise "LABEL or ID required" if label.blank? && id.blank? -- cgit v1.2.3 From 891ba18d65ba314c70280724f58882381a2f1b5a Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Wed, 6 Jan 2010 13:37:45 -0500 Subject: Update db:migrate error message to reflect the removal of config.frameworks --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 492d26c268..1ebb92e0f6 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -292,7 +292,7 @@ namespace :db do if File.exists?(file) load(file) else - abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to prevent active_record from loading: config.frameworks -= [ :active_record ]} + abort %{#{file} doesn't exist yet. Run "rake db:migrate" to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/boot.rb to limit the frameworks that will be loaded} end end end -- cgit v1.2.3 From 894ce1553498fce6463f38cd0e41c2efbac31c0d Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Wed, 6 Jan 2010 13:38:11 -0500 Subject: Use better defaults for fixtures: transactional=true, instantiated=false These reflect the settings that were previously included in the default test_helper.rb, removed in d7d9173. --- activerecord/lib/active_record/fixtures.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index 9da36a87ec..b528c14bcc 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -822,8 +822,8 @@ module ActiveRecord superclass_delegating_accessor :pre_loaded_fixtures self.fixture_table_names = [] - self.use_transactional_fixtures = false - self.use_instantiated_fixtures = true + self.use_transactional_fixtures = true + self.use_instantiated_fixtures = false self.pre_loaded_fixtures = false self.fixture_class_names = {} -- cgit v1.2.3 From caad6c634c51714fa83419dfd3bf98fd32ff55cb Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Wed, 6 Jan 2010 13:58:27 -0500 Subject: Restore require of 'active_record/fixtures' for Rake tasks that use them --- activerecord/lib/active_record/railties/databases.rake | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 1ebb92e0f6..03359221c3 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -245,6 +245,8 @@ namespace :db do namespace :fixtures do desc "Load fixtures into the current environment's database. Load specific fixtures using FIXTURES=x,y. Load from subdirectory in test/fixtures using FIXTURES_DIR=z. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." task :load => :environment do + require 'active_record/fixtures' + ActiveRecord::Base.establish_connection(Rails.env) base_dir = ENV['FIXTURES_PATH'] ? File.join(Rails.root, ENV['FIXTURES_PATH']) : File.join(Rails.root, 'test', 'fixtures') fixtures_dir = ENV['FIXTURES_DIR'] ? File.join(base_dir, ENV['FIXTURES_DIR']) : base_dir @@ -256,6 +258,8 @@ namespace :db do desc "Search for a fixture given a LABEL or ID. Specify an alternative path (eg. spec/fixtures) using FIXTURES_PATH=spec/fixtures." task :identify => :environment do + require 'active_record/fixtures' + label, id = ENV["LABEL"], ENV["ID"] raise "LABEL or ID required" if label.blank? && id.blank? -- cgit v1.2.3 From 0c2d933f3636a3c66ab189806725eca36f11655c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 6 Jan 2010 22:16:49 +0100 Subject: Ensure before_validation and after_validation accepts :on as option. --- activerecord/lib/active_record/callbacks.rb | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/callbacks.rb b/activerecord/lib/active_record/callbacks.rb index e2a8f03c8f..aecde5848c 100644 --- a/activerecord/lib/active_record/callbacks.rb +++ b/activerecord/lib/active_record/callbacks.rb @@ -9,7 +9,6 @@ module ActiveRecord # * (-) valid # * (1) before_validation # * (-) validate - # * (-) validate_on_create # * (2) after_validation # * (3) before_save # * (4) before_create @@ -223,9 +222,10 @@ module ActiveRecord extend ActiveModel::Callbacks + define_callbacks :validation, :terminator => "result == false", :scope => [:kind, :name] + define_model_callbacks :initialize, :find, :only => :after define_model_callbacks :save, :create, :update, :destroy - define_model_callbacks :validation, :only => [:before, :after] end module ClassMethods @@ -236,6 +236,24 @@ module ActiveRecord send(meth.to_sym, meth.to_sym) end end + + def before_validation(*args, &block) + options = args.last + if options.is_a?(Hash) && options[:on] + options[:if] = Array(options[:if]) + options[:if] << "@_on_validate == :#{options[:on]}" + end + set_callback(:validation, :before, *args, &block) + end + + def after_validation(*args, &block) + options = args.extract_options! + options[:prepend] = true + options[:if] = Array(options[:if]) + options[:if] << "!halted && value != false" + options[:if] << "@_on_validate == :#{options[:on]}" if options[:on] + set_callback(:validation, :after, *(args << options), &block) + end end def create_or_update_with_callbacks #:nodoc: -- cgit v1.2.3 From 1afa9fa5a973734852bd7a9d20a534355c221098 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Wed, 30 Dec 2009 22:10:18 +0100 Subject: Refactored nested attributes a bit around :reject_if => :all_blank. --- activerecord/lib/active_record/nested_attributes.rb | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index ff3a51d5c0..3e6f84f31d 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -188,6 +188,8 @@ module ActiveRecord # the parent model is saved. This happens inside the transaction initiated # by the parents save method. See ActiveRecord::AutosaveAssociation. module ClassMethods + REJECT_ALL_BLANK_PROC = proc { |attributes| attributes.all? { |_, value| value.blank? } } + # Defines an attributes writer for the specified association(s). If you # are using attr_protected or attr_accessible, then you # will need to add the attribute writer to the allowed list. @@ -229,6 +231,7 @@ module ActiveRecord options = { :allow_destroy => false, :update_only => false } options.update(attr_names.extract_options!) options.assert_valid_keys(:allow_destroy, :reject_if, :limit, :update_only) + options[:reject_if] = REJECT_ALL_BLANK_PROC if options[:reject_if] == :all_blank attr_names.each do |association_name| if reflection = reflect_on_association(association_name) @@ -241,11 +244,7 @@ module ActiveRecord reflection.options[:autosave] = true add_autosave_association_callbacks(reflection) - self.nested_attributes_options[association_name.to_sym] = options - - if options[:reject_if] == :all_blank - self.nested_attributes_options[association_name.to_sym][:reject_if] = proc { |attributes| attributes.all? {|k,v| v.blank?} } - end + nested_attributes_options[association_name.to_sym] = options # def pirate_attributes=(attributes) # assign_nested_attributes_for_one_to_one_association(:pirate, attributes) -- cgit v1.2.3 From f82adc7c5addb4f9bf6b9cb3f1fcf3fb47505e53 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Thu, 31 Dec 2009 14:03:04 +0100 Subject: Add AssociationReflection#collection_association? which returns true if it's for a has_many or has_and_belongs_to_many association. --- activerecord/lib/active_record/reflection.rb | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index b751c9ad68..69772bf9bb 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -252,10 +252,17 @@ module ActiveRecord end end + # Returns whether or not this association reflection is for a collection + # association. Returns +true+ if the +macro+ is one of +has_many+ or + # +has_and_belongs_to_many+, +false+ otherwise. + def collection_association? + [:has_many, :has_and_belongs_to_many].include?(macro) + end + private def derive_class_name class_name = name.to_s.camelize - class_name = class_name.singularize if [ :has_many, :has_and_belongs_to_many ].include?(macro) + class_name = class_name.singularize if collection_association? class_name end -- cgit v1.2.3 From 2171e0a1d13a176db1e9e9dd55242fdb319b526c Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Thu, 31 Dec 2009 14:07:56 +0100 Subject: Cleanup some code in nested_attributes.rb, autosave_association.rb, and associations.rb with AssociationReflection#collection_association? Also cache the result value. --- activerecord/lib/active_record/associations.rb | 7 +++---- activerecord/lib/active_record/autosave_association.rb | 3 +-- activerecord/lib/active_record/nested_attributes.rb | 8 +------- activerecord/lib/active_record/reflection.rb | 5 ++++- 4 files changed, 9 insertions(+), 14 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index b2ae447d5e..1320f5b624 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1770,7 +1770,7 @@ module ActiveRecord end def using_limitable_reflections?(reflections) - reflections.reject { |r| [ :belongs_to, :has_one ].include?(r.macro) }.length.zero? + reflections.collect(&:collection_association?).length.zero? end def column_aliases(join_dependency) @@ -1843,7 +1843,7 @@ module ActiveRecord case associations when Symbol, String reflection = base.reflections[associations] - if reflection && [:has_many, :has_and_belongs_to_many].include?(reflection.macro) + if reflection && reflection.collection_association? records.each { |record| record.send(reflection.name).target.uniq! } end when Array @@ -1853,12 +1853,11 @@ module ActiveRecord when Hash associations.keys.each do |name| reflection = base.reflections[name] - is_collection = [:has_many, :has_and_belongs_to_many].include?(reflection.macro) parent_records = [] records.each do |record| if descendant = record.send(reflection.name) - if is_collection + if reflection.collection_association? parent_records.concat descendant.target.uniq else parent_records << descendant diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 98ab64537e..62cb996b5d 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -167,8 +167,7 @@ module ActiveRecord validation_method = "validate_associated_records_for_#{reflection.name}" force_validation = (reflection.options[:validate] == true || reflection.options[:autosave] == true) - case reflection.macro - when :has_many, :has_and_belongs_to_many + if reflection.collection_association? unless method_defined?(save_method) before_save :before_save_collection_association diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 3e6f84f31d..e05e46916c 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -235,16 +235,10 @@ module ActiveRecord attr_names.each do |association_name| if reflection = reflect_on_association(association_name) - type = case reflection.macro - when :has_one, :belongs_to - :one_to_one - when :has_many, :has_and_belongs_to_many - :collection - end - reflection.options[:autosave] = true add_autosave_association_callbacks(reflection) nested_attributes_options[association_name.to_sym] = options + type = (reflection.collection_association? ? :collection : :one_to_one) # def pirate_attributes=(attributes) # assign_nested_attributes_for_one_to_one_association(:pirate, attributes) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 69772bf9bb..96aac96cda 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -256,7 +256,10 @@ module ActiveRecord # association. Returns +true+ if the +macro+ is one of +has_many+ or # +has_and_belongs_to_many+, +false+ otherwise. def collection_association? - [:has_many, :has_and_belongs_to_many].include?(macro) + if @collection_association.nil? + @collection_association = [:has_many, :has_and_belongs_to_many].include?(macro) + end + @collection_association end private -- cgit v1.2.3 From f866ced24ac9cdece7801f50ec79cdbe3ff5ad59 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Sat, 2 Jan 2010 00:49:49 +0100 Subject: Don't use strings for callbacks, as these will be evaled. Rather use symbols, which uses a direct method dispatch. Patch by Comron Sattari. [#3429 state:resolved] --- activerecord/lib/active_record/autosave_association.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 62cb996b5d..b3e28b9373 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -163,8 +163,8 @@ module ActiveRecord # guards that check if the save or validation methods have already been # defined before actually defining them. def add_autosave_association_callbacks(reflection) - save_method = "autosave_associated_records_for_#{reflection.name}" - validation_method = "validate_associated_records_for_#{reflection.name}" + save_method = :"autosave_associated_records_for_#{reflection.name}" + validation_method = :"validate_associated_records_for_#{reflection.name}" force_validation = (reflection.options[:validate] == true || reflection.options[:autosave] == true) if reflection.collection_association? -- cgit v1.2.3 From a559260e41170397ec940321f12853ccdb440993 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Sat, 2 Jan 2010 00:57:04 +0100 Subject: Removed unnecessary call to #try and cleaned up a bit more. --- activerecord/lib/active_record/nested_attributes.rb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index e05e46916c..c86c1a9d18 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -291,7 +291,7 @@ module ActiveRecord # update_only is true, and a :_destroy key set to a truthy value, # then the existing record will be marked for destruction. def assign_nested_attributes_for_one_to_one_association(association_name, attributes) - options = self.nested_attributes_options[association_name] + options = nested_attributes_options[association_name] attributes = attributes.with_indifferent_access check_existing_record = (options[:update_only] || !attributes['id'].blank?) @@ -336,7 +336,7 @@ module ActiveRecord # { :id => '2', :_destroy => true } # ]) def assign_nested_attributes_for_collection_association(association_name, attributes_collection) - options = self.nested_attributes_options[association_name] + options = nested_attributes_options[association_name] unless attributes_collection.is_a?(Hash) || attributes_collection.is_a?(Array) raise ArgumentError, "Hash or Array expected, got #{attributes_collection.class.name} (#{attributes_collection.inspect})" @@ -387,13 +387,11 @@ module ActiveRecord end def call_reject_if(association_name, attributes) - callback = self.nested_attributes_options[association_name][:reject_if] - - case callback + case callback = nested_attributes_options[association_name][:reject_if] when Symbol method(callback).arity == 0 ? send(callback) : send(callback, attributes) when Proc - callback.try(:call, attributes) + callback.call(attributes) end end end -- cgit v1.2.3 From fc6aae34597bbecaf441571b20ab861b021ea8a5 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Sat, 2 Jan 2010 14:59:29 +0100 Subject: Remove deprecated '_delete' option from NestedAttributes. --- activerecord/lib/active_record/nested_attributes.rb | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index c86c1a9d18..e1e80355c0 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -264,21 +264,11 @@ module ActiveRecord marked_for_destruction? end - # Deal with deprecated _delete. - # - def _delete #:nodoc: - ActiveSupport::Deprecation.warn "_delete is deprecated in nested attributes. Use _destroy instead." - _destroy - end - private # Attribute hash keys that should not be assigned as normal attributes. # These hash keys are nested attributes implementation details. - # - # TODO Remove _delete from UNASSIGNABLE_KEYS when deprecation warning are - # removed. - UNASSIGNABLE_KEYS = %w( id _destroy _delete ) + UNASSIGNABLE_KEYS = %w( id _destroy ) # Assigns the given attributes to the association. # @@ -375,8 +365,7 @@ module ActiveRecord # Determines if a hash contains a truthy _destroy key. def has_destroy_flag?(hash) - ConnectionAdapters::Column.value_to_boolean(hash['_destroy']) || - ConnectionAdapters::Column.value_to_boolean(hash['_delete']) # TODO Remove after deprecation. + ConnectionAdapters::Column.value_to_boolean(hash['_destroy']) end # Determines if a new record should be build by checking for -- cgit v1.2.3 From b6264c43f414f323656ed135d46213466cbe00fb Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Sun, 3 Jan 2010 21:45:08 +0100 Subject: Moved the validation logic to the association reflection and refactored autosave_association.rb a bit. --- .../lib/active_record/autosave_association.rb | 39 +++++++++------------- activerecord/lib/active_record/reflection.rb | 13 ++++++++ 2 files changed, 29 insertions(+), 23 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index b3e28b9373..f01d0903cd 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -158,46 +158,39 @@ module ActiveRecord # # For performance reasons, we don't check whether to validate at runtime, # but instead only define the method and callback when needed. However, - # this can change, for instance, when using nested attributes. Since we - # don't want the callbacks to get defined multiple times, there are - # guards that check if the save or validation methods have already been - # defined before actually defining them. + # this can change, for instance, when using nested attributes, which is + # called _after_ the association has been defined. Since we don't want + # the callbacks to get defined multiple times, there are guards that + # check if the save or validation methods have already been defined + # before actually defining them. def add_autosave_association_callbacks(reflection) save_method = :"autosave_associated_records_for_#{reflection.name}" validation_method = :"validate_associated_records_for_#{reflection.name}" - force_validation = (reflection.options[:validate] == true || reflection.options[:autosave] == true) + collection = reflection.collection_association? - if reflection.collection_association? - unless method_defined?(save_method) + unless method_defined?(save_method) + if collection before_save :before_save_collection_association define_method(save_method) { save_collection_association(reflection) } # Doesn't use after_save as that would save associations added in after_create/after_update twice after_create save_method after_update save_method - end - - if !method_defined?(validation_method) && - (force_validation || (reflection.macro == :has_many && reflection.options[:validate] != false)) - define_method(validation_method) { validate_collection_association(reflection) } - validate validation_method - end - else - unless method_defined?(save_method) - case reflection.macro - when :has_one + else + if reflection.macro == :has_one define_method(save_method) { save_has_one_association(reflection) } after_save save_method - when :belongs_to + else define_method(save_method) { save_belongs_to_association(reflection) } before_save save_method end end + end - if !method_defined?(validation_method) && force_validation - define_method(validation_method) { validate_single_association(reflection) } - validate validation_method - end + if reflection.validate? && !method_defined?(validation_method) + method = (collection ? :validate_collection_association : :validate_single_association) + define_method(validation_method) { send(method, reflection) } + validate validation_method end end end diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index 96aac96cda..d6fad5cf29 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -262,6 +262,19 @@ module ActiveRecord @collection_association end + # Returns whether or not the association should be validated as part of + # the parent's validation. + # + # Unless you explicitely disable validation with + # :validate => false, it will take place when: + # + # * you explicitely enable validation; :validate => true + # * you use autosave; :autosave => true + # * the association is a +has_many+ association + def validate? + !options[:validate].nil? ? options[:validate] : (options[:autosave] == true || macro == :has_many) + end + private def derive_class_name class_name = name.to_s.camelize -- cgit v1.2.3 From 9550916903c931161f04a98091fba712d7fa5c1d Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Sun, 3 Jan 2010 22:48:25 +0100 Subject: Raise a RecordNotFound if an ID in nested attributes is given but doesn't return a record. [#2415 state:resolved] --- activerecord/lib/active_record/nested_attributes.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index e1e80355c0..9038888d22 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -288,6 +288,10 @@ module ActiveRecord if check_existing_record && (record = send(association_name)) && (options[:update_only] || record.id.to_s == attributes['id'].to_s) assign_to_or_mark_for_destruction(record, attributes, options[:allow_destroy]) + + elsif attributes['id'] + raise_nested_attributes_record_not_found(association_name, attributes['id']) + elsif !reject_new_record?(association_name, attributes) method = "build_#{association_name}" if respond_to?(method) @@ -349,6 +353,8 @@ module ActiveRecord end elsif existing_record = send(association_name).detect { |record| record.id.to_s == attributes['id'].to_s } assign_to_or_mark_for_destruction(existing_record, attributes, options[:allow_destroy]) + else + raise_nested_attributes_record_not_found(association_name, attributes['id']) end end end @@ -383,5 +389,10 @@ module ActiveRecord callback.call(attributes) end end + + def raise_nested_attributes_record_not_found(association_name, record_id) + reflection = self.class.reflect_on_association(association_name) + raise RecordNotFound, "Couldn't find #{reflection.klass.name} with ID=#{record_id} for #{self.class.name} with ID=#{id}" + end end end -- cgit v1.2.3 From 190ce3ab37966957997cd18772d1260bf121c2e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 7 Jan 2010 01:04:25 +0100 Subject: Errors messages are now moved from :activerecord.errors to simply :errors on I18n yml files. --- activerecord/lib/active_record/locale/en.yml | 39 ++++++--------------------- activerecord/lib/active_record/validations.rb | 2 +- 2 files changed, 9 insertions(+), 32 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml index e33d389f8c..75cdedb442 100644 --- a/activerecord/lib/active_record/locale/en.yml +++ b/activerecord/lib/active_record/locale/en.yml @@ -1,33 +1,9 @@ en: - activerecord: - errors: - # model.errors.full_messages format. - format: "{{attribute}} {{message}}" - - # The values :model, :attribute and :value are always available for interpolation - # The value :count is available when applicable. Can be used for pluralization. - messages: - inclusion: "is not included in the list" - exclusion: "is reserved" - invalid: "is invalid" - confirmation: "doesn't match confirmation" - accepted: "must be accepted" - empty: "can't be empty" - blank: "can't be blank" - too_long: "is too long (maximum is {{count}} characters)" - too_short: "is too short (minimum is {{count}} characters)" - wrong_length: "is the wrong length (should be {{count}} characters)" - taken: "has already been taken" - not_a_number: "is not a number" - greater_than: "must be greater than {{count}}" - greater_than_or_equal_to: "must be greater than or equal to {{count}}" - equal_to: "must be equal to {{count}}" - less_than: "must be less than {{count}}" - less_than_or_equal_to: "must be less than or equal to {{count}}" - odd: "must be odd" - even: "must be even" - record_invalid: "Validation failed: {{errors}}" - # Append your own errors here or at the model/attributes scope. + errors: + messages: + taken: "has already been taken" + record_invalid: "Validation failed: {{errors}}" + # Append your own errors here or at the model/attributes scope. # You can define own errors for models or model attributes. # The values :model, :attribute and :value are always available for interpolation. @@ -42,7 +18,9 @@ en: # Will define custom blank validation message for User model and # custom blank validation message for login attribute of User model. #models: - + + # ActiveRecord models configuration + #activerecord: # Translate model names. Used in Model.human_name(). #models: # For example, @@ -55,4 +33,3 @@ en: # user: # login: "Handle" # will translate User attribute "login" as "Handle" - diff --git a/activerecord/lib/active_record/validations.rb b/activerecord/lib/active_record/validations.rb index 12c1f23763..d5adcba3ba 100644 --- a/activerecord/lib/active_record/validations.rb +++ b/activerecord/lib/active_record/validations.rb @@ -11,7 +11,7 @@ module ActiveRecord def initialize(record) @record = record errors = @record.errors.full_messages.join(I18n.t('support.array.words_connector', :default => ', ')) - super(I18n.t('activerecord.errors.messages.record_invalid', :errors => errors)) + super(I18n.t('errors.messages.record_invalid', :errors => errors)) end end -- cgit v1.2.3 From 821a160a49529f449605e127e26b7376cc83d4d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 7 Jan 2010 01:45:57 +0100 Subject: Add a deprecation message to activerecord.errors. --- activerecord/lib/active_record/railtie.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index a35edace19..55008271b7 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -67,5 +67,16 @@ module ActiveRecord end end + initializer "active_record.i18n_deprecation" do + require 'active_support/i18n' + + begin + I18n.t(:"activerecord.errors", :raise => true) + warn "[DEPRECATION] \"activerecord.errors\" namespace is deprecated in I18n " << + "yml files, please use just \"errors\" instead." + rescue Exception => e + # No message then. + end + end end end -- cgit v1.2.3 From a25ef06956725f5c4c8833f86f61f1f12c4323e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 7 Jan 2010 02:45:50 +0100 Subject: Allow to specify default attributes names translation in I18n yml files. For example, you could easily specify :created_at and :updated_at translations as: en: attributes: created_at: "Created at" updated_at: "Updated at" This configuration is built on ActiveModel, so it means those translations are shared between different ORMs as well (but always as a fallback). --- activerecord/lib/active_record/locale/en.yml | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/locale/en.yml b/activerecord/lib/active_record/locale/en.yml index 75cdedb442..4115cc8e17 100644 --- a/activerecord/lib/active_record/locale/en.yml +++ b/activerecord/lib/active_record/locale/en.yml @@ -19,6 +19,11 @@ en: # custom blank validation message for login attribute of User model. #models: + # Attributes names common to most models + #attributes: + #created_at: "Created at" + #updated_at: "Updated at" + # ActiveRecord models configuration #activerecord: # Translate model names. Used in Model.human_name(). -- cgit v1.2.3 From 4300f945330d885665c84f076182d9650a1a0867 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 7 Jan 2010 13:30:51 -0800 Subject: Revert "Remove connection method definition, since it's called just once." Restore the connection method which was added so it can be overridden. This reverts commit e428c75d2b67c6a7bd5f5e7e1719cdece84d497f. --- activerecord/lib/active_record/migration.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 09a558a636..0022e8cc37 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -339,6 +339,10 @@ module ActiveRecord self.verbose = save end + def connection + ActiveRecord::Base.connection + end + def method_missing(method, *arguments, &block) arg_list = arguments.map(&:inspect) * ', ' @@ -346,7 +350,7 @@ module ActiveRecord unless arguments.empty? || method == :execute arguments[0] = Migrator.proper_table_name(arguments.first) end - Base.connection.send(method, *arguments, &block) + connection.send(method, *arguments, &block) end end end -- cgit v1.2.3 From 5193fe9dd730f9bbb72db055f37625fe9558b6ca Mon Sep 17 00:00:00 2001 From: Lawrence Pit Date: Thu, 7 Jan 2010 19:53:15 +0100 Subject: Exclude unchanged records from the collection being considered for autosave. [#2578 state:resolved] Signed-off-by: Eloy Duran --- activerecord/lib/active_record/autosave_association.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index f01d0903cd..741fb2ef40 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -224,10 +224,10 @@ module ActiveRecord def associated_records_to_validate_or_save(association, new_record, autosave) if new_record association - elsif association.loaded? - autosave ? association : association.find_all { |record| record.new_record? } + elsif autosave + association.target.find_all { |record| record.new_record? || record.changed? || record.marked_for_destruction? } else - autosave ? association.target : association.target.find_all { |record| record.new_record? } + association.target.find_all { |record| record.new_record? } end end -- cgit v1.2.3 From f2aacd51405724cdf7cfd36a439c9dbfce16973a Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Fri, 8 Jan 2010 20:47:49 +0100 Subject: Rollback the transaction when one of the autosave associations fails to save. [#3391 state:resolved] --- activerecord/lib/active_record/autosave_association.rb | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 741fb2ef40..f7119a77cc 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -296,13 +296,15 @@ module ActiveRecord association.destroy(record) elsif autosave != false && (@new_record_before_save || record.new_record?) if autosave - association.send(:insert_record, record, false, false) + saved = association.send(:insert_record, record, false, false) else association.send(:insert_record, record) end elsif autosave - record.save(false) + saved = record.save(false) end + + raise ActiveRecord::Rollback if saved == false end end @@ -329,7 +331,9 @@ module ActiveRecord key = reflection.options[:primary_key] ? send(reflection.options[:primary_key]) : id if autosave != false && (new_record? || association.new_record? || association[reflection.primary_key_name] != key || autosave) association[reflection.primary_key_name] = key - association.save(!autosave) + saved = association.save(!autosave) + raise ActiveRecord::Rollback if !saved && autosave + saved end end end @@ -350,7 +354,7 @@ module ActiveRecord if autosave && association.marked_for_destruction? association.destroy elsif autosave != false - association.save(!autosave) if association.new_record? || autosave + saved = association.save(!autosave) if association.new_record? || autosave if association.updated? association_id = association.send(reflection.options[:primary_key] || :id) @@ -360,6 +364,8 @@ module ActiveRecord self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s end end + + saved if autosave end end end -- cgit v1.2.3 From 7f775ef1a92129fcc77079bc8e00c99a75b38a38 Mon Sep 17 00:00:00 2001 From: Eloy Duran Date: Fri, 8 Jan 2010 21:44:06 +0100 Subject: Renamed AssociationReflection #collection_association? to #collection?. --- activerecord/lib/active_record/associations.rb | 6 +++--- activerecord/lib/active_record/autosave_association.rb | 2 +- activerecord/lib/active_record/nested_attributes.rb | 2 +- activerecord/lib/active_record/reflection.rb | 10 +++++----- 4 files changed, 10 insertions(+), 10 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 1320f5b624..aceb83044b 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1770,7 +1770,7 @@ module ActiveRecord end def using_limitable_reflections?(reflections) - reflections.collect(&:collection_association?).length.zero? + reflections.collect(&:collection?).length.zero? end def column_aliases(join_dependency) @@ -1843,7 +1843,7 @@ module ActiveRecord case associations when Symbol, String reflection = base.reflections[associations] - if reflection && reflection.collection_association? + if reflection && reflection.collection? records.each { |record| record.send(reflection.name).target.uniq! } end when Array @@ -1857,7 +1857,7 @@ module ActiveRecord parent_records = [] records.each do |record| if descendant = record.send(reflection.name) - if reflection.collection_association? + if reflection.collection? parent_records.concat descendant.target.uniq else parent_records << descendant diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index f7119a77cc..7c4e81a617 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -166,7 +166,7 @@ module ActiveRecord def add_autosave_association_callbacks(reflection) save_method = :"autosave_associated_records_for_#{reflection.name}" validation_method = :"validate_associated_records_for_#{reflection.name}" - collection = reflection.collection_association? + collection = reflection.collection? unless method_defined?(save_method) if collection diff --git a/activerecord/lib/active_record/nested_attributes.rb b/activerecord/lib/active_record/nested_attributes.rb index 9038888d22..f79a12a95c 100644 --- a/activerecord/lib/active_record/nested_attributes.rb +++ b/activerecord/lib/active_record/nested_attributes.rb @@ -238,7 +238,7 @@ module ActiveRecord reflection.options[:autosave] = true add_autosave_association_callbacks(reflection) nested_attributes_options[association_name.to_sym] = options - type = (reflection.collection_association? ? :collection : :one_to_one) + type = (reflection.collection? ? :collection : :one_to_one) # def pirate_attributes=(attributes) # assign_nested_attributes_for_one_to_one_association(:pirate, attributes) diff --git a/activerecord/lib/active_record/reflection.rb b/activerecord/lib/active_record/reflection.rb index d6fad5cf29..32b9a2aa87 100644 --- a/activerecord/lib/active_record/reflection.rb +++ b/activerecord/lib/active_record/reflection.rb @@ -255,11 +255,11 @@ module ActiveRecord # Returns whether or not this association reflection is for a collection # association. Returns +true+ if the +macro+ is one of +has_many+ or # +has_and_belongs_to_many+, +false+ otherwise. - def collection_association? - if @collection_association.nil? - @collection_association = [:has_many, :has_and_belongs_to_many].include?(macro) + def collection? + if @collection.nil? + @collection = [:has_many, :has_and_belongs_to_many].include?(macro) end - @collection_association + @collection end # Returns whether or not the association should be validated as part of @@ -278,7 +278,7 @@ module ActiveRecord private def derive_class_name class_name = name.to_s.camelize - class_name = class_name.singularize if collection_association? + class_name = class_name.singularize if collection? class_name end -- cgit v1.2.3 From 652d1c19ea077fb0af70379181bd4629e08944b6 Mon Sep 17 00:00:00 2001 From: Jeffrey Hardy Date: Fri, 8 Jan 2010 12:38:32 -0500 Subject: Allow AR::Schema's migrations_path to be overwritten by subclasses. Defaults to 'db/migrate' [#3671 state:committed] Signed-off-by: Jeremy Kemper --- .../active_record/connection_adapters/abstract/schema_statements.rb | 4 ++-- activerecord/lib/active_record/migration.rb | 4 ++++ activerecord/lib/active_record/schema.rb | 6 +++++- 3 files changed, 11 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') 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 e731bc84f0..86ba7d72c3 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -344,12 +344,12 @@ module ActiveRecord end end - def assume_migrated_upto_version(version) + def assume_migrated_upto_version(version, migrations_path = ActiveRecord::Migrator.migrations_path) version = version.to_i sm_table = quote_table_name(ActiveRecord::Migrator.schema_migrations_table_name) migrated = select_values("SELECT version FROM #{sm_table}").map(&:to_i) - versions = Dir['db/migrate/[0-9]*_*.rb'].map do |filename| + versions = Dir["#{migrations_path}/[0-9]*_*.rb"].map do |filename| filename.split('/').last.split('_').first.to_i end diff --git a/activerecord/lib/active_record/migration.rb b/activerecord/lib/active_record/migration.rb index 0022e8cc37..aeeed24881 100644 --- a/activerecord/lib/active_record/migration.rb +++ b/activerecord/lib/active_record/migration.rb @@ -407,6 +407,10 @@ module ActiveRecord self.new(direction, migrations_path, target_version).run end + def migrations_path + 'db/migrate' + end + def schema_migrations_table_name Base.table_name_prefix + 'schema_migrations' + Base.table_name_suffix end diff --git a/activerecord/lib/active_record/schema.rb b/activerecord/lib/active_record/schema.rb index 8a32cf1ca2..a996a0ebac 100644 --- a/activerecord/lib/active_record/schema.rb +++ b/activerecord/lib/active_record/schema.rb @@ -28,6 +28,10 @@ module ActiveRecord class Schema < Migration private_class_method :new + def self.migrations_path + ActiveRecord::Migrator.migrations_path + end + # Eval the given block. All methods available to the current connection # adapter are available within the block, so you can easily use the # database definition DSL to build up your schema (+create_table+, @@ -44,7 +48,7 @@ module ActiveRecord unless info[:version].blank? initialize_schema_migrations_table - assume_migrated_upto_version info[:version] + assume_migrated_upto_version(info[:version], migrations_path) end end end -- cgit v1.2.3 From 93898b389bafebc4d1b30a95330b4bb7d000c92d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 8 Jan 2010 23:50:24 +0100 Subject: Ensure new validates works with uniqueness validator. --- activerecord/lib/active_record/validations/associated.rb | 3 +-- activerecord/lib/active_record/validations/uniqueness.rb | 9 ++++++--- 2 files changed, 7 insertions(+), 5 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/validations/associated.rb b/activerecord/lib/active_record/validations/associated.rb index 66b78682ad..e41635134c 100644 --- a/activerecord/lib/active_record/validations/associated.rb +++ b/activerecord/lib/active_record/validations/associated.rb @@ -40,8 +40,7 @@ module ActiveRecord # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). The # method, proc or string should return or evaluate to a true or false value. def validates_associated(*attr_names) - options = attr_names.extract_options! - validates_with AssociatedValidator, options.merge(:attributes => attr_names) + validates_with AssociatedValidator, _merge_attributes(attr_names) end end end diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 7efd312357..4ff851dfa1 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -2,10 +2,14 @@ module ActiveRecord module Validations class UniquenessValidator < ActiveModel::EachValidator def initialize(options) - @klass = options.delete(:klass) super(options.reverse_merge(:case_sensitive => true)) end + # Unfortunately, we have to tie Uniqueness validators to a class. + def setup(klass) + @klass = klass + end + def validate_each(record, attribute, value) finder_class = find_finder_class_for(record) table = finder_class.active_relation @@ -170,8 +174,7 @@ module ActiveRecord # such a case. # def validates_uniqueness_of(*attr_names) - options = attr_names.extract_options! - validates_with UniquenessValidator, options.merge(:attributes => attr_names, :klass => self) + validates_with UniquenessValidator, _merge_attributes(attr_names) end end end -- cgit v1.2.3 From c1239e6509d27f88db2fc59189fd9a95333283e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 11 Jan 2010 15:14:52 +0100 Subject: Send the connection in AR notifications to avoid checking out new connections in threads just for logging purposes. --- .../lib/active_record/connection_adapters/abstract_adapter.rb | 6 ++++-- activerecord/lib/active_record/railtie.rb | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 5eedf448a4..1ad54e7584 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -193,15 +193,17 @@ module ActiveRecord def log_info(sql, name, ms) if @logger && @logger.debug? - name = '%s (%.1fms)' % [name || 'SQL', ms] + name = '%s (%.1fms)' % [name, ms] @logger.debug(format_log_entry(name, sql.squeeze(' '))) end end protected def log(sql, name) + name ||= "SQL" result = nil - ActiveSupport::Notifications.instrument("active_record.sql", :sql => sql, :name => name) do + ActiveSupport::Notifications.instrument("active_record.sql", + :sql => sql, :name => name, :connection => self) do @runtime += Benchmark.ms { result = yield } end result diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 55008271b7..a07f33503d 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -63,7 +63,7 @@ module ActiveRecord require 'active_support/notifications' ActiveSupport::Notifications.subscribe("active_record.sql") do |name, before, after, instrumenter_id, payload| - ActiveRecord::Base.connection.log_info(payload[:sql], payload[:name], (after - before) * 1000) + payload[:connection].log_info(payload[:sql], payload[:name], (after - before) * 1000) end end -- cgit v1.2.3 From cccb9988002f7b44133c089654b417ae9808b65f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 11 Jan 2010 23:27:43 +0100 Subject: Expose connections available in the connection pool. --- .../lib/active_record/connection_adapters/abstract/connection_pool.rb | 2 +- activerecord/lib/active_record/connection_adapters/mysql_adapter.rb | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') 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 377f2a44c5..bf8c546d2e 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -56,7 +56,7 @@ module ActiveRecord # * +wait_timeout+: number of seconds to block and wait for a connection # before giving up and raising a timeout error (default 5 seconds). class ConnectionPool - attr_reader :spec + attr_reader :spec, :connections # Creates a new ConnectionPool object. +spec+ is a ConnectionSpecification # object which describes database connection information (e.g. adapter, diff --git a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb index fa28bc64df..8c0bf6396a 100644 --- a/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/mysql_adapter.rb @@ -579,6 +579,8 @@ module ActiveRecord protected def translate_exception(exception, message) + return super unless exception.respond_to?(:errno) + case exception.errno when 1062 RecordNotUnique.new(message, exception) -- cgit v1.2.3 From a04486dc997979a2d87fc013d30b6e71a3df4a64 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 12 Jan 2010 22:20:53 +0530 Subject: Delay building arel relation as long as possible for improved introspection --- activerecord/lib/active_record/associations.rb | 15 +- activerecord/lib/active_record/base.rb | 4 +- activerecord/lib/active_record/relation.rb | 72 +++++---- .../active_record/relation/calculation_methods.rb | 14 +- .../lib/active_record/relation/finder_methods.rb | 8 +- .../lib/active_record/relation/query_methods.rb | 165 +++++++++++---------- .../lib/active_record/relation/spawn_methods.rb | 89 +++++------ 7 files changed, 182 insertions(+), 185 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index aceb83044b..d748500340 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -2148,22 +2148,23 @@ module ActiveRecord end def relation + aliased = Arel::Table.new(table_name, :as => @aliased_table_name) + if reflection.macro == :has_and_belongs_to_many - [Arel::Table.new(table_alias_for(options[:join_table], aliased_join_table_name)), Arel::Table.new(table_name_and_alias)] + [Arel::Table.new(options[:join_table], :as => aliased_join_table_name), aliased] elsif reflection.options[:through] - [Arel::Table.new(table_alias_for(through_reflection.klass.table_name, aliased_join_table_name)), Arel::Table.new(table_name_and_alias)] + [Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name), aliased] else - Arel::Table.new(table_name_and_alias) + aliased end end def join_relation(joining_relation, join = nil) if (relations = relation).is_a?(Array) - joining_relation. - joins(relations.first, Arel::OuterJoin).on(association_join.first). - joins(relations.last, Arel::OuterJoin).on(association_join.last) + joining_relation.joins(Relation::JoinOperation.new(relations.first, Arel::OuterJoin, association_join.first)). + joins(Relation::JoinOperation.new(relations.last, Arel::OuterJoin, association_join.last)) else - joining_relation.joins(relations, Arel::OuterJoin).on(association_join) + joining_relation.joins(Relation::JoinOperation.new(relations, Arel::OuterJoin, association_join)) end end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 026bf55aaa..0ecef4abb4 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1663,7 +1663,7 @@ module ActiveRecord #:nodoc: def build_association_joins(joins) join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, joins, nil) - relation = active_relation.relation + relation = active_relation.table join_dependency.join_associations.map { |association| if (association_relation = association.relation).is_a?(Array) [Arel::InnerJoin.new(relation, association_relation.first, association.association_join.first).joins(relation), @@ -2039,7 +2039,7 @@ module ActiveRecord #:nodoc: def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name) attrs = expand_hash_conditions_for_aggregates(attrs) - table = Arel::Table.new(default_table_name, active_relation_engine) + table = Arel::Table.new(self.table_name, :engine => active_relation_engine, :as => default_table_name) builder = PredicateBuilder.new(active_relation_engine) builder.build_from_hash(attrs, table).map(&:to_sql).join(' AND ') end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 487b54f27d..33127194b0 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,19 +1,19 @@ module ActiveRecord class Relation - include QueryMethods, FinderMethods, CalculationMethods, SpawnMethods + JoinOperation = Struct.new(:relation, :join_class, :on) + ASSOCIATION_METHODS = [:includes, :eager_load, :preload] + MULTI_VALUE_METHODS = [:select, :group, :order, :joins, :where, :having] + SINGLE_VALUE_METHODS = [:limit, :offset, :lock, :readonly, :create_with, :from] + + include FinderMethods, CalculationMethods, SpawnMethods, QueryMethods delegate :length, :collect, :map, :each, :all?, :to => :to_a - attr_reader :relation, :klass - attr_writer :readonly, :table - attr_accessor :preload_associations, :eager_load_associations, :includes_associations, :create_with_attributes + attr_reader :table, :klass - def initialize(klass, relation) - @klass, @relation = klass, relation - @preload_associations = [] - @eager_load_associations = [] - @includes_associations = [] - @loaded, @readonly = false + def initialize(klass, table) + @klass, @table = klass, table + (ASSOCIATION_METHODS + MULTI_VALUE_METHODS).each {|v| instance_variable_set(:"@#{v}_values", [])} end def new(*args, &block) @@ -29,7 +29,7 @@ module ActiveRecord end def respond_to?(method, include_private = false) - return true if @relation.respond_to?(method, include_private) || Array.method_defined?(method) + return true if arel.respond_to?(method, include_private) || Array.method_defined?(method) if match = DynamicFinderMatch.match(method) return true if @klass.send(:all_attributes_exists?, match.attribute_names) @@ -43,33 +43,35 @@ module ActiveRecord def to_a return @records if loaded? - find_with_associations = @eager_load_associations.any? || references_eager_loaded_tables? + find_with_associations = @eager_load_values.any? || (@includes_values.any? && references_eager_loaded_tables?) @records = if find_with_associations begin @klass.send(:find_with_associations, { - :select => @relation.send(:select_clauses).join(', '), - :joins => @relation.joins(relation), - :group => @relation.send(:group_clauses).join(', '), + :select => arel.send(:select_clauses).join(', '), + :joins => arel.joins(arel), + :group => arel.send(:group_clauses).join(', '), :order => order_clause, :conditions => where_clause, - :limit => @relation.taken, - :offset => @relation.skipped, - :from => (@relation.send(:from_clauses) if @relation.send(:sources).present?) + :limit => arel.taken, + :offset => arel.skipped, + :from => (arel.send(:from_clauses) if arel.send(:sources).present?) }, - ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_associations + @includes_associations, nil)) + ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_values + @includes_values, nil)) rescue ThrowResult [] end else - @klass.find_by_sql(@relation.to_sql) + @klass.find_by_sql(arel.to_sql) end - preload = @preload_associations - preload += @includes_associations unless find_with_associations + preload = @preload_values + preload += @includes_values unless find_with_associations preload.each {|associations| @klass.send(:preload_associations, @records, associations) } - @records.each { |record| record.readonly! } if @readonly + # @readonly_value is true only if set explicity. @implicit_readonly is true if there are JOINS and no explicit SELECT. + readonly = @readonly_value.nil? ? @implicit_readonly : @readonly_value + @records.each { |record| record.readonly! } if readonly @loaded = true @records @@ -97,7 +99,7 @@ module ActiveRecord if block_given? to_a.many? { |*block_args| yield(*block_args) } else - @relation.send(:taken).present? ? to_a.many? : size > 1 + arel.send(:taken).present? ? to_a.many? : size > 1 end end @@ -107,7 +109,7 @@ module ActiveRecord end def delete_all - @relation.delete.tap { reset } + arel.delete.tap { reset } end def delete(id_or_array) @@ -124,28 +126,24 @@ module ActiveRecord end def reset - @first = @last = @to_sql = @order_clause = @scope_for_create = nil + @first = @last = @to_sql = @order_clause = @scope_for_create = @arel = nil @records = [] self end - def table - @table ||= Arel::Table.new(@klass.table_name, :engine => @klass.active_relation_engine) - end - def primary_key @primary_key ||= table[@klass.primary_key] end def to_sql - @to_sql ||= @relation.to_sql + @to_sql ||= arel.to_sql end protected def method_missing(method, *args, &block) - if @relation.respond_to?(method) - @relation.send(method, *args, &block) + if arel.respond_to?(method) + arel.send(method, *args, &block) elsif Array.method_defined?(method) to_a.send(method, *args, &block) elsif match = DynamicFinderMatch.match(method) @@ -168,7 +166,7 @@ module ActiveRecord def scope_for_create @scope_for_create ||= begin - @create_with_attributes || wheres.inject({}) do |hash, where| + @create_with_value || wheres.inject({}) do |hash, where| hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality) hash end @@ -176,15 +174,15 @@ module ActiveRecord end def where_clause(join_string = " AND ") - @relation.send(:where_clauses).join(join_string) + arel.send(:where_clauses).join(join_string) end def order_clause - @order_clause ||= @relation.send(:order_clauses).join(', ') + @order_clause ||= arel.send(:order_clauses).join(', ') end def references_eager_loaded_tables? - joined_tables = (tables_in_string(@relation.joins(relation)) + [table.name, table.table_alias]).compact.uniq + joined_tables = (tables_in_string(arel.joins(arel)) + [table.name, table.table_alias]).compact.uniq (tables_in_string(to_sql) - joined_tables).any? end diff --git a/activerecord/lib/active_record/relation/calculation_methods.rb b/activerecord/lib/active_record/relation/calculation_methods.rb index 5246c7bc5d..e6f62ee49a 100644 --- a/activerecord/lib/active_record/relation/calculation_methods.rb +++ b/activerecord/lib/active_record/relation/calculation_methods.rb @@ -25,7 +25,7 @@ module ActiveRecord operation = operation.to_s.downcase if operation == "count" - joins = @relation.joins(relation) + joins = arel.joins(arel) if joins.present? && joins =~ /LEFT OUTER/i distinct = true column_name = @klass.primary_key if column_name == :all @@ -40,7 +40,7 @@ module ActiveRecord distinct = options[:distinct] || distinct column_name = :all if column_name.blank? && operation == "count" - if @relation.send(:groupings).any? + if arel.send(:groupings).any? return execute_grouped_calculation(operation, column_name) else return execute_simple_calculation(operation, column_name, distinct) @@ -63,7 +63,7 @@ module ActiveRecord end def execute_grouped_calculation(operation, column_name) #:nodoc: - group_attr = @relation.send(:groupings).first.value + group_attr = arel.send(:groupings).first.value association = @klass.reflect_on_association(group_attr.to_sym) associated = association && association.macro == :belongs_to # only count belongs_to associations group_field = associated ? association.primary_key_name : group_attr @@ -165,12 +165,8 @@ module ActiveRecord column ? column.type_cast(value) : value end - def get_projection_name_from_chained_relations(relation = @relation) - if relation.respond_to?(:projections) && relation.projections.present? - relation.send(:select_clauses).join(', ') - elsif relation.respond_to?(:relation) - get_projection_name_from_chained_relations(relation.relation) - end + def get_projection_name_from_chained_relations + @select_values.join(", ") if @select_values.present? end end diff --git a/activerecord/lib/active_record/relation/finder_methods.rb b/activerecord/lib/active_record/relation/finder_methods.rb index c3e5f27838..3668b0997f 100644 --- a/activerecord/lib/active_record/relation/finder_methods.rb +++ b/activerecord/lib/active_record/relation/finder_methods.rb @@ -93,15 +93,15 @@ module ActiveRecord result = where(primary_key.in(ids)).all expected_size = - if @relation.taken && ids.size > @relation.taken - @relation.taken + if arel.taken && ids.size > arel.taken + arel.taken else ids.size end # 11 ids with limit 3, offset 9 should give 2 results. - if @relation.skipped && (ids.size - @relation.skipped < expected_size) - expected_size = ids.size - @relation.skipped + if arel.skipped && (ids.size - arel.skipped < expected_size) + expected_size = ids.size - arel.skipped end if result.size == expected_size diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 5d7bf0b7bc..c07eca44e3 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -1,68 +1,42 @@ module ActiveRecord module QueryMethods - - def preload(*associations) - spawn.tap {|r| r.preload_associations += Array.wrap(associations) } - end - - def includes(*associations) - spawn.tap {|r| r.includes_associations += Array.wrap(associations) } - end - - def eager_load(*associations) - spawn.tap {|r| r.eager_load_associations += Array.wrap(associations) } - end - - def readonly(status = true) - spawn.tap {|r| r.readonly = status } - end - - def create_with(attributes = {}) - spawn.tap {|r| r.create_with_attributes = attributes } - end - - def select(selects) - if selects.present? - relation = spawn(@relation.project(selects)) - relation.readonly = @relation.joins(relation).present? ? false : @readonly - relation - else - spawn + extend ActiveSupport::Concern + + included do + (ActiveRecord::Relation::ASSOCIATION_METHODS + ActiveRecord::Relation::MULTI_VALUE_METHODS).each do |query_method| + attr_accessor :"#{query_method}_values" + + class_eval <<-CEVAL + def #{query_method}(*args) + spawn.tap do |new_relation| + new_relation.#{query_method}_values ||= [] + value = args.size > 1 ? [args] : Array.wrap(args) + new_relation.#{query_method}_values += value + end + end + CEVAL end - end - - def from(from) - from.present? ? spawn(@relation.from(from)) : spawn - end - def having(*args) - return spawn if args.blank? + ActiveRecord::Relation::SINGLE_VALUE_METHODS.each do |query_method| + attr_accessor :"#{query_method}_value" - if [String, Hash, Array].include?(args.first.class) - havings = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first) - else - havings = args.first + class_eval <<-CEVAL + def #{query_method}(value = true) + spawn.tap do |new_relation| + new_relation.#{query_method}_value = value + end + end + CEVAL end - - spawn(@relation.having(havings)) - end - - def group(groups) - groups.present? ? spawn(@relation.group(groups)) : spawn - end - - def order(orders) - orders.present? ? spawn(@relation.order(orders)) : spawn end def lock(locks = true) + relation = spawn case locks - when String - spawn(@relation.lock(locks)) - when TrueClass, NilClass - spawn(@relation.lock) + when String, TrueClass, NilClass + spawn.tap {|new_relation| new_relation.lock_value = locks || true } else - spawn + spawn.tap {|new_relation| new_relation.lock_value = false } end end @@ -70,7 +44,7 @@ module ActiveRecord relation = spawn relation.instance_variable_set(:@orders, nil) - order_clause = @relation.send(:order_clauses).join(', ') + order_clause = arel.send(:order_clauses).join(', ') if order_clause.present? relation.order(reverse_sql_order(order_clause)) else @@ -78,39 +52,74 @@ module ActiveRecord end end - def limit(limits) - limits.present? ? spawn(@relation.take(limits)) : spawn + def arel + @arel ||= build_arel end - def offset(offsets) - offsets.present? ? spawn(@relation.skip(offsets)) : spawn - end + def build_arel + arel = table - def on(join) - spawn(@relation.on(join)) - end + @joins_values.each do |j| + next if j.blank? - def joins(join, join_type = nil) - return spawn if join.blank? + @implicit_readonly = true - join_relation = case join - when String - @relation.join(join) - when Hash, Array, Symbol - if @klass.send(:array_of_strings?, join) - @relation.join(join.join(' ')) + case j + when Relation::JoinOperation + arel = arel.join(j.relation, j.join_class).on(j.on) + when Hash, Array, Symbol + if @klass.send(:array_of_strings?, j) + arel = arel.join(j.join(' ')) + else + arel = arel.join(@klass.send(:build_association_joins, j)) + end else - @relation.join(@klass.send(:build_association_joins, join)) + arel = arel.join(j) end - else - @relation.join(join, join_type) end - spawn(join_relation).tap { |r| r.readonly = true } + @where_values.each do |where| + if conditions = build_where(where) + arel = conditions.is_a?(String) ? arel.where(conditions) : arel.where(*conditions) + end + end + + @having_values.each do |where| + if conditions = build_where(where) + arel = conditions.is_a?(String) ? arel.having(conditions) : arel.having(*conditions) + end + end + + arel = arel.take(@limit_value) if @limit_value.present? + arel = arel.skip(@offset_value) if @offset_value.present? + + @group_values.each do |g| + arel = arel.group(g) if g.present? + end + + @order_values.each do |o| + arel = arel.order(o) if o.present? + end + + @select_values.each do |s| + @implicit_readonly = false + arel = arel.project(s) if s.present? + end + + arel = arel.from(@from_value) if @from_value.present? + + case @lock_value + when TrueClass + arel = arel.lock + when String + arel = arel.lock(@lock_value) + end + + arel end - def where(*args) - return spawn if args.blank? + def build_where(*args) + return if args.blank? builder = PredicateBuilder.new(Arel::Sql::Engine.new(@klass)) @@ -124,7 +133,7 @@ module ActiveRecord args.first end - conditions.is_a?(String) ? spawn(@relation.where(conditions)) : spawn(@relation.where(*conditions)) + conditions end private diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 4ecee8c634..66eae69d92 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -1,49 +1,51 @@ module ActiveRecord module SpawnMethods - def spawn(relation = @relation) - relation = Relation.new(@klass, relation) - relation.readonly = @readonly - relation.preload_associations = @preload_associations - relation.eager_load_associations = @eager_load_associations - relation.includes_associations = @includes_associations - relation.create_with_attributes = @create_with_attributes - relation.table = table + def spawn(arel_table = self.table) + relation = Relation.new(@klass, arel_table) + + (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).each do |query_method| + relation.send(:"#{query_method}_values=", send(:"#{query_method}_values")) + end + + Relation::SINGLE_VALUE_METHODS.each do |query_method| + relation.send(:"#{query_method}_value=", send(:"#{query_method}_value")) + end + relation end def merge(r) raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass - merged_relation = spawn(table).eager_load(r.eager_load_associations).preload(r.preload_associations).includes(r.includes_associations) - merged_relation.readonly = r.readonly - - [self.relation, r.relation].each do |arel| - merged_relation = merged_relation. - joins(arel.joins(arel)). - group(arel.groupings). - limit(arel.taken). - offset(arel.skipped). - select(arel.send(:select_clauses)). - from(arel.sources). - having(arel.havings). - lock(arel.locked) - end + merged_relation = spawn.eager_load(r.eager_load_values).preload(r.preload_values).includes(r.includes_values) + + merged_relation.readonly_value = r.readonly_value unless merged_relation.readonly_value + merged_relation.limit_value = r.limit_value unless merged_relation.limit_value + merged_relation.lock_value = r.lock_value unless merged_relation.lock_value + + merged_relation = merged_relation. + joins(r.joins_values). + group(r.group_values). + offset(r.offset_value). + select(r.select_values). + from(r.from_value). + having(r.having_values) - relation_order = r.send(:order_clause) - merged_order = relation_order.present? ? relation_order : order_clause - merged_relation = merged_relation.order(merged_order) + relation_order = r.order_values + merged_order = relation_order.present? ? relation_order : order_values + merged_relation.order_values = merged_order - merged_relation.create_with_attributes = @create_with_attributes + merged_relation.create_with_value = @create_with_value - if @create_with_attributes && r.create_with_attributes - merged_relation.create_with_attributes = @create_with_attributes.merge(r.create_with_attributes) + if @create_with_value && r.create_with_value + merged_relation.create_with_value = @create_with_value.merge(r.create_with_value) else - merged_relation.create_with_attributes = r.create_with_attributes || @create_with_attributes + merged_relation.create_with_value = r.create_with_value || @create_with_value end - merged_wheres = @relation.wheres + merged_wheres = @where_values - r.wheres.each do |w| + r.where_values.each do |w| if w.is_a?(Arel::Predicates::Equality) merged_wheres = merged_wheres.reject {|p| p.is_a?(Arel::Predicates::Equality) && p.operand1.name == w.operand1.name } end @@ -51,32 +53,23 @@ module ActiveRecord merged_wheres << w end - merged_relation.where(*merged_wheres) + merged_relation.where_values = merged_wheres + + merged_relation end alias :& :merge def except(*skips) result = Relation.new(@klass, table) - result.table = table - [:eager_load, :preload, :includes].each do |load_method| - result = result.send(load_method, send(:"#{load_method}_associations")) + (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).each do |method| + result.send(:"#{method}_values=", send(:"#{method}_values")) unless skips.include?(method) end - result.readonly = self.readonly unless skips.include?(:readonly) - result.create_with_attributes = @create_with_attributes unless skips.include?(:create_with) - - result = result.joins(@relation.joins(@relation)) unless skips.include?(:joins) - result = result.group(@relation.groupings) unless skips.include?(:group) - result = result.limit(@relation.taken) unless skips.include?(:limit) - result = result.offset(@relation.skipped) unless skips.include?(:offset) - result = result.select(@relation.send(:select_clauses)) unless skips.include?(:select) - result = result.from(@relation.sources) unless skips.include?(:from) - result = result.order(order_clause) unless skips.include?(:order) - result = result.where(*@relation.wheres) unless skips.include?(:where) - result = result.having(*@relation.havings) unless skips.include?(:having) - result = result.lock(@relation.locked) unless skips.include?(:lock) + Relation::SINGLE_VALUE_METHODS.each do |method| + result.send(:"#{method}_value=", send(:"#{method}_value")) unless skips.include?(method) + end result end -- cgit v1.2.3 From 4938a5746ddd3527c119d946e638505d32a6416f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 12 Jan 2010 22:37:26 +0530 Subject: Ensure using proper engine for Arel::Table --- activerecord/lib/active_record/associations.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d748500340..50fd4aafec 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1955,7 +1955,7 @@ module ActiveRecord class JoinBase # :nodoc: attr_reader :active_record, :table_joins - delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :to => :active_record + delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :active_relation_engine, :to => :active_record def initialize(active_record, joins = nil) @active_record = active_record @@ -2148,12 +2148,12 @@ module ActiveRecord end def relation - aliased = Arel::Table.new(table_name, :as => @aliased_table_name) + aliased = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => active_relation_engine) if reflection.macro == :has_and_belongs_to_many - [Arel::Table.new(options[:join_table], :as => aliased_join_table_name), aliased] + [Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => active_relation_engine), aliased] elsif reflection.options[:through] - [Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name), aliased] + [Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => active_relation_engine), aliased] else aliased end -- cgit v1.2.3 From 006519d2c9f142837f3b43882bcd243caa7ccdb9 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Tue, 12 Jan 2010 22:55:57 +0530 Subject: Use Relation#except for reversing the order --- activerecord/lib/active_record/relation/query_methods.rb | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index c07eca44e3..7ceec40954 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -41,10 +41,9 @@ module ActiveRecord end def reverse_order - relation = spawn - relation.instance_variable_set(:@orders, nil) - order_clause = arel.send(:order_clauses).join(', ') + relation = except(:order) + if order_clause.present? relation.order(reverse_sql_order(order_clause)) else -- cgit v1.2.3 From b9a7ec6db50722e892145110c8527bca81c08041 Mon Sep 17 00:00:00 2001 From: Chad Woolley Date: Tue, 29 Sep 2009 02:28:07 -0700 Subject: reduce max size of fixture IDs to fix sqlite2 tests, because sqlite2 was getting negative and changing values for ID field. See http://www.sqlite.org/datatypes.html Signed-off-by: Yehuda Katz --- activerecord/lib/active_record/fixtures.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/fixtures.rb b/activerecord/lib/active_record/fixtures.rb index b528c14bcc..adc675966a 100644 --- a/activerecord/lib/active_record/fixtures.rb +++ b/activerecord/lib/active_record/fixtures.rb @@ -434,7 +434,7 @@ end # Any fixture labeled "DEFAULTS" is safely ignored. class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash) - MAX_ID = 2 ** 31 - 1 + MAX_ID = 2 ** 30 - 1 DEFAULT_FILTER_RE = /\.ya?ml$/ @@all_cached_fixtures = {} -- cgit v1.2.3 From 1a275730b290c1f06d4e8df680d22ae1b41ab585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 12 Jan 2010 14:11:47 +0100 Subject: Set deprecation warnings for RAILS_ENV and RAILS_DEFAULT_LOGGER. --- .../abstract/connection_specification.rb | 4 +- .../lib/active_record/railties/databases.rake | 58 +++++++++++----------- 2 files changed, 31 insertions(+), 31 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index bbc290f721..53cef19681 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -51,8 +51,8 @@ module ActiveRecord def self.establish_connection(spec = nil) case spec when nil - raise AdapterNotSpecified unless defined? RAILS_ENV - establish_connection(RAILS_ENV) + raise AdapterNotSpecified unless defined?(Rails.env) + establish_connection(Rails.env) when ConnectionSpecification @@connection_handler.establish_connection(name, spec) when Symbol, String diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 03359221c3..b39e064e45 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -26,9 +26,9 @@ namespace :db do end end - desc 'Create the database defined in config/database.yml for the current RAILS_ENV' + desc 'Create the database defined in config/database.yml for the current Rails.env' task :create => :load_config do - create_database(ActiveRecord::Base.configurations[RAILS_ENV]) + create_database(ActiveRecord::Base.configurations[Rails.env]) end def create_database(config) @@ -111,9 +111,9 @@ namespace :db do end end - desc 'Drops the database for the current RAILS_ENV' + desc 'Drops the database for the current Rails.env' task :drop => :load_config do - config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] + config = ActiveRecord::Base.configurations[Rails.env || 'development'] begin drop_database(config) rescue Exception => e @@ -188,7 +188,7 @@ namespace :db do desc "Retrieves the charset for the current environment's database" task :charset => :environment do - config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] + config = ActiveRecord::Base.configurations[Rails.env || 'development'] case config['adapter'] when 'mysql' ActiveRecord::Base.establish_connection(config) @@ -203,7 +203,7 @@ namespace :db do desc "Retrieves the collation for the current environment's database" task :collation => :environment do - config = ActiveRecord::Base.configurations[RAILS_ENV || 'development'] + config = ActiveRecord::Base.configurations[Rails.env || 'development'] case config['adapter'] when 'mysql' ActiveRecord::Base.establish_connection(config) @@ -305,36 +305,36 @@ namespace :db do desc "Dump the database structure to a SQL file" task :dump => :environment do abcs = ActiveRecord::Base.configurations - case abcs[RAILS_ENV]["adapter"] + case abcs[Rails.env]["adapter"] when "mysql", "oci", "oracle" - ActiveRecord::Base.establish_connection(abcs[RAILS_ENV]) - File.open("#{Rails.root}/db/#{RAILS_ENV}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } + ActiveRecord::Base.establish_connection(abcs[Rails.env]) + File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } when "postgresql" - ENV['PGHOST'] = abcs[RAILS_ENV]["host"] if abcs[RAILS_ENV]["host"] - ENV['PGPORT'] = abcs[RAILS_ENV]["port"].to_s if abcs[RAILS_ENV]["port"] - ENV['PGPASSWORD'] = abcs[RAILS_ENV]["password"].to_s if abcs[RAILS_ENV]["password"] - search_path = abcs[RAILS_ENV]["schema_search_path"] + ENV['PGHOST'] = abcs[Rails.env]["host"] if abcs[Rails.env]["host"] + ENV['PGPORT'] = abcs[Rails.env]["port"].to_s if abcs[Rails.env]["port"] + ENV['PGPASSWORD'] = abcs[Rails.env]["password"].to_s if abcs[Rails.env]["password"] + search_path = abcs[Rails.env]["schema_search_path"] unless search_path.blank? search_path = search_path.split(",").map{|search_path| "--schema=#{search_path.strip}" }.join(" ") end - `pg_dump -i -U "#{abcs[RAILS_ENV]["username"]}" -s -x -O -f db/#{RAILS_ENV}_structure.sql #{search_path} #{abcs[RAILS_ENV]["database"]}` + `pg_dump -i -U "#{abcs[Rails.env]["username"]}" -s -x -O -f db/#{Rails.env}_structure.sql #{search_path} #{abcs[Rails.env]["database"]}` raise "Error dumping database" if $?.exitstatus == 1 when "sqlite", "sqlite3" - dbfile = abcs[RAILS_ENV]["database"] || abcs[RAILS_ENV]["dbfile"] - `#{abcs[RAILS_ENV]["adapter"]} #{dbfile} .schema > db/#{RAILS_ENV}_structure.sql` + dbfile = abcs[Rails.env]["database"] || abcs[Rails.env]["dbfile"] + `#{abcs[Rails.env]["adapter"]} #{dbfile} .schema > db/#{Rails.env}_structure.sql` when "sqlserver" - `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /f db\\#{RAILS_ENV}_structure.sql /q /A /r` - `scptxfr /s #{abcs[RAILS_ENV]["host"]} /d #{abcs[RAILS_ENV]["database"]} /I /F db\ /q /A /r` + `scptxfr /s #{abcs[Rails.env]["host"]} /d #{abcs[Rails.env]["database"]} /I /f db\\#{Rails.env}_structure.sql /q /A /r` + `scptxfr /s #{abcs[Rails.env]["host"]} /d #{abcs[Rails.env]["database"]} /I /F db\ /q /A /r` when "firebird" - set_firebird_env(abcs[RAILS_ENV]) - db_string = firebird_db_string(abcs[RAILS_ENV]) - sh "isql -a #{db_string} > #{Rails.root}/db/#{RAILS_ENV}_structure.sql" + set_firebird_env(abcs[Rails.env]) + db_string = firebird_db_string(abcs[Rails.env]) + sh "isql -a #{db_string} > #{Rails.root}/db/#{Rails.env}_structure.sql" else raise "Task not supported by '#{abcs["test"]["adapter"]}'" end if ActiveRecord::Base.connection.supports_migrations? - File.open("#{Rails.root}/db/#{RAILS_ENV}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } + File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } end end end @@ -357,28 +357,28 @@ namespace :db do when "mysql" ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') - IO.readlines("#{Rails.root}/db/#{RAILS_ENV}_structure.sql").join.split("\n\n").each do |table| + IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split("\n\n").each do |table| ActiveRecord::Base.connection.execute(table) end when "postgresql" ENV['PGHOST'] = abcs["test"]["host"] if abcs["test"]["host"] ENV['PGPORT'] = abcs["test"]["port"].to_s if abcs["test"]["port"] ENV['PGPASSWORD'] = abcs["test"]["password"].to_s if abcs["test"]["password"] - `psql -U "#{abcs["test"]["username"]}" -f #{Rails.root}/db/#{RAILS_ENV}_structure.sql #{abcs["test"]["database"]}` + `psql -U "#{abcs["test"]["username"]}" -f #{Rails.root}/db/#{Rails.env}_structure.sql #{abcs["test"]["database"]}` when "sqlite", "sqlite3" dbfile = abcs["test"]["database"] || abcs["test"]["dbfile"] - `#{abcs["test"]["adapter"]} #{dbfile} < #{Rails.root}/db/#{RAILS_ENV}_structure.sql` + `#{abcs["test"]["adapter"]} #{dbfile} < #{Rails.root}/db/#{Rails.env}_structure.sql` when "sqlserver" - `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` + `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{Rails.env}_structure.sql` when "oci", "oracle" ActiveRecord::Base.establish_connection(:test) - IO.readlines("#{Rails.root}/db/#{RAILS_ENV}_structure.sql").join.split(";\n\n").each do |ddl| + IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end when "firebird" set_firebird_env(abcs["test"]) db_string = firebird_db_string(abcs["test"]) - sh "isql -i #{Rails.root}/db/#{RAILS_ENV}_structure.sql #{db_string}" + sh "isql -i #{Rails.root}/db/#{Rails.env}_structure.sql #{db_string}" else raise "Task not supported by '#{abcs["test"]["adapter"]}'" end @@ -401,7 +401,7 @@ namespace :db do when "sqlserver" dropfkscript = "#{abcs["test"]["host"]}.#{abcs["test"]["database"]}.DP1".gsub(/\\/,'-') `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{dropfkscript}` - `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{RAILS_ENV}_structure.sql` + `osql -E -S #{abcs["test"]["host"]} -d #{abcs["test"]["database"]} -i db\\#{Rails.env}_structure.sql` when "oci", "oracle" ActiveRecord::Base.establish_connection(:test) ActiveRecord::Base.connection.structure_drop.split(";\n\n").each do |ddl| -- cgit v1.2.3 From 7f25ccf38d6cab2b96494ed1a6b7cdffeef8eae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 12 Jan 2010 15:46:09 +0100 Subject: Remove allow_concurrency and verification_timeout from ActiveRecord::Base (deprecated since 2.2). --- .../abstract/connection_specification.rb | 20 -------------------- .../connection_adapters/abstract_adapter.rb | 5 ----- 2 files changed, 25 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb index 53cef19681..2f36bec764 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_specification.rb @@ -88,26 +88,6 @@ module ActiveRecord end class << self - # Deprecated and no longer has any effect. - def allow_concurrency - ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_concurrency has been deprecated and no longer has any effect. Please remove all references to allow_concurrency.") - end - - # Deprecated and no longer has any effect. - def allow_concurrency=(flag) - ActiveSupport::Deprecation.warn("ActiveRecord::Base.allow_concurrency= has been deprecated and no longer has any effect. Please remove all references to allow_concurrency=.") - end - - # Deprecated and no longer has any effect. - def verification_timeout - ActiveSupport::Deprecation.warn("ActiveRecord::Base.verification_timeout has been deprecated and no longer has any effect. Please remove all references to verification_timeout.") - end - - # Deprecated and no longer has any effect. - def verification_timeout=(flag) - ActiveSupport::Deprecation.warn("ActiveRecord::Base.verification_timeout= has been deprecated and no longer has any effect. Please remove all references to verification_timeout=.") - 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/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 1ad54e7584..1b8d3f7a05 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -39,7 +39,6 @@ module ActiveRecord def initialize(connection, logger = nil) #:nodoc: @connection, @logger = connection, logger @runtime = 0 - @last_verification = 0 @query_cache_enabled = false end @@ -208,10 +207,6 @@ module ActiveRecord end result rescue Exception => e - # Log message and raise exception. - # Set last_verification to 0, so that connection gets verified - # upon reentering the request loop - @last_verification = 0 message = "#{e.class.name}: #{e.message}: #{sql}" log_info(message, name, 0) raise translate_exception(e, message) -- cgit v1.2.3 From dc2e291a939e1089300bdac972cb7b63548d9ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 12 Jan 2010 18:29:48 +0100 Subject: Move AR logging responsibilities to ActiveRecord::Railties::Subscriber. --- .../connection_adapters/abstract/query_cache.rb | 3 ++- .../connection_adapters/abstract_adapter.rb | 26 +-------------------- activerecord/lib/active_record/railtie.rb | 12 ++++------ .../lib/active_record/railties/subscriber.rb | 27 ++++++++++++++++++++++ 4 files changed, 34 insertions(+), 34 deletions(-) create mode 100644 activerecord/lib/active_record/railties/subscriber.rb (limited to 'activerecord/lib/active_record') 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 00c71090f3..020acbbe5a 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/query_cache.rb @@ -75,7 +75,8 @@ module ActiveRecord def cache_sql(sql) result = if @query_cache.has_key?(sql) - log_info(sql, "CACHE", 0.0) + ActiveSupport::Notifications.instrument("active_record.sql", + :sql => sql, :name => "CACHE", :connection_id => self.object_id) @query_cache[sql] else @query_cache[sql] = yield diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 1b8d3f7a05..4a9cc09032 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -190,13 +190,6 @@ module ActiveRecord "active_record_#{open_transactions}" end - def log_info(sql, name, ms) - if @logger && @logger.debug? - name = '%s (%.1fms)' % [name, ms] - @logger.debug(format_log_entry(name, sql.squeeze(' '))) - end - end - protected def log(sql, name) name ||= "SQL" @@ -208,7 +201,7 @@ module ActiveRecord result rescue Exception => e message = "#{e.class.name}: #{e.message}: #{sql}" - log_info(message, name, 0) + @logger.debug message if @logger raise translate_exception(e, message) end @@ -217,23 +210,6 @@ module ActiveRecord ActiveRecord::StatementInvalid.new(message) end - def format_log_entry(message, dump = nil) - if ActiveRecord::Base.colorize_logging - if @@row_even - @@row_even = false - message_color, dump_color = "4;36;1", "0;1" - else - @@row_even = true - message_color, dump_color = "4;35;1", "0" - end - - log_entry = " \e[#{message_color}m#{message}\e[0m " - log_entry << "\e[#{dump_color}m%#{String === dump ? 's' : 'p'}\e[0m" % dump if dump - log_entry - else - "%s %s" % [message, dump] - end - end end end end diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index a07f33503d..88d0aa624f 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -14,6 +14,10 @@ module ActiveRecord load "active_record/railties/databases.rake" end + # TODO If we require the wrong file, the error never comes up. + require "active_record/railties/subscriber" + subscriber ActiveRecord::Railties::Subscriber.new + initializer "active_record.set_configs" do |app| app.config.active_record.each do |k,v| ActiveRecord::Base.send "#{k}=", v @@ -59,14 +63,6 @@ module ActiveRecord ActiveRecord::Base.logger ||= ::Rails.logger end - initializer "active_record.notifications" do - require 'active_support/notifications' - - ActiveSupport::Notifications.subscribe("active_record.sql") do |name, before, after, instrumenter_id, payload| - payload[:connection].log_info(payload[:sql], payload[:name], (after - before) * 1000) - end - end - initializer "active_record.i18n_deprecation" do require 'active_support/i18n' diff --git a/activerecord/lib/active_record/railties/subscriber.rb b/activerecord/lib/active_record/railties/subscriber.rb new file mode 100644 index 0000000000..fd873dbff8 --- /dev/null +++ b/activerecord/lib/active_record/railties/subscriber.rb @@ -0,0 +1,27 @@ +module ActiveRecord + module Railties + class Subscriber < Rails::Subscriber + def sql(event) + name = '%s (%.1fms)' % [event.payload[:name], event.duration] + sql = event.payload[:sql].squeeze(' ') + + if odd? + name = color(name, :cyan, true) + sql = color(sql, nil, true) + else + name = color(name, :magenta, true) + end + + debug " #{name} #{sql}" + end + + def odd? + @odd_or_even = !@odd_or_even + end + + def logger + ActiveRecord::Base.logger + end + end + end +end \ No newline at end of file -- cgit v1.2.3 From 743cafb7f4a54d162a432c43974a5e5be55fd0b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 12 Jan 2010 20:00:53 +0100 Subject: Create Rails::Subscriber::TestHelper and use it to make ActiveRecord subscriber tests run in both sync and async scenarios. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 1 + 1 file changed, 1 insertion(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index 4a9cc09032..d697cb8d11 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -191,6 +191,7 @@ module ActiveRecord end protected + def log(sql, name) name ||= "SQL" result = nil -- cgit v1.2.3 From 8d78a82d797bf8809acb1d2ebb30cf81488ac99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 12 Jan 2010 22:07:30 +0100 Subject: Deprecate ActiveRecord::Base.colorize_logging. --- activerecord/lib/active_record/base.rb | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 0ecef4abb4..cc592aa2d3 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -516,14 +516,6 @@ module ActiveRecord #:nodoc: cattr_accessor :pluralize_table_names, :instance_writer => false @@pluralize_table_names = true - ## - # :singleton-method: - # Determines whether to use ANSI codes to colorize the logging statements committed by the connection adapter. These colors - # make it much easier to overview things during debugging (when used through a reader like +tail+ and on a black background), but - # may complicate matters if you use software like syslog. This is true, by default. - cattr_accessor :colorize_logging, :instance_writer => false - @@colorize_logging = true - ## # :singleton-method: # Determines whether to use Time.local (using :local) or Time.utc (using :utc) when pulling dates and times from the database. @@ -557,6 +549,13 @@ module ActiveRecord #:nodoc: self.default_scoping = [] class << self # Class methods + def colorize_logging(*args) + ActiveSupport::Deprecation.warn "ActiveRecord::Base.colorize_logging and " << + "config.active_record.colorize_logging are deprecated. Please use " << + "Rails::Subscriber.colorize_logging or config.colorize_logging instead", caller + end + alias :colorize_logging= :colorize_logging + # Find operates with four different retrieval approaches: # # * Find by id - This can either be a specific id (1), a list of ids (1, 5, 6), or an array of ids ([5, 6, 10]). -- cgit v1.2.3 From da5978c22374b8a3b15a421ff4920e0940435253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 13 Jan 2010 00:41:04 +0100 Subject: Add subscriber for ActionPack and move all logging inside it. --- activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb | 2 +- activerecord/lib/active_record/railties/subscriber.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 78b897add6..0a52f3a6a2 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -14,7 +14,7 @@ module ActiveRecord # Allow database path relative to Rails.root, but only if # the database path is not the special path that tells # Sqlite to build a database only in memory. - if Object.const_defined?(:Rails) && ':memory:' != config[:database] + if defined?(Rails.root) && ':memory:' != config[:database] config[:database] = File.expand_path(config[:database], Rails.root) end end diff --git a/activerecord/lib/active_record/railties/subscriber.rb b/activerecord/lib/active_record/railties/subscriber.rb index fd873dbff8..7c2a10cf0f 100644 --- a/activerecord/lib/active_record/railties/subscriber.rb +++ b/activerecord/lib/active_record/railties/subscriber.rb @@ -12,7 +12,7 @@ module ActiveRecord name = color(name, :magenta, true) end - debug " #{name} #{sql}" + debug "#{name} #{sql}" end def odd? -- cgit v1.2.3 From f921ad5c97048c692859b395bc78245a343bf833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 13 Jan 2010 23:19:11 +0100 Subject: Tidy up ActiveRecord and Views runtime information on process action logger. --- activerecord/lib/active_record/railties/controller_runtime.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index 535e967ec3..a4f4e15c2e 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -21,9 +21,9 @@ module ActiveRecord module ClassMethods def log_process_action(controller) - super - db_runtime = controller.send :db_runtime - logger.info(" ActiveRecord runtime: %.1fms" % db_runtime.to_f) if db_runtime + messages, db_runtime = super, controller.send(:db_runtime) + messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime + messages end end end -- cgit v1.2.3 From 363545aa20014c56f6da223acc4a46de5c873143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 14 Jan 2010 01:43:57 +0100 Subject: Different nested validations should not be ignore [#3638 status:resolved] --- activerecord/lib/active_record/autosave_association.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/autosave_association.rb b/activerecord/lib/active_record/autosave_association.rb index 7c4e81a617..e178cb4ef2 100644 --- a/activerecord/lib/active_record/autosave_association.rb +++ b/activerecord/lib/active_record/autosave_association.rb @@ -260,7 +260,8 @@ module ActiveRecord if reflection.options[:autosave] association.errors.each do |attribute, message| attribute = "#{reflection.name}.#{attribute}" - errors[attribute] << message if errors[attribute].empty? + errors[attribute] << message + errors[attribute].uniq! end else errors.add(reflection.name) -- cgit v1.2.3 From 8c8942ed4f2da52aa42ccd46560acb0b5fd37cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Thu, 14 Jan 2010 19:53:07 +0100 Subject: Move Dispatcher setup to Railties and add instrumentation hook. --- activerecord/lib/active_record/railtie.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/railtie.rb b/activerecord/lib/active_record/railtie.rb index 88d0aa624f..bc06333f1c 100644 --- a/activerecord/lib/active_record/railtie.rb +++ b/activerecord/lib/active_record/railtie.rb @@ -56,6 +56,19 @@ module ActiveRecord initializer "active_record.load_observers" do ActiveRecord::Base.instantiate_observers + + ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do + ActiveRecord::Base.instantiate_observers + end + end + + initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app| + unless app.config.cache_classes + ActionDispatch::Callbacks.after do + ActiveRecord::Base.reset_subclasses + ActiveRecord::Base.clear_reloadable_connections! + end + end end # TODO: ActiveRecord::Base.logger should delegate to its own config.logger -- cgit v1.2.3 From 6932f6751e224c8f18e686c6cde5b3885bfc71a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 15 Jan 2010 11:03:15 +0100 Subject: ActiveRecord should give the connection id as payload, and not the whole connection. --- activerecord/lib/active_record/connection_adapters/abstract_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb index d697cb8d11..7e80347f75 100755 --- a/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb @@ -196,7 +196,7 @@ module ActiveRecord name ||= "SQL" result = nil ActiveSupport::Notifications.instrument("active_record.sql", - :sql => sql, :name => name, :connection => self) do + :sql => sql, :name => name, :connection_id => self.object_id) do @runtime += Benchmark.ms { result = yield } end result -- cgit v1.2.3 From 88dd60298ef393e2406ab543a1124a4304a19f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 15 Jan 2010 11:55:11 +0100 Subject: Do not send the whole controller in notifications, cherry pick required pieces. --- activerecord/lib/active_record/railties/controller_runtime.rb | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/railties/controller_runtime.rb b/activerecord/lib/active_record/railties/controller_runtime.rb index a4f4e15c2e..aed1c59b00 100644 --- a/activerecord/lib/active_record/railties/controller_runtime.rb +++ b/activerecord/lib/active_record/railties/controller_runtime.rb @@ -5,6 +5,8 @@ module ActiveRecord module ControllerRuntime extend ActiveSupport::Concern + protected + attr_internal :db_runtime def cleanup_view_runtime @@ -19,9 +21,14 @@ module ActiveRecord end end + def append_info_to_payload(payload) + super + payload[:db_runtime] = db_runtime + end + module ClassMethods - def log_process_action(controller) - messages, db_runtime = super, controller.send(:db_runtime) + def log_process_action(payload) + messages, db_runtime = super, payload[:db_runtime] messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime messages end -- cgit v1.2.3 From bed9179aa1496f6d28891cf515af0d7e515ebbab Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Thu, 14 Jan 2010 13:36:33 +0530 Subject: Make scopes use relations under the hood --- activerecord/lib/active_record/associations.rb | 219 +++++++++------------ .../associations/association_collection.rb | 9 +- .../associations/has_many_through_association.rb | 2 - .../associations/through_association_scope.rb | 3 +- activerecord/lib/active_record/base.rb | 144 +++++++------- activerecord/lib/active_record/calculations.rb | 73 ++++--- activerecord/lib/active_record/named_scope.rb | 6 +- activerecord/lib/active_record/relation.rb | 15 +- .../active_record/relation/calculation_methods.rb | 5 +- .../lib/active_record/relation/query_methods.rb | 94 ++++++--- .../lib/active_record/relation/spawn_methods.rb | 28 ++- 11 files changed, 329 insertions(+), 269 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index 50fd4aafec..d6ecd0a5e3 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1703,24 +1703,30 @@ module ActiveRecord end def construct_finder_arel_with_included_associations(options, join_dependency) - scope = scope(:find) - relation = active_relation for association in join_dependency.join_associations relation = association.join_relation(relation) end - relation = relation.joins(construct_join(options[:joins], scope)). + relation = relation.joins(options[:joins]). select(column_aliases(join_dependency)). - group(options[:group] || (scope && scope[:group])). - having(options[:having] || (scope && scope[:having])). - order(construct_order(options[:order], scope)). - where(construct_conditions(options[:conditions], scope)). - from((scope && scope[:from]) || options[:from]) + group(options[:group]). + having(options[:having]). + order(options[:order]). + where(options[:conditions]). + from(options[:from]) + + scoped_relation = current_scoped_methods + scoped_relation_limit = scoped_relation.taken if scoped_relation + + relation = current_scoped_methods.except(:limit).merge(relation) if current_scoped_methods - relation = relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - relation = relation.limit(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) + if !using_limitable_reflections?(join_dependency.reflections) && ((scoped_relation && scoped_relation.taken) || options[:limit]) + relation = relation.where(construct_arel_limited_ids_condition(options, join_dependency)) + end + + relation = relation.limit(options[:limit] || scoped_relation_limit) if using_limitable_reflections?(join_dependency.reflections) relation end @@ -1748,23 +1754,23 @@ module ActiveRecord end def construct_finder_sql_for_association_limiting(options, join_dependency) - scope = scope(:find) - relation = active_relation for association in join_dependency.join_associations relation = association.join_relation(relation) end - relation = relation.joins(construct_join(options[:joins], scope)). - where(construct_conditions(options[:conditions], scope)). - group(options[:group] || (scope && scope[:group])). - having(options[:having] || (scope && scope[:having])). - order(construct_order(options[:order], scope)). - limit(construct_limit(options[:limit], scope)). - offset(construct_limit(options[:offset], scope)). - from(options[:from]). - select(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", construct_order(options[:order], scope(:find)).join(","))) + relation = relation.joins(options[:joins]). + where(options[:conditions]). + group(options[:group]). + having(options[:having]). + order(options[:order]). + limit(options[:limit]). + offset(options[:offset]). + from(options[:from]) + + relation = current_scoped_methods.except(:select, :includes, :eager_load).merge(relation) if current_scoped_methods + relation = relation.select(connection.distinct("#{connection.quote_table_name table_name}.#{primary_key}", options[:order])) relation.to_sql end @@ -2030,118 +2036,85 @@ module ActiveRecord def association_join return @join if @join - connection = reflection.active_record.connection + + aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => active_relation_engine) + parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => active_relation_engine) + @join = case reflection.macro - when :has_and_belongs_to_many - ["%s.%s = %s.%s " % [ - connection.quote_table_name(aliased_join_table_name), - options[:foreign_key] || reflection.active_record.to_s.foreign_key, - connection.quote_table_name(parent.aliased_table_name), - reflection.active_record.primary_key], - "%s.%s = %s.%s " % [ - connection.quote_table_name(aliased_table_name), - klass.primary_key, - connection.quote_table_name(aliased_join_table_name), - options[:association_foreign_key] || klass.to_s.foreign_key - ] - ] - when :has_many, :has_one - if reflection.options[:through] - jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil - first_key = second_key = as_extra = nil - - if through_reflection.options[:as] # has_many :through against a polymorphic join - jt_foreign_key = through_reflection.options[:as].to_s + '_id' - jt_as_extra = " AND %s.%s = %s" % [ - connection.quote_table_name(aliased_join_table_name), - connection.quote_column_name(through_reflection.options[:as].to_s + '_type'), - klass.quote_value(parent.active_record.base_class.name) - ] + when :has_and_belongs_to_many + join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => active_relation_engine) + fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key + klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key + + [ + join_table[fk].eq(parent_table[reflection.active_record.primary_key]), + aliased_table[klass.primary_key].eq(join_table[klass_fk]) + ] + when :has_many, :has_one + if reflection.options[:through] + join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => active_relation_engine) + jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil + first_key = second_key = as_extra = nil + + if through_reflection.options[:as] # has_many :through against a polymorphic join + jt_foreign_key = through_reflection.options[:as].to_s + '_id' + jt_as_extra = join_table[through_reflection.options[:as].to_s + '_type'].eq(parent.active_record.base_class.name) + else + jt_foreign_key = through_reflection.primary_key_name + end + + case source_reflection.macro + when :has_many + if source_reflection.options[:as] + first_key = "#{source_reflection.options[:as]}_id" + second_key = options[:foreign_key] || primary_key + as_extra = aliased_table["#{source_reflection.options[:as]}_type"].eq(source_reflection.active_record.base_class.name) else - jt_foreign_key = through_reflection.primary_key_name + first_key = through_reflection.klass.base_class.to_s.foreign_key + second_key = options[:foreign_key] || primary_key end - case source_reflection.macro - when :has_many - if source_reflection.options[:as] - first_key = "#{source_reflection.options[:as]}_id" - second_key = options[:foreign_key] || primary_key - as_extra = " AND %s.%s = %s" % [ - connection.quote_table_name(aliased_table_name), - connection.quote_column_name("#{source_reflection.options[:as]}_type"), - klass.quote_value(source_reflection.active_record.base_class.name) - ] - else - first_key = through_reflection.klass.base_class.to_s.foreign_key - second_key = options[:foreign_key] || primary_key - end - - unless through_reflection.klass.descends_from_active_record? - jt_sti_extra = " AND %s.%s = %s" % [ - connection.quote_table_name(aliased_join_table_name), - connection.quote_column_name(through_reflection.active_record.inheritance_column), - through_reflection.klass.quote_value(through_reflection.klass.sti_name)] - end - when :belongs_to - first_key = primary_key - if reflection.options[:source_type] - second_key = source_reflection.association_foreign_key - jt_source_extra = " AND %s.%s = %s" % [ - connection.quote_table_name(aliased_join_table_name), - connection.quote_column_name(reflection.source_reflection.options[:foreign_type]), - klass.quote_value(reflection.options[:source_type]) - ] - else - second_key = source_reflection.primary_key_name - end + unless through_reflection.klass.descends_from_active_record? + jt_sti_extra = join_table[through_reflection.active_record.inheritance_column].eq(through_reflection.klass.sti_name) + end + when :belongs_to + first_key = primary_key + if reflection.options[:source_type] + second_key = source_reflection.association_foreign_key + jt_source_extra = join_table[reflection.source_reflection.options[:foreign_type]].eq(reflection.options[:source_type]) + else + second_key = source_reflection.primary_key_name end - - ["(%s.%s = %s.%s%s%s%s) " % [ - connection.quote_table_name(parent.aliased_table_name), - connection.quote_column_name(parent.primary_key), - connection.quote_table_name(aliased_join_table_name), - connection.quote_column_name(jt_foreign_key), - jt_as_extra, jt_source_extra, jt_sti_extra], - "(%s.%s = %s.%s%s) " % [ - connection.quote_table_name(aliased_table_name), - connection.quote_column_name(first_key), - connection.quote_table_name(aliased_join_table_name), - connection.quote_column_name(second_key), - as_extra] - ] - - elsif reflection.options[:as] - "%s.%s = %s.%s AND %s.%s = %s" % [ - connection.quote_table_name(aliased_table_name), - "#{reflection.options[:as]}_id", - connection.quote_table_name(parent.aliased_table_name), - parent.primary_key, - connection.quote_table_name(aliased_table_name), - "#{reflection.options[:as]}_type", - klass.quote_value(parent.active_record.base_class.name) - ] - else - foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key - "%s.%s = %s.%s " % [ - aliased_table_name, - foreign_key, - parent.aliased_table_name, - reflection.options[:primary_key] || parent.primary_key - ] end - when :belongs_to - "%s.%s = %s.%s " % [ - connection.quote_table_name(aliased_table_name), - reflection.klass.primary_key, - connection.quote_table_name(parent.aliased_table_name), - options[:foreign_key] || reflection.primary_key_name + + [ + [parent_table[parent.primary_key].eq(join_table[jt_foreign_key]), jt_as_extra, jt_source_extra, jt_sti_extra].reject{|x| x.blank? }, + aliased_table[first_key].eq(join_table[second_key]) ] + elsif reflection.options[:as] + id_rel = aliased_table["#{reflection.options[:as]}_id"].eq(parent_table[parent.primary_key]) + type_rel = aliased_table["#{reflection.options[:as]}_type"].eq(parent.active_record.base_class.name) + [id_rel, type_rel] + else + foreign_key = options[:foreign_key] || reflection.active_record.name.foreign_key + [aliased_table[foreign_key].eq(parent_table[reflection.options[:primary_key] || parent.primary_key])] + end + when :belongs_to + [aliased_table[reflection.klass.primary_key].eq(parent_table[options[:foreign_key] || reflection.primary_key_name])] + end + + unless klass.descends_from_active_record? + sti_column = aliased_table[klass.inheritance_column] + sti_condition = sti_column.eq(klass.sti_name) + klass.send(:subclasses).each {|subclass| sti_condition = sti_condition.or(sti_column.eq(subclass.sti_name)) } + + @join << sti_condition end - @join << %(AND %s) % [ - klass.send(:type_condition, aliased_table_name)] unless klass.descends_from_active_record? [through_reflection, reflection].each do |ref| - @join << "AND #{interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name))} " if ref && ref.options[:conditions] + if ref && ref.options[:conditions] + @join << interpolate_sql(sanitize_sql(ref.options[:conditions], aliased_table_name)) + end end @join diff --git a/activerecord/lib/active_record/associations/association_collection.rb b/activerecord/lib/active_record/associations/association_collection.rb index 358db6df1d..64dd5cf629 100644 --- a/activerecord/lib/active_record/associations/association_collection.rb +++ b/activerecord/lib/active_record/associations/association_collection.rb @@ -58,11 +58,14 @@ module ActiveRecord find_scope = construct_scope[:find].slice(:conditions, :order) with_scope(:find => find_scope) do - relation = @reflection.klass.send(:construct_finder_arel, options) + relation = @reflection.klass.send(:construct_finder_arel, options, @reflection.klass.send(:current_scoped_methods)) case args.first - when :first, :last, :all + when :first, :last relation.send(args.first) + when :all + records = relation.all + @reflection.options[:uniq] ? uniq(records) : records else relation.find(*args) end @@ -402,7 +405,7 @@ module ActiveRecord end elsif @reflection.klass.scopes.include?(method) @reflection.klass.scopes[method].call(self, *args) - else + else with_scope(construct_scope) do if block_given? @reflection.klass.send(method, *args) { |*block_args| yield(*block_args) } diff --git a/activerecord/lib/active_record/associations/has_many_through_association.rb b/activerecord/lib/active_record/associations/has_many_through_association.rb index 214ce5959a..387b85aacd 100644 --- a/activerecord/lib/active_record/associations/has_many_through_association.rb +++ b/activerecord/lib/active_record/associations/has_many_through_association.rb @@ -51,8 +51,6 @@ module ActiveRecord end def construct_find_options!(options) - options[:select] = construct_select(options[:select]) - options[:from] ||= construct_from options[:joins] = construct_joins(options[:joins]) options[:include] = @reflection.source_reflection.options[:include] if options[:include].nil? && @reflection.source_reflection.options[:include] end diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 1924156e2a..1001199daa 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -6,8 +6,7 @@ module ActiveRecord def construct_scope { :create => construct_owner_attributes(@reflection), - :find => { :from => construct_from, - :conditions => construct_conditions, + :find => { :conditions => construct_conditions, :joins => construct_joins, :include => @reflection.options[:include] || @reflection.source_reflection.options[:include], :select => construct_select, diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index cc592aa2d3..7e4ee4b840 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -644,7 +644,7 @@ module ActiveRecord #:nodoc: options = args.extract_options! set_readonly_option!(options) - relation = construct_finder_arel(options) + relation = construct_finder_arel(options, current_scoped_methods) case args.first when :first, :last, :all @@ -870,20 +870,21 @@ module ActiveRecord #:nodoc: # # Update all books that match our conditions, but limit it to 5 ordered by date # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5 def update_all(updates, conditions = nil, options = {}) - scope = scope(:find) - relation = active_relation - if conditions = construct_conditions(conditions, scope) + if conditions = construct_conditions(conditions, nil) relation = relation.where(Arel::SqlLiteral.new(conditions)) end - relation = if options.has_key?(:limit) || (scope && scope[:limit]) + relation = relation.limit(options[:limit]) if options[:limit].present? + relation = relation.order(options[:order]) if options[:order].present? + + if current_scoped_methods && current_scoped_methods.limit_value.present? && current_scoped_methods.order_values.present? # Only take order from scope if limit is also provided by scope, this # is useful for updating a has_many association with a limit. - relation.order(construct_order(options[:order], scope)).limit(construct_limit(options[:limit], scope)) + relation = current_scoped_methods.merge(relation) if current_scoped_methods else - relation.order(options[:order]) + relation = current_scoped_methods.except(:limit, :order).merge(relation) if current_scoped_methods end relation.update(sanitize_sql_for_assignment(updates)) @@ -1572,26 +1573,26 @@ module ActiveRecord #:nodoc: end end - def construct_finder_arel(options = {}, scope = scope(:find)) + def construct_finder_arel(options = {}, scope = nil) validate_find_options(options) relation = active_relation. - joins(construct_join(options[:joins], scope)). - where(construct_conditions(options[:conditions], scope)). - select(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))). - group(options[:group] || (scope && scope[:group])). - having(options[:having] || (scope && scope[:having])). - order(construct_order(options[:order], scope)). - limit(construct_limit(options[:limit], scope)). - offset(construct_offset(options[:offset], scope)). + joins(options[:joins]). + where(options[:conditions]). + select(options[:select]). + group(options[:group]). + having(options[:having]). + order(options[:order]). + limit(options[:limit]). + offset(options[:offset]). from(options[:from]). - includes( merge_includes(scope && scope[:include], options[:include])) - - lock = (scope && scope[:lock]) || options[:lock] - relation = relation.lock if lock.present? + includes(options[:include]) - relation = relation.readonly if options[:readonly] + relation = relation.where(type_condition) if finder_needs_type_condition? + relation = relation.lock(options[:lock]) if options[:lock].present? + relation = relation.readonly(options[:readonly]) if options.has_key?(:readonly) + relation = scope.merge(relation) if scope relation end @@ -1665,10 +1666,10 @@ module ActiveRecord #:nodoc: relation = active_relation.table join_dependency.join_associations.map { |association| if (association_relation = association.relation).is_a?(Array) - [Arel::InnerJoin.new(relation, association_relation.first, association.association_join.first).joins(relation), - Arel::InnerJoin.new(relation, association_relation.last, association.association_join.last).joins(relation)].join() + [Arel::InnerJoin.new(relation, association_relation.first, *association.association_join.first).joins(relation), + Arel::InnerJoin.new(relation, association_relation.last, *association.association_join.last).joins(relation)].join() else - Arel::InnerJoin.new(relation, association_relation, association.association_join).joins(relation) + Arel::InnerJoin.new(relation, association_relation, *association.association_join).joins(relation) end }.join(" ") end @@ -1713,7 +1714,7 @@ module ActiveRecord #:nodoc: super unless all_attributes_exists?(attribute_names) if match.finder? options = arguments.extract_options! - relation = options.any? ? construct_finder_arel(options) : scoped + relation = options.any? ? construct_finder_arel(options, current_scoped_methods) : scoped relation.send :find_by_attributes, match, attribute_names, *arguments elsif match.instantiator? scoped.send :find_or_instantiator_by_attributes, match, attribute_names, *arguments, &block @@ -1830,52 +1831,49 @@ module ActiveRecord #:nodoc: def with_scope(method_scoping = {}, action = :merge, &block) method_scoping = method_scoping.method_scoping if method_scoping.respond_to?(:method_scoping) - # Dup first and second level of hash (method and params). - method_scoping = method_scoping.inject({}) do |hash, (method, params)| - hash[method] = (params == true) ? params : params.dup - hash - end + if method_scoping.is_a?(Hash) + # Dup first and second level of hash (method and params). + method_scoping = method_scoping.inject({}) do |hash, (method, params)| + hash[method] = (params == true) ? params : params.dup + hash + end - method_scoping.assert_valid_keys([ :find, :create ]) + method_scoping.assert_valid_keys([ :find, :create ]) - if f = method_scoping[:find] - f.assert_valid_keys(VALID_FIND_OPTIONS) - set_readonly_option! f - end + if f = method_scoping[:find] + f.assert_valid_keys(VALID_FIND_OPTIONS) + set_readonly_option! f + end - # Merge scopings - if [:merge, :reverse_merge].include?(action) && current_scoped_methods - method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)| - case hash[method] - when Hash - if method == :find - (hash[method].keys + params.keys).uniq.each do |key| - merge = hash[method][key] && params[key] # merge if both scopes have the same key - if key == :conditions && merge - if params[key].is_a?(Hash) && hash[method][key].is_a?(Hash) - hash[method][key] = merge_conditions(hash[method][key].deep_merge(params[key])) - else - hash[method][key] = merge_conditions(params[key], hash[method][key]) - end - elsif key == :include && merge - hash[method][key] = merge_includes(hash[method][key], params[key]).uniq - elsif key == :joins && merge - hash[method][key] = merge_joins(params[key], hash[method][key]) - else - hash[method][key] = hash[method][key] || params[key] - end - end - else - if action == :reverse_merge - hash[method] = hash[method].merge(params) - else - hash[method] = params.merge(hash[method]) - end - end - else - hash[method] = params + relation = construct_finder_arel(method_scoping[:find] || {}) + + if current_scoped_methods && current_scoped_methods.create_with_value && method_scoping[:create] + scope_for_create = case action + when :merge + current_scoped_methods.create_with_value.merge(method_scoping[:create]) + when :reverse_merge + method_scoping[:create].merge(current_scoped_methods.create_with_value) + else + method_scoping[:create] end - hash + + relation = relation.create_with(scope_for_create) + else + scope_for_create = method_scoping[:create] + scope_for_create ||= current_scoped_methods.create_with_value if current_scoped_methods + relation = relation.create_with(scope_for_create) if scope_for_create + end + + method_scoping = relation + end + + if current_scoped_methods + case action + when :merge + method_scoping = current_scoped_methods.merge(method_scoping) + when :reverse_merge + method_scoping = current_scoped_methods.except(:where).merge(method_scoping) + method_scoping = method_scoping.merge(current_scoped_methods.only(:where)) end end @@ -1904,20 +1902,22 @@ module ActiveRecord #:nodoc: # default_scope :order => 'last_name, first_name' # end def default_scope(options = {}) - self.default_scoping << { :find => options, :create => options[:conditions].is_a?(Hash) ? options[:conditions] : {} } + self.default_scoping << construct_finder_arel(options) end # Test whether the given method and optional key are scoped. def scoped?(method, key = nil) #:nodoc: - if current_scoped_methods && (scope = current_scoped_methods[method]) - !key || !scope[key].nil? + case method + when :create + current_scoped_methods.send(:scope_for_create).present? if current_scoped_methods end end # Retrieve the scope for the given method and optional key. def scope(method, key = nil) #:nodoc: - if current_scoped_methods && (scope = current_scoped_methods[method]) - key ? scope[key] : scope + case method + when :create + current_scoped_methods.send(:scope_for_create) if current_scoped_methods end end diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index 20d287faeb..bbb5922e0e 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -46,19 +46,19 @@ module ActiveRecord def count(*args) case args.size when 0 - construct_calculation_arel.count + construct_calculation_arel({}, current_scoped_methods).count when 1 if args[0].is_a?(Hash) options = args[0] distinct = options.has_key?(:distinct) ? options.delete(:distinct) : false - construct_calculation_arel(options).count(options[:select], :distinct => distinct) + construct_calculation_arel(options, current_scoped_methods).count(options[:select], :distinct => distinct) else - construct_calculation_arel.count(args[0]) + construct_calculation_arel({}, current_scoped_methods).count(args[0]) end when 2 column_name, options = args distinct = options.has_key?(:distinct) ? options.delete(:distinct) : false - construct_calculation_arel(options).count(column_name, :distinct => distinct) + construct_calculation_arel(options, current_scoped_methods).count(column_name, :distinct => distinct) else raise ArgumentError, "Unexpected parameters passed to count(): #{args.inspect}" end @@ -141,7 +141,7 @@ module ActiveRecord # Person.minimum(:age, :having => 'min(age) > 17', :group => :last_name) # Selects the minimum age for any family without any minors # Person.sum("2 * age") def calculate(operation, column_name, options = {}) - construct_calculation_arel(options).calculate(operation, column_name, options.slice(:distinct)) + construct_calculation_arel(options, current_scoped_methods).calculate(operation, column_name, options.slice(:distinct)) rescue ThrowResult 0 end @@ -151,49 +151,74 @@ module ActiveRecord options.assert_valid_keys(CALCULATIONS_OPTIONS) end - def construct_calculation_arel(options = {}) + def construct_calculation_arel(options = {}, merge_with_relation = nil) validate_calculation_options(options) options = options.except(:distinct) - scope = scope(:find) - includes = merge_includes(scope ? scope[:include] : [], options[:include]) + includes = merge_includes(merge_with_relation ? merge_with_relation.includes_values : [], options[:include]) if includes.any? - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, includes, construct_join(options[:joins], scope)) - construct_calculation_arel_with_included_associations(options, join_dependency) + merge_with_joins = merge_with_relation ? merge_with_relation.joins_values : [] + joins = (merge_with_joins + Array.wrap(options[:joins])).uniq + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, includes, construct_join(joins, nil)) + construct_calculation_arel_with_included_associations(options, join_dependency, merge_with_relation) else - active_relation. - joins(construct_join(options[:joins], scope)). - from((scope && scope[:from]) || options[:from]). - where(construct_conditions(options[:conditions], scope)). + relation = active_relation. + joins(options[:joins]). + where(options[:conditions]). order(options[:order]). limit(options[:limit]). offset(options[:offset]). group(options[:group]). - having(options[:having]). - select(options[:select] || (scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))) + having(options[:having]) + + if merge_with_relation + relation = merge_with_relation.except(:select, :order, :limit, :offset, :group, :from).merge(relation) + else + relation = relation.where(type_condition) if finder_needs_type_condition? + end + + from = merge_with_relation.from_value if merge_with_relation && merge_with_relation.from_value.present? + from = options[:from] if from.blank? && options[:from].present? + relation = relation.from(from) + + select = options[:select].presence || (merge_with_relation ? merge_with_relation.select_values.join(", ") : nil) + relation = relation.select(select) + + relation end end - def construct_calculation_arel_with_included_associations(options, join_dependency) - scope = scope(:find) - + def construct_calculation_arel_with_included_associations(options, join_dependency, merge_with_relation = nil) relation = active_relation for association in join_dependency.join_associations relation = association.join_relation(relation) end - relation = relation.joins(construct_join(options[:joins], scope)). + if merge_with_relation + relation.joins_values = (merge_with_relation.joins_values + relation.joins_values).uniq + relation.where_values = merge_with_relation.where_values + + merge_limit = merge_with_relation.taken + else + relation = relation.where(type_condition) if finder_needs_type_condition? + end + + relation = relation.joins(options[:joins]). select(column_aliases(join_dependency)). group(options[:group]). having(options[:having]). order(options[:order]). - where(construct_conditions(options[:conditions], scope)). - from((scope && scope[:from]) || options[:from]) + where(options[:conditions]). + from(options[:from]) + + + if !using_limitable_reflections?(join_dependency.reflections) && (merge_limit || options[:limit]) + relation = relation.where(construct_arel_limited_ids_condition(options, join_dependency)) + end - relation = relation.where(construct_arel_limited_ids_condition(options, join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) - relation = relation.limit(construct_limit(options[:limit], scope)) if using_limitable_reflections?(join_dependency.reflections) + relation = relation.limit(options[:limit] || merge_limit) if using_limitable_reflections?(join_dependency.reflections) relation end diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index f63b249241..47b69dec62 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -26,10 +26,12 @@ module ActiveRecord if options.present? Scope.new(self, options, &block) else - unless scoped?(:find) + current_scope = current_scoped_methods + + unless current_scope finder_needs_type_condition? ? active_relation.where(type_condition) : active_relation.spawn else - construct_finder_arel + construct_finder_arel({}, current_scoped_methods) end end end diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 33127194b0..8a86d2e60a 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -47,17 +47,20 @@ module ActiveRecord @records = if find_with_associations begin - @klass.send(:find_with_associations, { - :select => arel.send(:select_clauses).join(', '), + options = { + :select => @select_values.any? ? @select_values.join(", ") : nil, :joins => arel.joins(arel), - :group => arel.send(:group_clauses).join(', '), + :group => @group_values.any? ? @group_values.join(", ") : nil, :order => order_clause, :conditions => where_clause, :limit => arel.taken, :offset => arel.skipped, :from => (arel.send(:from_clauses) if arel.send(:sources).present?) - }, - ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, @eager_load_values + @includes_values, nil)) + } + + including = (@eager_load_values + @includes_values).uniq + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, including, nil) + @klass.send(:find_with_associations, options, join_dependency) rescue ThrowResult [] end @@ -161,7 +164,7 @@ module ActiveRecord end def with_create_scope - @klass.send(:with_scope, :create => scope_for_create) { yield } + @klass.send(:with_scope, :create => scope_for_create, :find => {}) { yield } end def scope_for_create diff --git a/activerecord/lib/active_record/relation/calculation_methods.rb b/activerecord/lib/active_record/relation/calculation_methods.rb index e6f62ee49a..2477481ec8 100644 --- a/activerecord/lib/active_record/relation/calculation_methods.rb +++ b/activerecord/lib/active_record/relation/calculation_methods.rb @@ -40,7 +40,7 @@ module ActiveRecord distinct = options[:distinct] || distinct column_name = :all if column_name.blank? && operation == "count" - if arel.send(:groupings).any? + if @group_values.any? return execute_grouped_calculation(operation, column_name) else return execute_simple_calculation(operation, column_name, distinct) @@ -63,7 +63,7 @@ module ActiveRecord end def execute_grouped_calculation(operation, column_name) #:nodoc: - group_attr = arel.send(:groupings).first.value + group_attr = @group_values.first association = @klass.reflect_on_association(group_attr.to_sym) associated = association && association.macro == :belongs_to # only count belongs_to associations group_field = associated ? association.primary_key_name : group_attr @@ -106,7 +106,6 @@ module ActiveRecord column_name = :all # Handles count(), count(:column), count(:distinct => true), count(:column, :distinct => true) - # TODO : relation.projections only works when .select() was last in the chain. Fix it! case args.size when 0 select = get_projection_name_from_chained_relations diff --git a/activerecord/lib/active_record/relation/query_methods.rb b/activerecord/lib/active_record/relation/query_methods.rb index 7ceec40954..a3ac58bc81 100644 --- a/activerecord/lib/active_record/relation/query_methods.rb +++ b/activerecord/lib/active_record/relation/query_methods.rb @@ -10,8 +10,20 @@ module ActiveRecord def #{query_method}(*args) spawn.tap do |new_relation| new_relation.#{query_method}_values ||= [] - value = args.size > 1 ? [args] : Array.wrap(args) - new_relation.#{query_method}_values += value + value = Array.wrap(args.flatten).reject {|x| x.blank? } + new_relation.#{query_method}_values += value if value.present? + end + end + CEVAL + end + + [:where, :having].each do |query_method| + class_eval <<-CEVAL + def #{query_method}(*args) + spawn.tap do |new_relation| + new_relation.#{query_method}_values ||= [] + value = build_where(*args) + new_relation.#{query_method}_values += [*value] if value.present? end end CEVAL @@ -58,51 +70,83 @@ module ActiveRecord def build_arel arel = table - @joins_values.each do |j| - next if j.blank? + joined_associations = [] + association_joins = [] + + joins = @joins_values.map {|j| j.respond_to?(:strip) ? j.strip : j}.uniq + + # Build association joins first + joins.each do |join| + association_joins << join if [Hash, Array, Symbol].include?(join.class) && !@klass.send(:array_of_strings?, join) + end + + if association_joins.any? + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(@klass, association_joins.uniq, nil) + to_join = [] + + join_dependency.join_associations.each do |association| + if (association_relation = association.relation).is_a?(Array) + to_join << [association_relation.first, association.association_join.first] + to_join << [association_relation.last, association.association_join.last] + else + to_join << [association_relation, association.association_join] + end + end + + to_join.each do |tj| + unless joined_associations.detect {|ja| ja[0] == tj[0] && ja[1] == tj[1] } + joined_associations << tj + arel = arel.join(tj[0]).on(*tj[1]) + end + end + end + + joins.each do |join| + next if join.blank? @implicit_readonly = true - case j + case join when Relation::JoinOperation - arel = arel.join(j.relation, j.join_class).on(j.on) + arel = arel.join(join.relation, join.join_class).on(*join.on) when Hash, Array, Symbol - if @klass.send(:array_of_strings?, j) - arel = arel.join(j.join(' ')) - else - arel = arel.join(@klass.send(:build_association_joins, j)) + if @klass.send(:array_of_strings?, join) + join_string = join.join(' ') + arel = arel.join(join_string) end else - arel = arel.join(j) + arel = arel.join(join) end end - @where_values.each do |where| - if conditions = build_where(where) - arel = conditions.is_a?(String) ? arel.where(conditions) : arel.where(*conditions) - end + @where_values.uniq.each do |w| + arel = w.is_a?(String) ? arel.where(w) : arel.where(*w) end - @having_values.each do |where| - if conditions = build_where(where) - arel = conditions.is_a?(String) ? arel.having(conditions) : arel.having(*conditions) - end + @having_values.uniq.each do |h| + arel = h.is_a?(String) ? arel.having(h) : arel.having(*h) end arel = arel.take(@limit_value) if @limit_value.present? arel = arel.skip(@offset_value) if @offset_value.present? - @group_values.each do |g| + @group_values.uniq.each do |g| arel = arel.group(g) if g.present? end - @order_values.each do |o| + @order_values.uniq.each do |o| arel = arel.order(o) if o.present? end - @select_values.each do |s| - @implicit_readonly = false - arel = arel.project(s) if s.present? + selects = @select_values.uniq + + if selects.present? + selects.each do |s| + @implicit_readonly = false + arel = arel.project(s) if s.present? + end + elsif joins.present? + arel = arel.project(@klass.quoted_table_name + '.*') end arel = arel.from(@from_value) if @from_value.present? @@ -120,7 +164,7 @@ module ActiveRecord def build_where(*args) return if args.blank? - builder = PredicateBuilder.new(Arel::Sql::Engine.new(@klass)) + builder = PredicateBuilder.new(table.engine) conditions = if [String, Array].include?(args.first.class) merged = @klass.send(:merge_conditions, args.size > 1 ? Array.wrap(args) : args.first) diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 66eae69d92..a248c72715 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -19,21 +19,19 @@ module ActiveRecord merged_relation = spawn.eager_load(r.eager_load_values).preload(r.preload_values).includes(r.includes_values) - merged_relation.readonly_value = r.readonly_value unless merged_relation.readonly_value - merged_relation.limit_value = r.limit_value unless merged_relation.limit_value + merged_relation.readonly_value = r.readonly_value unless r.readonly_value.nil? + merged_relation.limit_value = r.limit_value if r.limit_value.present? merged_relation.lock_value = r.lock_value unless merged_relation.lock_value + merged_relation.offset_value = r.offset_value if r.offset_value.present? merged_relation = merged_relation. joins(r.joins_values). group(r.group_values). - offset(r.offset_value). select(r.select_values). from(r.from_value). having(r.having_values) - relation_order = r.order_values - merged_order = relation_order.present? ? relation_order : order_values - merged_relation.order_values = merged_order + merged_relation.order_values = Array.wrap(order_values) + Array.wrap(r.order_values) merged_relation.create_with_value = @create_with_value @@ -50,7 +48,7 @@ module ActiveRecord merged_wheres = merged_wheres.reject {|p| p.is_a?(Arel::Predicates::Equality) && p.operand1.name == w.operand1.name } end - merged_wheres << w + merged_wheres += [w] end merged_relation.where_values = merged_wheres @@ -74,5 +72,21 @@ module ActiveRecord result end + def only(*onlies) + result = Relation.new(@klass, table) + + onlies.each do |only| + if (Relation::ASSOCIATION_METHODS + Relation::MULTI_VALUE_METHODS).include?(only) + result.send(:"#{only}_values=", send(:"#{only}_values")) + elsif Relation::SINGLE_VALUE_METHODS.include?(only) + result.send(:"#{only}_value=", send(:"#{only}_value")) + else + raise "Invalid argument : #{only}" + end + end + + result + end + end end -- cgit v1.2.3 From 3da29f647bad5e79c90721ac23658940abddd27c Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 01:42:01 +0530 Subject: Remove AR#scope() method --- activerecord/lib/active_record/base.rb | 33 ++++++++---------------------- activerecord/lib/active_record/relation.rb | 18 ++++++++-------- 2 files changed, 17 insertions(+), 34 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 7e4ee4b840..2eb8700bd7 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -642,7 +642,6 @@ module ActiveRecord #:nodoc: # end def find(*args) options = args.extract_options! - set_readonly_option!(options) relation = construct_finder_arel(options, current_scoped_methods) @@ -815,7 +814,7 @@ module ActiveRecord #:nodoc: # # Delete multiple rows # Todo.delete([2,3,4]) def delete(id_or_array) - active_relation.where(construct_conditions(nil, scope(:find))).delete(id_or_array) + active_relation.where(construct_conditions(nil, current_scoped_methods)).delete(id_or_array) end # Destroy an object (or multiple objects) that has the given id, the object is instantiated first, @@ -938,7 +937,7 @@ module ActiveRecord #:nodoc: # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) - active_relation.where(construct_conditions(conditions, scope(:find))).delete_all + active_relation.where(construct_conditions(conditions, current_scoped_methods)).delete_all end # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part. @@ -1842,7 +1841,6 @@ module ActiveRecord #:nodoc: if f = method_scoping[:find] f.assert_valid_keys(VALID_FIND_OPTIONS) - set_readonly_option! f end relation = construct_finder_arel(method_scoping[:find] || {}) @@ -1913,14 +1911,6 @@ module ActiveRecord #:nodoc: end end - # Retrieve the scope for the given method and optional key. - def scope(method, key = nil) #:nodoc: - case method - when :create - current_scoped_methods.send(:scope_for_create) if current_scoped_methods - end - end - def scoped_methods #:nodoc: Thread.current[:"#{self}_scoped_methods"] ||= self.default_scoping.dup end @@ -2129,18 +2119,6 @@ module ActiveRecord #:nodoc: options.assert_valid_keys(VALID_FIND_OPTIONS) end - def set_readonly_option!(options) #:nodoc: - # Inherit :readonly from finder scope if set. Otherwise, - # if :joins is not blank then :readonly defaults to true. - unless options.has_key?(:readonly) - if scoped_readonly = scope(:find, :readonly) - options[:readonly] = scoped_readonly - elsif !options[:joins].blank? && !options[:select] - options[:readonly] = true - end - end - end - def encode_quoted_value(value) #:nodoc: quoted_value = connection.quote(value) quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) " @@ -2159,7 +2137,12 @@ module ActiveRecord #:nodoc: @new_record = true ensure_proper_type self.attributes = attributes unless attributes.nil? - self.class.send(:scope, :create).each { |att,value| self.send("#{att}=", value) } if self.class.send(:scoped?, :create) + + if scope = self.class.send(:current_scoped_methods) + create_with = scope.scope_for_create + create_with.each { |att,value| self.send("#{att}=", value) } if create_with + end + result = yield self if block_given? _run_initialize_callbacks result diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index 8a86d2e60a..85bf878416 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -142,6 +142,15 @@ module ActiveRecord @to_sql ||= arel.to_sql end + def scope_for_create + @scope_for_create ||= begin + @create_with_value || wheres.inject({}) do |hash, where| + hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality) + hash + end + end + end + protected def method_missing(method, *args, &block) @@ -167,15 +176,6 @@ module ActiveRecord @klass.send(:with_scope, :create => scope_for_create, :find => {}) { yield } end - def scope_for_create - @scope_for_create ||= begin - @create_with_value || wheres.inject({}) do |hash, where| - hash[where.operand1.name] = where.operand2.value if where.is_a?(Arel::Predicates::Equality) - hash - end - end - end - def where_clause(join_string = " AND ") arel.send(:where_clauses).join(join_string) end -- cgit v1.2.3 From c5f3811a496065dbac6a165a85dca92a751a5ef0 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 02:35:53 +0530 Subject: Model.delete should just use scoped.delete --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 2eb8700bd7..a7936c18a3 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -814,7 +814,7 @@ module ActiveRecord #:nodoc: # # Delete multiple rows # Todo.delete([2,3,4]) def delete(id_or_array) - active_relation.where(construct_conditions(nil, current_scoped_methods)).delete(id_or_array) + scoped.delete(id_or_array) end # Destroy an object (or multiple objects) that has the given id, the object is instantiated first, -- cgit v1.2.3 From 6c997c3c39094aa5df773d0808023031507ca627 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 02:38:19 +0530 Subject: Simplify Model.delete_all --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index a7936c18a3..4bacf2390b 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -937,7 +937,7 @@ module ActiveRecord #:nodoc: # Both calls delete the affected posts all at once with a single DELETE statement. If you need to destroy dependent # associations or call your before_* or +after_destroy+ callbacks, use the +destroy_all+ method instead. def delete_all(conditions = nil) - active_relation.where(construct_conditions(conditions, current_scoped_methods)).delete_all + where(conditions).delete_all end # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part. -- cgit v1.2.3 From 951e485b888c15fc651439686676a4e8956d0255 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 02:56:13 +0530 Subject: Remove construct_conditions --- activerecord/lib/active_record/base.rb | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 4bacf2390b..19a80a335e 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -871,10 +871,8 @@ module ActiveRecord #:nodoc: def update_all(updates, conditions = nil, options = {}) relation = active_relation - if conditions = construct_conditions(conditions, nil) - relation = relation.where(Arel::SqlLiteral.new(conditions)) - end - + relation = relation.where(conditions) if conditions + relation = relation.where(type_condition) if finder_needs_type_condition? relation = relation.limit(options[:limit]) if options[:limit].present? relation = relation.order(options[:order]) if options[:order].present? @@ -1635,13 +1633,6 @@ module ActiveRecord #:nodoc: offset end - def construct_conditions(conditions, scope) - conditions = [conditions] - conditions << scope[:conditions] if scope - conditions << type_condition if finder_needs_type_condition? - merge_conditions(*conditions) - end - # Merges includes so that the result is a valid +include+ def merge_includes(first, second) (Array.wrap(first) + Array.wrap(second)).uniq -- cgit v1.2.3 From 21ce8eac9b9bd8e14e3396caf2e30751889e26cb Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 03:37:04 +0530 Subject: Remove stale construct_* methods --- activerecord/lib/active_record/base.rb | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 19a80a335e..ab78248608 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1609,30 +1609,6 @@ module ActiveRecord #:nodoc: end end - def construct_order(order, scope) - orders = [] - - scoped_order = scope[:order] if scope - if order - orders << order - orders << scoped_order if scoped_order && scoped_order != order - elsif scoped_order - orders << scoped_order - end - - orders.reject {|o| o.blank?} - end - - def construct_limit(limit, scope) - limit ||= scope[:limit] if scope - limit - end - - def construct_offset(offset, scope) - offset ||= scope[:offset] if scope - offset - end - # Merges includes so that the result is a valid +include+ def merge_includes(first, second) (Array.wrap(first) + Array.wrap(second)).uniq -- cgit v1.2.3 From 2ee130abec222342c6f7ade3b651840dc89c1bcd Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 03:50:51 +0530 Subject: Remove scope related code from construct_join --- activerecord/lib/active_record/base.rb | 26 ++++++-------------------- activerecord/lib/active_record/calculations.rb | 2 +- 2 files changed, 7 insertions(+), 21 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index ab78248608..6b4a1d0071 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1593,17 +1593,16 @@ module ActiveRecord #:nodoc: relation end - def construct_join(joins, scope) - merged_joins = scope && scope[:joins] && joins ? merge_joins(scope[:joins], joins) : (joins || scope && scope[:joins]) - case merged_joins + def construct_join(joins) + case joins when Symbol, Hash, Array - if array_of_strings?(merged_joins) - merged_joins.join(' ') + " " + if array_of_strings?(joins) + joins.join(' ') + " " else - build_association_joins(merged_joins) + build_association_joins(joins) end when String - " #{merged_joins} " + " #{joins} " else "" end @@ -1614,19 +1613,6 @@ module ActiveRecord #:nodoc: (Array.wrap(first) + Array.wrap(second)).uniq end - def merge_joins(*joins) - if joins.any?{|j| j.is_a?(String) || array_of_strings?(j) } - joins = joins.collect do |join| - join = [join] if join.is_a?(String) - join = build_association_joins(join) unless array_of_strings?(join) - join - end - joins.flatten.map{|j| j.strip}.uniq - else - joins.collect{|j| Array.wrap(j)}.flatten.uniq - end - end - def build_association_joins(joins) join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, joins, nil) relation = active_relation.table diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index bbb5922e0e..cd7bd185c5 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -160,7 +160,7 @@ module ActiveRecord if includes.any? merge_with_joins = merge_with_relation ? merge_with_relation.joins_values : [] joins = (merge_with_joins + Array.wrap(options[:joins])).uniq - join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, includes, construct_join(joins, nil)) + join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, includes, construct_join(joins)) construct_calculation_arel_with_included_associations(options, join_dependency, merge_with_relation) else relation = active_relation. -- cgit v1.2.3 From 99dd107760191b834d0a97e89c5ae991c088a4a8 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 04:10:35 +0530 Subject: Remove unused default_select --- activerecord/lib/active_record/base.rb | 8 -------- 1 file changed, 8 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 6b4a1d0071..38a012202d 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1562,14 +1562,6 @@ module ActiveRecord #:nodoc: end end - def default_select(qualified) - if qualified - quoted_table_name + '.*' - else - '*' - end - end - def construct_finder_arel(options = {}, scope = nil) validate_find_options(options) -- cgit v1.2.3 From 2d0bc08a7e1a0ecee577674deba2c0a17fe665e4 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 04:17:23 +0530 Subject: Make type_condition return Arel predicate and not a string condition --- .../lib/active_record/associations/through_association_scope.rb | 2 +- activerecord/lib/active_record/base.rb | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations/through_association_scope.rb b/activerecord/lib/active_record/associations/through_association_scope.rb index 1001199daa..6f0f698f1e 100644 --- a/activerecord/lib/active_record/associations/through_association_scope.rb +++ b/activerecord/lib/active_record/associations/through_association_scope.rb @@ -144,7 +144,7 @@ module ActiveRecord end def build_sti_condition - @reflection.through_reflection.klass.send(:type_condition) + @reflection.through_reflection.klass.send(:type_condition).to_sql end alias_method :sql_conditions, :conditions diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 38a012202d..4a53a7bd22 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1622,14 +1622,12 @@ module ActiveRecord #:nodoc: o.is_a?(Array) && o.all?{|obj| obj.is_a?(String)} end - def type_condition(table_alias = nil) - table = Arel::Table.new(table_name, :engine => active_relation_engine, :as => table_alias) - - sti_column = table[inheritance_column] + def type_condition + sti_column = active_relation_table[inheritance_column] condition = sti_column.eq(sti_name) subclasses.each{|subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) } - condition.to_sql + condition end # Guesses the table name, but does not decorate it with prefix and suffix information. -- cgit v1.2.3 From 81cd11259c52544dd1bc401b7097e4a0e5d34fe6 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 04:38:16 +0530 Subject: Remove protected method Class#scoped? --- activerecord/lib/active_record/base.rb | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 4a53a7bd22..6ecd5061d4 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1846,14 +1846,6 @@ module ActiveRecord #:nodoc: self.default_scoping << construct_finder_arel(options) end - # Test whether the given method and optional key are scoped. - def scoped?(method, key = nil) #:nodoc: - case method - when :create - current_scoped_methods.send(:scope_for_create).present? if current_scoped_methods - end - end - def scoped_methods #:nodoc: Thread.current[:"#{self}_scoped_methods"] ||= self.default_scoping.dup end @@ -2111,7 +2103,11 @@ module ActiveRecord #:nodoc: @attributes_cache = {} @new_record = true ensure_proper_type - self.class.send(:scope, :create).each { |att, value| self.send("#{att}=", value) } if self.class.send(:scoped?, :create) + + if scope = self.class.send(:current_scoped_methods) + create_with = scope.scope_for_create + create_with.each { |att,value| self.send("#{att}=", value) } if create_with + end end # Returns a String, which Action Pack uses for constructing an URL to this -- cgit v1.2.3 From 717a2941e15b32d07cc456bb0d81742ecfc5b4a3 Mon Sep 17 00:00:00 2001 From: Chris Hapgood Date: Fri, 15 Jan 2010 18:34:49 -0500 Subject: Fix #microseconds conversion and #fast_string_to_time * Use direct integer parsing in #fast_string_to_time to avoid convoluted conversions and errors due to truncation. * Use Float#round in #microseconds to avoid truncation errors. Signed-off-by: Michael Koziarski --- .../connection_adapters/abstract/schema_definitions.rb | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') 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 520f3c8c0c..5e8a01644d 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -13,6 +13,7 @@ module ActiveRecord module Format ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/ ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/ + NEW_ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(?:\.(\d+))?\z/ end attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale @@ -167,10 +168,11 @@ module ActiveRecord end protected - # '0.123456' -> 123456 - # '1.123456' -> 123456 + # Rational(123456, 1_000_000) -> 123456 + # The sec_fraction component returned by Date._parse is a Rational fraction of a second or nil + # NB: This method is optimized for performance by immediately converting away from Rational. def microseconds(time) - ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i + ((time[:sec_fraction].to_f % 1) * 1_000_000).round end def new_date(year, mon, mday) @@ -194,9 +196,8 @@ module ActiveRecord # Doesn't handle time zones. def fast_string_to_time(string) - if string =~ Format::ISO_DATETIME - microsec = ($7.to_f * 1_000_000).to_i - new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec + if md = Format::NEW_ISO_DATETIME.match(string) + new_time *md.to_a[1..7].map(&:to_i) end end -- cgit v1.2.3 From 5a52523a800c8a57d1ad80ad3a0ba81711cce38e Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 16:02:30 +0530 Subject: Rename active_relation_engine -> arel_engine and active_relation_table -> arel_table --- activerecord/lib/active_record/associations.rb | 16 ++++++++-------- activerecord/lib/active_record/base.rb | 20 ++++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index d6ecd0a5e3..b52b298027 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1961,7 +1961,7 @@ module ActiveRecord class JoinBase # :nodoc: attr_reader :active_record, :table_joins - delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :active_relation_engine, :to => :active_record + delegate :table_name, :column_names, :primary_key, :reflections, :sanitize_sql, :arel_engine, :to => :active_record def initialize(active_record, joins = nil) @active_record = active_record @@ -2037,12 +2037,12 @@ module ActiveRecord def association_join return @join if @join - aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => active_relation_engine) - parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => active_relation_engine) + aliased_table = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine) + parent_table = Arel::Table.new(parent.table_name, :as => parent.aliased_table_name, :engine => arel_engine) @join = case reflection.macro when :has_and_belongs_to_many - join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => active_relation_engine) + join_table = Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine) fk = options[:foreign_key] || reflection.active_record.to_s.foreign_key klass_fk = options[:association_foreign_key] || klass.to_s.foreign_key @@ -2052,7 +2052,7 @@ module ActiveRecord ] when :has_many, :has_one if reflection.options[:through] - join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => active_relation_engine) + join_table = Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine) jt_foreign_key = jt_as_extra = jt_source_extra = jt_sti_extra = nil first_key = second_key = as_extra = nil @@ -2121,12 +2121,12 @@ module ActiveRecord end def relation - aliased = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => active_relation_engine) + aliased = Arel::Table.new(table_name, :as => @aliased_table_name, :engine => arel_engine) if reflection.macro == :has_and_belongs_to_many - [Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => active_relation_engine), aliased] + [Arel::Table.new(options[:join_table], :as => aliased_join_table_name, :engine => arel_engine), aliased] elsif reflection.options[:through] - [Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => active_relation_engine), aliased] + [Arel::Table.new(through_reflection.klass.table_name, :as => aliased_join_table_name, :engine => arel_engine), aliased] else aliased end diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 6ecd5061d4..e84130f42e 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1389,7 +1389,7 @@ module ActiveRecord #:nodoc: def reset_column_information undefine_attribute_methods @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil - @active_relation = @active_relation_engine = nil + @active_relation = @arel_engine = nil end def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: @@ -1503,19 +1503,19 @@ module ActiveRecord #:nodoc: end def active_relation - @active_relation ||= Relation.new(self, active_relation_table) + @active_relation ||= Relation.new(self, arel_table) end - def active_relation_table(table_name_alias = nil) - Arel::Table.new(table_name, :as => table_name_alias, :engine => active_relation_engine) + def arel_table(table_name_alias = nil) + Arel::Table.new(table_name, :as => table_name_alias, :engine => arel_engine) end - def active_relation_engine - @active_relation_engine ||= begin + def arel_engine + @arel_engine ||= begin if self == ActiveRecord::Base Arel::Table.engine else - connection_handler.connection_pools[name] ? Arel::Sql::Engine.new(self) : superclass.active_relation_engine + connection_handler.connection_pools[name] ? Arel::Sql::Engine.new(self) : superclass.arel_engine end end end @@ -1623,7 +1623,7 @@ module ActiveRecord #:nodoc: end def type_condition - sti_column = active_relation_table[inheritance_column] + sti_column = arel_table[inheritance_column] condition = sti_column.eq(sti_name) subclasses.each{|subclass| condition = condition.or(sti_column.eq(subclass.sti_name)) } @@ -1963,8 +1963,8 @@ module ActiveRecord #:nodoc: def sanitize_sql_hash_for_conditions(attrs, default_table_name = self.table_name) attrs = expand_hash_conditions_for_aggregates(attrs) - table = Arel::Table.new(self.table_name, :engine => active_relation_engine, :as => default_table_name) - builder = PredicateBuilder.new(active_relation_engine) + table = Arel::Table.new(self.table_name, :engine => arel_engine, :as => default_table_name) + builder = PredicateBuilder.new(arel_engine) builder.build_from_hash(attrs, table).map(&:to_sql).join(' AND ') end alias_method :sanitize_sql_hash, :sanitize_sql_hash_for_conditions -- cgit v1.2.3 From 61e831564aa4b22983646ecdc15d43991bc0e522 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 21:21:20 +0530 Subject: Add Relation#apply_finder_options for applying old finder options --- activerecord/lib/active_record/base.rb | 30 +--------------------- .../lib/active_record/relation/spawn_methods.rb | 23 +++++++++++++++++ 2 files changed, 24 insertions(+), 29 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index e84130f42e..5bd24ac3eb 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1563,24 +1563,8 @@ module ActiveRecord #:nodoc: end def construct_finder_arel(options = {}, scope = nil) - validate_find_options(options) - - relation = active_relation. - joins(options[:joins]). - where(options[:conditions]). - select(options[:select]). - group(options[:group]). - having(options[:having]). - order(options[:order]). - limit(options[:limit]). - offset(options[:offset]). - from(options[:from]). - includes(options[:include]) - + relation = active_relation.apply_finder_options(options) relation = relation.where(type_condition) if finder_needs_type_condition? - relation = relation.lock(options[:lock]) if options[:lock].present? - relation = relation.readonly(options[:readonly]) if options.has_key?(:readonly) - relation = scope.merge(relation) if scope relation end @@ -1781,11 +1765,6 @@ module ActiveRecord #:nodoc: end method_scoping.assert_valid_keys([ :find, :create ]) - - if f = method_scoping[:find] - f.assert_valid_keys(VALID_FIND_OPTIONS) - end - relation = construct_finder_arel(method_scoping[:find] || {}) if current_scoped_methods && current_scoped_methods.create_with_value && method_scoping[:create] @@ -2047,13 +2026,6 @@ module ActiveRecord #:nodoc: end end - VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, - :order, :select, :readonly, :group, :having, :from, :lock ] - - def validate_find_options(options) #:nodoc: - options.assert_valid_keys(VALID_FIND_OPTIONS) - end - def encode_quoted_value(value) #:nodoc: quoted_value = connection.quote(value) quoted_value = "'#{quoted_value[1..-2].gsub(/\'/, "\\\\'")}'" if quoted_value.include?("\\\'") # (for ruby mode) " diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index a248c72715..59cfca85ae 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -88,5 +88,28 @@ module ActiveRecord result end + VALID_FIND_OPTIONS = [ :conditions, :include, :joins, :limit, :offset, + :order, :select, :readonly, :group, :having, :from, :lock ] + + def apply_finder_options(options) + options.assert_valid_keys(VALID_FIND_OPTIONS) + + relation = joins(options[:joins]). + where(options[:conditions]). + select(options[:select]). + group(options[:group]). + having(options[:having]). + order(options[:order]). + limit(options[:limit]). + offset(options[:offset]). + from(options[:from]). + includes(options[:include]) + + relation = relation.lock(options[:lock]) if options[:lock].present? + relation = relation.readonly(options[:readonly]) if options.has_key?(:readonly) + + relation + end + end end -- cgit v1.2.3 From f80be3ea0fa2bb3416e180901d441b0834001c7f Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 22:05:01 +0530 Subject: Use Relation#apply_finder_options from calculations --- activerecord/lib/active_record/calculations.rb | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index cd7bd185c5..a79ceb1d05 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -163,14 +163,7 @@ module ActiveRecord join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, includes, construct_join(joins)) construct_calculation_arel_with_included_associations(options, join_dependency, merge_with_relation) else - relation = active_relation. - joins(options[:joins]). - where(options[:conditions]). - order(options[:order]). - limit(options[:limit]). - offset(options[:offset]). - group(options[:group]). - having(options[:having]) + relation = active_relation.apply_finder_options(options.slice(:joins, :conditions, :order, :limit, :offset, :group, :having)) if merge_with_relation relation = merge_with_relation.except(:select, :order, :limit, :offset, :group, :from).merge(relation) @@ -205,14 +198,8 @@ module ActiveRecord relation = relation.where(type_condition) if finder_needs_type_condition? end - relation = relation.joins(options[:joins]). - select(column_aliases(join_dependency)). - group(options[:group]). - having(options[:having]). - order(options[:order]). - where(options[:conditions]). - from(options[:from]) - + relation = relation.apply_finder_options(options.slice(:joins, :group, :having, :order, :conditions, :from)). + select(column_aliases(join_dependency)) if !using_limitable_reflections?(join_dependency.reflections) && (merge_limit || options[:limit]) relation = relation.where(construct_arel_limited_ids_condition(options, join_dependency)) -- cgit v1.2.3 From cfdfd899262c79c37ac89e030f4d90c8f9868b50 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 22:14:10 +0530 Subject: Use new finder methods for association preloading --- .../lib/active_record/association_preload.rb | 33 +++++++++++----------- 1 file changed, 16 insertions(+), 17 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/association_preload.rb b/activerecord/lib/active_record/association_preload.rb index 9f7b2a60b2..a43c4d09d6 100644 --- a/activerecord/lib/active_record/association_preload.rb +++ b/activerecord/lib/active_record/association_preload.rb @@ -188,11 +188,11 @@ module ActiveRecord conditions << append_conditions(reflection, preload_options) associated_records = reflection.klass.with_exclusive_scope do - reflection.klass.find(:all, :conditions => [conditions, ids], - :include => options[:include], - :joins => "INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}", - :select => "#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id", - :order => options[:order]) + reflection.klass.where([conditions, ids]). + includes(options[:include]). + joins("INNER JOIN #{connection.quote_table_name options[:join_table]} t0 ON #{reflection.klass.quoted_table_name}.#{reflection.klass.primary_key} = t0.#{reflection.association_foreign_key}"). + select("#{options[:select] || table_name+'.*'}, t0.#{reflection.primary_key_name} as the_parent_record_id"). + order(options[:order]).to_a end set_association_collection_records(id_to_record_map, reflection.name, associated_records, 'the_parent_record_id') end @@ -327,6 +327,7 @@ module ActiveRecord table_name = klass.quoted_table_name primary_key = klass.primary_key column_type = klass.columns.detect{|c| c.name == primary_key}.type + ids = id_map.keys.map do |id| if column_type == :integer id.to_i @@ -336,15 +337,14 @@ module ActiveRecord id end end + conditions = "#{table_name}.#{connection.quote_column_name(primary_key)} #{in_or_equals_for_ids(ids)}" conditions << append_conditions(reflection, preload_options) + associated_records = klass.with_exclusive_scope do - klass.find(:all, :conditions => [conditions, ids], - :include => options[:include], - :select => options[:select], - :joins => options[:joins], - :order => options[:order]) + klass.where([conditions, ids]).apply_finder_options(options.slice(:include, :select, :joins, :order)).to_a end + set_association_single_records(id_map, reflection.name, associated_records, primary_key) end end @@ -363,13 +363,12 @@ module ActiveRecord conditions << append_conditions(reflection, preload_options) reflection.klass.with_exclusive_scope do - reflection.klass.find(:all, - :select => (preload_options[:select] || options[:select] || "#{table_name}.*"), - :include => preload_options[:include] || options[:include], - :conditions => [conditions, ids], - :joins => options[:joins], - :group => preload_options[:group] || options[:group], - :order => preload_options[:order] || options[:order]) + reflection.klass.select(preload_options[:select] || options[:select] || "#{table_name}.*"). + includes(preload_options[:include] || options[:include]). + where([conditions, ids]). + joins(options[:joins]). + group(preload_options[:group] || options[:group]). + order(preload_options[:order] || options[:order]) end end -- cgit v1.2.3 From 3968825f5ff6a75cb83400716d56ec10f261e41a Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 23:11:35 +0530 Subject: Make sure Model#active_relation always adds STI conditions if needed --- activerecord/lib/active_record/base.rb | 5 ++--- activerecord/lib/active_record/calculations.rb | 4 ---- activerecord/lib/active_record/named_scope.rb | 2 +- activerecord/lib/active_record/relation/spawn_methods.rb | 4 +++- 4 files changed, 6 insertions(+), 9 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 5bd24ac3eb..d0db9fadcd 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -872,7 +872,6 @@ module ActiveRecord #:nodoc: relation = active_relation relation = relation.where(conditions) if conditions - relation = relation.where(type_condition) if finder_needs_type_condition? relation = relation.limit(options[:limit]) if options[:limit].present? relation = relation.order(options[:order]) if options[:order].present? @@ -1389,7 +1388,7 @@ module ActiveRecord #:nodoc: def reset_column_information undefine_attribute_methods @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil - @active_relation = @arel_engine = nil + @arel_engine = @active_relation = @arel_engine = nil end def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: @@ -1504,6 +1503,7 @@ module ActiveRecord #:nodoc: def active_relation @active_relation ||= Relation.new(self, arel_table) + finder_needs_type_condition? ? @active_relation.where(type_condition) : @active_relation end def arel_table(table_name_alias = nil) @@ -1564,7 +1564,6 @@ module ActiveRecord #:nodoc: def construct_finder_arel(options = {}, scope = nil) relation = active_relation.apply_finder_options(options) - relation = relation.where(type_condition) if finder_needs_type_condition? relation = scope.merge(relation) if scope relation end diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index a79ceb1d05..ed4218807d 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -167,8 +167,6 @@ module ActiveRecord if merge_with_relation relation = merge_with_relation.except(:select, :order, :limit, :offset, :group, :from).merge(relation) - else - relation = relation.where(type_condition) if finder_needs_type_condition? end from = merge_with_relation.from_value if merge_with_relation && merge_with_relation.from_value.present? @@ -194,8 +192,6 @@ module ActiveRecord relation.where_values = merge_with_relation.where_values merge_limit = merge_with_relation.taken - else - relation = relation.where(type_condition) if finder_needs_type_condition? end relation = relation.apply_finder_options(options.slice(:joins, :group, :having, :order, :conditions, :from)). diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 47b69dec62..531419fd8e 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -29,7 +29,7 @@ module ActiveRecord current_scope = current_scoped_methods unless current_scope - finder_needs_type_condition? ? active_relation.where(type_condition) : active_relation.spawn + active_relation.spawn else construct_finder_arel({}, current_scoped_methods) end diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 59cfca85ae..33df8fd06c 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -94,7 +94,9 @@ module ActiveRecord def apply_finder_options(options) options.assert_valid_keys(VALID_FIND_OPTIONS) - relation = joins(options[:joins]). + relation = spawn + + relation = relation.joins(options[:joins]). where(options[:conditions]). select(options[:select]). group(options[:group]). -- cgit v1.2.3 From 07e41a83616966fef848797c3474eb7704a62794 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 23:17:14 +0530 Subject: No need to set @arel_engine to nil twice. Committed by mistake in 3968825f --- activerecord/lib/active_record/base.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index d0db9fadcd..acebf1e7fd 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1388,7 +1388,7 @@ module ActiveRecord #:nodoc: def reset_column_information undefine_attribute_methods @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil - @arel_engine = @active_relation = @arel_engine = nil + @arel_engine = @active_relation = nil end def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: -- cgit v1.2.3 From 7f8d4d3c4ed82a90f94251438d61d395a544026c Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sat, 16 Jan 2010 23:59:57 +0530 Subject: Get rid of Base#merge_includes --- activerecord/lib/active_record/base.rb | 5 ----- activerecord/lib/active_record/calculations.rb | 3 ++- 2 files changed, 2 insertions(+), 6 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index acebf1e7fd..bf42a5b221 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1583,11 +1583,6 @@ module ActiveRecord #:nodoc: end end - # Merges includes so that the result is a valid +include+ - def merge_includes(first, second) - (Array.wrap(first) + Array.wrap(second)).uniq - end - def build_association_joins(joins) join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, joins, nil) relation = active_relation.table diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index ed4218807d..f2de56ae5e 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -155,7 +155,8 @@ module ActiveRecord validate_calculation_options(options) options = options.except(:distinct) - includes = merge_includes(merge_with_relation ? merge_with_relation.includes_values : [], options[:include]) + merge_with_includes = merge_with_relation ? merge_with_relation.includes_values : [] + includes = (merge_with_includes + Array.wrap(options[:include])).uniq if includes.any? merge_with_joins = merge_with_relation ? merge_with_relation.joins_values : [] -- cgit v1.2.3 From 468cfcedd311a428339fa01e5f62b935f2995ec0 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 17 Jan 2010 02:08:42 +0530 Subject: Improve the error message for class mismatch on Relation#merge --- activerecord/lib/active_record/relation/spawn_methods.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 33df8fd06c..f4abaae43e 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -15,7 +15,9 @@ module ActiveRecord end def merge(r) - raise ArgumentError, "Cannot merge a #{r.klass.name} relation with #{@klass.name} relation" if r.klass != @klass + if r.klass != @klass + raise ArgumentError, "Cannot merge a #{r.klass.name}(##{r.klass.object_id}) relation with #{@klass.name}(##{@klass.object_id}) relation" + end merged_relation = spawn.eager_load(r.eager_load_values).preload(r.preload_values).includes(r.includes_values) -- cgit v1.2.3 From cd90dcb1bde5c411a55bcec97597a8fe22b56a5d Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 17 Jan 2010 02:59:42 +0530 Subject: Rename Model.active_relation to Model.unscoped --- activerecord/lib/active_record/associations.rb | 4 ++-- activerecord/lib/active_record/base.rb | 24 +++++++++++----------- activerecord/lib/active_record/calculations.rb | 4 ++-- .../lib/active_record/locking/optimistic.rb | 2 +- activerecord/lib/active_record/named_scope.rb | 2 +- .../active_record/relation/calculation_methods.rb | 4 ++-- .../lib/active_record/validations/uniqueness.rb | 2 +- 7 files changed, 21 insertions(+), 21 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb index b52b298027..3034e9d237 100755 --- a/activerecord/lib/active_record/associations.rb +++ b/activerecord/lib/active_record/associations.rb @@ -1703,7 +1703,7 @@ module ActiveRecord end def construct_finder_arel_with_included_associations(options, join_dependency) - relation = active_relation + relation = unscoped for association in join_dependency.join_associations relation = association.join_relation(relation) @@ -1754,7 +1754,7 @@ module ActiveRecord end def construct_finder_sql_for_association_limiting(options, join_dependency) - relation = active_relation + relation = unscoped for association in join_dependency.join_associations relation = association.join_relation(relation) diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index bf42a5b221..b0c9ed2548 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -869,7 +869,7 @@ module ActiveRecord #:nodoc: # # Update all books that match our conditions, but limit it to 5 ordered by date # Book.update_all "author = 'David'", "title LIKE '%Rails%'", :order => 'created_at', :limit => 5 def update_all(updates, conditions = nil, options = {}) - relation = active_relation + relation = unscoped relation = relation.where(conditions) if conditions relation = relation.limit(options[:limit]) if options[:limit].present? @@ -1388,7 +1388,7 @@ module ActiveRecord #:nodoc: def reset_column_information undefine_attribute_methods @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil - @arel_engine = @active_relation = nil + @arel_engine = @unscoped = nil end def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: @@ -1501,9 +1501,9 @@ module ActiveRecord #:nodoc: "(#{segments.join(') AND (')})" unless segments.empty? end - def active_relation - @active_relation ||= Relation.new(self, arel_table) - finder_needs_type_condition? ? @active_relation.where(type_condition) : @active_relation + def unscoped + @unscoped ||= Relation.new(self, arel_table) + finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped end def arel_table(table_name_alias = nil) @@ -1563,7 +1563,7 @@ module ActiveRecord #:nodoc: end def construct_finder_arel(options = {}, scope = nil) - relation = active_relation.apply_finder_options(options) + relation = unscoped.apply_finder_options(options) relation = scope.merge(relation) if scope relation end @@ -1585,7 +1585,7 @@ module ActiveRecord #:nodoc: def build_association_joins(joins) join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, joins, nil) - relation = active_relation.table + relation = unscoped.table join_dependency.join_associations.map { |association| if (association_relation = association.relation).is_a?(Array) [Arel::InnerJoin.new(relation, association_relation.first, *association.association_join.first).joins(relation), @@ -2193,7 +2193,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - self.class.active_relation.where(self.class.active_relation[self.class.primary_key].eq(id)).delete_all + self.class.unscoped.where(self.class.unscoped[self.class.primary_key].eq(id)).delete_all end @destroyed = true @@ -2480,7 +2480,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - self.class.active_relation.where(self.class.active_relation[self.class.primary_key].eq(id)).update(attributes_with_values) + self.class.unscoped.where(self.class.unscoped[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes @@ -2493,9 +2493,9 @@ module ActiveRecord #:nodoc: attributes_values = arel_attributes_values new_id = if attributes_values.empty? - self.class.active_relation.insert connection.empty_insert_statement_value + self.class.unscoped.insert connection.empty_insert_statement_value else - self.class.active_relation.insert attributes_values + self.class.unscoped.insert attributes_values end self.id ||= new_id @@ -2590,7 +2590,7 @@ module ActiveRecord #:nodoc: if value && ((self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))) || value.is_a?(Hash) || value.is_a?(Array)) value = value.to_yaml end - attrs[self.class.active_relation[name]] = value + attrs[self.class.unscoped[name]] = value end end end diff --git a/activerecord/lib/active_record/calculations.rb b/activerecord/lib/active_record/calculations.rb index f2de56ae5e..e4b3caab4e 100644 --- a/activerecord/lib/active_record/calculations.rb +++ b/activerecord/lib/active_record/calculations.rb @@ -164,7 +164,7 @@ module ActiveRecord join_dependency = ActiveRecord::Associations::ClassMethods::JoinDependency.new(self, includes, construct_join(joins)) construct_calculation_arel_with_included_associations(options, join_dependency, merge_with_relation) else - relation = active_relation.apply_finder_options(options.slice(:joins, :conditions, :order, :limit, :offset, :group, :having)) + relation = unscoped.apply_finder_options(options.slice(:joins, :conditions, :order, :limit, :offset, :group, :having)) if merge_with_relation relation = merge_with_relation.except(:select, :order, :limit, :offset, :group, :from).merge(relation) @@ -182,7 +182,7 @@ module ActiveRecord end def construct_calculation_arel_with_included_associations(options, join_dependency, merge_with_relation = nil) - relation = active_relation + relation = unscoped for association in join_dependency.join_associations relation = association.join_relation(relation) diff --git a/activerecord/lib/active_record/locking/optimistic.rb b/activerecord/lib/active_record/locking/optimistic.rb index f9e538c586..9fcdabdb44 100644 --- a/activerecord/lib/active_record/locking/optimistic.rb +++ b/activerecord/lib/active_record/locking/optimistic.rb @@ -78,7 +78,7 @@ module ActiveRecord attribute_names.uniq! begin - relation = self.class.active_relation + relation = self.class.unscoped affected_rows = relation.where( relation[self.class.primary_key].eq(quoted_id).and( diff --git a/activerecord/lib/active_record/named_scope.rb b/activerecord/lib/active_record/named_scope.rb index 531419fd8e..90fd700437 100644 --- a/activerecord/lib/active_record/named_scope.rb +++ b/activerecord/lib/active_record/named_scope.rb @@ -29,7 +29,7 @@ module ActiveRecord current_scope = current_scoped_methods unless current_scope - active_relation.spawn + unscoped.spawn else construct_finder_arel({}, current_scoped_methods) end diff --git a/activerecord/lib/active_record/relation/calculation_methods.rb b/activerecord/lib/active_record/relation/calculation_methods.rb index 2477481ec8..91de89e607 100644 --- a/activerecord/lib/active_record/relation/calculation_methods.rb +++ b/activerecord/lib/active_record/relation/calculation_methods.rb @@ -53,7 +53,7 @@ module ActiveRecord def execute_simple_calculation(operation, column_name, distinct) #:nodoc: column = if @klass.column_names.include?(column_name.to_s) - Arel::Attribute.new(@klass.active_relation, column_name) + Arel::Attribute.new(@klass.unscoped, column_name) else Arel::SqlLiteral.new(column_name == :all ? "*" : column_name.to_s) end @@ -77,7 +77,7 @@ module ActiveRecord select_statement = if operation == 'count' && column_name == :all "COUNT(*) AS count_all" else - Arel::Attribute.new(@klass.active_relation, column_name).send(operation).as(aggregate_alias).to_sql + Arel::Attribute.new(@klass.unscoped, column_name).send(operation).as(aggregate_alias).to_sql end select_statement << ", #{group_field} AS #{group_alias}" diff --git a/activerecord/lib/active_record/validations/uniqueness.rb b/activerecord/lib/active_record/validations/uniqueness.rb index 4ff851dfa1..e28808df98 100644 --- a/activerecord/lib/active_record/validations/uniqueness.rb +++ b/activerecord/lib/active_record/validations/uniqueness.rb @@ -12,7 +12,7 @@ module ActiveRecord def validate_each(record, attribute, value) finder_class = find_finder_class_for(record) - table = finder_class.active_relation + table = finder_class.unscoped table_name = record.class.quoted_table_name sql, params = mount_sql_and_params(finder_class, table_name, attribute, value) -- cgit v1.2.3 From 0ab30637dd5bc7536c5accd66b45ce0263134a14 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 17 Jan 2010 03:04:11 +0530 Subject: Revert "Fix #microseconds conversion and #fast_string_to_time" This reverts commit 717a2941e15b32d07cc456bb0d81742ecfc5b4a3. Bunch of failures when running postgresql tests. --- .../connection_adapters/abstract/schema_definitions.rb | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'activerecord/lib/active_record') 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 5e8a01644d..520f3c8c0c 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb @@ -13,7 +13,6 @@ module ActiveRecord module Format ISO_DATE = /\A(\d{4})-(\d\d)-(\d\d)\z/ ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(\.\d+)?\z/ - NEW_ISO_DATETIME = /\A(\d{4})-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)(?:\.(\d+))?\z/ end attr_reader :name, :default, :type, :limit, :null, :sql_type, :precision, :scale @@ -168,11 +167,10 @@ module ActiveRecord end protected - # Rational(123456, 1_000_000) -> 123456 - # The sec_fraction component returned by Date._parse is a Rational fraction of a second or nil - # NB: This method is optimized for performance by immediately converting away from Rational. + # '0.123456' -> 123456 + # '1.123456' -> 123456 def microseconds(time) - ((time[:sec_fraction].to_f % 1) * 1_000_000).round + ((time[:sec_fraction].to_f % 1) * 1_000_000).to_i end def new_date(year, mon, mday) @@ -196,8 +194,9 @@ module ActiveRecord # Doesn't handle time zones. def fast_string_to_time(string) - if md = Format::NEW_ISO_DATETIME.match(string) - new_time *md.to_a[1..7].map(&:to_i) + if string =~ Format::ISO_DATETIME + microsec = ($7.to_f * 1_000_000).to_i + new_time $1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i, microsec end end -- cgit v1.2.3 From 6806483b913aba611af48c1630f229a76a98ecc7 Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 17 Jan 2010 03:10:07 +0530 Subject: Use arel_table[] instead of unscoped[] to get arel attribute --- activerecord/lib/active_record/base.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index b0c9ed2548..3c4a9fe99d 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2193,7 +2193,7 @@ module ActiveRecord #:nodoc: # be made (since they can't be persisted). def destroy unless new_record? - self.class.unscoped.where(self.class.unscoped[self.class.primary_key].eq(id)).delete_all + self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all end @destroyed = true @@ -2480,7 +2480,7 @@ module ActiveRecord #:nodoc: def update(attribute_names = @attributes.keys) attributes_with_values = arel_attributes_values(false, false, attribute_names) return 0 if attributes_with_values.empty? - self.class.unscoped.where(self.class.unscoped[self.class.primary_key].eq(id)).update(attributes_with_values) + self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).update(attributes_with_values) end # Creates a record with values matching those of the instance attributes @@ -2590,7 +2590,7 @@ module ActiveRecord #:nodoc: if value && ((self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))) || value.is_a?(Hash) || value.is_a?(Array)) value = value.to_yaml end - attrs[self.class.unscoped[name]] = value + attrs[self.class.arel_table[name]] = value end end end -- cgit v1.2.3 From 6e3bee6cf1f0d2684152292db0a8b757249824fd Mon Sep 17 00:00:00 2001 From: Pratik Naik Date: Sun, 17 Jan 2010 03:14:17 +0530 Subject: Cache Model.arel_table --- activerecord/lib/active_record/base.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'activerecord/lib/active_record') diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 3c4a9fe99d..82e91a80ad 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -1388,7 +1388,7 @@ module ActiveRecord #:nodoc: def reset_column_information undefine_attribute_methods @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @inheritance_column = nil - @arel_engine = @unscoped = nil + @arel_engine = @unscoped = @arel_table = nil end def reset_column_information_and_inheritable_attributes_for_all_subclasses#:nodoc: @@ -1506,8 +1506,8 @@ module ActiveRecord #:nodoc: finder_needs_type_condition? ? @unscoped.where(type_condition) : @unscoped end - def arel_table(table_name_alias = nil) - Arel::Table.new(table_name, :as => table_name_alias, :engine => arel_engine) + def arel_table + @arel_table ||= Arel::Table.new(table_name, :engine => arel_engine) end def arel_engine -- cgit v1.2.3