diff options
50 files changed, 291 insertions, 113 deletions
@@ -118,7 +118,7 @@ platforms :ruby, :mswin, :mswin64, :mingw, :x64_mingw do gem "racc", ">=1.4.6", require: false # Active Record. - gem "sqlite3", "~> 1.3.6" + gem "sqlite3", "~> 1.3", ">= 1.3.6" group :db do gem "pg", ">= 0.18.0" diff --git a/Gemfile.lock b/Gemfile.lock index 996e9d5942..b05ae7915e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -577,7 +577,7 @@ DEPENDENCIES sidekiq sneakers sprockets-export - sqlite3 (~> 1.3.6) + sqlite3 (~> 1.3, >= 1.3.6) stackprof sucker_punch turbolinks (~> 5) diff --git a/actioncable/Rakefile b/actioncable/Rakefile index 35de50f05a..121037844d 100644 --- a/actioncable/Rakefile +++ b/actioncable/Rakefile @@ -1,5 +1,6 @@ # frozen_string_literal: true +require "base64" require "rake/testtask" require "pathname" require "open3" @@ -25,7 +26,8 @@ namespace :test do end task :integration do - system("yarn test") || raise("Failures") + system(Hash[*Base64.decode64(ENV.fetch("ENCODED", "")).split(/[ =]/)], "yarn", "test") + exit($?.exitstatus) unless $?.success? end end diff --git a/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt b/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt index 33baaa5a22..ddf6b2d79b 100644 --- a/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt +++ b/actioncable/lib/rails/generators/channel/templates/javascript/channel.js.tt @@ -1,15 +1,15 @@ import consumer from "./consumer" consumer.subscriptions.create("<%= class_name %>Channel", { - connected: function() { + connected() { // Called when the subscription is ready for use on the server }, - disconnected: function() { + disconnected() { // Called when the subscription has been terminated by the server }, - received: function(data) { + received(data) { // Called when there's incoming data on the websocket for this channel }<%= actions.any? ? ",\n" : '' %> <% actions.each do |action| -%> diff --git a/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt b/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt index eec7e54b8a..0eceb59b18 100644 --- a/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt +++ b/actioncable/lib/rails/generators/channel/templates/javascript/consumer.js.tt @@ -1,6 +1,6 @@ // Action Cable provides the framework to deal with WebSockets in Rails. // You can generate new channels where WebSocket features live using the `rails generate channel` command. -import ActionCable from "@rails/actioncable" +import { createConsumer } from "@rails/actioncable" -export default ActionCable.createConsumer() +export default createConsumer() diff --git a/actioncable/test/subscription_adapter/redis_test.rb b/actioncable/test/subscription_adapter/redis_test.rb index ac2d8ef724..35840a4036 100644 --- a/actioncable/test/subscription_adapter/redis_test.rb +++ b/actioncable/test/subscription_adapter/redis_test.rb @@ -11,7 +11,11 @@ class RedisAdapterTest < ActionCable::TestCase include ChannelPrefixTest def cable_config - { adapter: "redis", driver: "ruby" } + { adapter: "redis", driver: "ruby" }.tap do |x| + if host = URI(ENV["REDIS_URL"] || "").hostname + x[:host] = host + end + end end end @@ -25,7 +29,7 @@ class RedisAdapterTest::AlternateConfiguration < RedisAdapterTest def cable_config alt_cable_config = super.dup alt_cable_config.delete(:url) - alt_cable_config.merge(host: "127.0.0.1", port: 6379, db: 12) + alt_cable_config.merge(host: URI(ENV["REDIS_URL"] || "").hostname || "127.0.0.1", port: 6379, db: 12) end end diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 6348bef4cb..23ac619f50 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,3 +1,7 @@ +* Make debug exceptions works in an environment where ActiveStorage is not loaded. + + *Tomoyuki Kurosawa* + * `ActionDispatch::SystemTestCase.driven_by` can now be called with a block to define specific browser capabilities. diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb index e8454acfad..d144fc1cd2 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb @@ -10,7 +10,7 @@ <div id="container"> <h2> <%= h @exception.message %> - <% if @exception.message.match? %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}} %> + <% if defined?(ActiveStorage) && @exception.message.match?(%r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}) %> <br />To resolve this issue run: rails active_storage:install <% end %> </h2> diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb index e5e3196710..55aaf58713 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb @@ -4,7 +4,7 @@ <% end %> <%= @exception.message %> -<% if @exception.message.match? %r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}} %> +<% if defined?(ActiveStorage) && @exception.message.match?(%r{#{ActiveStorage::Blob.table_name}|#{ActiveStorage::Attachment.table_name}}) %> To resolve this issue run: rails active_storage:install <% end %> diff --git a/actionpack/test/dispatch/session/mem_cache_store_test.rb b/actionpack/test/dispatch/session/mem_cache_store_test.rb index 9b51ee1cad..ac685a7dca 100644 --- a/actionpack/test/dispatch/session/mem_cache_store_test.rb +++ b/actionpack/test/dispatch/session/mem_cache_store_test.rb @@ -38,8 +38,9 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest begin require "dalli" - ss = Dalli::Client.new("localhost:11211").stats - raise Dalli::DalliError unless ss["localhost:11211"] + servers = ENV["MEMCACHE_SERVERS"] || "localhost:11211" + ss = Dalli::Client.new(servers).stats + raise Dalli::DalliError unless ss[servers] def test_setting_and_getting_session_value with_test_route_set do @@ -195,7 +196,9 @@ class MemCacheStoreTest < ActionDispatch::IntegrationTest end @app = self.class.build_app(set) do |middleware| - middleware.use ActionDispatch::Session::MemCacheStore, key: "_session_id", namespace: "mem_cache_store_test:#{SecureRandom.hex(10)}" + middleware.use ActionDispatch::Session::MemCacheStore, + key: "_session_id", namespace: "mem_cache_store_test:#{SecureRandom.hex(10)}", + memcache_server: ENV["MEMCACHE_SERVERS"] || "localhost:11211" middleware.delete ActionDispatch::ShowExceptions end diff --git a/actionview/CHANGELOG.md b/actionview/CHANGELOG.md index e2cd9f7558..5e17e65bde 100644 --- a/actionview/CHANGELOG.md +++ b/actionview/CHANGELOG.md @@ -1,3 +1,9 @@ +* Ensure unique DOM IDs for collection inputs with float values. + Fixes #34974 + + *Mark Edmondson* + + ## Rails 6.0.0.beta1 (January 18, 2019) ## * [Rename npm package](https://github.com/rails/rails/pull/34905) from diff --git a/actionview/Rakefile b/actionview/Rakefile index 7851a2b6bf..237e458b6f 100644 --- a/actionview/Rakefile +++ b/actionview/Rakefile @@ -31,9 +31,26 @@ namespace :test do desc "Run tests for rails-ujs" task :ujs do + system("npm run lint") + exit $?.exitstatus unless $?.success? + begin + listen_host = "localhost" + listen_port = "4567" + + runner_command = %w(ruby ../ci/qunit-selenium-runner.rb) + if ENV["SELENIUM_DRIVER_URL"] + require "socket" + runner_command += %W(http://#{Socket.gethostname}:#{listen_port}/ #{ENV["SELENIUM_DRIVER_URL"]}) + listen_host = "0.0.0.0" + else + runner_command += %W(http://localhost:#{listen_port}/) + end + Dir.mkdir("log") - pid = spawn("bundle exec rackup test/ujs/config.ru -p 4567 -s puma > log/test.log 2>&1", pgroup: true) + pid = File.open("log/test.log", "w") do |f| + spawn(*%W(rackup test/ujs/config.ru -o #{listen_host} -p #{listen_port} -s puma), out: f, err: f, pgroup: true) + end start_time = Time.now @@ -41,12 +58,16 @@ namespace :test do break if system("lsof -i :4567", 1 => File::NULL) if Time.now - start_time > 5 - puts "Timed out after 5 seconds" + puts "Failed to start puma after 5 seconds" + puts + puts File.read("log/test.log") exit 1 end + + sleep 0.2 end - system("npm run lint && bundle exec ruby ../ci/qunit-selenium-runner.rb http://localhost:4567/") + system(*runner_command) status = $?.exitstatus ensure Process.kill("KILL", -pid) if pid diff --git a/actionview/lib/action_view/helpers/tags/base.rb b/actionview/lib/action_view/helpers/tags/base.rb index eef527d36f..0adecf362a 100644 --- a/actionview/lib/action_view/helpers/tags/base.rb +++ b/actionview/lib/action_view/helpers/tags/base.rb @@ -138,7 +138,7 @@ module ActionView end def sanitized_value(value) - value.to_s.gsub(/\s/, "_").gsub(/[^-[[:word:]]]/, "").mb_chars.downcase.to_s + value.to_s.gsub(/[\s\.]/, "_").gsub(/[^-[[:word:]]]/, "").mb_chars.downcase.to_s end def select_content_tag(option_tags, options, html_options) diff --git a/actionview/lib/action_view/helpers/translation_helper.rb b/actionview/lib/action_view/helpers/translation_helper.rb index ae1c93e12f..67c86592b9 100644 --- a/actionview/lib/action_view/helpers/translation_helper.rb +++ b/actionview/lib/action_view/helpers/translation_helper.rb @@ -138,7 +138,7 @@ module ActionView end def html_safe_translation_key?(key) - /(\b|_|\.)html$/.match?(key.to_s) + /([_.]|\b)html\z/.match?(key.to_s) end end end diff --git a/actionview/test/template/form_collections_helper_test.rb b/actionview/test/template/form_collections_helper_test.rb index 6db55a1447..ca117d4a30 100644 --- a/actionview/test/template/form_collections_helper_test.rb +++ b/actionview/test/template/form_collections_helper_test.rb @@ -48,8 +48,16 @@ class FormCollectionsHelperTest < ActionView::TestCase test "collection radio should sanitize collection values for labels correctly" do with_collection_radio_buttons :user, :name, ["$0.99", "$1.99"], :to_s, :to_s - assert_select "label[for=user_name_099]", "$0.99" - assert_select "label[for=user_name_199]", "$1.99" + assert_select "label[for=user_name_0_99]", "$0.99" + assert_select "label[for=user_name_1_99]", "$1.99" + end + + test "collection radio correctly builds unique DOM IDs for float values" do + with_collection_radio_buttons :user, :name, [1.0, 10], :to_s, :to_s + assert_select "label[for=user_name_1_0]", "1.0" + assert_select "label[for=user_name_10]", "10" + assert_select 'input#user_name_1_0[type=radio][value="1.0"]' + assert_select 'input#user_name_10[type=radio][value="10"]' end test "collection radio accepts checked item" do @@ -302,8 +310,16 @@ class FormCollectionsHelperTest < ActionView::TestCase test "collection check box should sanitize collection values for labels correctly" do with_collection_check_boxes :user, :name, ["$0.99", "$1.99"], :to_s, :to_s - assert_select "label[for=user_name_099]", "$0.99" - assert_select "label[for=user_name_199]", "$1.99" + assert_select "label[for=user_name_0_99]", "$0.99" + assert_select "label[for=user_name_1_99]", "$1.99" + end + + test "collection check boxes correctly builds unique DOM IDs for float values" do + with_collection_check_boxes :user, :name, [1.0, 10], :to_s, :to_s + assert_select "label[for=user_name_1_0]", "1.0" + assert_select "label[for=user_name_10]", "10" + assert_select 'input#user_name_1_0[type=checkbox][value="1.0"]' + assert_select 'input#user_name_10[type=checkbox][value="10"]' end test "collection check boxes generates labels for non-English values correctly" do diff --git a/actionview/test/ujs/server.rb b/actionview/test/ujs/server.rb index 56f436c8b8..d7a6271587 100644 --- a/actionview/test/ujs/server.rb +++ b/actionview/test/ujs/server.rb @@ -23,6 +23,7 @@ module UJS config.public_file_server.enabled = true config.logger = Logger.new(STDOUT) config.log_level = :error + config.hosts << proc { true } config.content_security_policy do |policy| policy.default_src :self, :https diff --git a/activejob/test/support/integration/adapters/backburner.rb b/activejob/test/support/integration/adapters/backburner.rb index 1163ae8178..0c248dda01 100644 --- a/activejob/test/support/integration/adapters/backburner.rb +++ b/activejob/test/support/integration/adapters/backburner.rb @@ -4,6 +4,7 @@ module BackburnerJobsManager def setup ActiveJob::Base.queue_adapter = :backburner Backburner.configure do |config| + config.beanstalk_url = ENV["BEANSTALK_URL"] if ENV["BEANSTALK_URL"] config.logger = Rails.logger end unless can_run? diff --git a/activejob/test/support/integration/adapters/resque.rb b/activejob/test/support/integration/adapters/resque.rb index 2ed8302277..cd129e72b2 100644 --- a/activejob/test/support/integration/adapters/resque.rb +++ b/activejob/test/support/integration/adapters/resque.rb @@ -3,7 +3,7 @@ module ResqueJobsManager def setup ActiveJob::Base.queue_adapter = :resque - Resque.redis = Redis::Namespace.new "active_jobs_int_test", redis: Redis.new(url: "redis://127.0.0.1:6379/12", thread_safe: true) + Resque.redis = Redis::Namespace.new "active_jobs_int_test", redis: Redis.new(url: ENV["REDIS_URL"] || "redis://127.0.0.1:6379/12", thread_safe: true) Resque.logger = Rails.logger unless can_run? puts "Cannot run integration tests for resque. To be able to run integration tests for resque you need to install and start redis.\n" diff --git a/activejob/test/support/integration/adapters/sneakers.rb b/activejob/test/support/integration/adapters/sneakers.rb index eb8d4cc2d5..89dc61ca28 100644 --- a/activejob/test/support/integration/adapters/sneakers.rb +++ b/activejob/test/support/integration/adapters/sneakers.rb @@ -7,7 +7,7 @@ module SneakersJobsManager def setup ActiveJob::Base.queue_adapter = :sneakers Sneakers.configure heartbeat: 2, - amqp: "amqp://guest:guest@localhost:5672", + amqp: ENV["RABBITMQ_URL"] || "amqp://guest:guest@localhost:5672", vhost: "/", exchange: "active_jobs_sneakers_int_test", exchange_type: :direct, diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index 654caafc92..c412646cc9 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -1,3 +1,9 @@ +* Chaining named scope is no longer leaking to class level querying methods. + + Fixes #14003. + + *Ryuta Kamizono* + * Allow applications to automatically switch connections. Adds a middleware and configuration options that can be used in your diff --git a/activerecord/Rakefile b/activerecord/Rakefile index 013e81c959..9824787658 100644 --- a/activerecord/Rakefile +++ b/activerecord/Rakefile @@ -89,18 +89,23 @@ end namespace :db do namespace :mysql do + connection_arguments = lambda do |connection_name| + config = ARTest.config["connections"]["mysql2"][connection_name] + ["--user=#{config["username"]}", "--password=#{config["password"]}", ("--host=#{config["host"]}" if config["host"])].join(" ") + end + desc "Build the MySQL test databases" task :build do config = ARTest.config["connections"]["mysql2"] - %x( mysql --user=#{config["arunit"]["username"]} --password=#{config["arunit"]["password"]} -e "create DATABASE #{config["arunit"]["database"]} DEFAULT CHARACTER SET utf8mb4" ) - %x( mysql --user=#{config["arunit2"]["username"]} --password=#{config["arunit2"]["password"]} -e "create DATABASE #{config["arunit2"]["database"]} DEFAULT CHARACTER SET utf8mb4" ) + %x( mysql #{connection_arguments["arunit"]} -e "create DATABASE #{config["arunit"]["database"]} DEFAULT CHARACTER SET utf8mb4" ) + %x( mysql #{connection_arguments["arunit2"]} -e "create DATABASE #{config["arunit2"]["database"]} DEFAULT CHARACTER SET utf8mb4" ) end desc "Drop the MySQL test databases" task :drop do config = ARTest.config["connections"]["mysql2"] - %x( mysqladmin --user=#{config["arunit"]["username"]} --password=#{config["arunit"]["password"]} -f drop #{config["arunit"]["database"]} ) - %x( mysqladmin --user=#{config["arunit2"]["username"]} --password=#{config["arunit2"]["password"]} -f drop #{config["arunit2"]["database"]} ) + %x( mysqladmin #{connection_arguments["arunit"]} -f drop #{config["arunit"]["database"]} ) + %x( mysqladmin #{connection_arguments["arunit2"]} -f drop #{config["arunit2"]["database"]} ) end desc "Rebuild the MySQL test databases" diff --git a/activerecord/lib/active_record/associations/collection_association.rb b/activerecord/lib/active_record/associations/collection_association.rb index 4a25567c9d..6f5df807fe 100644 --- a/activerecord/lib/active_record/associations/collection_association.rb +++ b/activerecord/lib/active_record/associations/collection_association.rb @@ -210,7 +210,8 @@ module ActiveRecord # This method is abstract in the sense that it relies on # +count_records+, which is a method descendants have to provide. def size - if !find_target? || loaded? + if !find_target? + loaded! unless loaded? target.size elsif @association_ids @association_ids.size @@ -233,7 +234,7 @@ module ActiveRecord # loaded and you are going to fetch the records anyway it is better to # check <tt>collection.length.zero?</tt>. def empty? - if loaded? || @association_ids + if loaded? || @association_ids || reflection.has_cached_counter? size.zero? else target.empty? && !scope.exists? diff --git a/activerecord/lib/active_record/associations/has_many_association.rb b/activerecord/lib/active_record/associations/has_many_association.rb index eb22db838c..6f67934a79 100644 --- a/activerecord/lib/active_record/associations/has_many_association.rb +++ b/activerecord/lib/active_record/associations/has_many_association.rb @@ -36,14 +36,6 @@ module ActiveRecord super end - def empty? - if reflection.has_cached_counter? - size.zero? - else - super - end - end - private # Returns the number of records in this collection. @@ -60,20 +52,24 @@ module ActiveRecord # If the collection is empty the target is set to an empty array and # the loaded flag is set to true as well. def count_records - count = if reflection.has_cached_counter? - owner._read_attribute(reflection.counter_cache_column).to_i - else - scope.count(:all) - end + count = counter_cache_value || scope.count(:all) # If there's nothing in the database and @target has no new records # we are certain the current target is an empty array. This is a # documented side-effect of the method that may avoid an extra SELECT. - (@target ||= []) && loaded! if count == 0 + loaded! if count == 0 [association_scope.limit_value, count].compact.min end + def counter_cache_value + reflection.has_cached_counter? ? owner._read_attribute(reflection.counter_cache_column).to_i : nil + end + + def find_target? + super && !counter_cache_value&.zero? + end + def update_counter(difference, reflection = reflection()) if reflection.has_cached_counter? owner.increment!(reflection.counter_cache_column, difference) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb index 14dbd20bcd..7b3630662b 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite3_adapter.rb @@ -9,7 +9,7 @@ require "active_record/connection_adapters/sqlite3/schema_definitions" require "active_record/connection_adapters/sqlite3/schema_dumper" require "active_record/connection_adapters/sqlite3/schema_statements" -gem "sqlite3", "~> 1.3.6" +gem "sqlite3", "~> 1.3", ">= 1.3.6" require "sqlite3" module ActiveRecord diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index a863227276..bab00ef065 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -312,7 +312,7 @@ module ActiveRecord # Please check unscoped if you want to remove all previous scopes (including # the default_scope) during the execution of a block. def scoping - @delegate_to_klass ? yield : klass._scoping(self) { yield } + @delegate_to_klass && klass.current_scope ? yield : klass._scoping(self) { yield } end def _exec_scope(*args, &block) # :nodoc: diff --git a/activerecord/lib/active_record/relation/delegation.rb b/activerecord/lib/active_record/relation/delegation.rb index 6e8a1fcad4..f7c3b3783f 100644 --- a/activerecord/lib/active_record/relation/delegation.rb +++ b/activerecord/lib/active_record/relation/delegation.rb @@ -132,7 +132,7 @@ module ActiveRecord private def respond_to_missing?(method, _) - super || @klass.respond_to?(method) || arel.respond_to?(method) + super || @klass.respond_to?(method) end end end diff --git a/activerecord/lib/active_record/relation/spawn_methods.rb b/activerecord/lib/active_record/relation/spawn_methods.rb index 7874c4c35a..d758e289ca 100644 --- a/activerecord/lib/active_record/relation/spawn_methods.rb +++ b/activerecord/lib/active_record/relation/spawn_methods.rb @@ -8,7 +8,7 @@ module ActiveRecord module SpawnMethods # This is overridden by Associations::CollectionProxy def spawn #:nodoc: - @delegate_to_klass ? klass.all : clone + @delegate_to_klass && klass.current_scope ? klass.all : clone end # Merges in the conditions from <tt>other</tt>, if <tt>other</tt> is an ActiveRecord::Relation. diff --git a/activerecord/lib/active_record/scoping.rb b/activerecord/lib/active_record/scoping.rb index 9eba1254a4..c3a56b2174 100644 --- a/activerecord/lib/active_record/scoping.rb +++ b/activerecord/lib/active_record/scoping.rb @@ -23,11 +23,11 @@ module ActiveRecord current_scope end - private - def current_scope(skip_inherited_scope = false) - ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope) - end + def current_scope(skip_inherited_scope = false) + ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope) + end + private def current_scope=(scope) ScopeRegistry.set_value_for(:current_scope, self, scope) end @@ -84,7 +84,7 @@ module ActiveRecord base = model.base_class while klass <= base value = @registry[scope_type][klass.name] - return value if value + return value || nil unless value.nil? klass = klass.superclass end end diff --git a/activerecord/lib/active_record/scoping/named.rb b/activerecord/lib/active_record/scoping/named.rb index 5278eb29a9..987e6bd63f 100644 --- a/activerecord/lib/active_record/scoping/named.rb +++ b/activerecord/lib/active_record/scoping/named.rb @@ -180,7 +180,8 @@ module ActiveRecord if body.respond_to?(:to_proc) singleton_class.define_method(name) do |*args| - scope = all._exec_scope(*args, &body) + scope = all + scope = _scoping(false) { scope._exec_scope(*args, &body) } scope = scope.extending(extension) if extension scope end diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb index 2f090d9862..4c9e4d0ad2 100644 --- a/activerecord/test/cases/associations/has_many_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_associations_test.rb @@ -1224,12 +1224,15 @@ class HasManyAssociationsTest < ActiveRecord::TestCase def test_has_many_without_counter_cache_option # Ship has a conventionally named `treasures_count` column, but the counter_cache # option is not given on the association. - ship = Ship.create(name: "Countless", treasures_count: 10) + ship = Ship.create!(name: "Countless", treasures_count: 10) assert_not_predicate Ship.reflect_on_association(:treasures), :has_cached_counter? # Count should come from sql count() of treasures rather than treasures_count attribute - assert_equal ship.treasures.size, 0 + assert_queries(1) do + assert_equal ship.treasures.size, 0 + assert_predicate ship.treasures, :loaded? + end assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do ship.treasures.create(name: "Gold") @@ -1350,6 +1353,20 @@ class HasManyAssociationsTest < ActiveRecord::TestCase post = posts(:welcome) assert_no_queries do assert_not_empty post.comments + assert_equal 2, post.comments.size + assert_not_predicate post.comments, :loaded? + end + post = posts(:misc_by_bob) + assert_no_queries do + assert_empty post.comments + assert_predicate post.comments, :loaded? + end + end + + def test_empty_association_loading_with_counter_cache + post = posts(:misc_by_bob) + assert_no_queries do + assert_empty post.comments.to_a end end @@ -1986,9 +2003,22 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_not_predicate company.clients, :loaded? end - def test_counter_cache_on_unloaded_association - car = Car.create(name: "My AppliCar") - assert_equal car.engines.size, 0 + def test_zero_counter_cache_usage_on_unloaded_association + car = Car.create!(name: "My AppliCar") + assert_no_queries do + assert_equal car.engines.size, 0 + assert_predicate car.engines, :loaded? + end + end + + def test_counter_cache_on_new_record_unloaded_association + car = Car.new(name: "My AppliCar") + # Ensure no schema queries inside assertion + Engine.primary_key + assert_no_queries do + assert_equal car.engines.size, 0 + assert_predicate car.engines, :loaded? + end end def test_get_ids_ignores_include_option diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb index b600c999a6..63ae438de3 100644 --- a/activerecord/test/cases/relation/delegation_test.rb +++ b/activerecord/test/cases/relation/delegation_test.rb @@ -5,7 +5,7 @@ require "models/post" require "models/comment" module ActiveRecord - module ArrayDelegationTests + module DelegationTests ARRAY_DELEGATES = [ :+, :-, :|, :&, :[], :shuffle, :all?, :collect, :compact, :detect, :each, :each_cons, :each_with_index, @@ -21,10 +21,14 @@ module ActiveRecord assert_respond_to target, method end end + + def test_not_respond_to_arel_method + assert_not_respond_to target, :exists + end end class DelegationAssociationTest < ActiveRecord::TestCase - include ArrayDelegationTests + include DelegationTests def target Post.new.comments @@ -32,7 +36,7 @@ module ActiveRecord end class DelegationRelationTest < ActiveRecord::TestCase - include ArrayDelegationTests + include DelegationTests def target Comment.all diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index 756eeca35f..6b5b877260 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -480,21 +480,6 @@ class RelationTest < ActiveRecord::TestCase assert_nothing_raised { Topic.reorder([]) } end - def test_respond_to_delegates_to_arel - relation = Topic.all - fake_arel = Struct.new(:responds) { - def respond_to?(method, access = false) - responds << [method, access] - end - }.new [] - - relation.extend(Module.new { attr_accessor :arel }) - relation.arel = fake_arel - - relation.respond_to?(:matching_attributes) - assert_equal [:matching_attributes, false], fake_arel.responds.first - end - def test_respond_to_dynamic_finders relation = Topic.all diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index 418a2ae04e..27f9df295f 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -447,6 +447,16 @@ class NamedScopingTest < ActiveRecord::TestCase assert_equal [posts(:sti_comments)], Post.with_special_comments.with_post(4).to_a.uniq end + def test_chaining_doesnt_leak_conditions_to_another_scopes + expected = Topic.where(approved: false).where(id: Topic.children.select(:parent_id)) + assert_equal expected.to_a, Topic.rejected.has_children.to_a + end + + def test_nested_scoping + expected = Reply.approved + assert_equal expected.to_a, Topic.rejected.nested_scoping(expected) + end + def test_scopes_batch_finders assert_equal 4, Topic.approved.count diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml index 33962f9e5e..f5e3ac3c19 100644 --- a/activerecord/test/config.example.yml +++ b/activerecord/test/config.example.yml @@ -54,10 +54,16 @@ connections: username: rails encoding: utf8mb4 collation: utf8mb4_unicode_ci +<% if ENV['MYSQL_HOST'] %> + host: <%= ENV['MYSQL_HOST'] %> +<% end %> arunit2: username: rails encoding: utf8mb4 collation: utf8mb4_general_ci +<% if ENV['MYSQL_HOST'] %> + host: <%= ENV['MYSQL_HOST'] %> +<% end %> oracle: arunit: diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index a6a47687a2..fdb461ed7f 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -10,6 +10,9 @@ class Topic < ActiveRecord::Base scope :approved, -> { where(approved: true) } scope :rejected, -> { where(approved: false) } + scope :children, -> { where.not(parent_id: nil) } + scope :has_children, -> { where(id: Topic.children.select(:parent_id)) } + scope :scope_with_lambda, lambda { all } scope :by_lifo, -> { where(author_name: "lifo") } @@ -88,6 +91,10 @@ class Topic < ActiveRecord::Base write_attribute(:approved, val) end + def self.nested_scoping(scope) + scope.base + end + private def default_written_on diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 1e726ceb54..2da774ca66 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,4 +1,8 @@ -* Remove the `` Kernel#` `` override that suppresses ENOENT and accidentally returns nil on Unix systems +* Add `before_reset` callback to `CurrentAttributes` and define `after_reset` as an alias of `resets` for symmetry. + + *Rosa Gutierrez* + +* Remove the `` Kernel#` `` override that suppresses ENOENT and accidentally returns nil on Unix systems. *Akinori Musha* @@ -8,7 +12,6 @@ *Stefan Schüßler* - ## Rails 6.0.0.beta1 (January 18, 2019) ## * Remove deprecated `Module#reachable?` method. diff --git a/activesupport/lib/active_support/current_attributes.rb b/activesupport/lib/active_support/current_attributes.rb index 3145ff87a1..67ebe102d7 100644 --- a/activesupport/lib/active_support/current_attributes.rb +++ b/activesupport/lib/active_support/current_attributes.rb @@ -119,10 +119,16 @@ module ActiveSupport end end + # Calls this block before #reset is called on the instance. Used for resetting external collaborators that depend on current values. + def before_reset(&block) + set_callback :reset, :before, &block + end + # Calls this block after #reset is called on the instance. Used for resetting external collaborators, like Time.zone. def resets(&block) set_callback :reset, :after, &block end + alias_method :after_reset, :resets delegate :set, :reset, to: :instance diff --git a/activesupport/lib/active_support/encrypted_file.rb b/activesupport/lib/active_support/encrypted_file.rb index c66f1b557e..2b7db568a5 100644 --- a/activesupport/lib/active_support/encrypted_file.rb +++ b/activesupport/lib/active_support/encrypted_file.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "pathname" +require "tmpdir" require "active_support/message_encryptor" module ActiveSupport @@ -67,7 +68,7 @@ module ActiveSupport write(updated_contents) if updated_contents != contents ensure - FileUtils.rm(tmp_path) if tmp_path.exist? + FileUtils.rm(tmp_path) if tmp_path&.exist? end diff --git a/activesupport/test/cache/stores/redis_cache_store_test.rb b/activesupport/test/cache/stores/redis_cache_store_test.rb index 305a2c184d..273b196458 100644 --- a/activesupport/test/cache/stores/redis_cache_store_test.rb +++ b/activesupport/test/cache/stores/redis_cache_store_test.rb @@ -204,7 +204,7 @@ module ActiveSupport::Cache::RedisCacheStoreTests class RedisDistributedConnectionPoolBehaviourTest < ConnectionPoolBehaviourTest private def store_options - { url: %w[ redis://localhost:6379/0 redis://localhost:6379/0 ] } + { url: [ENV["REDIS_URL"] || "redis://localhost:6379/0"] * 2 } end end diff --git a/activesupport/test/current_attributes_test.rb b/activesupport/test/current_attributes_test.rb index 1669f08f68..adbdc646bc 100644 --- a/activesupport/test/current_attributes_test.rb +++ b/activesupport/test/current_attributes_test.rb @@ -3,13 +3,18 @@ require "abstract_unit" class CurrentAttributesTest < ActiveSupport::TestCase - Person = Struct.new(:name, :time_zone) + Person = Struct.new(:id, :name, :time_zone) class Current < ActiveSupport::CurrentAttributes attribute :world, :account, :person, :request delegate :time_zone, to: :person - resets { Time.zone = "UTC" } + before_reset { Session.previous = person.try(:id) } + + resets do + Time.zone = "UTC" + Session.current = nil + end def account=(account) super @@ -19,6 +24,7 @@ class CurrentAttributesTest < ActiveSupport::TestCase def person=(person) super Time.zone = person.try(:time_zone) + Session.current = person.try(:id) end def request @@ -30,9 +36,14 @@ class CurrentAttributesTest < ActiveSupport::TestCase end end + class Session < ActiveSupport::CurrentAttributes + attribute :current, :previous + end + setup do @original_time_zone = Time.zone Current.reset + Session.reset end teardown do @@ -56,16 +67,28 @@ class CurrentAttributesTest < ActiveSupport::TestCase end test "set auxiliary class via overwritten method" do - Current.person = Person.new("David", "Central Time (US & Canada)") + Current.person = Person.new(42, "David", "Central Time (US & Canada)") assert_equal "Central Time (US & Canada)", Time.zone.name + assert_equal 42, Session.current end - test "resets auxiliary class via callback" do - Current.person = Person.new("David", "Central Time (US & Canada)") + test "resets auxiliary classes via callback" do + Current.person = Person.new(42, "David", "Central Time (US & Canada)") assert_equal "Central Time (US & Canada)", Time.zone.name Current.reset assert_equal "UTC", Time.zone.name + assert_nil Session.current + end + + test "set auxiliary class based on current attributes via before callback" do + Current.person = Person.new(42, "David", "Central Time (US & Canada)") + assert_nil Session.previous + assert_equal 42, Session.current + + Current.reset + assert_equal 42, Session.previous + assert_nil Session.current end test "set attribute only via scope" do @@ -92,13 +115,13 @@ class CurrentAttributesTest < ActiveSupport::TestCase end test "delegation" do - Current.person = Person.new("David", "Central Time (US & Canada)") + Current.person = Person.new(42, "David", "Central Time (US & Canada)") assert_equal "Central Time (US & Canada)", Current.time_zone assert_equal "Central Time (US & Canada)", Current.instance.time_zone end test "all methods forward to the instance" do - Current.person = Person.new("David", "Central Time (US & Canada)") + Current.person = Person.new(42, "David", "Central Time (US & Canada)") assert_equal "David, in Central Time (US & Canada)", Current.intro assert_equal "David, in Central Time (US & Canada)", Current.instance.intro end diff --git a/ci/qunit-selenium-runner.rb b/ci/qunit-selenium-runner.rb index 132b3d17eb..1df6aedb36 100644 --- a/ci/qunit-selenium-runner.rb +++ b/ci/qunit-selenium-runner.rb @@ -1,14 +1,20 @@ # frozen_string_literal: true require "qunit/selenium/test_runner" -require "chromedriver-helper" -driver_options = Selenium::WebDriver::Chrome::Options.new -driver_options.add_argument("--headless") -driver_options.add_argument("--disable-gpu") -driver_options.add_argument("--no-sandbox") +if ARGV[1] + driver = ::Selenium::WebDriver.for(:remote, url: ARGV[1], desired_capabilities: :chrome) +else + require "chromedriver-helper" + + driver_options = Selenium::WebDriver::Chrome::Options.new + driver_options.add_argument("--headless") + driver_options.add_argument("--disable-gpu") + driver_options.add_argument("--no-sandbox") + + driver = ::Selenium::WebDriver.for(:chrome, options: driver_options) +end -driver = ::Selenium::WebDriver.for(:chrome, options: driver_options) result = QUnit::Selenium::TestRunner.new(driver).open(ARGV[0], timeout: 60) driver.quit diff --git a/guides/Rakefile b/guides/Rakefile index 4116e6f9cc..b7425f6de4 100644 --- a/guides/Rakefile +++ b/guides/Rakefile @@ -88,4 +88,15 @@ HELP end end +task :test do + templates = Dir.glob("bug_report_templates/*.rb") + counter = templates.count do |file| + puts "--- Running #{file}" + Bundler.clean_system(Gem.ruby, "-w", file) || + puts("+++ 💥 FAILED (exit #{$?.exitstatus})") + end + puts "+++ #{counter} / #{templates.size} templates executed successfully" + exit 1 if counter < templates.size +end + task default: "guides:help" diff --git a/guides/bug_report_templates/active_record_gem.rb b/guides/bug_report_templates/active_record_gem.rb index d88304a219..74b329115d 100644 --- a/guides/bug_report_templates/active_record_gem.rb +++ b/guides/bug_report_templates/active_record_gem.rb @@ -9,7 +9,7 @@ gemfile(true) do # Activate the gem you are reporting the issue against. gem "activerecord", "5.2.0" - gem "sqlite3" + gem "sqlite3", "~> 1.3.6" end require "active_record" diff --git a/guides/bug_report_templates/active_record_migrations_gem.rb b/guides/bug_report_templates/active_record_migrations_gem.rb index 5dfd49fb38..8d71863034 100644 --- a/guides/bug_report_templates/active_record_migrations_gem.rb +++ b/guides/bug_report_templates/active_record_migrations_gem.rb @@ -9,7 +9,7 @@ gemfile(true) do # Activate the gem you are reporting the issue against. gem "activerecord", "5.2.0" - gem "sqlite3" + gem "sqlite3", "~> 1.3.6" end require "active_record" diff --git a/railties/Rakefile b/railties/Rakefile index 445f6217b3..1c4725e2d6 100644 --- a/railties/Rakefile +++ b/railties/Rakefile @@ -36,10 +36,20 @@ namespace :test do dirs = (ENV["TEST_DIR"] || ENV["TEST_DIRS"] || "**").split(",") test_options = ENV["TESTOPTS"].to_s.split(/[\s]+/) - test_files = dirs.map { |dir| "test/#{dir}/*_test.rb" } - Dir[*test_files].each do |file| - next true if file.start_with?("test/fixtures/") + test_patterns = dirs.map { |dir| "test/#{dir}/*_test.rb" } + test_files = Dir[*test_patterns].select do |file| + !file.start_with?("test/fixtures/") + end.sort + if ENV["BUILDKITE_PARALLEL_JOB_COUNT"] + n = ENV["BUILDKITE_PARALLEL_JOB"].to_i + m = ENV["BUILDKITE_PARALLEL_JOB_COUNT"].to_i + + test_files = test_files.each_slice(m).map { |slice| slice[n] }.compact + end + + test_files.each do |file| + puts "--- #{file}" fake_command = Shellwords.join([ FileUtils::RUBY, "-w", @@ -59,10 +69,13 @@ namespace :test do unless $?.success? failing_files << file + puts "^^^ +++" end end + puts "--- All tests completed" unless failing_files.empty? + puts "^^^ +++" puts puts "Failed in:" failing_files.each do |file| @@ -70,7 +83,7 @@ namespace :test do end puts - raise "Failure in isolated test runner" + exit 1 end end end diff --git a/railties/lib/rails/generators/database.rb b/railties/lib/rails/generators/database.rb index be3e61bde8..18fc7be3ff 100644 --- a/railties/lib/rails/generators/database.rb +++ b/railties/lib/rails/generators/database.rb @@ -15,6 +15,7 @@ module Rails case database when "mysql" then ["mysql2", [">= 0.4.4"]] when "postgresql" then ["pg", [">= 0.18", "< 2.0"]] + when "sqlite3" then ["sqlite3", ["~> 1.3", ">= 1.3.6"]] when "oracle" then ["activerecord-oracle_enhanced-adapter", nil] when "frontbase" then ["ruby-frontbase", nil] when "sqlserver" then ["activerecord-sqlserver-adapter", nil] diff --git a/railties/test/application/rake/dbs_test.rb b/railties/test/application/rake/dbs_test.rb index 84ac2f057e..5879949b61 100644 --- a/railties/test/application/rake/dbs_test.rb +++ b/railties/test/application/rake/dbs_test.rb @@ -93,6 +93,8 @@ module ApplicationTests def with_bad_permissions Dir.chdir(app_path) do + skip "Can't avoid permissions as root" if Process.uid.zero? + set_database_url FileUtils.chmod("-w", "db") yield diff --git a/railties/test/application/server_test.rb b/railties/test/application/server_test.rb index f4bd09903a..5b8aab08d5 100644 --- a/railties/test/application/server_test.rb +++ b/railties/test/application/server_test.rb @@ -29,16 +29,18 @@ module ApplicationTests primary, replica = PTY.open pid = nil - begin - pid = Process.spawn("#{app_path}/bin/rails server -P tmp/dummy.pid", in: replica, out: replica, err: replica) - assert_output("Listening", primary) - - rails("restart") - - assert_output("Restarting", primary) - assert_output("Inherited", primary) - ensure - kill(pid) if pid + Bundler.with_original_env do + begin + pid = Process.spawn("bin/rails server -P tmp/dummy.pid", chdir: app_path, in: replica, out: replica, err: replica) + assert_output("Listening", primary) + + rails("restart") + + assert_output("Restarting", primary) + assert_output("Inherited", primary) + ensure + kill(pid) if pid + end end end diff --git a/railties/test/application/url_generation_test.rb b/railties/test/application/url_generation_test.rb index f22b9fda3d..6bcc0b0acb 100644 --- a/railties/test/application/url_generation_test.rb +++ b/railties/test/application/url_generation_test.rb @@ -19,6 +19,7 @@ module ApplicationTests config.session_store :cookie_store, key: "_myapp_session" config.active_support.deprecation = :log config.eager_load = false + config.hosts << proc { true } end Rails.application.initialize! diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 860e2bf63e..1ee9e43e89 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -526,7 +526,7 @@ class AppGeneratorTest < Rails::Generators::TestCase if defined?(JRUBY_VERSION) assert_gem "activerecord-jdbcsqlite3-adapter" else - assert_gem "sqlite3" + assert_gem "sqlite3", "'~> 1.3', '>= 1.3.6'" end end |