diff options
-rw-r--r-- | activerecord/CHANGELOG.md | 12 | ||||
-rw-r--r-- | activerecord/activerecord.gemspec | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/association_scope.rb | 1 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/builder/collection_association.rb | 3 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/collection_association.rb | 2 | ||||
-rw-r--r-- | activerecord/lib/active_record/associations/collection_proxy.rb | 1 | ||||
-rw-r--r-- | activerecord/lib/active_record/persistence.rb | 26 | ||||
-rw-r--r-- | activerecord/test/cases/associations/has_many_associations_test.rb | 24 | ||||
-rw-r--r-- | activerecord/test/models/post.rb | 14 | ||||
-rw-r--r-- | guides/source/action_mailer_basics.md | 2 | ||||
-rw-r--r-- | guides/source/command_line.md | 2 | ||||
-rw-r--r-- | railties/lib/rails/info.rb | 2 | ||||
-rw-r--r-- | railties/test/application/runner_test.rb | 12 |
13 files changed, 77 insertions, 26 deletions
diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 5a0c391154..45a2b59d47 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,5 +1,16 @@ ## Rails 4.0.0 (unreleased) ## +* Collection associations `#empty?` always respects builded records. + Fix #8879. + + Example: + + widget = Widget.new + widget.things.build + widget.things.empty? # => false + + *Yves Senn* + * Remove support for parsing YAML parameters from request. *Aaron Patterson* @@ -974,7 +985,6 @@ * `:conditions` becomes `:where`. * `:include` becomes `:includes`. - * `:extend` becomes `:extending`. The code to implement the deprecated features has been moved out to the `activerecord-deprecated_finders` gem. This gem is a dependency diff --git a/activerecord/activerecord.gemspec b/activerecord/activerecord.gemspec index 31ddb01123..bfc2e54aba 100644 --- a/activerecord/activerecord.gemspec +++ b/activerecord/activerecord.gemspec @@ -25,5 +25,5 @@ Gem::Specification.new do |s| s.add_dependency 'activemodel', version s.add_dependency 'arel', '~> 3.0.2' - s.add_dependency 'activerecord-deprecated_finders', '0.0.1' + s.add_dependency 'activerecord-deprecated_finders', '0.0.2' end diff --git a/activerecord/lib/active_record/associations/association_scope.rb b/activerecord/lib/active_record/associations/association_scope.rb index 1303822868..300f67959d 100644 --- a/activerecord/lib/active_record/associations/association_scope.rb +++ b/activerecord/lib/active_record/associations/association_scope.rb @@ -16,6 +16,7 @@ module ActiveRecord def scope scope = klass.unscoped scope.merge! eval_scope(klass, reflection.scope) if reflection.scope + scope.extending! Array(options[:extend]) add_constraints(scope) end diff --git a/activerecord/lib/active_record/associations/builder/collection_association.rb b/activerecord/lib/active_record/associations/builder/collection_association.rb index fcdfc1e150..fdead16761 100644 --- a/activerecord/lib/active_record/associations/builder/collection_association.rb +++ b/activerecord/lib/active_record/associations/builder/collection_association.rb @@ -6,7 +6,8 @@ module ActiveRecord::Associations::Builder CALLBACKS = [:before_add, :after_add, :before_remove, :after_remove] def valid_options - super + [:table_name, :finder_sql, :counter_sql, :before_add, :after_add, :before_remove, :after_remove] + super + [:table_name, :finder_sql, :counter_sql, :before_add, + :after_add, :before_remove, :after_remove, :extend] end attr_reader :block_extension, :extension_module diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 832b963052..5feb149946 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -273,7 +273,7 @@ module ActiveRecord if loaded? || options[:counter_sql] size.zero? else - !scope.exists? + @target.blank? && !scope.exists? end end diff --git a/activerecord/lib/active_record/associations/collection_proxy.rb b/activerecord/lib/active_record/associations/collection_proxy.rb index 33dce58982..e93e700c93 100644 --- a/activerecord/lib/active_record/associations/collection_proxy.rb +++ b/activerecord/lib/active_record/associations/collection_proxy.rb @@ -33,6 +33,7 @@ module ActiveRecord def initialize(klass, association) #:nodoc: @association = association super klass, klass.arel_table + self.default_scoped = true merge! association.scope(nullify: false) end diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 3011f959a5..1b2aa9349e 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -236,26 +236,26 @@ module ActiveRecord alias update_attributes! update! - # Updates a single attribute of an object, without having to explicitly call save on that object. - # - # * Validation is skipped. - # * Callbacks are skipped. - # * updated_at/updated_on column is not updated if that column is available. - # - # Raises an +ActiveRecordError+ when called on new objects, or when the +name+ - # attribute is marked as readonly. + # Equivalent to <code>update_columns(name => value)</code>. def update_column(name, value) update_columns(name => value) end - # Updates the attributes from the passed-in hash, without having to explicitly call save on that object. + # Updates the attributes directly in the database issuing an UPDATE SQL + # statement and sets them in the receiver: # - # * Validation is skipped. + # user.update_columns(last_request_at: Time.current) + # + # This is the fastest way to update attributes because it goes straight to + # the database, but take into account that in consequence the regular update + # procedures are totally bypassed. In particular: + # + # * Validations are skipped. # * Callbacks are skipped. - # * updated_at/updated_on column is not updated if that column is available. + # * +updated_at+/+updated_on+ are not updated. # - # Raises an +ActiveRecordError+ when called on new objects, or when at least - # one of the attributes is marked as readonly. + # This method raises an +ActiveRecord::ActiveRecordError+ when called on new + # objects, or when at least one of the attributes is marked as readonly. def update_columns(attributes) raise ActiveRecordError, "can not update on a new record object" unless persisted? diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 3f9667fa94..031978f065 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -625,6 +625,13 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 3, company.clients_of_firm.size end + def test_collection_not_empty_after_building + company = companies(:first_firm) + assert_predicate company.contracts, :empty? + company.contracts.build + assert_not_predicate company.contracts, :empty? + end + def test_collection_size_twice_for_regressions post = posts(:thinking) assert_equal 0, post.readers.size @@ -1705,4 +1712,21 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 0, post.comments.count end end + + test "collection proxy respects default scope" do + author = authors(:mary) + assert !author.first_posts.exists? + end + + test "association with extend option" do + post = posts(:welcome) + assert_equal "lifo", post.comments_with_extend.author + assert_equal "hello", post.comments_with_extend.greeting + end + + test "association with extend option with multiple extensions" do + post = posts(:welcome) + assert_equal "lifo", post.comments_with_extend_2.author + assert_equal "hello", post.comments_with_extend_2.greeting + end end diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb index c995f59a15..4433550dd5 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -5,6 +5,12 @@ class Post < ActiveRecord::Base end end + module NamedExtension2 + def greeting + "hello" + end + end + scope :containing_the_letter_a, -> { where("body LIKE '%a%'") } scope :ranked_by_comments, -> { order("comments_count DESC") } @@ -43,6 +49,14 @@ class Post < ActiveRecord::Base end end + has_many :comments_with_extend, extend: NamedExtension, class_name: "Comment", foreign_key: "post_id" do + def greeting + "hello" + end + end + + has_many :comments_with_extend_2, extend: [NamedExtension, NamedExtension2], class_name: "Comment", foreign_key: "post_id" + has_many :author_favorites, :through => :author has_many :author_categorizations, :through => :author, :source => :categorizations has_many :author_addresses, :through => :author diff --git a/guides/source/action_mailer_basics.md b/guides/source/action_mailer_basics.md index 313104e5a6..590ad5738e 100644 --- a/guides/source/action_mailer_basics.md +++ b/guides/source/action_mailer_basics.md @@ -588,7 +588,7 @@ class SandboxEmailInterceptor end ``` -Before the interceptor can do it's job you need to register it with the Action Mailer framework. You can do this in an initializer file `config/initializers/sandbox_email_interceptor.rb` +Before the interceptor can do its job you need to register it with the Action Mailer framework. You can do this in an initializer file `config/initializers/sandbox_email_interceptor.rb` ```ruby ActionMailer::Base.register_interceptor(SandboxEmailInterceptor) if Rails.env.staging? diff --git a/guides/source/command_line.md b/guides/source/command_line.md index 0081e6427e..2790a4740a 100644 --- a/guides/source/command_line.md +++ b/guides/source/command_line.md @@ -442,7 +442,7 @@ app/model/post.rb: NOTE. When using specific annotations and custom annotations, the annotation name (FIXME, BUG etc) is not displayed in the output lines. -By default, `rake notes` will look in the `app`, `config`, `lib`, `script` and `test` directories. If you would like to search other directories, you can provide them as a comma separated list in an environment variable `SOURCE_ANNOTATION_DIRECTORIES`. +By default, `rake notes` will look in the `app`, `config`, `lib`, `bin` and `test` directories. If you would like to search other directories, you can provide them as a comma separated list in an environment variable `SOURCE_ANNOTATION_DIRECTORIES`. ```bash $ export SOURCE_ANNOTATION_DIRECTORIES='rspec,vendor' diff --git a/railties/lib/rails/info.rb b/railties/lib/rails/info.rb index aacc1be2fc..592e74726e 100644 --- a/railties/lib/rails/info.rb +++ b/railties/lib/rails/info.rb @@ -46,7 +46,7 @@ module Rails alias inspect to_s def to_html - (table = '<table>').tap do + '<table>'.tap do |table| properties.each do |(name, value)| table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>) formatted_value = if value.kind_of?(Array) diff --git a/railties/test/application/runner_test.rb b/railties/test/application/runner_test.rb index f65b5e2f2d..6595c40f8b 100644 --- a/railties/test/application/runner_test.rb +++ b/railties/test/application/runner_test.rb @@ -37,27 +37,27 @@ module ApplicationTests end def test_should_run_file - app_file "script/count_users.rb", <<-SCRIPT + app_file "bin/count_users.rb", <<-SCRIPT puts User.count SCRIPT - assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "script/count_users.rb"` } + assert_match "42", Dir.chdir(app_path) { `bundle exec rails runner "bin/count_users.rb"` } end def test_should_set_dollar_0_to_file - app_file "script/dollar0.rb", <<-SCRIPT + app_file "bin/dollar0.rb", <<-SCRIPT puts $0 SCRIPT - assert_match "script/dollar0.rb", Dir.chdir(app_path) { `bundle exec rails runner "script/dollar0.rb"` } + assert_match "bin/dollar0.rb", Dir.chdir(app_path) { `bundle exec rails runner "bin/dollar0.rb"` } end def test_should_set_dollar_program_name_to_file - app_file "script/program_name.rb", <<-SCRIPT + app_file "bin/program_name.rb", <<-SCRIPT puts $PROGRAM_NAME SCRIPT - assert_match "script/program_name.rb", Dir.chdir(app_path) { `bundle exec rails runner "script/program_name.rb"` } + assert_match "bin/program_name.rb", Dir.chdir(app_path) { `bundle exec rails runner "bin/program_name.rb"` } end def test_with_hook |