aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--activemodel/lib/active_model/secure_password.rb4
-rw-r--r--activerecord/RUNNING_UNIT_TESTS.rdoc1
-rw-r--r--activerecord/lib/active_record/associations.rb2
-rw-r--r--activerecord/lib/active_record/associations/preloader/association.rb42
-rw-r--r--activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb7
-rw-r--r--activerecord/lib/active_record/associations/preloader/through_association.rb17
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb10
-rw-r--r--guides/source/command_line.md2
-rw-r--r--railties/lib/rails/generators/base.rb31
9 files changed, 66 insertions, 50 deletions
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index cc9483e67b..8b9ac97bbb 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -2,7 +2,9 @@ module ActiveModel
module SecurePassword
extend ActiveSupport::Concern
- class << self; attr_accessor :min_cost; end
+ class << self
+ attr_accessor :min_cost # :nodoc:
+ end
self.min_cost = false
module ClassMethods
diff --git a/activerecord/RUNNING_UNIT_TESTS.rdoc b/activerecord/RUNNING_UNIT_TESTS.rdoc
index c3ee34da55..ca1f2fd665 100644
--- a/activerecord/RUNNING_UNIT_TESTS.rdoc
+++ b/activerecord/RUNNING_UNIT_TESTS.rdoc
@@ -24,6 +24,7 @@ Simply executing <tt>bundle exec rake test</tt> is equivalent to the following:
$ bundle exec rake test_mysql2
$ bundle exec rake test_postgresql
$ bundle exec rake test_sqlite3
+ $ bundle exec rake test_sqlite3_mem
There should be tests available for each database backend listed in the {Config
File}[rdoc-label:label-Config+File]. (the exact set of available tests is
diff --git a/activerecord/lib/active_record/associations.rb b/activerecord/lib/active_record/associations.rb
index 5ceda933f2..33cbafc6aa 100644
--- a/activerecord/lib/active_record/associations.rb
+++ b/activerecord/lib/active_record/associations.rb
@@ -164,7 +164,7 @@ module ActiveRecord
private
# Returns the specified association instance if it responds to :loaded?, nil otherwise.
def association_instance_get(name)
- @association_cache[name.to_sym]
+ @association_cache[name]
end
# Set the specified association instance.
diff --git a/activerecord/lib/active_record/associations/preloader/association.rb b/activerecord/lib/active_record/associations/preloader/association.rb
index 0cc836f991..928da71eed 100644
--- a/activerecord/lib/active_record/associations/preloader/association.rb
+++ b/activerecord/lib/active_record/associations/preloader/association.rb
@@ -29,6 +29,10 @@ module ActiveRecord
end
def records_for(ids)
+ query_scope(ids)
+ end
+
+ def query_scope(ids)
scope.where(association_key.in(ids))
end
@@ -52,12 +56,9 @@ module ActiveRecord
raise NotImplementedError
end
- # We're converting to a string here because postgres will return the aliased association
- # key in a habtm as a string (for whatever reason)
def owners_by_key
@owners_by_key ||= owners.group_by do |owner|
- key = owner[owner_key_name]
- key && key.to_s
+ owner[owner_key_name]
end
end
@@ -71,27 +72,34 @@ module ActiveRecord
owners_map = owners_by_key
owner_keys = owners_map.keys.compact
- if klass.nil? || owner_keys.empty?
- records = []
- else
+ # Each record may have multiple owners, and vice-versa
+ records_by_owner = Hash[owners.map { |owner| [owner, []] }]
+
+ if klass && owner_keys.any?
# Some databases impose a limit on the number of ids in a list (in Oracle it's 1000)
# Make several smaller queries if necessary or make one query if the adapter supports it
sliced = owner_keys.each_slice(klass.connection.in_clause_length || owner_keys.size)
- records = sliced.flat_map { |slice| records_for(slice).to_a }
+ sliced.each { |slice|
+ records = records_for(slice)
+ caster = type_caster(records, association_key_name)
+ records.each do |record|
+ owner_key = caster.call record[association_key_name]
+
+ owners_map[owner_key].each do |owner|
+ records_by_owner[owner] << record
+ end
+ end
+ }
end
- # Each record may have multiple owners, and vice-versa
- records_by_owner = Hash[owners.map { |owner| [owner, []] }]
- records.each do |record|
- owner_key = record[association_key_name].to_s
-
- owners_map[owner_key].each do |owner|
- records_by_owner[owner] << record
- end
- end
records_by_owner
end
+ IDENTITY = lambda { |value| value }
+ def type_caster(results, name)
+ IDENTITY
+ end
+
def reflection_scope
@reflection_scope ||= reflection.scope ? klass.unscoped.instance_exec(nil, &reflection.scope) : klass.unscoped
end
diff --git a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb b/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb
index 9a3fada380..c042a44b21 100644
--- a/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb
+++ b/activerecord/lib/active_record/associations/preloader/has_and_belongs_to_many.rb
@@ -12,7 +12,7 @@ module ActiveRecord
# Unlike the other associations, we want to get a raw array of rows so that we can
# access the aliased column on the join table
def records_for(ids)
- scope = super
+ scope = query_scope ids
klass.connection.select_all(scope.arel, 'SQL', scope.bind_values)
end
@@ -40,6 +40,11 @@ module ActiveRecord
end
end
+ def type_caster(results, name)
+ caster = results.column_types.fetch(name, results.identity_type)
+ lambda { |value| caster.type_cast value }
+ end
+
def build_scope
super.joins(join).select(join_select)
end
diff --git a/activerecord/lib/active_record/associations/preloader/through_association.rb b/activerecord/lib/active_record/associations/preloader/through_association.rb
index de06931845..2c625cec04 100644
--- a/activerecord/lib/active_record/associations/preloader/through_association.rb
+++ b/activerecord/lib/active_record/associations/preloader/through_association.rb
@@ -27,17 +27,16 @@ module ActiveRecord
def through_records_by_owner
Preloader.new(owners, through_reflection.name, through_scope).run
- Hash[owners.map do |owner|
- through_records = Array.wrap(owner.send(through_reflection.name))
+ should_reset = (through_scope != through_reflection.klass.unscoped) ||
+ (reflection.options[:source_type] && through_reflection.collection?)
- # Dont cache the association - we would only be caching a subset
- if (through_scope != through_reflection.klass.unscoped) ||
- (reflection.options[:source_type] && through_reflection.collection?)
- owner.association(through_reflection.name).reset
- end
+ owners.each_with_object({}) do |owner, h|
+ association = owner.association through_reflection.name
+ h[owner] = Array(association.reader)
- [owner, through_records]
- end]
+ # Dont cache the association - we would only be caching a subset
+ association.reset if should_reset
+ end
end
def through_scope
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index e693d34f99..811d91f849 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -52,12 +52,10 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
def test_cascaded_eager_association_loading_with_join_for_count
categories = Category.joins(:categorizations).includes([{:posts=>:comments}, :authors])
- assert_nothing_raised do
- assert_equal 4, categories.count
- assert_equal 4, categories.to_a.count
- assert_equal 3, categories.distinct.count
- assert_equal 3, categories.to_a.uniq.size # Must uniq since instantiating with inner joins will get dupes
- end
+ assert_equal 4, categories.count
+ assert_equal 4, categories.to_a.count
+ assert_equal 3, categories.distinct.count
+ assert_equal 3, categories.to_a.uniq.size # Must uniq since instantiating with inner joins will get dupes
end
def test_cascaded_eager_association_loading_with_duplicated_includes
diff --git a/guides/source/command_line.md b/guides/source/command_line.md
index 639476eeeb..f11e08a0c1 100644
--- a/guides/source/command_line.md
+++ b/guides/source/command_line.md
@@ -471,7 +471,7 @@ spec/models/user_spec.rb:
INFO: A good description of unit testing in Rails is given in [A Guide to Testing Rails Applications](testing.html)
-Rails comes with a test suite called `Test::Unit`. Rails owes its stability to the use of tests. The tasks available in the `test:` namespace helps in running the different tests you will hopefully write.
+Rails comes with a test suite called Minitest. Rails owes its stability to the use of tests. The tasks available in the `test:` namespace helps in running the different tests you will hopefully write.
### `tmp`
diff --git a/railties/lib/rails/generators/base.rb b/railties/lib/rails/generators/base.rb
index 7e938fab47..8aec8bc8f9 100644
--- a/railties/lib/rails/generators/base.rb
+++ b/railties/lib/rails/generators/base.rb
@@ -168,15 +168,15 @@ module Rails
as_hook = options.delete(:as) || generator_name
names.each do |name|
- defaults = if options[:type] == :boolean
- { }
- elsif [true, false].include?(default_value_for_option(name, options))
- { banner: "" }
- else
- { desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" }
- end
-
unless class_options.key?(name)
+ defaults = if options[:type] == :boolean
+ { }
+ elsif [true, false].include?(default_value_for_option(name, options))
+ { banner: "" }
+ else
+ { desc: "#{name.to_s.humanize} to be invoked", banner: "NAME" }
+ end
+
class_option(name, defaults.merge!(options))
end
@@ -255,12 +255,7 @@ module Rails
# Split the class from its module nesting
nesting = class_name.split('::')
last_name = nesting.pop
-
- # Extract the last Module in the nesting
- last = nesting.inject(Object) do |last_module, nest|
- break unless last_module.const_defined?(nest, false)
- last_module.const_get(nest)
- end
+ last = extract_last_module(nesting)
if last && last.const_defined?(last_name.camelize, false)
raise Error, "The name '#{class_name}' is either already used in your application " <<
@@ -270,6 +265,14 @@ module Rails
end
end
+ # Takes in an array of nested modules and extracts the last module
+ def extract_last_module(nesting)
+ nesting.inject(Object) do |last_module, nest|
+ break unless last_module.const_defined?(nest, false)
+ last_module.const_get(nest)
+ end
+ end
+
# Use Rails default banner.
def self.banner
"rails generate #{namespace.sub(/^rails:/,'')} #{self.arguments.map{ |a| a.usage }.join(' ')} [options]".gsub(/\s+/, ' ')