Changeset 3553
- Timestamp:
- 02/09/06 09:17:40 (3 years ago)
- Files:
-
- trunk/actionpack/CHANGELOG (modified) (1 diff)
- trunk/actionpack/lib/action_controller/pagination.rb (modified) (2 diffs)
- trunk/actionpack/Rakefile (modified) (1 diff)
- trunk/actionpack/test/active_record_unit.rb (added)
- trunk/actionpack/test/activerecord (added)
- trunk/actionpack/test/activerecord/active_record_assertions_test.rb (added)
- trunk/actionpack/test/activerecord/pagination_test.rb (added)
- trunk/actionpack/test/controller/active_record_assertions_test.rb (deleted)
- trunk/actionpack/test/fixtures/companies.yml (added)
- trunk/actionpack/test/fixtures/company.rb (added)
- trunk/actionpack/test/fixtures/db_definitions (added)
- trunk/actionpack/test/fixtures/db_definitions/sqlite.sql (added)
- trunk/actionpack/test/fixtures/developer.rb (added)
- trunk/actionpack/test/fixtures/developers_projects.yml (added)
- trunk/actionpack/test/fixtures/developers.yml (added)
- trunk/actionpack/test/fixtures/project.rb (added)
- trunk/actionpack/test/fixtures/projects.yml (added)
- trunk/actionpack/test/fixtures/replies.yml (added)
- trunk/actionpack/test/fixtures/reply.rb (added)
- trunk/actionpack/test/fixtures/topic.rb (added)
- trunk/actionpack/test/fixtures/topics.yml (added)
- trunk/activerecord/CHANGELOG (modified) (1 diff)
- trunk/activerecord/lib/active_record/associations.rb (modified) (4 diffs)
- trunk/activerecord/lib/active_record/base.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb (modified) (1 diff)
- trunk/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb (modified) (1 diff)
- trunk/activerecord/test/associations_go_eager_test.rb (modified) (2 diffs)
- trunk/activerecord/test/base_test.rb (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/CHANGELOG
r3552 r3553 1 1 *SVN* 2 3 * Fix problems with pagination and :include. [Kevin Clark] 4 5 * Add ActiveRecordTestCase for testing AR integration. [Kevin Clark] 6 7 * Add Unit Tests for pagination [Kevin Clark] 2 8 3 9 * Add :html option for specifying form tag options in form_for. [Sam Stephenson] trunk/actionpack/lib/action_controller/pagination.rb
r2878 r3553 164 164 # the +model+ and given +conditions+. Override this method to implement a 165 165 # custom counter. 166 def count_collection_for_pagination(model, conditions, joins) 167 model.count(conditions,joins) 168 end 169 166 def count_collection_for_pagination(model, options) 167 model.count(:conditions => options[:conditions], 168 :joins => options[:join] || options[:joins], :include => options[:include]) 169 end 170 170 171 # Returns a collection of items for the given +model+ and +options[conditions]+, 171 172 # ordered by +options[order]+, for the current page in the given +paginator+. … … 186 187 klass = options[:class_name].constantize 187 188 page = @params[options[:parameter]] 188 count = count_collection_for_pagination(klass, options[:conditions], 189 options[:join] || options[:joins]) 190 189 count = count_collection_for_pagination(klass, options) 191 190 paginator = Paginator.new(self, count, options[:per_page], page) 192 191 collection = find_collection_for_pagination(klass, options, paginator) 193 192 194 193 return paginator, collection 195 194 end trunk/actionpack/Rakefile
r3543 r3553 31 31 t.verbose = true 32 32 } 33 34 desc 'ActiveRecord Integration Tests' 35 Rake::TestTask.new(:test_active_record_integration) do |t| 36 t.libs << "test" 37 t.test_files = Dir.glob("test/activerecord/*_test.rb") 38 t.verbose = true 39 end 33 40 34 41 trunk/activerecord/CHANGELOG
r3544 r3553 1 1 *SVN* 2 3 * Fix problems with count when used with :include [Jeremy Hopple and Kevin Clark] 2 4 3 5 * ActiveRecord::RecordInvalid now states which validations failed in its default error message [Tobias Luetke] trunk/activerecord/lib/active_record/associations.rb
r3331 r3553 780 780 end 781 781 end 782 783 def count_with_associations(options = {}) 784 reflections = reflect_on_included_associations(options[:include]) 785 return count_by_sql(construct_counter_sql_with_included_associations(options, reflections)) 786 end 782 787 783 788 def find_with_associations(options = {}) … … 997 1002 ) 998 1003 end 1004 1005 def construct_counter_sql_with_included_associations(options, reflections) 1006 sql = "SELECT COUNT(DISTINCT #{table_name}.#{primary_key})" 1007 1008 # A (slower) workaround if we're using a backend, like sqlite, that doesn't support COUNT DISTINCT. 1009 if !Base.connection.supports_count_distinct? 1010 sql = "SELECT COUNT(*) FROM (SELECT DISTINCT #{table_name}.#{primary_key}" 1011 end 1012 1013 sql << " FROM #{table_name} " 1014 sql << reflections.collect { |reflection| association_join(reflection) }.to_s 1015 sql << "#{options[:joins]} " if options[:joins] 1016 1017 add_conditions!(sql, options[:conditions]) 1018 add_sti_conditions!(sql, reflections) 1019 add_limited_ids_condition!(sql, options, reflections) if !using_limitable_reflections?(reflections) && options[:limit] 1020 1021 add_limit!(sql, options) if using_limitable_reflections?(reflections) 1022 1023 if !Base.connection.supports_count_distinct? 1024 sql << ")" 1025 end 1026 1027 return sanitize_sql(sql) 1028 end 999 1029 1000 1030 def construct_finder_sql_with_included_associations(options, schema_abbreviations, reflections) … … 1002 1032 sql << reflections.collect { |reflection| association_join(reflection) }.to_s 1003 1033 sql << "#{options[:joins]} " if options[:joins] 1004 1034 1005 1035 add_conditions!(sql, options[:conditions]) 1006 1036 add_sti_conditions!(sql, reflections) 1007 add_limited_ids_condition!(sql, options ) if !using_limitable_reflections?(reflections) && options[:limit]1037 add_limited_ids_condition!(sql, options, reflections) if !using_limitable_reflections?(reflections) && options[:limit] 1008 1038 1009 1039 sql << "ORDER BY #{options[:order]} " if options[:order] 1010 1040 1011 1041 add_limit!(sql, options) if using_limitable_reflections?(reflections) 1012 1042 1013 1043 return sanitize_sql(sql) 1014 1044 end 1015 1016 def add_limited_ids_condition!(sql, options )1017 unless (id_list = select_limited_ids_list(options )).empty?1045 1046 def add_limited_ids_condition!(sql, options, reflections) 1047 unless (id_list = select_limited_ids_list(options, reflections)).empty? 1018 1048 sql << "#{condition_word(sql)} #{table_name}.#{primary_key} IN (#{id_list}) " 1019 1049 end 1020 1050 end 1021 1022 def select_limited_ids_list(options )1051 1052 def select_limited_ids_list(options, reflections) 1023 1053 connection.select_values( 1024 construct_finder_sql_for_association_limiting(options ),1054 construct_finder_sql_for_association_limiting(options, reflections), 1025 1055 "#{name} Load IDs For Limited Eager Loading" 1026 1056 ).collect { |id| connection.quote(id) }.join(", ") 1027 1057 end 1028 1029 def construct_finder_sql_for_association_limiting(options) 1030 raise(ArgumentError, "Limited eager loads and conditions on the eager tables is incompatible") if include_eager_conditions?(options) 1031 1032 sql = "SELECT #{primary_key} FROM #{table_name} " 1058 1059 def construct_finder_sql_for_association_limiting(options, reflections) 1060 #sql = "SELECT DISTINCT #{table_name}.#{primary_key} FROM #{table_name} " 1061 sql = "SELECT " 1062 sql << "DISTINCT #{table_name}." if include_eager_conditions?(options) || include_eager_order?(options) 1063 sql << "#{primary_key} FROM #{table_name} " 1064 1065 if include_eager_conditions?(options) || include_eager_order?(options) 1066 sql << reflections.collect { |reflection| association_join(reflection) }.to_s 1067 sql << "#{options[:joins]} " if options[:joins] 1068 end 1069 1033 1070 add_conditions!(sql, options[:conditions]) 1034 1071 sql << "ORDER BY #{options[:order]} " if options[:order] … … 1043 1080 conditions.scan(/(\w+)\.\w+/).flatten.any? do |condition_table_name| 1044 1081 condition_table_name != table_name 1082 end 1083 end 1084 1085 def include_eager_order?(options) 1086 order = options[:order] 1087 return false unless order 1088 order.scan(/(\w+)\.\w+/).flatten.any? do |order_table_name| 1089 order_table_name != table_name 1045 1090 end 1046 1091 end trunk/activerecord/lib/active_record/base.rb
r3533 r3553 496 496 end 497 497 498 # Returns the number of records that meet the +conditions+. Zero is returned if no records match. Example: 499 # Product.count "sales > 1" 500 def count(conditions = nil, joins = nil) 501 sql = "SELECT COUNT(*) FROM #{table_name} " 502 sql << " #{joins} " if joins 503 add_conditions!(sql, conditions) 504 count_by_sql(sql) 498 # Count operates using three different approaches. 499 # 500 # * Count all: By not passing any parameters to count, it will return a count of all the rows for the model. 501 # * Count by conditions or joins: For backwards compatibility, you can pass in +conditions+ and +joins+ as individual parameters. 502 # * Count using options will find the row count matched by the options used. 503 # 504 # The last approach, count using options, accepts an option hash as the only parameter. The options are: 505 # 506 # * <tt>:conditions</tt>: An SQL fragment like "administrator = 1" or [ "user_name = ?", username ]. See conditions in the intro. 507 # * <tt>:joins</tt>: An SQL fragment for additional joins like "LEFT JOIN comments ON comments.post_id = id". (Rarely needed). 508 # The records will be returned read-only since they will have attributes that do not correspond to the table's columns. 509 # * <tt>:include</tt>: Named associations that should be loaded alongside using LEFT OUTER JOINs. The symbols named refer 510 # to already defined associations. When using named associations count returns the number DISTINCT items for the model you're counting. 511 # See eager loading under Associations. 512 # 513 # Examples for counting all: 514 # Person.count # returns the total count of all people 515 # 516 # Examples for count by +conditions+ and +joins+ (for backwards compatibility): 517 # Person.count("age > 26") # returns the number of people older than 26 518 # Person.find("age > 26 AND job.salary > 60000", "LEFT JOIN jobs on jobs.person_id = person.id") # returns the total number of rows matching the conditions and joins fetched by SELECT COUNT(*). 519 # 520 # Examples for count with options: 521 # Person.count(:conditions => "age > 26") 522 # Person.count(:conditions => "age > 26 AND job.salary > 60000", :include => :job) # because of the named association, it finds the DISTINCT count using LEFT OUTER JOIN. 523 # Person.count(:conditions => "age > 26 AND job.salary > 60000", :joins => "LEFT JOIN jobs on jobs.person_id = person.id") # finds the number of rows matching the conditions and joins. 524 def count(*args) 525 options = {} 526 527 #For backwards compatibility, we need to handle both count(conditions=nil, joins=nil) or count(options={}). 528 if args.size >= 0 and args.size <= 2 529 if args.first.is_a?(Hash) 530 options = args.first 531 #should we verify the options hash??? 532 else 533 #Handle legacy paramter options: def count(conditions=nil, joins=nil) 534 options.merge!(:conditions => args[0]) if args.length > 0 535 options.merge!(:joins => args[1]) if args.length > 1 536 end 537 else 538 raise(ArgumentError, "Unexpected parameters passed to count(*args): expected either count(conditions=nil, joins=nil) or count(options={})") 539 end 540 541 options[:include] ? count_with_associations(options) : count_by_sql(construct_counter_sql(options)) 542 end 543 544 def construct_counter_sql(options) 545 sql = "SELECT COUNT(" 546 sql << "DISTINCT " if options[:distinct] 547 sql << "#{table_name}.#{primary_key}) FROM #{table_name} " 548 sql << " #{options[:joins]} " if options[:joins] 549 add_conditions!(sql, options[:conditions]) 550 sql 505 551 end 506 552 trunk/activerecord/lib/active_record/connection_adapters/abstract_adapter.rb
r3219 r3553 38 38 def supports_migrations? 39 39 false 40 end 41 42 # Does this adapter support using DISTINCT within COUNT? This is +true+ 43 # for all adapters except sqlite. 44 def supports_count_distinct? 45 true 40 46 end 41 47 trunk/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb
r3294 r3553 98 98 def supports_migrations? #:nodoc: 99 99 true 100 end 101 102 def supports_count_distinct? #:nodoc: 103 false 100 104 end 101 105 trunk/activerecord/test/associations_go_eager_test.rb
r3254 r3553 121 121 122 122 def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers 123 assert_raises(ArgumentError) do 124 posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ]) 125 end 123 posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ]) 124 assert_equal 2, posts.size 125 126 count = Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ]) 127 assert_equal count, posts.size 126 128 end 127 129 … … 142 144 143 145 def test_eager_with_has_many_and_limit_and_conditions_on_the_eagers 144 assert_raises(ArgumentError) do 145 posts = authors(:david).posts.find(:all, 146 :include => :comments, 147 :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'", 148 :limit => 2 149 ) 150 end 146 posts = authors(:david).posts.find(:all, 147 :include => :comments, 148 :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'", 149 :limit => 2 150 ) 151 assert_equal 2, posts.size 152 153 count = Post.count( 154 :include => [ :comments, :author ], 155 :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')", 156 :limit => 2 157 ) 158 assert_equal count, posts.size 151 159 end 152 160 trunk/activerecord/test/base_test.rb
r3452 r3553 1057 1057 end 1058 1058 assert_equal res, res2 1059 1060 res3 = res + 1 1061 assert_nothing_raised do 1062 res3 = Post.count(:conditions => "posts.#{QUOTED_TYPE} = 'Post'", 1063 :joins => "LEFT JOIN comments ON posts.id=comments.post_id") 1064 end 1065 assert_equal res, res3 1059 1066 end 1060 1067