mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add support for :include to with_scope [andrew@redlinesoftware.com]
Remove overrated warning from adapter_test git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@4064 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
parent
b8e23e37dd
commit
445cb5c08d
6 changed files with 101 additions and 20 deletions
|
@ -1,5 +1,7 @@
|
|||
*SVN*
|
||||
|
||||
* Add support for :include to with_scope [andrew@redlinesoftware.com]
|
||||
|
||||
* Support the use of public synonyms with the Oracle adapter; required ruby-oci8 v0.1.14 #4390 [schoenm@earthlink.net]
|
||||
|
||||
* Change periods (.) in table aliases to _'s. Closes #4251 [jeff@ministrycentered.com]
|
||||
|
|
|
@ -929,12 +929,12 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def count_with_associations(options = {})
|
||||
join_dependency = JoinDependency.new(self, options[:include], options[:joins])
|
||||
join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
|
||||
return count_by_sql(construct_counter_sql_with_included_associations(options, join_dependency))
|
||||
end
|
||||
|
||||
def find_with_associations(options = {})
|
||||
join_dependency = JoinDependency.new(self, options[:include], options[:joins])
|
||||
join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), options[:include]), options[:joins])
|
||||
rows = select_all_rows(options, join_dependency)
|
||||
return join_dependency.instantiate(rows)
|
||||
end
|
||||
|
@ -1084,10 +1084,10 @@ module ActiveRecord
|
|||
|
||||
sql << " FROM #{table_name} "
|
||||
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
||||
sql << "#{options[:joins]} " if options[:joins]
|
||||
|
||||
|
||||
add_joins!(sql, options)
|
||||
add_conditions!(sql, options[:conditions])
|
||||
add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && options[:limit]
|
||||
add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && (scope(:find, :limit) || options[:limit])
|
||||
|
||||
add_limit!(sql, options) if using_limitable_reflections?(join_dependency.reflections)
|
||||
|
||||
|
@ -1099,10 +1099,10 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def construct_finder_sql_with_included_associations(options, join_dependency)
|
||||
sql = "SELECT #{column_aliases(join_dependency)} FROM #{options[:from] || table_name} "
|
||||
sql = "SELECT #{column_aliases(join_dependency)} FROM #{scope(:find, :from) || options[:from] || table_name} "
|
||||
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
||||
sql << "#{options[:joins]} " if options[:joins]
|
||||
|
||||
add_joins!(sql, options)
|
||||
add_conditions!(sql, options[:conditions])
|
||||
add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && options[:limit]
|
||||
|
||||
|
@ -1134,7 +1134,7 @@ module ActiveRecord
|
|||
|
||||
if include_eager_conditions?(options) || include_eager_order?(options)
|
||||
sql << join_dependency.join_associations.collect{|join| join.association_join }.join
|
||||
sql << "#{options[:joins]} " if options[:joins]
|
||||
add_joins!(sql, options)
|
||||
end
|
||||
|
||||
add_conditions!(sql, options[:conditions])
|
||||
|
@ -1144,7 +1144,7 @@ module ActiveRecord
|
|||
end
|
||||
|
||||
def include_eager_conditions?(options)
|
||||
conditions = options[:conditions]
|
||||
conditions = scope(:find, :conditions) || options[:conditions]
|
||||
return false unless conditions
|
||||
conditions = conditions.first if conditions.is_a?(Array)
|
||||
conditions.scan(/(\w+)\.\w+/).flatten.any? do |condition_table_name|
|
||||
|
|
|
@ -388,7 +388,7 @@ module ActiveRecord #:nodoc:
|
|||
when :first
|
||||
find(:all, options.merge(options[:include] ? { } : { :limit => 1 })).first
|
||||
when :all
|
||||
records = options[:include] ? find_with_associations(options) : find_by_sql(construct_finder_sql(options))
|
||||
records = (scoped?(:find, :include) || options[:include]) ? find_with_associations(options) : find_by_sql(construct_finder_sql(options))
|
||||
records.each { |record| record.readonly! } if options[:readonly]
|
||||
records
|
||||
else
|
||||
|
@ -834,7 +834,7 @@ module ActiveRecord #:nodoc:
|
|||
|
||||
# Scope parameters to method calls within the block. Takes a hash of method_name => parameters hash.
|
||||
# method_name may be :find or :create. :find parameters may include the <tt>:conditions</tt>, <tt>:joins</tt>,
|
||||
# <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. :create parameters are an attributes hash.
|
||||
# <tt>:include</tt>, <tt>:offset</tt>, <tt>:limit</tt>, and <tt>:readonly</tt> options. :create parameters are an attributes hash.
|
||||
#
|
||||
# Article.with_scope(:find => { :conditions => "blog_id = 1" }, :create => { :blog_id => 1 }) do
|
||||
# Article.find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
|
||||
|
@ -873,7 +873,7 @@ module ActiveRecord #:nodoc:
|
|||
method_scoping.assert_valid_keys([ :find, :create ])
|
||||
|
||||
if f = method_scoping[:find]
|
||||
f.assert_valid_keys([ :conditions, :joins, :select, :from, :offset, :limit, :readonly ])
|
||||
f.assert_valid_keys([ :conditions, :joins, :select, :include, :from, :offset, :limit, :readonly ])
|
||||
f[:readonly] = true if !f[:joins].blank? && !f.has_key?(:readonly)
|
||||
end
|
||||
|
||||
|
@ -882,10 +882,13 @@ module ActiveRecord #:nodoc:
|
|||
method_scoping = current_scoped_methods.inject(method_scoping) do |hash, (method, params)|
|
||||
case hash[method]
|
||||
when Hash
|
||||
if method == :find && hash[method][:conditions] && params[:conditions]
|
||||
if method == :find
|
||||
(hash[method].keys + params.keys).uniq.each do |key|
|
||||
if key == :conditions
|
||||
merge = hash[method][key] && params[key] # merge if both scopes have the same key
|
||||
if key == :conditions && merge
|
||||
hash[method][key] = [params[key], hash[method][key]].collect{|sql| "( %s )" % sanitize_sql(sql)}.join(" AND ")
|
||||
elsif key == :include && merge
|
||||
hash[method][key] = merge_includes(hash[method][key], params[key]).uniq
|
||||
else
|
||||
hash[method][key] = hash[method][key] || params[key]
|
||||
end
|
||||
|
@ -994,6 +997,23 @@ module ActiveRecord #:nodoc:
|
|||
sql
|
||||
end
|
||||
|
||||
# Merges includes so that the result is a valid +include+
|
||||
def merge_includes(first, second)
|
||||
safe_to_array(first) + safe_to_array(second)
|
||||
end
|
||||
|
||||
# Object#to_a is deprecated, though it does have the desired behaviour
|
||||
def safe_to_array(o)
|
||||
case o
|
||||
when NilClass
|
||||
[]
|
||||
when Array
|
||||
o
|
||||
else
|
||||
[o]
|
||||
end
|
||||
end
|
||||
|
||||
def add_limit!(sql, options)
|
||||
options[:limit] ||= scope(:find, :limit)
|
||||
options[:offset] ||= scope(:find, :offset)
|
||||
|
|
|
@ -61,7 +61,7 @@ module ActiveRecord
|
|||
raise(ArgumentError, "Unexpected parameters passed to count(*args): expected either count(conditions=nil, joins=nil) or count(options={})")
|
||||
end
|
||||
|
||||
options[:include] ? count_with_associations(options) : calculate(:count, :all, options)
|
||||
(scope(:find, :include) || options[:include]) ? count_with_associations(options) : calculate(:count, :all, options)
|
||||
end
|
||||
|
||||
# Calculates average value on a given column. The value is returned as a float. See #calculate for examples with options.
|
||||
|
@ -221,4 +221,4 @@ module ActiveRecord
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -43,8 +43,6 @@ class AdapterTest < Test::Unit::TestCase
|
|||
def test_current_database
|
||||
if @connection.respond_to?(:current_database)
|
||||
assert_equal "activerecord_unittest", @connection.current_database
|
||||
else
|
||||
warn "#{@connection.class} does not respond to #current_database"
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
require 'abstract_unit'
|
||||
require 'fixtures/developer'
|
||||
require 'fixtures/project'
|
||||
require 'fixtures/comment'
|
||||
require 'fixtures/post'
|
||||
require 'fixtures/category'
|
||||
|
||||
class MethodScopingTest < Test::Unit::TestCase
|
||||
fixtures :developers, :comments, :posts
|
||||
fixtures :developers, :projects, :comments, :posts
|
||||
|
||||
def test_set_conditions
|
||||
Developer.with_scope(:find => { :conditions => 'just a test...' }) do
|
||||
|
@ -60,6 +61,23 @@ class MethodScopingTest < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_scoped_find_include
|
||||
# with the include, will retrieve only developers for the given project
|
||||
scoped_developers = Developer.with_scope(:find => { :include => :projects }) do
|
||||
Developer.find(:all, :conditions => 'projects.id = 2')
|
||||
end
|
||||
assert scoped_developers.include?(developers(:david))
|
||||
assert !scoped_developers.include?(developers(:jamis))
|
||||
assert_equal 1, scoped_developers.size
|
||||
end
|
||||
|
||||
def test_scoped_count_include
|
||||
# with the include, will retrieve only developers for the given project
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
assert_equal 1, Developer.count('projects.id = 2')
|
||||
end
|
||||
end
|
||||
|
||||
def test_scoped_create
|
||||
new_comment = nil
|
||||
|
||||
|
@ -108,7 +126,7 @@ class MethodScopingTest < Test::Unit::TestCase
|
|||
end
|
||||
|
||||
class NestedScopingTest < Test::Unit::TestCase
|
||||
fixtures :developers, :comments, :posts
|
||||
fixtures :developers, :projects, :comments, :posts
|
||||
|
||||
def test_merge_options
|
||||
Developer.with_scope(:find => { :conditions => 'salary = 80000' }) do
|
||||
|
@ -160,6 +178,49 @@ class NestedScopingTest < Test::Unit::TestCase
|
|||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_include
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
Developer.with_scope(:find => { :conditions => "projects.id = 2" }) do
|
||||
assert_nothing_raised { Developer.find(1) }
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_merged_include
|
||||
# :include's remain unique and don't "double up" when merging
|
||||
Developer.with_scope(:find => { :include => :projects, :conditions => "projects.id = 2" }) do
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
|
||||
# the nested scope doesn't remove the first :include
|
||||
Developer.with_scope(:find => { :include => :projects, :conditions => "projects.id = 2" }) do
|
||||
Developer.with_scope(:find => { :include => [] }) do
|
||||
assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
|
||||
# mixing array and symbol include's will merge correctly
|
||||
Developer.with_scope(:find => { :include => [:projects], :conditions => "projects.id = 2" }) do
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
assert_equal 1, Developer.instance_eval('current_scoped_methods')[:find][:include].length
|
||||
assert_equal('David', Developer.find(:first).name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_nested_scoped_find_replace_include
|
||||
Developer.with_scope(:find => { :include => :projects }) do
|
||||
Developer.with_exclusive_scope(:find => { :include => [] }) do
|
||||
assert_equal 0, Developer.instance_eval('current_scoped_methods')[:find][:include].length
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_three_level_nested_exclusive_scoped_find
|
||||
Developer.with_scope(:find => { :conditions => "name = 'Jamis'" }) do
|
||||
assert_equal('Jamis', Developer.find(:first).name)
|
||||
|
|
Loading…
Reference in a new issue